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