]> git.pond.sub.org Git - empserver/blob - src/lib/subs/retreat.c
(marc, navi, retreat_ship1, retreat_land1): Treat any negative value
[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, &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     int changed;
149
150     sp->shp_mission = 0;
151     if (sp->shp_own == 0)
152         return 0;
153
154     if (isupper(code))
155         code = tolower(code);
156
157     n = 0;
158     if (sp->shp_effic < SHIP_MINEFF) {
159         wu(0, sp->shp_own,
160            "%s %s,\nbut it died in the attack, and so couldn't retreat!\n",
161            prship(sp), conditions[findcondition(code)].desc[orig]);
162         if (!orig)
163             putship(sp->shp_uid, sp);
164         return 0;
165     }
166
167     if (opt_SAIL) {
168         /* can't retreat a ship that's sailin, bad things happend */
169         if (*sp->shp_path) {
170             wu(0, sp->shp_own,
171                "%s %s,\nbut had sailing orders, and couldn't retreat!\n",
172                prship(sp), conditions[findcondition(code)].desc[orig]);
173             if (!orig)
174                 putship(sp->shp_uid, sp);
175             return 0;
176         }
177     }
178     /* check crew - uws don't count */
179     if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
180         wu(0, sp->shp_own,
181            "%s %s,\nbut had no crew, and couldn't retreat!\n", prship(sp),
182            conditions[findcondition(code)].desc[orig]);
183         if (!orig)
184             putship(sp->shp_uid, sp);
185         return 0;
186     }
187
188     getsect(sp->shp_x, sp->shp_y, &sect);
189     switch (check_nav(&sect)) {
190     case CN_CONSTRUCTION:
191         wu(0, sp->shp_own,
192            "%s %s,\nbut was caught in a construction zone, and couldn't retreat!\n",
193            prship(sp), conditions[findcondition(code)].desc[orig]);
194         if (!orig)
195             putship(sp->shp_uid, sp);
196         return 0;
197     case CN_LANDLOCKED:
198         wu(0, sp->shp_own,
199            "%s %s,\nbut was landlocked, and couldn't retreat!\n",
200            prship(sp), conditions[findcondition(code)].desc[orig]);
201         if (!orig)
202             putship(sp->shp_uid, sp);
203         return 0;
204         /*NOTREACHED*/
205     case CN_NAVIGABLE:
206         break;
207     case CN_ERROR:
208     default:
209         wu(0, sp->shp_own,
210            "%s %s,\nbut was subject to an empire error, and couldn't retreat!\n",
211            prship(sp), conditions[findcondition(code)].desc[orig]);
212         if (!orig)
213             putship(sp->shp_uid, sp);
214         return 0;
215         /*NOTREACHED*/
216     }
217
218     if (sp->shp_mobil <= 0.0) {
219         wu(0, sp->shp_own,
220            "%s %s,\nbut had no mobility, and couldn't retreat!\n",
221            prship(sp), conditions[findcondition(code)].desc[orig]);
222         if (!orig)
223             putship(sp->shp_uid, sp);
224         return 0;
225     }
226
227     n = (-1 * MAX_RETREAT);
228     stopping = 0;
229     time_to_stop = 0;
230     while ((!stopping) && n) {
231         dx = dy = 0;
232         if (sp->shp_rpath[0] == 0 || sp->shp_rpath[0] == 0) {
233             stopping = 1;
234             continue;
235         }
236         if (sp->shp_mobil <= 0.0) {
237             wu(0, sp->shp_own,
238                "%s %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
239                prship(sp), conditions[findcondition(code)].desc[orig]);
240             if (!orig)
241                 putship(sp->shp_uid, sp);
242             return 0;
243         }
244         dir = chkdir(sp->shp_rpath[0], DIR_STOP, DIR_VIEW);
245         memmove(sp->shp_rpath, sp->shp_rpath+1, sizeof(sp->shp_rpath) - 1);
246         if (dir < 0)
247             continue;
248         if (dir == DIR_STOP)
249             stopping++;
250         else {
251             dx = diroff[dir][0];
252             dy = diroff[dir][1];
253         }
254         n++;
255
256         mcp = &mchr[(int)sp->shp_type];
257         newx = xnorm(sp->shp_x + dx);
258         newy = ynorm(sp->shp_y + dy);
259         mobcost = sp->shp_effic * 0.01 * sp->shp_speed;
260         mobcost = 480.0 / (mobcost + techfact(sp->shp_tech, mobcost));
261
262         getsect(newx, newy, &sect);
263         if (check_nav(&sect) != CN_NAVIGABLE ||
264             (sect.sct_own && !player->owner &&
265              getrel(getnatp(sect.sct_own), sp->shp_own) < FRIENDLY)) {
266             wu(0, sp->shp_own, "%s %s,\nbut could not retreat to %s!\n",
267                prship(sp), conditions[findcondition(code)].desc[orig],
268                xyas(newx, newy, sp->shp_own));
269             if (!orig)
270                 putship(sp->shp_uid, sp);
271             return 0;
272         }
273         sp->shp_x = newx;
274         sp->shp_y = newy;
275         sp->shp_mobil -= mobcost;
276         if (stopping)
277             continue;
278
279         mines = sect.sct_mines;
280         changed = 0;
281         if ((mcp->m_flags & M_SWEEP) && mines > 0 && !player->owner) {
282             max = mcp->m_item[I_SHELL];
283             shells = sp->shp_item[I_SHELL];
284             for (m = 0; mines > 0 && m < 5; m++) {
285                 if (chance(0.66)) {
286                     mines--;
287                     shells = min(max, shells + 1);
288                     changed |= map_set(sp->shp_own, sp->shp_x, sp->shp_y,
289                         'X', 0);
290                 }
291             }
292             if (sect.sct_mines != mines) {
293                 wu(0, sp->shp_own,
294                    "%s cleared %d mine%s in %s while retreating\n",
295                    prship(sp), sect.sct_mines-mines, splur(sect.sct_mines-mines),
296                    xyas(newx, newy, sp->shp_own));
297             }
298             sect.sct_mines = mines;
299             sect.sct_item[I_SHELL] = shells;
300             putsect(&sect);
301             if (changed)
302                 writemap(sp->shp_own);
303         }
304         if (mines > 0 && !player->owner && chance(DMINE_HITCHANCE(mines))) {
305             wu(0, sp->shp_own,
306                "%s %s,\nand hit a mine in %s while retreating!\n",
307                prship(sp), conditions[findcondition(code)].desc[orig],
308                xyas(newx, newy, sp->shp_own));
309             nreport(sp->shp_own, N_HIT_MINE, 0, 1);
310             m = MINE_DAMAGE();
311             shipdamage(sp, m);
312             mines--;
313             if (map_set(sp->shp_own, sp->shp_x, sp->shp_y, 'X', 0))
314                 writemap(sp->shp_own);
315             sect.sct_mines = mines;
316             putsect(&sect);
317             if (sp->shp_effic < SHIP_MINEFF)
318                 time_to_stop = 1;
319             if (!orig)
320                 putship(sp->shp_uid, sp);
321             return 0;
322         }
323         if (time_to_stop)
324             stopping = 1;
325     }
326
327     if (orig) {
328         wu(0, sp->shp_own, "%s %s, and retreated to %s\n", prship(sp),
329            conditions[findcondition(code)].desc[orig],
330            xyas(sp->shp_x, sp->shp_y, sp->shp_own));
331     } else {
332         wu(0, sp->shp_own, "%s %s, and ended up at %s\n",
333            prship(sp),
334            conditions[findcondition(code)].desc[orig],
335            xyas(sp->shp_x, sp->shp_y, sp->shp_own));
336     }
337     if (!orig)
338         putship(sp->shp_uid, sp);
339     return 1;
340 }
341
342 static int
343 findcondition(s_char code)
344 {
345     int x;
346
347     x = 0;
348     while (conditions[x].code) {
349         if (conditions[x].code == code)
350             return x;
351         x++;
352     }
353
354     return x;
355 }
356
357 int
358 check_retreat_and_do_landdamage(struct lndstr *lp, int dam)
359 {
360     if (dam <= 0)
361         return 0;
362
363     landdamage(lp, dam);
364     if (lp->lnd_rflags & RET_INJURED)
365         retreat_land(lp, 'i');
366
367     return 1;
368 }
369
370 void
371 retreat_land(struct lndstr *lp, s_char code)
372 {
373     struct nstr_item ni;
374     struct lndstr land;
375     s_char buf[2];
376
377     if (lp->lnd_rflags & RET_GROUP) {
378         buf[0] = lp->lnd_army;
379         buf[1] = 0;
380         snxtitem(&ni, EF_SHIP, buf);
381         while (nxtitem(&ni, &land))
382             if ((land.lnd_army == buf[0]) && (land.lnd_own == lp->lnd_own)) {
383                 if (land.lnd_uid == lp->lnd_uid) {
384                     retreat_land1(lp, code, 1);
385                     if (lp->lnd_rpath[0] == 0)
386                         lp->lnd_rflags = 0;
387                 } else {
388                     retreat_land1(&land, code, 0);
389                     getland(land.lnd_uid, &land);
390                     if (land.lnd_rpath[0] == 0) {
391                         land.lnd_rflags = 0;
392                         putland(land.lnd_uid, &land);
393                     }
394                 }
395             }
396     } else {
397         retreat_land1(lp, code, 1);
398         if (lp->lnd_rpath[0] == 0)
399             lp->lnd_rflags = 0;
400     }
401 }
402
403 static int
404 retreat_land1(struct lndstr *lp, s_char code, int orig)
405
406
407                         /* Is this the originally scared unit, or a follower */
408 {
409     struct sctstr sect;
410     int n;
411     int m;
412     int max;
413     int dir;
414     coord newx;
415     coord newy;
416     coord dx;
417     coord dy;
418     int stopping;
419     int mines;
420     int shells;
421     double mobcost;
422     struct lchrstr *lcp;
423     int time_to_stop;
424
425     lp->lnd_mission = 0;
426     if (lp->lnd_own == 0)
427         return 0;
428
429     if (isupper(code))
430         code = tolower(code);
431
432     n = 0;
433     if (lp->lnd_effic < LAND_MINEFF) {
434         wu(0, lp->lnd_own,
435            "%s %s,\nbut it died in the attack, and so couldn't retreat!\n",
436            prland(lp), conditions[findcondition(code)].desc[orig]);
437         if (!orig)
438             putland(lp->lnd_uid, lp);
439         return 0;
440     }
441
442     getsect(lp->lnd_x, lp->lnd_y, &sect);
443
444     if (lp->lnd_mobil <= 0.0) {
445         wu(0, lp->lnd_own,
446            "%s %s,\nbut had no mobility, and couldn't retreat!\n",
447            prland(lp), conditions[findcondition(code)].desc[orig]);
448         if (!orig)
449             putland(lp->lnd_uid, lp);
450         return 0;
451     }
452
453     n = (-1 * MAX_RETREAT);
454     stopping = 0;
455     time_to_stop = 0;
456     while ((!stopping) && n) {
457         dx = dy = 0;
458         if (lp->lnd_rpath[0] == 0 || lp->lnd_rpath[0] == 0) {
459             stopping = 1;
460             continue;
461         }
462         if (lp->lnd_mobil <= 0.0) {
463             wu(0, lp->lnd_own,
464                "%s %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
465                prland(lp), conditions[findcondition(code)].desc[orig]);
466             if (!orig)
467                 putland(lp->lnd_uid, lp);
468             return 0;
469         }
470         dir = chkdir(lp->lnd_rpath[0], DIR_STOP, DIR_VIEW);
471         memmove(lp->lnd_rpath, lp->lnd_rpath+1, sizeof(lp->lnd_rpath) - 1);
472         if (dir < 0)
473             continue;
474         if (dir == DIR_STOP)
475             stopping++;
476         else {
477             dx = diroff[dir][0];
478             dy = diroff[dir][1];
479         }
480         n++;
481
482         lcp = &lchr[(int)lp->lnd_type];
483         newx = xnorm(lp->lnd_x + dx);
484         newy = ynorm(lp->lnd_y + dy);
485
486         getsect(newx, newy, &sect);
487         if ((sect.sct_type == SCT_WATER) ||
488             (sect.sct_type == SCT_MOUNT) ||
489             (sect.sct_type == SCT_SANCT) ||
490             (sect.sct_type == SCT_WASTE) ||
491             (sect.sct_own != lp->lnd_own)) {
492             wu(0, lp->lnd_own, "%s %s,\nbut could not retreat to %s!\n",
493                prland(lp),
494                conditions[findcondition(code)].desc[orig],
495                xyas(newx, newy, lp->lnd_own));
496             if (!orig)
497                 putland(lp->lnd_uid, lp);
498             return 0;
499         }
500         mobcost = lnd_mobcost(lp, &sect, MOB_ROAD);
501         lp->lnd_x = newx;
502         lp->lnd_y = newy;
503         lp->lnd_mobil -= mobcost;
504         if (stopping)
505             continue;
506
507         mines = sect.sct_mines;
508         if ((lcp->l_flags & L_ENGINEER) && mines > 0 &&
509             (sect.sct_oldown != lp->lnd_own)) {
510             max = lcp->l_item[I_SHELL];
511             shells = lp->lnd_item[I_SHELL];
512             for (m = 0; mines > 0 && m < 5; m++) {
513                 if (chance(0.66)) {
514                     mines--;
515                     shells = min(max, shells + 1);
516                 }
517             }
518             sect.sct_mines = mines;
519             lp->lnd_item[I_SHELL] = shells;
520             putsect(&sect);
521         }
522         if (mines > 0 && (sect.sct_oldown != lp->lnd_own) &&
523             chance(DMINE_HITCHANCE(mines))) {
524             wu(0, lp->lnd_own,
525                "%s %s,\nand hit a mine in %s while retreating!\n",
526                prland(lp),
527                conditions[findcondition(code)].desc[orig],
528                xyas(newx, newy, lp->lnd_own));
529             nreport(lp->lnd_own, N_LHIT_MINE, 0, 1);
530             m = MINE_LDAMAGE();
531             landdamage(lp, m);
532             mines--;
533             sect.sct_mines = mines;
534             putsect(&sect);
535             if (lp->lnd_effic < LAND_MINEFF)
536                 time_to_stop = 1;
537             if (!orig)
538                 putland(lp->lnd_uid, lp);
539             return 0;
540         }
541         if (time_to_stop)
542             stopping = 1;
543     }
544
545     if (orig) {
546         wu(0, lp->lnd_own, "%s %s, and retreated to %s\n",
547            prland(lp),
548            conditions[findcondition(code)].desc[orig],
549            xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
550     } else {
551         wu(0, lp->lnd_own, "%s %s, and ended up at %s\n",
552            prland(lp),
553            conditions[findcondition(code)].desc[orig],
554            xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
555     }
556     if (!orig)
557         putland(lp->lnd_uid, lp);
558     return 1;
559 }