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