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