]> git.pond.sub.org Git - empserver/blob - src/lib/subs/retreat.c
Move declarations for chance.c to new chance.h
[empserver] / src / lib / subs / retreat.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  retreat.c: Retreat subroutines
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 2000
31  */
32
33 #include <config.h>
34
35 #include "chance.h"
36 #include "damage.h"
37 #include "file.h"
38 #include "land.h"
39 #include "map.h"
40 #include "misc.h"
41 #include "nat.h"
42 #include "news.h"
43 #include "nsc.h"
44 #include "optlist.h"
45 #include "path.h"
46 #include "prototypes.h"
47 #include "retreat.h"
48 #include "sect.h"
49 #include "ship.h"
50 #include "xy.h"
51
52 static int findcondition(char);
53 static int retreat_land1(struct lndstr *, char, int);
54 static int retreat_ship1(struct shpstr *, char, int);
55
56 struct ccode {
57     char code;
58     char *desc[2];
59 };
60
61 static struct ccode conditions[] = {
62     { 'i', { "retreated with a damaged friend",
63              "was damaged" } },
64     { 't', { "retreated with a torpedoed ship",
65              "was hit by a torpedo" } },
66     { 's', { "retreated with a ship scared by sonar",
67              "detected a sonar ping" } },
68     { 'h', { "retreated with a helpless ship",
69              "was fired upon with no one able to defend it" } },
70     { 'b', { "retreated with a bombed friend",
71              "was bombed" } },
72     { 'd', { "retreated with a depth-charged ship",
73              "was depth-charged" } },
74     { 'u', { "retreated with a boarded ship", "was boarded" } },
75     { 0,   { "panicked", "panicked"} }
76 };
77
78 int
79 check_retreat_and_do_shipdamage(struct shpstr *sp, int dam)
80 {
81     if (dam <= 0)
82         return 0;
83
84     shipdamage(sp, dam);
85     if (sp->shp_rflags & RET_INJURED)
86         retreat_ship(sp, 'i');
87
88     return 1;
89 }
90
91 void
92 retreat_ship(struct shpstr *sp, char code)
93 {
94     struct nstr_item ni;
95     struct shpstr ship;
96
97     if (sp->shp_rflags & RET_GROUP) {
98         snxtitem_group(&ni, EF_SHIP, sp->shp_fleet);
99         while (nxtitem(&ni, &ship))
100             if (ship.shp_own == sp->shp_own) {
101                 if (ship.shp_uid == sp->shp_uid) {
102                     retreat_ship1(sp, code, 1);
103                     if (sp->shp_rpath[0] == 0)
104                         sp->shp_rflags = 0;
105                 } else {
106                     retreat_ship1(&ship, code, 0);
107                     getship(ship.shp_uid, &ship);
108                     if (ship.shp_rpath[0] == 0) {
109                         ship.shp_rflags = 0;
110                         putship(ship.shp_uid, &ship);
111                     }
112                 }
113             }
114     } else {
115         retreat_ship1(sp, code, 1);
116         if (sp->shp_rpath[0] == 0)
117             sp->shp_rflags = 0;
118     }
119 }
120
121 static int
122 retreat_ship1(struct shpstr *sp, char code, int orig)
123
124
125                         /* Is this the originally scared ship, or a follower */
126 {
127     struct sctstr sect;
128     int n;
129     int m;
130     int max;
131     int dir;
132     coord newx;
133     coord newy;
134     coord dx;
135     coord dy;
136     int stopping;
137     int mines;
138     int shells;
139     double mobcost;
140     struct mchrstr *mcp;
141     int changed;
142
143     sp->shp_mission = 0;
144     if (sp->shp_own == 0)
145         return 0;
146
147     if (sp->shp_effic < SHIP_MINEFF) {
148         wu(0, sp->shp_own,
149            "%s %s,\nbut it died in the attack, and so couldn't retreat!\n",
150            prship(sp), conditions[findcondition(code)].desc[orig]);
151         if (!orig)
152             putship(sp->shp_uid, sp);
153         return 0;
154     }
155
156     if (opt_SAIL) {
157         /* can't retreat a ship that's sailin, bad things happend */
158         if (*sp->shp_path) {
159             wu(0, sp->shp_own,
160                "%s %s,\nbut had sailing orders, and couldn't retreat!\n",
161                prship(sp), conditions[findcondition(code)].desc[orig]);
162             if (!orig)
163                 putship(sp->shp_uid, sp);
164             return 0;
165         }
166     }
167     /* check crew - uws don't count */
168     if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
169         wu(0, sp->shp_own,
170            "%s %s,\nbut had no crew, and couldn't retreat!\n", prship(sp),
171            conditions[findcondition(code)].desc[orig]);
172         if (!orig)
173             putship(sp->shp_uid, sp);
174         return 0;
175     }
176
177     getsect(sp->shp_x, sp->shp_y, &sect);
178     switch (shp_check_nav(&sect, sp)) {
179     case CN_CONSTRUCTION:
180         wu(0, sp->shp_own,
181            "%s %s,\nbut was caught in a construction zone, and couldn't retreat!\n",
182            prship(sp), conditions[findcondition(code)].desc[orig]);
183         if (!orig)
184             putship(sp->shp_uid, sp);
185         return 0;
186     case CN_LANDLOCKED:
187         wu(0, sp->shp_own,
188            "%s %s,\nbut was landlocked, and couldn't retreat!\n",
189            prship(sp), conditions[findcondition(code)].desc[orig]);
190         if (!orig)
191             putship(sp->shp_uid, sp);
192         return 0;
193         /*NOTREACHED*/
194     case CN_NAVIGABLE:
195         break;
196     case CN_ERROR:
197     default:
198         wu(0, sp->shp_own,
199            "%s %s,\nbut was subject to an empire error, and couldn't retreat!\n",
200            prship(sp), conditions[findcondition(code)].desc[orig]);
201         if (!orig)
202             putship(sp->shp_uid, sp);
203         return 0;
204         /*NOTREACHED*/
205     }
206
207     if (sp->shp_mobil <= 0.0) {
208         wu(0, sp->shp_own,
209            "%s %s,\nbut had no mobility, and couldn't retreat!\n",
210            prship(sp), conditions[findcondition(code)].desc[orig]);
211         if (!orig)
212             putship(sp->shp_uid, sp);
213         return 0;
214     }
215
216     n = -MAX_RETREAT;
217     stopping = 0;
218     while (!stopping && n) {
219         dx = dy = 0;
220         if (sp->shp_rpath[0] == 0) {
221             stopping = 1;
222             continue;
223         }
224         if (sp->shp_mobil <= 0.0) {
225             wu(0, sp->shp_own,
226                "%s %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
227                prship(sp), conditions[findcondition(code)].desc[orig]);
228             if (!orig)
229                 putship(sp->shp_uid, sp);
230             return 0;
231         }
232         dir = chkdir(sp->shp_rpath[0], DIR_STOP, DIR_LAST);
233         memmove(sp->shp_rpath, sp->shp_rpath+1, sizeof(sp->shp_rpath) - 1);
234         if (dir < 0)
235             continue;
236         if (dir == DIR_STOP)
237             stopping++;
238         else {
239             dx = diroff[dir][0];
240             dy = diroff[dir][1];
241         }
242         n++;
243
244         mcp = &mchr[(int)sp->shp_type];
245         newx = xnorm(sp->shp_x + dx);
246         newy = ynorm(sp->shp_y + dy);
247         mobcost = shp_mobcost(sp);
248
249         getsect(newx, newy, &sect);
250         if (shp_check_nav(&sect, sp) != CN_NAVIGABLE ||
251             (sect.sct_own
252              && relations_with(sect.sct_own, sp->shp_own) < FRIENDLY)) {
253             wu(0, sp->shp_own, "%s %s,\nbut could not retreat to %s!\n",
254                prship(sp), conditions[findcondition(code)].desc[orig],
255                xyas(newx, newy, sp->shp_own));
256             if (!orig)
257                 putship(sp->shp_uid, sp);
258             return 0;
259         }
260         sp->shp_x = newx;
261         sp->shp_y = newy;
262         sp->shp_mobil -= mobcost;
263         if (stopping)
264             continue;
265
266         mines = sect.sct_mines;
267         changed = 0;
268         if (sect.sct_type != SCT_WATER || mines <= 0)
269             continue;
270         if (mcp->m_flags & M_SWEEP) {
271             max = mcp->m_item[I_SHELL];
272             shells = sp->shp_item[I_SHELL];
273             for (m = 0; mines > 0 && m < 5; m++) {
274                 if (chance(0.66)) {
275                     mines--;
276                     shells = MIN(max, shells + 1);
277                     changed |= map_set(sp->shp_own, sp->shp_x, sp->shp_y,
278                                        'X', 0);
279                 }
280             }
281             if (sect.sct_mines != mines) {
282                 wu(0, sp->shp_own,
283                    "%s cleared %d mine%s in %s while retreating\n",
284                    prship(sp), sect.sct_mines - mines,
285                    splur(sect.sct_mines - mines),
286                    xyas(newx, newy, sp->shp_own));
287                 sect.sct_mines = mines;
288                 sp->shp_item[I_SHELL] = shells;
289                 putsect(&sect);
290             }
291             if (changed)
292                 writemap(sp->shp_own);
293         }
294         if (chance(DMINE_HITCHANCE(mines))) {
295             wu(0, sp->shp_own,
296                "%s %s,\nand hit a mine in %s while retreating!\n",
297                prship(sp), conditions[findcondition(code)].desc[orig],
298                xyas(newx, newy, sp->shp_own));
299             nreport(sp->shp_own, N_HIT_MINE, 0, 1);
300             m = MINE_DAMAGE();
301             shipdamage(sp, m);
302             mines--;
303             if (map_set(sp->shp_own, sp->shp_x, sp->shp_y, 'X', 0))
304                 writemap(sp->shp_own);
305             sect.sct_mines = mines;
306             putsect(&sect);
307             if (!orig)
308                 putship(sp->shp_uid, sp);
309             return 0;
310         }
311     }
312
313     if (orig) {
314         wu(0, sp->shp_own, "%s %s, and retreated to %s\n",
315            prship(sp), conditions[findcondition(code)].desc[orig],
316            xyas(sp->shp_x, sp->shp_y, sp->shp_own));
317     } else {
318         wu(0, sp->shp_own, "%s %s, and ended up at %s\n",
319            prship(sp),
320            conditions[findcondition(code)].desc[orig],
321            xyas(sp->shp_x, sp->shp_y, sp->shp_own));
322     }
323     if (!orig)
324         putship(sp->shp_uid, sp);
325     return 1;
326 }
327
328 static int
329 findcondition(char code)
330 {
331     int i;
332
333     for (i = 0; conditions[i].code && conditions[i].code != code; i++) ;
334     CANT_HAPPEN(!conditions[i].code);
335     return i;
336 }
337
338 int
339 check_retreat_and_do_landdamage(struct lndstr *lp, int dam)
340 {
341     if (dam <= 0)
342         return 0;
343
344     landdamage(lp, dam);
345     if (lp->lnd_rflags & RET_INJURED)
346         retreat_land(lp, 'i');
347
348     return 1;
349 }
350
351 void
352 retreat_land(struct lndstr *lp, char code)
353 {
354     struct nstr_item ni;
355     struct lndstr land;
356
357     if (lp->lnd_rflags & RET_GROUP) {
358         snxtitem_group(&ni, EF_SHIP, lp->lnd_army);
359         while (nxtitem(&ni, &land))
360             if (land.lnd_own == lp->lnd_own) {
361                 if (land.lnd_uid == lp->lnd_uid) {
362                     retreat_land1(lp, code, 1);
363                     if (lp->lnd_rpath[0] == 0)
364                         lp->lnd_rflags = 0;
365                 } else {
366                     retreat_land1(&land, code, 0);
367                     getland(land.lnd_uid, &land);
368                     if (land.lnd_rpath[0] == 0) {
369                         land.lnd_rflags = 0;
370                         putland(land.lnd_uid, &land);
371                     }
372                 }
373             }
374     } else {
375         retreat_land1(lp, code, 1);
376         if (lp->lnd_rpath[0] == 0)
377             lp->lnd_rflags = 0;
378     }
379 }
380
381 static int
382 retreat_land1(struct lndstr *lp, char code, int orig)
383
384
385                         /* Is this the originally scared unit, or a follower */
386 {
387     struct sctstr sect;
388     int n;
389     int m;
390     int max;
391     int dir;
392     coord newx;
393     coord newy;
394     coord dx;
395     coord dy;
396     int stopping;
397     int mines;
398     int shells;
399     double mobcost;
400     struct lchrstr *lcp;
401
402     lp->lnd_mission = 0;
403     if (lp->lnd_own == 0)
404         return 0;
405
406     if (lp->lnd_effic < LAND_MINEFF) {
407         wu(0, lp->lnd_own,
408            "%s %s,\nbut it died in the attack, and so couldn't retreat!\n",
409            prland(lp), conditions[findcondition(code)].desc[orig]);
410         if (!orig)
411             putland(lp->lnd_uid, lp);
412         return 0;
413     }
414
415     getsect(lp->lnd_x, lp->lnd_y, &sect);
416
417     if (lp->lnd_mobil <= 0.0) {
418         wu(0, lp->lnd_own,
419            "%s %s,\nbut had no mobility, and couldn't retreat!\n",
420            prland(lp), conditions[findcondition(code)].desc[orig]);
421         if (!orig)
422             putland(lp->lnd_uid, lp);
423         return 0;
424     }
425
426     n = -MAX_RETREAT;
427     stopping = 0;
428     while (!stopping && n) {
429         dx = dy = 0;
430         if (lp->lnd_rpath[0] == 0) {
431             stopping = 1;
432             continue;
433         }
434         if (lp->lnd_mobil <= 0.0) {
435             wu(0, lp->lnd_own,
436                "%s %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
437                prland(lp), conditions[findcondition(code)].desc[orig]);
438             if (!orig)
439                 putland(lp->lnd_uid, lp);
440             return 0;
441         }
442         dir = chkdir(lp->lnd_rpath[0], DIR_STOP, DIR_LAST);
443         memmove(lp->lnd_rpath, lp->lnd_rpath+1, sizeof(lp->lnd_rpath) - 1);
444         if (dir < 0)
445             continue;
446         if (dir == DIR_STOP)
447             stopping++;
448         else {
449             dx = diroff[dir][0];
450             dy = diroff[dir][1];
451         }
452         n++;
453
454         lcp = &lchr[(int)lp->lnd_type];
455         newx = xnorm(lp->lnd_x + dx);
456         newy = ynorm(lp->lnd_y + dy);
457
458         getsect(newx, newy, &sect);
459         mobcost = lnd_mobcost(lp, &sect);
460         if (mobcost < 0
461             || sect.sct_type == SCT_MOUNT
462             || sect.sct_own != lp->lnd_own) {
463             wu(0, lp->lnd_own, "%s %s,\nbut could not retreat to %s!\n",
464                prland(lp),
465                conditions[findcondition(code)].desc[orig],
466                xyas(newx, newy, lp->lnd_own));
467             if (!orig)
468                 putland(lp->lnd_uid, lp);
469             return 0;
470         }
471         lp->lnd_x = newx;
472         lp->lnd_y = newy;
473         lp->lnd_mobil -= mobcost;
474         if (stopping)
475             continue;
476
477         mines = SCT_LANDMINES(&sect);
478         if (mines <= 0 || sect.sct_oldown == lp->lnd_own)
479             continue;
480         if (lcp->l_flags & L_ENGINEER) {
481             max = lcp->l_item[I_SHELL];
482             shells = lp->lnd_item[I_SHELL];
483             for (m = 0; mines > 0 && m < 5; m++) {
484                 if (chance(0.66)) {
485                     mines--;
486                     shells = MIN(max, shells + 1);
487                 }
488             }
489             sect.sct_mines = mines;
490             lp->lnd_item[I_SHELL] = shells;
491             putsect(&sect);
492         }
493         if (chance(DMINE_LHITCHANCE(mines))) {
494             wu(0, lp->lnd_own,
495                "%s %s,\nand hit a mine in %s while retreating!\n",
496                prland(lp),
497                conditions[findcondition(code)].desc[orig],
498                xyas(newx, newy, lp->lnd_own));
499             nreport(lp->lnd_own, N_LHIT_MINE, 0, 1);
500             m = MINE_LDAMAGE();
501             if (lcp->l_flags & L_ENGINEER)
502                 m /= 2;
503             landdamage(lp, m);
504             mines--;
505             sect.sct_mines = mines;
506             putsect(&sect);
507             if (!orig)
508                 putland(lp->lnd_uid, lp);
509             return 0;
510         }
511     }
512
513     if (orig) {
514         wu(0, lp->lnd_own, "%s %s, and retreated to %s\n",
515            prland(lp),
516            conditions[findcondition(code)].desc[orig],
517            xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
518     } else {
519         wu(0, lp->lnd_own, "%s %s, and ended up at %s\n",
520            prland(lp),
521            conditions[findcondition(code)].desc[orig],
522            xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
523     }
524     if (!orig)
525         putland(lp->lnd_uid, lp);
526     return 1;
527 }