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