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