]> git.pond.sub.org Git - empserver/blob - src/lib/subs/shpsub.c
navigate: Fix buffer overrun for impossibly long paths taken
[empserver] / src / lib / subs / shpsub.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  *  shpsub.c: Ship subroutine stuff
28  *
29  *  Known contributors to this file:
30  *     Ken Stevens, 1995
31  *     Steve McClure, 1996-2000
32  *     Markus Armbruster, 2006-2014
33  */
34
35 #include <config.h>
36
37 #include <stdlib.h>
38 #include "chance.h"
39 #include "damage.h"
40 #include "empobj.h"
41 #include "file.h"
42 #include "map.h"
43 #include "misc.h"
44 #include "mission.h"
45 #include "news.h"
46 #include "nsc.h"
47 #include "optlist.h"
48 #include "path.h"
49 #include "player.h"
50 #include "prototypes.h"
51 #include "queue.h"
52 #include "server.h"
53 #include "unit.h"
54 #include "xy.h"
55
56 static void shp_nav_put_one(struct ulist *);
57 static int shp_check_one_mines(struct ulist *);
58 static int shp_hit_mine(struct shpstr *);
59 static void shp_stays(natid, char *, struct ulist *);
60
61 void
62 shp_sel(struct nstr_item *ni, struct emp_qelem *list)
63 {
64     struct shpstr ship;
65
66     emp_initque(list);
67     while (nxtitem(ni, &ship)) {
68         /*
69          * It would be nice to let deities navigate foreign ships, but
70          * much of the code assumes that only the ship's owner can
71          * navigate it.
72          */
73         if (!ship.shp_own || ship.shp_own != player->cnum)
74             continue;
75         if (opt_MARKET) {
76             if (ontradingblock(EF_SHIP, &ship)) {
77                 pr("ship #%d inelligible - it's for sale.\n",
78                    ship.shp_uid);
79                 continue;
80             }
81         }
82         ship.shp_mission = 0;
83         ship.shp_rflags = 0;
84         memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
85         putship(ship.shp_uid, &ship);
86         shp_insque(&ship, list);
87     }
88 }
89
90 /*
91  * Append SP to LIST.
92  * Return the new list link.
93  */
94 struct ulist *
95 shp_insque(struct shpstr *sp, struct emp_qelem *list)
96 {
97     struct ulist *mlp = malloc(sizeof(struct ulist));
98
99     mlp->chrp = (struct empobj_chr *)&mchr[sp->shp_type];
100     mlp->unit.ship = *sp;
101     mlp->mobil = sp->shp_mobil;
102     emp_insque(&mlp->queue, list);
103     return mlp;
104 }
105
106 /* This function assumes that the list was created by shp_sel */
107 void
108 shp_nav(struct emp_qelem *list, double *minmobp, double *maxmobp,
109         int *togetherp, natid actor)
110 {
111     struct emp_qelem *qp;
112     struct emp_qelem *next;
113     struct ulist *mlp;
114     struct shpstr *sp;
115     struct sctstr sect;
116     coord allx;
117     coord ally;
118     int first = 1;
119
120     *minmobp = 9876.0;
121     *maxmobp = -9876.0;
122     *togetherp = 1;
123     for (qp = list->q_back; qp != list; qp = next) {
124         next = qp->q_back;
125         mlp = (struct ulist *)qp;
126         sp = &mlp->unit.ship;
127         getship(sp->shp_uid, sp);
128         if (sp->shp_own != actor) {
129             mpr(actor, "%s was sunk at %s\n",
130                 prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
131             emp_remque(&mlp->queue);
132             free(mlp);
133             continue;
134         }
135         if (opt_SAIL) {
136             if (*sp->shp_path && !update_running) {
137                 shp_stays(actor, "has a sail path", mlp);
138                 mpr(actor, "Use `sail <#> -' to reset\n");
139                 continue;
140             }
141         }
142         /* check crew - uws don't count */
143         if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
144             shp_stays(actor, "is crewless", mlp);
145             continue;
146         }
147         if (!getsect(sp->shp_x, sp->shp_y, &sect)) {
148             shp_stays(actor, "was sucked into the sky by a strange looking spaceship", mlp);    /* heh -KHS */
149             continue;
150         }
151         switch (shp_check_nav(sp, &sect)) {
152         case NAV_02:
153         case NAV_60:
154             shp_stays(actor, "is caught in a construction zone", mlp);
155             continue;
156         case NAV_NONE:
157         case NAV_CANAL:
158             shp_stays(actor, "is landlocked", mlp);
159             continue;
160         case NAVOK:
161             break;
162         default:
163             CANT_REACH();
164             shp_stays(actor, "was just swallowed by a big green worm", mlp);
165             continue;
166         }
167         if (first) {
168             allx = sp->shp_x;
169             ally = sp->shp_y;
170             first = 0;
171         }
172         if (sp->shp_x != allx || sp->shp_y != ally)
173             *togetherp = 0;
174         if (sp->shp_mobil + 1 < (int)mlp->mobil) {
175             mlp->mobil = sp->shp_mobil;
176         }
177         if (mlp->mobil < *minmobp)
178             *minmobp = mlp->mobil;
179         if (mlp->mobil > *maxmobp)
180             *maxmobp = mlp->mobil;
181     }
182 }
183
184 void
185 shp_nav_put(struct emp_qelem *list, natid actor)
186 {
187     struct emp_qelem *qp, *next;
188     struct ulist *mlp;
189     struct shpstr *sp;
190
191     for (qp = list->q_back; qp != list; qp = next) {
192         next = qp->q_back;
193         mlp = (struct ulist *)qp;
194         sp = &mlp->unit.ship;
195         mpr(actor, "%s stopped at %s\n",
196             prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
197         shp_nav_put_one(mlp);
198     }
199 }
200
201 static void
202 shp_nav_put_one(struct ulist *mlp)
203 {
204     mlp->unit.ship.shp_mobil = (int)mlp->mobil;
205     putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
206     emp_remque(&mlp->queue);
207     free(mlp);
208 }
209
210 int
211 shp_sweep(struct emp_qelem *ship_list, int verbose, int takemob, natid actor)
212 {
213     struct emp_qelem *qp;
214     struct emp_qelem *next;
215     struct ulist *mlp;
216     struct sctstr sect;
217     int mines, m, max, shells;
218     int changed = 0;
219     int stopping = 0;
220
221     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
222         next = qp->q_back;
223         mlp = (struct ulist *)qp;
224         if (!(((struct mchrstr *)mlp->chrp)->m_flags & M_SWEEP)) {
225             if (verbose)
226                 mpr(actor, "%s doesn't have minesweeping capability!\n",
227                     prship(&mlp->unit.ship));
228             continue;
229         }
230         if (takemob && mlp->mobil <= 0.0) {
231             if (verbose)
232                 mpr(actor, "%s is out of mobility!\n",
233                     prship(&mlp->unit.ship));
234             continue;
235         }
236         getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
237         if (sect.sct_type != SCT_WATER) {
238             if (verbose)
239                 mpr(actor, "%s is not at sea.  No mines there!\n",
240                     prship(&mlp->unit.ship));
241             continue;
242         }
243         if (takemob) {
244             mlp->mobil -= shp_mobcost(&mlp->unit.ship);
245             mlp->unit.ship.shp_mobil = (int)mlp->mobil;
246         }
247         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
248         if (!(mines = sect.sct_mines))
249             continue;
250         max = ((struct mchrstr *)mlp->chrp)->m_item[I_SHELL];
251         shells = mlp->unit.ship.shp_item[I_SHELL];
252         for (m = 0; mines > 0 && m < 5; m++) {
253             if (chance(0.66)) {
254                 mpr(actor, "Sweep...\n");
255                 mines--;
256                 shells = MIN(max, shells + 1);
257                 changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
258             }
259         }
260         sect.sct_mines = mines;
261         mlp->unit.ship.shp_item[I_SHELL] = shells;
262         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
263         putsect(&sect);
264         if (shp_check_one_mines(mlp)) {
265             stopping = 1;
266             emp_remque(qp);
267             free(qp);
268         }
269     }
270     if (changed)
271         writemap(actor);
272     return stopping;
273 }
274
275 static int
276 shp_check_one_mines(struct ulist *mlp)
277 {
278     struct sctstr sect;
279     int actor;
280
281     getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
282     if (sect.sct_type != SCT_WATER)
283         return 0;
284     if (!sect.sct_mines)
285         return 0;
286     if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
287         actor = mlp->unit.ship.shp_own;
288         shp_hit_mine(&mlp->unit.ship);
289         sect.sct_mines--;
290         if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
291             writemap(actor);
292         putsect(&sect);
293         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
294         if (!mlp->unit.ship.shp_own)
295             return 1;
296     }
297     return 0;
298 }
299
300 static int
301 shp_check_mines(struct emp_qelem *ship_list)
302 {
303     struct emp_qelem *qp;
304     struct emp_qelem *next;
305     struct ulist *mlp;
306     int stopping = 0;
307
308     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
309         next = qp->q_back;
310         mlp = (struct ulist *)qp;
311         if (shp_check_one_mines(mlp)) {
312             stopping = 1;
313             emp_remque(qp);
314             free(qp);
315         }
316     }
317     return stopping;
318 }
319
320
321 static void
322 shp_stays(natid actor, char *str, struct ulist *mlp)
323 {
324     mpr(actor, "%s %s & stays in %s\n",
325         prship(&mlp->unit.ship), str,
326         xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
327     shp_nav_put_one(mlp);
328 }
329
330 /*
331  * Can SP navigate in SECTP?
332  * Sector ownership is *not* considered!
333  * Return NAVOK when yes.
334  * Return NAV_02 when it could if the sector was at least 2% efficient.
335  * Return NAV_60 when it could if the sector was at least 60% efficient.
336  * Return NAV_CANAL when it lacks capability M_CANAL.
337  * Return NAV_NONE when this sector type isn't navigable at all.
338  */
339 enum d_navigation
340 shp_check_nav(struct shpstr *sp, struct sctstr *sectp)
341 {
342     switch (dchr[sectp->sct_type].d_nav) {
343     case NAVOK:
344         break;
345     case NAV_CANAL:
346         if (mchr[sp->shp_type].m_flags & M_CANAL) {
347             if (sectp->sct_effic < 2)
348                 return NAV_02;
349         } else
350             return NAV_CANAL;
351         break;
352     case NAV_02:
353         if (sectp->sct_effic < 2)
354             return NAV_02;
355         break;
356     case NAV_60:
357         if (sectp->sct_effic < 60)
358             return NAV_60;
359         break;
360     default:
361         CANT_REACH();
362     case NAV_NONE:
363         return NAV_NONE;
364     }
365     return NAVOK;
366 }
367
368 int
369 sect_has_dock(struct sctstr *sect)
370 {
371     switch (dchr[sect->sct_type].d_nav) {
372     case NAV_02:
373     case NAV_CANAL:
374         return 1;
375     default:
376         return 0;
377     }
378 }
379
380 static int
381 shp_count(struct emp_qelem *list, int wantflags, int nowantflags,
382           int x, int y)
383 {
384     struct emp_qelem *qp;
385     struct emp_qelem *next;
386     struct ulist *mlp;
387     int count = 0;
388
389     for (qp = list->q_back; qp != list; qp = next) {
390         next = qp->q_back;
391         mlp = (struct ulist *)qp;
392         if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
393             continue;
394         if (wantflags &&
395             (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
396             continue;
397         if (nowantflags &&
398             ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
399             continue;
400         ++count;
401     }
402     return count;
403 }
404
405 static void
406 shp_damage_one(struct ulist *mlp, int dam)
407 {
408     /* ship might have changed (launched interceptors, missile defense) */
409     getship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
410     shipdamage(&mlp->unit.ship, dam);
411     putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
412     if (!mlp->unit.ship.shp_own) {
413         emp_remque(&mlp->queue);
414         free(mlp);
415     }
416 }
417
418 static int
419 shp_damage(struct emp_qelem *list, int totdam, int wantflags,
420            int nowantflags, int x, int y)
421 {
422     struct emp_qelem *qp;
423     struct emp_qelem *next;
424     struct ulist *mlp;
425     int dam;
426     int count;
427
428     if (!totdam
429         || !(count = shp_count(list, wantflags, nowantflags, x, y)))
430         return 0;
431     dam = ldround((double)totdam / count, 1);
432     for (qp = list->q_back; qp != list; qp = next) {
433         next = qp->q_back;
434         mlp = (struct ulist *)qp;
435         if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
436             continue;
437         if (wantflags &&
438             (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
439             continue;
440         if (nowantflags &&
441             ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
442             continue;
443         shp_damage_one(mlp, dam);
444     }
445     return dam;
446 }
447
448 static int
449 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags,
450              int nowantflags)
451 {
452     struct emp_qelem *qp;
453     struct emp_qelem *next;
454     struct ulist *mlp;
455
456     for (qp = list->q_back; qp != list; qp = next) {
457         next = qp->q_back;
458         mlp = (struct ulist *)qp;
459 /* If the ship isn't in the requested sector, then continue */
460         if (newx != mlp->unit.ship.shp_x || newy != mlp->unit.ship.shp_y)
461             continue;
462         if (wantflags &&
463             (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
464             continue;
465         if (nowantflags &&
466             ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
467             continue;
468         return 1;
469     }
470     return 0;
471 }
472
473 static struct ulist *
474 most_valuable_ship(struct emp_qelem *list, coord x, coord y)
475 {
476     struct emp_qelem *qp;
477     struct emp_qelem *next;
478     struct ulist *mlp;
479     struct ulist *mvs = NULL;
480
481     for (qp = list->q_back; qp != list; qp = next) {
482         next = qp->q_back;
483         mlp = (struct ulist *)qp;
484         if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
485             continue;
486         if (((struct mchrstr *)mlp->chrp)->m_flags & M_SUB)
487             continue;
488         if (!((struct mchrstr *)mlp->chrp)->m_nxlight &&
489             !((struct mchrstr *)mlp->chrp)->m_nchoppers &&
490             ((struct mchrstr *)mlp->chrp)->m_cost < 1000 &&
491             !((struct mchrstr *)mlp->chrp)->m_nplanes &&
492             !((struct mchrstr *)mlp->chrp)->m_nland)
493             continue;
494         if (!mvs) {
495             mvs = mlp;
496             continue;
497         }
498         if (((struct mchrstr *)mlp->chrp)->m_cost * mlp->unit.ship.shp_effic >
499             ((struct mchrstr *)mvs->chrp)->m_cost * mvs->unit.ship.shp_effic)
500             mvs = mlp;
501     }
502     return mvs;
503 }
504
505 static int
506 shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
507 {
508     struct emp_qelem *qp;
509     struct emp_qelem *next;
510     struct ulist *mlp;
511     int hard;
512     int easiest = 9876;         /* things start great for victim */
513     int count = 0;
514
515     for (qp = list->q_back; qp != list; qp = next) {
516         next = qp->q_back;
517         mlp = (struct ulist *)qp;
518         if (wantflags &&
519             (((struct mchrstr *)mlp->chrp)->m_flags & wantflags) != wantflags)
520             continue;
521         if (nowantflags &&
522             ((struct mchrstr *)mlp->chrp)->m_flags & nowantflags)
523             continue;
524         hard = shp_hardtarget(&mlp->unit.ship);
525         if (hard < easiest)
526             easiest = hard;     /* things get worse for victim */
527         ++count;
528     }
529     return easiest - count;
530 }
531
532 static int
533 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
534                          natid victim)
535 {
536     int dam, sublaunch;
537     int stopping = 0;
538     struct emp_qelem msl_list, *qp, *newqp;
539     struct plist *plp;
540     struct ulist *mvs;
541
542     mvs = most_valuable_ship(list, newx, newy);
543     if (!mvs)
544         return 0;
545
546     msl_sel(&msl_list, newx, newy, victim, P_T | P_MAR, 0, MI_INTERDICT);
547
548     for (qp = msl_list.q_back; qp != &msl_list; qp = newqp) {
549         newqp = qp->q_back;
550         plp = (struct plist *)qp;
551
552         if (mvs && mission_pln_equip(plp, NULL, 'p') >= 0) {
553             if (msl_launch(&plp->plane, EF_SHIP, prship(&mvs->unit.ship),
554                            newx, newy, victim, &sublaunch) < 0)
555                 goto use_up_msl;
556             stopping = 1;
557             if (msl_hit(&plp->plane,
558                         shp_hardtarget(&mvs->unit.ship), EF_SHIP,
559                         N_SHP_MISS, N_SHP_SMISS, sublaunch, victim)) {
560                 dam = pln_damage(&plp->plane, 'p', 1);
561                 mpr(victim,
562                     "missile interdiction mission does %d damage to %s!\n",
563                     dam, prship(&mvs->unit.ship));
564                 shp_damage_one(mvs, dam);
565             } else {
566                 dam = pln_damage(&plp->plane, 'p', 0);
567                 collateral_damage(newx, newy, dam);
568             }
569             mvs = most_valuable_ship(list, newx, newy);
570         use_up_msl:
571             plp->plane.pln_effic = 0;
572             putplane(plp->plane.pln_uid, &plp->plane);
573         }
574         emp_remque(qp);
575         free(qp);
576     }
577
578     return stopping;
579 }
580
581 /* Note that this function has a side effect - it uses coastwatch
582  * ranges to see if it should fire upon a ship.  So, this function
583  * is expected to return positive if a ship is in range, and 0 if a
584  * ship is not in range. */
585 static int
586 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
587 {
588     struct emp_qelem *qp;
589     struct emp_qelem *next;
590     struct ulist *mlp;
591     struct natstr *natp;
592     int vrange;
593
594     natp = getnatp(sectp->sct_own);
595
596     vrange = sectp->sct_type == SCT_RADAR ? 14 : 4;
597     vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
598
599     if (vrange < 1)
600         vrange = 1;
601
602     if (vrange < trange)
603         return 0;
604
605     for (qp = list->q_back; qp != list; qp = next) {
606         next = qp->q_back;
607         mlp = (struct ulist *)qp;
608         if (((struct mchrstr *)mlp->chrp)->m_flags & M_SUB)
609             continue;
610         if (natp->nat_flags & NF_COASTWATCH)
611             wu(0, sectp->sct_own,
612                "%s %s sighted at %s\n",
613                cname(mlp->unit.ship.shp_own),
614                prship(&mlp->unit.ship),
615                xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
616                     sectp->sct_own));
617         if (opt_HIDDEN)
618             setcont(sectp->sct_own, mlp->unit.ship.shp_own, FOUND_COAST);
619     }
620
621     return 1;
622 }
623
624 static int
625 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy,
626                       natid victim)
627 {
628     struct nstr_sect ns;
629     struct sctstr fsect;
630     int trange, range;
631     int dam;
632     int stopping = 0;
633     int totdam = 0;
634     signed char notified[MAXNOC];
635     int i;
636
637     /* Inform neutral and worse */
638     for (i = 0; i < MAXNOC; ++i) {
639         if (relations_with(i, victim) <= NEUTRAL)
640             notified[i] = 0;
641         else
642             notified[i] = 1;
643     }
644
645     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
646     while (nxtsct(&ns, &fsect)) {
647         if (!fsect.sct_own)
648             continue;
649         if (fsect.sct_own == victim)
650             continue;
651         if (notified[fsect.sct_own])
652             continue;
653         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
654         if (notify_coastguard(list, trange, &fsect))
655             notified[fsect.sct_own] = 1;
656     }
657     if (opt_NO_FORT_FIRE)
658         return 0;               /* Only coastwatch notify in nofortfire */
659     /* Only fire at Hostile ships */
660     for (i = 0; i < MAXNOC; ++i) {
661         if (relations_with(i, victim) >= NEUTRAL)
662             notified[i] = 0;
663     }
664     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
665     while (nxtsct(&ns, &fsect)) {
666         if (!notified[fsect.sct_own])
667             continue;
668         range = roundrange(fortrange(&fsect));
669         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
670         if (trange > range)
671             continue;
672         dam = fort_fire(&fsect);
673         putsect(&fsect);
674         if (dam < 0)
675             continue;
676         stopping = 1;
677         totdam += dam;
678         mpr(victim, "Incoming fire does %d damage!\n", dam);
679 #if 0
680         mpr(victim, "%s fires at you for %d!\n",
681             xyas(fsect.sct_x, fsect.sct_y, victim), dam);
682 #endif
683         wu(0, fsect.sct_own,
684            "%s fires at %s ships in %s for %d!\n",
685            xyas(fsect.sct_x, fsect.sct_y,
686                 fsect.sct_own),
687            cname(victim), xyas(newx, newy, fsect.sct_own), dam);
688         nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
689     }
690     if (totdam > 0)
691         shp_damage(list, totdam, 0, M_SUB, newx, newy);
692     return stopping;
693 }
694
695 static int
696 shp_mission_interdiction(struct emp_qelem *list, coord x, coord y,
697                          natid victim, int subs)
698 {
699     char *what = subs ? "subs" : "ships";
700     int wantflags = subs ? M_SUB : 0;
701     int nowantflags = subs ? 0 : M_SUB;
702     int mission = subs ? MI_SINTERDICT : MI_INTERDICT;
703     int dam;
704
705     dam = unit_interdict(x, y, victim, what,
706                          shp_easiest_target(list, wantflags, nowantflags),
707                          mission);
708     if (dam >= 0)
709         shp_damage(list, dam, wantflags, nowantflags, x, y);
710     return dam >= 0;
711 }
712
713 static int
714 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
715 {
716     int stopping = 0;
717
718     if (shp_contains(list, newx, newy, 0, M_SUB)) {
719         stopping |= shp_fort_interdiction(list, newx, newy, victim);
720
721         if (shp_contains(list, newx, newy, 0, M_SUB)) {
722             stopping |= shp_mission_interdiction(list, newx, newy, victim, 0);
723             stopping |= shp_missile_interdiction(list, newx, newy, victim);
724         }
725     }
726     if (shp_contains(list, newx, newy, M_SUB, 0))
727         stopping |= shp_mission_interdiction(list, newx, newy, victim, 1);
728     return stopping;
729 }
730
731 /* high value of hardtarget is harder to hit */
732 int
733 shp_hardtarget(struct shpstr *sp)
734 {
735     struct sctstr sect;
736     int vis, onsea;
737     struct mchrstr *mcp = mchr + sp->shp_type;
738
739     vis = shp_visib(sp);
740     getsect(sp->shp_x, sp->shp_y, &sect);
741     onsea = sect.sct_type == SCT_WATER;
742     if (mcp->m_flags & M_SUB)
743         vis *= 4;
744     return (int)((sp->shp_effic / 100.0) *
745                  (20 + shp_speed(sp) * onsea / 2.0 - vis));
746 }
747
748 static int
749 shp_hit_mine(struct shpstr *sp)
750 {
751     double m;
752
753     mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
754         xyas(sp->shp_x, sp->shp_y, sp->shp_own));
755
756     nreport(sp->shp_own, N_HIT_MINE, 0, 1);
757
758     m = MINE_DAMAGE();
759     if (mchr[sp->shp_type].m_flags & M_SWEEP)
760         m /= 2.0;
761
762     shipdamage(sp, ldround(m, 1));
763
764     return (int)m;
765 }
766
767 int
768 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor,
769                    int together)
770 {
771     struct sctstr sect;
772     struct emp_qelem *qp;
773     struct emp_qelem *next;
774     struct ulist *mlp;
775     struct emp_qelem done;
776     coord dx;
777     coord dy;
778     coord newx;
779     coord newy;
780     int stopping = 0;
781     double mobcost;
782     char dp[80];
783     int navigate;
784
785     if (dir <= DIR_STOP || dir >= DIR_VIEW) {
786         shp_nav_put(list, actor);
787         return 1;
788     }
789     dx = diroff[dir][0];
790     dy = diroff[dir][1];
791     for (qp = list->q_back; qp != list; qp = next) {
792         next = qp->q_back;
793         mlp = (struct ulist *)qp;
794         newx = xnorm(mlp->unit.ship.shp_x + dx);
795         newy = ynorm(mlp->unit.ship.shp_y + dy);
796         getsect(newx, newy, &sect);
797         navigate = shp_check_nav(&mlp->unit.ship, &sect);
798         if (navigate != NAVOK ||
799             (sect.sct_own
800              && relations_with(sect.sct_own, actor) < FRIENDLY)) {
801             if (navigate == NAV_CANAL)
802                 sprintf(dp,
803                         "is too large to fit into the canal system at %s",
804                         xyas(newx, newy, actor));
805             else
806                 sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
807             if (together) {
808                 mpr(actor, "%s\n", dp);
809                 return 1;
810             } else {
811                 shp_stays(actor, dp, mlp);
812                 continue;
813             }
814         }
815
816         if (mlp->mobil <= 0.0) {
817             shp_stays(actor, "is out of mobility", mlp);
818             continue;
819         }
820         mobcost = shp_mobcost(&mlp->unit.ship);
821         mlp->unit.ship.shp_x = newx;
822         mlp->unit.ship.shp_y = newy;
823         if (mlp->mobil - mobcost < -127) {
824             mlp->mobil = -127;
825         } else {
826             mlp->mobil -= mobcost;
827         }
828         mlp->unit.ship.shp_mobil = (int)mlp->mobil;
829         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
830
831         /* Now update the map for this ship */
832         rad_map_set(mlp->unit.ship.shp_own,
833                     mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
834                     mlp->unit.ship.shp_effic, mlp->unit.ship.shp_tech,
835                     ((struct mchrstr *)mlp->chrp)->m_vrnge);
836     }
837     if (QEMPTY(list))
838         return stopping;
839     stopping |= shp_sweep(list, 0, 0, actor);
840     if (QEMPTY(list))
841         return stopping;
842     stopping |= shp_check_mines(list);
843     if (QEMPTY(list))
844         return stopping;
845
846     /* interdict ships sector by sector */
847     emp_initque(&done);
848     while (!QEMPTY(list)) {
849         mlp = (struct ulist *)list->q_back;
850         newx = mlp->unit.ship.shp_x;
851         newy = mlp->unit.ship.shp_y;
852         stopping |= shp_interdict(list, newx, newy, actor);
853         /* move survivors in this sector to done */
854         for (qp = list->q_back; qp != list; qp = next) {
855             next = qp->q_back;
856             mlp = (struct ulist *)qp;
857             if (mlp->unit.ship.shp_x == newx &&
858                 mlp->unit.ship.shp_y == newy) {
859                 emp_remque(qp);
860                 emp_insque(qp, &done);
861             }
862         }
863     }
864     /* assign surviving ships back to list */
865     emp_insque(list, &done);
866     emp_remque(&done);
867
868     return stopping;
869 }
870
871 /*
872  * shp_miss_defence
873  * Check for incoming missiles with a P_MAR flag.
874  * Return True=1 if the missile was shotdown.
875  * Or False=0
876  *
877  * Chad Zabel, July 95
878  */
879
880 int
881 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
882 {
883     struct nstr_item ni;
884     struct shpstr ship;
885     int hitchance, hit;
886     double gun, eff, teff;
887
888     snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
889
890     while (nxtitem(&ni, &ship)) {
891         if (!ship.shp_own)
892             continue;
893
894         if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
895             continue;
896
897         if (relations_with(ship.shp_own, bombown) >= NEUTRAL)
898             continue;
899
900         if (ship.shp_effic < 60)
901             continue;
902
903         if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
904             continue;
905         if (ship.shp_item[I_GUN] < 1)   /* we need at least 1 gun */
906             continue;
907         if (!shp_supply(&ship, I_SHELL, 2))
908             continue;
909         ship.shp_item[I_SHELL] -= 2;
910         putship(ship.shp_uid, &ship);
911
912         /* now calculate the odds */
913         gun = shp_usable_guns(&ship);
914         eff = ship.shp_effic / 100.0;
915         teff = ship.shp_tech / (ship.shp_tech + 200.0);
916         /* raise 4.5 for better interception -KHS */
917         hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
918         hitchance = LIMIT_TO(hitchance, 0, 100);
919         hit = pct_chance(hitchance);
920
921         mpr(bombown, "%s anti-missile system activated...%s\n",
922             cname(ship.shp_own),
923             hit ? "KABOOOM!! Missile destroyed\n"
924             : "SWOOSH!!  anti-missile system failed!!");
925         mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
926             ship.shp_uid);
927         mpr(ship.shp_own, "%d%% hitchance...%s\n", hitchance,
928             hit ? "KABOOOM!!  Incoming missile destroyed!\n"
929             : "SWOOSH!!  Missile evades anti-missile systems\n");
930
931         if (hit)
932             return 1;
933     }
934     return 0;                   /* all attempts failed */
935 }
936
937
938 /* Fire missiles at a ship which has fired shells */
939 void
940 shp_missdef(struct shpstr *sp, natid victim)
941 {
942     struct emp_qelem list;
943     struct ulist *mlp;
944     int eff;
945     char buf[512];
946
947     emp_initque(&list);
948     mlp = shp_insque(sp, &list);
949     sprintf(buf, "%s", prship(&mlp->unit.ship));
950
951     eff = sp->shp_effic;
952     shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
953     getship(sp->shp_uid, sp);
954
955     if (!sp->shp_own) {
956         wu(0, victim,
957            "missiles launched in defense did 100%% damage to %s\n",
958            buf);
959         wu(0, victim, "%s sunk!\n", buf);
960     } else if (eff > 0 && sp->shp_effic < eff) {
961         wu(0, victim,
962            "missiles launched in defense did %d%% damage to %s\n",
963            100 * (eff - sp->shp_effic) / eff, buf);
964     }
965     if (!QEMPTY(&list))
966         free(mlp);
967 }
968
969 double
970 shp_mobcost(struct shpstr *sp)
971 {
972     return speed_factor(sp->shp_effic * 0.01 * shp_speed(sp),
973                         sp->shp_tech);
974 }
975
976 /*
977  * Set SP's tech to TLEV along with everything else that depends on it.
978  */
979 void
980 shp_set_tech(struct shpstr *sp, int tlev)
981 {
982     struct mchrstr *mcp = mchr + sp->shp_type;
983
984     if (CANT_HAPPEN(tlev < mcp->m_tech))
985         tlev = mcp->m_tech;
986
987     sp->shp_tech = tlev;
988 }