]> git.pond.sub.org Git - empserver/blob - src/lib/subs/retreat.c
retreat: Don't charge mobility for retreating in direction 'h'
[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 i;
133     int m;
134     int max;
135     int dir;
136     coord newx;
137     coord newy;
138     coord dx;
139     coord dy;
140     int mines;
141     int shells;
142     double mobcost;
143     struct mchrstr *mcp;
144     int changed;
145
146     if (opt_SAIL) {
147         /* can't retreat a ship that's sailin, bad things happend */
148         if (*sp->shp_path) {
149             wu(0, sp->shp_own,
150                "%s %s,\nbut had sailing orders, and couldn't retreat!\n",
151                prship(sp), conditions[findcondition(code)].desc[orig]);
152             return 0;
153         }
154     }
155     /* check crew - uws don't count */
156     if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
157         wu(0, sp->shp_own,
158            "%s %s,\nbut had no crew, and couldn't retreat!\n", prship(sp),
159            conditions[findcondition(code)].desc[orig]);
160         return 0;
161     }
162
163     getsect(sp->shp_x, sp->shp_y, &sect);
164     switch (shp_check_nav(sp, &sect)) {
165     case NAV_02:
166     case NAV_60:
167         wu(0, sp->shp_own,
168            "%s %s,\nbut was caught in a construction zone, and couldn't retreat!\n",
169            prship(sp), conditions[findcondition(code)].desc[orig]);
170         return 0;
171     case NAV_NONE:
172     case NAV_CANAL:
173         wu(0, sp->shp_own,
174            "%s %s,\nbut was landlocked, and couldn't retreat!\n",
175            prship(sp), conditions[findcondition(code)].desc[orig]);
176         return 0;
177     case NAVOK:
178         break;
179     default:
180         CANT_REACH();
181         wu(0, sp->shp_own,
182            "%s %s,\nbut was subject to an empire error, and couldn't retreat!\n",
183            prship(sp), conditions[findcondition(code)].desc[orig]);
184         return 0;
185     }
186
187     if (sp->shp_mobil <= 0.0) {
188         wu(0, sp->shp_own,
189            "%s %s,\nbut had no mobility, and couldn't retreat!\n",
190            prship(sp), conditions[findcondition(code)].desc[orig]);
191         return 0;
192     }
193
194     for (i = 0; i < MAX_RETREAT && sp->shp_rpath[0]; i++) {
195         if (sp->shp_mobil <= 0.0) {
196             wu(0, sp->shp_own,
197                "%s %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
198                prship(sp), conditions[findcondition(code)].desc[orig]);
199             if (!orig)
200                 putship(sp->shp_uid, sp);
201             return 0;
202         }
203         dir = chkdir(sp->shp_rpath[0], DIR_STOP, DIR_LAST);
204         memmove(sp->shp_rpath, sp->shp_rpath+1, sizeof(sp->shp_rpath) - 1);
205         if (dir < 0)
206             continue;
207         if (dir == DIR_STOP)
208             break;
209         dx = diroff[dir][0];
210         dy = diroff[dir][1];
211
212         mcp = &mchr[(int)sp->shp_type];
213         newx = xnorm(sp->shp_x + dx);
214         newy = ynorm(sp->shp_y + dy);
215         mobcost = shp_mobcost(sp);
216
217         getsect(newx, newy, &sect);
218         if (shp_check_nav(sp, &sect) != NAVOK ||
219             (sect.sct_own
220              && relations_with(sect.sct_own, sp->shp_own) < FRIENDLY)) {
221             wu(0, sp->shp_own, "%s %s,\nbut could not retreat to %s!\n",
222                prship(sp), conditions[findcondition(code)].desc[orig],
223                xyas(newx, newy, sp->shp_own));
224             if (!orig)
225                 putship(sp->shp_uid, sp);
226             return 0;
227         }
228         sp->shp_x = newx;
229         sp->shp_y = newy;
230         sp->shp_mobil -= mobcost;
231         sp->shp_mission = 0;
232
233         mines = sect.sct_mines;
234         changed = 0;
235         if (sect.sct_type != SCT_WATER || mines <= 0)
236             continue;
237         if (mcp->m_flags & M_SWEEP) {
238             max = mcp->m_item[I_SHELL];
239             shells = sp->shp_item[I_SHELL];
240             for (m = 0; mines > 0 && m < 5; m++) {
241                 if (chance(0.66)) {
242                     mines--;
243                     shells = MIN(max, shells + 1);
244                     changed |= map_set(sp->shp_own, sp->shp_x, sp->shp_y,
245                                        'X', 0);
246                 }
247             }
248             if (sect.sct_mines != mines) {
249                 wu(0, sp->shp_own,
250                    "%s cleared %d mine%s in %s while retreating\n",
251                    prship(sp), sect.sct_mines - mines,
252                    splur(sect.sct_mines - mines),
253                    xyas(newx, newy, sp->shp_own));
254                 sect.sct_mines = mines;
255                 sp->shp_item[I_SHELL] = shells;
256                 putsect(&sect);
257             }
258             if (changed)
259                 writemap(sp->shp_own);
260         }
261         if (chance(DMINE_HITCHANCE(mines))) {
262             wu(0, sp->shp_own,
263                "%s %s,\nand hit a mine in %s while retreating!\n",
264                prship(sp), conditions[findcondition(code)].desc[orig],
265                xyas(newx, newy, sp->shp_own));
266             nreport(sp->shp_own, N_HIT_MINE, 0, 1);
267             m = MINE_DAMAGE();
268             shipdamage(sp, m);
269             mines--;
270             if (map_set(sp->shp_own, sp->shp_x, sp->shp_y, 'X', 0))
271                 writemap(sp->shp_own);
272             sect.sct_mines = mines;
273             putsect(&sect);
274             if (!orig)
275                 putship(sp->shp_uid, sp);
276             return 0;
277         }
278     }
279
280     if (orig) {
281         wu(0, sp->shp_own, "%s %s, and retreated to %s\n",
282            prship(sp), conditions[findcondition(code)].desc[orig],
283            xyas(sp->shp_x, sp->shp_y, sp->shp_own));
284     } else {
285         wu(0, sp->shp_own, "%s %s, and ended up at %s\n",
286            prship(sp),
287            conditions[findcondition(code)].desc[orig],
288            xyas(sp->shp_x, sp->shp_y, sp->shp_own));
289     }
290     if (!orig)
291         putship(sp->shp_uid, sp);
292     return 1;
293 }
294
295 static int
296 findcondition(char code)
297 {
298     int i;
299
300     for (i = 0; conditions[i].code && conditions[i].code != code; i++) ;
301     CANT_HAPPEN(!conditions[i].code);
302     return i;
303 }
304
305 int
306 check_retreat_and_do_landdamage(struct lndstr *lp, int dam)
307 {
308     if (dam <= 0)
309         return 0;
310
311     landdamage(lp, dam);
312     if (lp->lnd_rflags & RET_INJURED)
313         retreat_land(lp, 'i');
314
315     return 1;
316 }
317
318 void
319 retreat_land(struct lndstr *lp, char code)
320 {
321     struct nstr_item ni;
322     struct lndstr land;
323
324     if (lp->lnd_effic < LAND_MINEFF || CANT_HAPPEN(!lp->lnd_own))
325         return;
326     if (lp->lnd_own == player->cnum)
327         return;
328
329     retreat_land1(lp, code, 1);
330     if (lp->lnd_rpath[0] == 0)
331         lp->lnd_rflags = 0;
332
333     if (lp->lnd_rflags & RET_GROUP) {
334         snxtitem_group(&ni, EF_LAND, lp->lnd_army);
335         while (nxtitem(&ni, &land))
336             if (land.lnd_own == lp->lnd_own) {
337                 if (land.lnd_uid != lp->lnd_uid) {
338                     retreat_land1(&land, code, 0);
339                     getland(land.lnd_uid, &land);
340                     if (land.lnd_rpath[0] == 0) {
341                         land.lnd_rflags = 0;
342                         putland(land.lnd_uid, &land);
343                     }
344                 }
345             }
346     }
347 }
348
349 static int
350 retreat_land1(struct lndstr *lp, char code, int orig)
351
352
353                         /* Is this the originally scared unit, or a follower */
354 {
355     struct sctstr sect;
356     int i;
357     int m;
358     int max;
359     int dir;
360     coord newx;
361     coord newy;
362     coord dx;
363     coord dy;
364     int mines;
365     int shells;
366     double mobcost;
367     struct lchrstr *lcp;
368
369     getsect(lp->lnd_x, lp->lnd_y, &sect);
370
371     if (lp->lnd_mobil <= 0.0) {
372         wu(0, lp->lnd_own,
373            "%s %s,\nbut had no mobility, and couldn't retreat!\n",
374            prland(lp), conditions[findcondition(code)].desc[orig]);
375         return 0;
376     }
377
378     for (i = 0; i < MAX_RETREAT && lp->lnd_rpath[0]; i++) {
379         if (lp->lnd_mobil <= 0.0) {
380             wu(0, lp->lnd_own,
381                "%s %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
382                prland(lp), conditions[findcondition(code)].desc[orig]);
383             if (!orig)
384                 putland(lp->lnd_uid, lp);
385             return 0;
386         }
387         dir = chkdir(lp->lnd_rpath[0], DIR_STOP, DIR_LAST);
388         memmove(lp->lnd_rpath, lp->lnd_rpath+1, sizeof(lp->lnd_rpath) - 1);
389         if (dir < 0)
390             continue;
391         if (dir == DIR_STOP)
392             break;
393         dx = diroff[dir][0];
394         dy = diroff[dir][1];
395
396         lcp = &lchr[(int)lp->lnd_type];
397         newx = xnorm(lp->lnd_x + dx);
398         newy = ynorm(lp->lnd_y + dy);
399
400         getsect(newx, newy, &sect);
401         mobcost = lnd_mobcost(lp, &sect);
402         if (mobcost < 0
403             || sect.sct_type == SCT_MOUNT
404             || sect.sct_own != lp->lnd_own) {
405             wu(0, lp->lnd_own, "%s %s,\nbut could not retreat to %s!\n",
406                prland(lp),
407                conditions[findcondition(code)].desc[orig],
408                xyas(newx, newy, lp->lnd_own));
409             if (!orig)
410                 putland(lp->lnd_uid, lp);
411             return 0;
412         }
413         lp->lnd_x = newx;
414         lp->lnd_y = newy;
415         lp->lnd_mobil -= mobcost;
416         lp->lnd_mission = 0;
417
418         mines = SCT_LANDMINES(&sect);
419         if (mines <= 0 || sect.sct_oldown == lp->lnd_own)
420             continue;
421         if (lcp->l_flags & L_ENGINEER) {
422             max = lcp->l_item[I_SHELL];
423             shells = lp->lnd_item[I_SHELL];
424             for (m = 0; mines > 0 && m < 5; m++) {
425                 if (chance(0.66)) {
426                     mines--;
427                     shells = MIN(max, shells + 1);
428                 }
429             }
430             sect.sct_mines = mines;
431             lp->lnd_item[I_SHELL] = shells;
432             putsect(&sect);
433         }
434         if (chance(DMINE_LHITCHANCE(mines))) {
435             wu(0, lp->lnd_own,
436                "%s %s,\nand hit a mine in %s while retreating!\n",
437                prland(lp),
438                conditions[findcondition(code)].desc[orig],
439                xyas(newx, newy, lp->lnd_own));
440             nreport(lp->lnd_own, N_LHIT_MINE, 0, 1);
441             m = MINE_LDAMAGE();
442             if (lcp->l_flags & L_ENGINEER)
443                 m /= 2;
444             landdamage(lp, m);
445             mines--;
446             sect.sct_mines = mines;
447             putsect(&sect);
448             if (!orig)
449                 putland(lp->lnd_uid, lp);
450             return 0;
451         }
452     }
453
454     if (orig) {
455         wu(0, lp->lnd_own, "%s %s, and retreated to %s\n",
456            prland(lp),
457            conditions[findcondition(code)].desc[orig],
458            xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
459     } else {
460         wu(0, lp->lnd_own, "%s %s, and ended up at %s\n",
461            prland(lp),
462            conditions[findcondition(code)].desc[orig],
463            xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
464     }
465     if (!orig)
466         putland(lp->lnd_uid, lp);
467     return 1;
468 }