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