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