]> git.pond.sub.org Git - empserver/blob - src/lib/subs/shpsub.c
subs: Clean up direction code range checks
[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-2014
33  */
34
35 #include <config.h>
36
37 #include <stdlib.h>
38 #include "chance.h"
39 #include "damage.h"
40 #include "empobj.h"
41 #include "file.h"
42 #include "map.h"
43 #include "misc.h"
44 #include "mission.h"
45 #include "news.h"
46 #include "nsc.h"
47 #include "optlist.h"
48 #include "path.h"
49 #include "player.h"
50 #include "prototypes.h"
51 #include "queue.h"
52 #include "server.h"
53 #include "unit.h"
54 #include "xy.h"
55
56 static void shp_nav_put_one(struct ulist *);
57 static int shp_check_one_mines(struct ulist *);
58 static int shp_hit_mine(struct shpstr *);
59 static void shp_stays(natid, char *, struct ulist *);
60
61 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 void
76 shp_sel(struct nstr_item *ni, struct emp_qelem *list)
77 {
78     struct shpstr ship;
79
80     emp_initque(list);
81     while (nxtitem(ni, &ship)) {
82         /*
83          * It would be nice to let deities navigate foreign ships, but
84          * much of the code assumes that only the ship's owner can
85          * navigate it.
86          */
87         if (!ship.shp_own || ship.shp_own != player->cnum)
88             continue;
89         if (opt_MARKET) {
90             if (ontradingblock(EF_SHIP, &ship)) {
91                 pr("ship #%d inelligible - it's for sale.\n",
92                    ship.shp_uid);
93                 continue;
94             }
95         }
96         ship.shp_mission = 0;
97         ship.shp_rflags = 0;
98         memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
99         putship(ship.shp_uid, &ship);
100         shp_insque(&ship, list);
101     }
102 }
103
104 /*
105  * Append SP to LIST.
106  * Return the new list link.
107  */
108 struct ulist *
109 shp_insque(struct shpstr *sp, struct emp_qelem *list)
110 {
111     struct ulist *mlp = malloc(sizeof(struct ulist));
112
113     mlp->unit.ship = *sp;
114     mlp->mobil = sp->shp_mobil;
115     emp_insque(&mlp->queue, list);
116     return mlp;
117 }
118
119 /* This function assumes that the list was created by shp_sel */
120 void
121 shp_nav(struct emp_qelem *list, double *minmobp, double *maxmobp,
122         natid actor)
123 {
124     struct emp_qelem *qp;
125     struct emp_qelem *next;
126     struct ulist *mlp;
127     struct shpstr *sp, *flg = NULL;
128     struct sctstr sect;
129
130     *minmobp = 9876.0;
131     *maxmobp = -9876.0;
132     for (qp = list->q_back; qp != list; qp = next) {
133         next = qp->q_back;
134         mlp = (struct ulist *)qp;
135         sp = &mlp->unit.ship;
136         getship(sp->shp_uid, sp);
137         if (sp->shp_own != actor) {
138             mpr(actor, "%s was sunk at %s\n",
139                 prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
140             emp_remque(&mlp->queue);
141             free(mlp);
142             continue;
143         }
144         /* check crew - uws don't count */
145         if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
146             shp_stays(actor, "is crewless", mlp);
147             continue;
148         }
149         if (!getsect(sp->shp_x, sp->shp_y, &sect)) {
150             shp_stays(actor, "was sucked into the sky by a strange looking spaceship", mlp);    /* heh -KHS */
151             continue;
152         }
153         switch (shp_check_nav(sp, &sect)) {
154         case SHP_STUCK_NOT:
155             break;
156         case SHP_STUCK_CONSTRUCTION:
157             shp_stays(actor, "is caught in a construction zone", mlp);
158             continue;
159         default:
160             CANT_REACH();
161             /* fall through */
162         case SHP_STUCK_CANAL:
163         case SHP_STUCK_IMPASSABLE:
164             shp_stays(actor, "is landlocked", mlp);
165             continue;
166         }
167         if (!flg)
168             flg = sp;
169         else if (sp->shp_x != flg->shp_x || sp->shp_y != flg->shp_y) {
170             shp_stays(actor, "is not with the flagship", mlp);
171             continue;
172         }
173         if (sp->shp_mobil + 1 < (int)mlp->mobil) {
174             mlp->mobil = sp->shp_mobil;
175         }
176         if (mlp->mobil < *minmobp)
177             *minmobp = mlp->mobil;
178         if (mlp->mobil > *maxmobp)
179             *maxmobp = mlp->mobil;
180     }
181 }
182
183 static void
184 shp_nav_put(struct emp_qelem *list, natid actor)
185 {
186     struct emp_qelem *qp, *next;
187     struct ulist *mlp;
188     struct shpstr *sp;
189
190     for (qp = list->q_back; qp != list; qp = next) {
191         next = qp->q_back;
192         mlp = (struct ulist *)qp;
193         sp = &mlp->unit.ship;
194         mpr(actor, "%s stopped at %s\n",
195             prship(sp), xyas(sp->shp_x, sp->shp_y, actor));
196         shp_nav_put_one(mlp);
197     }
198 }
199
200 static void
201 shp_nav_put_one(struct ulist *mlp)
202 {
203     mlp->unit.ship.shp_mobil = (int)mlp->mobil;
204     putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
205     emp_remque(&mlp->queue);
206     free(mlp);
207 }
208
209 /*
210  * Sweep seamines with engineers in SHIP_LIST for ACTOR.
211  * All ships in SHIP_LIST must be in the same sector.
212  * If EXPLICIT is non-zero, this is for an explicit sweep command from
213  * a player.  Else it's an automatic "on the move" sweep.
214  * If TAKEMOB is non-zero, require and charge mobility.
215  * Return non-zero when the ships should stop.
216  */
217 int
218 shp_sweep(struct emp_qelem *ship_list, int explicit, int takemob,
219           natid actor)
220 {
221     struct emp_qelem *qp;
222     struct emp_qelem *next;
223     struct ulist *mlp;
224     struct sctstr sect;
225     int mines, m, max, shells;
226     int changed = 0;
227     int stopping = 0, first = 1;
228
229     mlp = shp_find_capable(ship_list, M_SWEEP);
230     if (!mlp) {
231         if (explicit)
232             mpr(actor, "No minesweepers!\n");
233         return 0;
234     }
235
236     getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
237     if (sect.sct_type != SCT_WATER) {
238         if (explicit)
239             mpr(actor, "%s is a %s.  No seamines there!\n",
240                xyas(sect.sct_x, sect.sct_y, actor),
241                dchr[sect.sct_type].d_name);
242         return 0;
243     }
244
245     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
246         next = qp->q_back;
247         mlp = (struct ulist *)qp;
248         if (!(mchr[mlp->unit.ship.shp_type].m_flags & M_SWEEP))
249             continue;
250         if (takemob) {
251             if (mlp->mobil <= 0.0) {
252                 if (explicit)
253                     mpr(actor, "%s is out of mobility!\n",
254                         prship(&mlp->unit.ship));
255                 continue;
256             }
257             mlp->mobil -= shp_mobcost(&mlp->unit.ship);
258             mlp->unit.ship.shp_mobil = (int)mlp->mobil;
259         }
260         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
261         getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
262         if (!(mines = sect.sct_mines))
263             continue;
264         max = mchr[mlp->unit.ship.shp_type].m_item[I_SHELL];
265         shells = mlp->unit.ship.shp_item[I_SHELL];
266         for (m = 0; mines > 0 && m < 5; m++) {
267             if (chance(0.66)) {
268                 if (first) {
269                     mpr(actor, "Approaching minefield at %s...\n",
270                         xyas(sect.sct_x, sect.sct_y, actor));
271                     first = 0;
272                 }
273                 mpr(actor, "Sweep...\n");
274                 mines--;
275                 shells = MIN(max, shells + 1);
276                 changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
277             }
278         }
279         sect.sct_mines = mines;
280         mlp->unit.ship.shp_item[I_SHELL] = shells;
281         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
282         putsect(&sect);
283         if (shp_check_one_mines(mlp)) {
284             stopping = 1;
285             emp_remque(qp);
286             free(qp);
287         }
288     }
289     if (changed)
290         writemap(actor);
291     return stopping;
292 }
293
294 static int
295 shp_check_one_mines(struct ulist *mlp)
296 {
297     struct sctstr sect;
298     int actor;
299
300     getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
301     if (sect.sct_type != SCT_WATER)
302         return 0;
303     if (!sect.sct_mines)
304         return 0;
305     if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
306         actor = mlp->unit.ship.shp_own;
307         shp_hit_mine(&mlp->unit.ship);
308         sect.sct_mines--;
309         if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
310             writemap(actor);
311         putsect(&sect);
312         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
313         if (!mlp->unit.ship.shp_own)
314             return 1;
315     }
316     return 0;
317 }
318
319 static int
320 shp_check_mines(struct emp_qelem *ship_list)
321 {
322     struct emp_qelem *qp;
323     struct emp_qelem *next;
324     struct ulist *mlp;
325     int stopping = 0;
326
327     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
328         next = qp->q_back;
329         mlp = (struct ulist *)qp;
330         if (shp_check_one_mines(mlp)) {
331             stopping = 1;
332             emp_remque(qp);
333             free(qp);
334         }
335     }
336     return stopping;
337 }
338
339
340 static void
341 shp_stays(natid actor, char *str, struct ulist *mlp)
342 {
343     mpr(actor, "%s %s & stays in %s\n",
344         prship(&mlp->unit.ship), str,
345         xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
346     shp_nav_put_one(mlp);
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_one_sector(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     int stopping = 0;
793     double mobcost;
794     char dp[80];
795
796     if (CANT_HAPPEN(QEMPTY(list)))
797         return 1;
798
799     if (dir <= DIR_STOP || dir > DIR_LAST) {
800         CANT_HAPPEN(dir != DIR_STOP);
801         shp_nav_put(list, actor);
802         return 1;
803     }
804     dx = diroff[dir][0];
805     dy = diroff[dir][1];
806
807     mlp = (struct ulist *)list->q_back;
808     newx = xnorm(mlp->unit.ship.shp_x + dx);
809     newy = ynorm(mlp->unit.ship.shp_y + dy);
810     getsect(newx, newy, &sect);
811
812     if (sect.sct_own && relations_with(sect.sct_own, actor) < FRIENDLY) {
813         mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
814         return 1;
815     }
816
817     move = 0;
818     for (qp = list->q_back; qp != list; qp = next) {
819         next = qp->q_back;
820         mlp = (struct ulist *)qp;
821         switch (shp_check_nav(&mlp->unit.ship, &sect)) {
822         case SHP_STUCK_NOT:
823             move = 1;
824             break;
825         case SHP_STUCK_CANAL:
826             break;
827         default:
828             CANT_REACH();
829             /* fall through */
830         case SHP_STUCK_CONSTRUCTION:
831         case SHP_STUCK_IMPASSABLE:
832             mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
833             return 1;
834         }
835     }
836     if (!move) {
837         mpr(actor, "is too large to fit into the canal system at %s\n",
838             xyas(newx, newy, actor));
839         return 1;
840     }
841
842     for (qp = list->q_back; qp != list; qp = next) {
843         next = qp->q_back;
844         mlp = (struct ulist *)qp;
845         stuck = shp_check_nav(&mlp->unit.ship, &sect);
846         if (stuck == SHP_STUCK_CANAL) {
847             sprintf(dp,
848                     "is too large to fit into the canal system at %s",
849                     xyas(newx, newy, actor));
850             shp_stays(actor, dp, mlp);
851             continue;
852         } else if (CANT_HAPPEN(stuck != SHP_STUCK_NOT)) {
853             sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
854             shp_stays(actor, dp, mlp);
855             continue;
856         }
857
858         if (mlp->mobil <= 0.0) {
859             shp_stays(actor, "is out of mobility", mlp);
860             continue;
861         }
862         mobcost = shp_mobcost(&mlp->unit.ship);
863         mlp->unit.ship.shp_x = newx;
864         mlp->unit.ship.shp_y = newy;
865         if (mlp->mobil - mobcost < -127) {
866             mlp->mobil = -127;
867         } else {
868             mlp->mobil -= mobcost;
869         }
870         mlp->unit.ship.shp_mobil = (int)mlp->mobil;
871         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
872
873         /* Now update the map for this ship */
874         rad_map_set(mlp->unit.ship.shp_own,
875                     mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
876                     mlp->unit.ship.shp_effic, mlp->unit.ship.shp_tech,
877                     mchr[mlp->unit.ship.shp_type].m_vrnge);
878     }
879     if (QEMPTY(list))
880         return stopping;
881     stopping |= shp_sweep(list, 0, 0, actor);
882     if (QEMPTY(list))
883         return stopping;
884     stopping |= shp_check_mines(list);
885     if (QEMPTY(list))
886         return stopping;
887     stopping |= shp_interdict(list, newx, newy, actor);
888
889     return stopping;
890 }
891
892 /*
893  * shp_miss_defence
894  * Check for incoming missiles with a P_MAR flag.
895  * Return True=1 if the missile was shotdown.
896  * Or False=0
897  *
898  * Chad Zabel, July 95
899  */
900
901 int
902 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
903 {
904     struct nstr_item ni;
905     struct shpstr ship;
906     int hitchance, hit;
907     double gun, eff, teff;
908
909     snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
910
911     while (nxtitem(&ni, &ship)) {
912         if (!ship.shp_own)
913             continue;
914
915         if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
916             continue;
917
918         if (relations_with(ship.shp_own, bombown) >= NEUTRAL)
919             continue;
920
921         if (ship.shp_effic < 60)
922             continue;
923
924         if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
925             continue;
926         if (ship.shp_item[I_GUN] < 1)   /* we need at least 1 gun */
927             continue;
928         if (!shp_supply(&ship, I_SHELL, 2))
929             continue;
930         ship.shp_item[I_SHELL] -= 2;
931         putship(ship.shp_uid, &ship);
932
933         /* now calculate the odds */
934         gun = shp_usable_guns(&ship);
935         eff = ship.shp_effic / 100.0;
936         teff = ship.shp_tech / (ship.shp_tech + 200.0);
937         /* raise 4.5 for better interception -KHS */
938         hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
939         hitchance = LIMIT_TO(hitchance, 0, 100);
940         hit = pct_chance(hitchance);
941
942         mpr(bombown, "%s anti-missile system activated...%s\n",
943             cname(ship.shp_own),
944             hit ? "KABOOOM!! Missile destroyed\n"
945             : "SWOOSH!!  anti-missile system failed!!");
946         mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
947             ship.shp_uid);
948         mpr(ship.shp_own, "%d%% hitchance...%s\n", hitchance,
949             hit ? "KABOOOM!!  Incoming missile destroyed!\n"
950             : "SWOOSH!!  Missile evades anti-missile systems\n");
951
952         if (hit)
953             return 1;
954     }
955     return 0;                   /* all attempts failed */
956 }
957
958
959 /* Fire missiles at a ship which has fired shells */
960 void
961 shp_missdef(struct shpstr *sp, natid victim)
962 {
963     struct emp_qelem list;
964     struct ulist *mlp;
965     int eff;
966     char buf[512];
967
968     emp_initque(&list);
969     mlp = shp_insque(sp, &list);
970     sprintf(buf, "%s", prship(&mlp->unit.ship));
971
972     eff = sp->shp_effic;
973     shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
974     getship(sp->shp_uid, sp);
975
976     if (!sp->shp_own) {
977         wu(0, victim,
978            "missiles launched in defense did 100%% damage to %s\n",
979            buf);
980         wu(0, victim, "%s sunk!\n", buf);
981     } else if (eff > 0 && sp->shp_effic < eff) {
982         wu(0, victim,
983            "missiles launched in defense did %d%% damage to %s\n",
984            100 * (eff - sp->shp_effic) / eff, buf);
985     }
986     if (!QEMPTY(&list))
987         free(mlp);
988 }
989
990 double
991 shp_mobcost(struct shpstr *sp)
992 {
993     return speed_factor(sp->shp_effic * 0.01 * shp_speed(sp),
994                         sp->shp_tech);
995 }
996
997 /*
998  * Set SP's tech to TLEV along with everything else that depends on it.
999  */
1000 void
1001 shp_set_tech(struct shpstr *sp, int tlev)
1002 {
1003     struct mchrstr *mcp = mchr + sp->shp_type;
1004
1005     if (CANT_HAPPEN(tlev < mcp->m_tech))
1006         tlev = mcp->m_tech;
1007
1008     sp->shp_tech = tlev;
1009 }