]> git.pond.sub.org Git - empserver/blob - src/lib/subs/shpsub.c
navigate march: Nicer error messages for sub-command 'm'
[empserver] / src / lib / subs / shpsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2014, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  shpsub.c: Ship subroutine stuff
28  *
29  *  Known contributors to this file:
30  *     Ken Stevens, 1995
31  *     Steve McClure, 1996-2000
32  *     Markus Armbruster, 2006-2014
33  */
34
35 #include <config.h>
36
37 #include <stdlib.h>
38 #include "chance.h"
39 #include "damage.h"
40 #include "empobj.h"
41 #include "file.h"
42 #include "map.h"
43 #include "misc.h"
44 #include "mission.h"
45 #include "news.h"
46 #include "nsc.h"
47 #include "optlist.h"
48 #include "path.h"
49 #include "player.h"
50 #include "prototypes.h"
51 #include "queue.h"
52 #include "server.h"
53 #include "unit.h"
54 #include "xy.h"
55
56 static void shp_nav_put_one(struct ulist *);
57 static int shp_check_one_mines(struct ulist *);
58 static int shp_hit_mine(struct shpstr *);
59 static void shp_stays(natid, char *, struct ulist *);
60
61 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;
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                 mpr(actor, "Sweep...\n");
269                 mines--;
270                 shells = MIN(max, shells + 1);
271                 changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
272             }
273         }
274         sect.sct_mines = mines;
275         mlp->unit.ship.shp_item[I_SHELL] = shells;
276         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
277         putsect(&sect);
278         if (shp_check_one_mines(mlp)) {
279             stopping = 1;
280             emp_remque(qp);
281             free(qp);
282         }
283     }
284     if (changed)
285         writemap(actor);
286     return stopping;
287 }
288
289 static int
290 shp_check_one_mines(struct ulist *mlp)
291 {
292     struct sctstr sect;
293     int actor;
294
295     getsect(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, &sect);
296     if (sect.sct_type != SCT_WATER)
297         return 0;
298     if (!sect.sct_mines)
299         return 0;
300     if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
301         actor = mlp->unit.ship.shp_own;
302         shp_hit_mine(&mlp->unit.ship);
303         sect.sct_mines--;
304         if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
305             writemap(actor);
306         putsect(&sect);
307         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
308         if (!mlp->unit.ship.shp_own)
309             return 1;
310     }
311     return 0;
312 }
313
314 static int
315 shp_check_mines(struct emp_qelem *ship_list)
316 {
317     struct emp_qelem *qp;
318     struct emp_qelem *next;
319     struct ulist *mlp;
320     int stopping = 0;
321
322     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
323         next = qp->q_back;
324         mlp = (struct ulist *)qp;
325         if (shp_check_one_mines(mlp)) {
326             stopping = 1;
327             emp_remque(qp);
328             free(qp);
329         }
330     }
331     return stopping;
332 }
333
334
335 static void
336 shp_stays(natid actor, char *str, struct ulist *mlp)
337 {
338     mpr(actor, "%s %s & stays in %s\n",
339         prship(&mlp->unit.ship), str,
340         xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y, actor));
341     shp_nav_put_one(mlp);
342 }
343
344 /*
345  * Return whether and why SP would be stuck in SECTP.
346  */
347 enum shp_stuck
348 shp_check_nav(struct shpstr *sp, struct sctstr *sectp)
349 {
350     switch (dchr[sectp->sct_type].d_nav) {
351     case NAVOK:
352         break;
353     case NAV_CANAL:
354         if (!(mchr[sp->shp_type].m_flags & M_CANAL)) {
355             return SHP_STUCK_CANAL;
356         }
357         /* fall through */
358     case NAV_02:
359         if (sectp->sct_effic < 2)
360             return SHP_STUCK_CONSTRUCTION;
361         break;
362     case NAV_60:
363         if (sectp->sct_effic < 60)
364             return SHP_STUCK_CONSTRUCTION;
365         break;
366     default:
367         CANT_REACH();
368         /* fall through */
369     case NAV_NONE:
370         return SHP_STUCK_IMPASSABLE;
371     }
372     return SHP_STUCK_NOT;
373 }
374
375 int
376 sect_has_dock(struct sctstr *sect)
377 {
378     switch (dchr[sect->sct_type].d_nav) {
379     case NAV_02:
380     case NAV_CANAL:
381         return 1;
382     default:
383         return 0;
384     }
385 }
386
387 static int
388 shp_count(struct emp_qelem *list, int wantflags, int nowantflags,
389           int x, int y)
390 {
391     struct emp_qelem *qp;
392     struct emp_qelem *next;
393     struct ulist *mlp;
394     int count = 0;
395
396     for (qp = list->q_back; qp != list; qp = next) {
397         next = qp->q_back;
398         mlp = (struct ulist *)qp;
399         if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
400             continue;
401         if (wantflags &&
402             (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
403             continue;
404         if (nowantflags &&
405             mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
406             continue;
407         ++count;
408     }
409     return count;
410 }
411
412 static void
413 shp_damage_one(struct ulist *mlp, int dam)
414 {
415     /* ship might have changed (launched interceptors, missile defense) */
416     getship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
417     shipdamage(&mlp->unit.ship, dam);
418     putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
419     if (!mlp->unit.ship.shp_own) {
420         emp_remque(&mlp->queue);
421         free(mlp);
422     }
423 }
424
425 static int
426 shp_damage(struct emp_qelem *list, int totdam, int wantflags,
427            int nowantflags, int x, int y)
428 {
429     struct emp_qelem *qp;
430     struct emp_qelem *next;
431     struct ulist *mlp;
432     int dam;
433     int count;
434
435     if (!totdam
436         || !(count = shp_count(list, wantflags, nowantflags, x, y)))
437         return 0;
438     dam = ldround((double)totdam / count, 1);
439     for (qp = list->q_back; qp != list; qp = next) {
440         next = qp->q_back;
441         mlp = (struct ulist *)qp;
442         if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
443             continue;
444         if (wantflags &&
445             (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
446             continue;
447         if (nowantflags &&
448             mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
449             continue;
450         shp_damage_one(mlp, dam);
451     }
452     return dam;
453 }
454
455 static int
456 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags,
457              int nowantflags)
458 {
459     struct emp_qelem *qp;
460     struct emp_qelem *next;
461     struct ulist *mlp;
462
463     for (qp = list->q_back; qp != list; qp = next) {
464         next = qp->q_back;
465         mlp = (struct ulist *)qp;
466 /* If the ship isn't in the requested sector, then continue */
467         if (newx != mlp->unit.ship.shp_x || newy != mlp->unit.ship.shp_y)
468             continue;
469         if (wantflags &&
470             (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
471             continue;
472         if (nowantflags &&
473             mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
474             continue;
475         return 1;
476     }
477     return 0;
478 }
479
480 static struct ulist *
481 most_valuable_ship(struct emp_qelem *list, coord x, coord y)
482 {
483     struct emp_qelem *qp;
484     struct emp_qelem *next;
485     struct ulist *mlp;
486     struct ulist *mvs = NULL;
487
488     for (qp = list->q_back; qp != list; qp = next) {
489         next = qp->q_back;
490         mlp = (struct ulist *)qp;
491         if (mlp->unit.ship.shp_x != x || mlp->unit.ship.shp_y != y)
492             continue;
493         if (mchr[mlp->unit.ship.shp_type].m_flags & M_SUB)
494             continue;
495         if (!mchr[mlp->unit.ship.shp_type].m_nxlight &&
496             !mchr[mlp->unit.ship.shp_type].m_nchoppers &&
497             mchr[mlp->unit.ship.shp_type].m_cost < 1000 &&
498             !mchr[mlp->unit.ship.shp_type].m_nplanes &&
499             !mchr[mlp->unit.ship.shp_type].m_nland)
500             continue;
501         if (!mvs) {
502             mvs = mlp;
503             continue;
504         }
505         if (mchr[mlp->unit.ship.shp_type].m_cost * mlp->unit.ship.shp_effic >
506             mchr[mvs->unit.ship.shp_type].m_cost * mvs->unit.ship.shp_effic)
507             mvs = mlp;
508     }
509     return mvs;
510 }
511
512 static int
513 shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
514 {
515     struct emp_qelem *qp;
516     struct emp_qelem *next;
517     struct ulist *mlp;
518     int hard;
519     int easiest = 9876;         /* things start great for victim */
520     int count = 0;
521
522     for (qp = list->q_back; qp != list; qp = next) {
523         next = qp->q_back;
524         mlp = (struct ulist *)qp;
525         if (wantflags &&
526             (mchr[mlp->unit.ship.shp_type].m_flags & wantflags) != wantflags)
527             continue;
528         if (nowantflags &&
529             mchr[mlp->unit.ship.shp_type].m_flags & nowantflags)
530             continue;
531         hard = shp_hardtarget(&mlp->unit.ship);
532         if (hard < easiest)
533             easiest = hard;     /* things get worse for victim */
534         ++count;
535     }
536     return easiest - count;
537 }
538
539 static int
540 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
541                          natid victim)
542 {
543     int dam, sublaunch;
544     int stopping = 0;
545     struct emp_qelem msl_list, *qp, *newqp;
546     struct plist *plp;
547     struct ulist *mvs;
548
549     mvs = most_valuable_ship(list, newx, newy);
550     if (!mvs)
551         return 0;
552
553     msl_sel(&msl_list, newx, newy, victim, P_T | P_MAR, 0, MI_INTERDICT);
554
555     for (qp = msl_list.q_back; qp != &msl_list; qp = newqp) {
556         newqp = qp->q_back;
557         plp = (struct plist *)qp;
558
559         if (mvs && mission_pln_equip(plp, NULL, 'p') >= 0) {
560             if (msl_launch(&plp->plane, EF_SHIP, prship(&mvs->unit.ship),
561                            newx, newy, victim, &sublaunch) < 0)
562                 goto use_up_msl;
563             stopping = 1;
564             if (msl_hit(&plp->plane,
565                         shp_hardtarget(&mvs->unit.ship), EF_SHIP,
566                         N_SHP_MISS, N_SHP_SMISS, sublaunch, victim)) {
567                 dam = pln_damage(&plp->plane, 'p', 1);
568                 mpr(victim,
569                     "missile interdiction mission does %d damage to %s!\n",
570                     dam, prship(&mvs->unit.ship));
571                 shp_damage_one(mvs, dam);
572             } else {
573                 dam = pln_damage(&plp->plane, 'p', 0);
574                 collateral_damage(newx, newy, dam);
575             }
576             mvs = most_valuable_ship(list, newx, newy);
577         use_up_msl:
578             plp->plane.pln_effic = 0;
579             putplane(plp->plane.pln_uid, &plp->plane);
580         }
581         emp_remque(qp);
582         free(qp);
583     }
584
585     return stopping;
586 }
587
588 /* Note that this function has a side effect - it uses coastwatch
589  * ranges to see if it should fire upon a ship.  So, this function
590  * is expected to return positive if a ship is in range, and 0 if a
591  * ship is not in range. */
592 static int
593 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
594 {
595     struct emp_qelem *qp;
596     struct emp_qelem *next;
597     struct ulist *mlp;
598     struct natstr *natp;
599     int vrange;
600
601     natp = getnatp(sectp->sct_own);
602
603     vrange = sectp->sct_type == SCT_RADAR ? 14 : 4;
604     vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
605
606     if (vrange < 1)
607         vrange = 1;
608
609     if (vrange < trange)
610         return 0;
611
612     for (qp = list->q_back; qp != list; qp = next) {
613         next = qp->q_back;
614         mlp = (struct ulist *)qp;
615         if (mchr[mlp->unit.ship.shp_type].m_flags & M_SUB)
616             continue;
617         if (natp->nat_flags & NF_COASTWATCH)
618             wu(0, sectp->sct_own,
619                "%s %s sighted at %s\n",
620                cname(mlp->unit.ship.shp_own),
621                prship(&mlp->unit.ship),
622                xyas(mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
623                     sectp->sct_own));
624         if (opt_HIDDEN)
625             setcont(sectp->sct_own, mlp->unit.ship.shp_own, FOUND_COAST);
626     }
627
628     return 1;
629 }
630
631 static int
632 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy,
633                       natid victim)
634 {
635     struct nstr_sect ns;
636     struct sctstr fsect;
637     int trange, range;
638     int dam;
639     int stopping = 0;
640     int totdam = 0;
641     signed char notified[MAXNOC];
642     int i;
643
644     /* Inform neutral and worse */
645     for (i = 0; i < MAXNOC; ++i) {
646         if (relations_with(i, victim) <= NEUTRAL)
647             notified[i] = 0;
648         else
649             notified[i] = 1;
650     }
651
652     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
653     while (nxtsct(&ns, &fsect)) {
654         if (!fsect.sct_own)
655             continue;
656         if (fsect.sct_own == victim)
657             continue;
658         if (notified[fsect.sct_own])
659             continue;
660         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
661         if (notify_coastguard(list, trange, &fsect))
662             notified[fsect.sct_own] = 1;
663     }
664     if (opt_NO_FORT_FIRE)
665         return 0;               /* Only coastwatch notify in nofortfire */
666     /* Only fire at Hostile ships */
667     for (i = 0; i < MAXNOC; ++i) {
668         if (relations_with(i, victim) >= NEUTRAL)
669             notified[i] = 0;
670     }
671     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
672     while (nxtsct(&ns, &fsect)) {
673         if (!notified[fsect.sct_own])
674             continue;
675         range = roundrange(fortrange(&fsect));
676         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
677         if (trange > range)
678             continue;
679         dam = fort_fire(&fsect);
680         putsect(&fsect);
681         if (dam < 0)
682             continue;
683         stopping = 1;
684         totdam += dam;
685         mpr(victim, "Incoming fire does %d damage!\n", dam);
686 #if 0
687         mpr(victim, "%s fires at you for %d!\n",
688             xyas(fsect.sct_x, fsect.sct_y, victim), dam);
689 #endif
690         wu(0, fsect.sct_own,
691            "%s fires at %s ships in %s for %d!\n",
692            xyas(fsect.sct_x, fsect.sct_y,
693                 fsect.sct_own),
694            cname(victim), xyas(newx, newy, fsect.sct_own), dam);
695         nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
696     }
697     if (totdam > 0)
698         shp_damage(list, totdam, 0, M_SUB, newx, newy);
699     return stopping;
700 }
701
702 static int
703 shp_mission_interdiction(struct emp_qelem *list, coord x, coord y,
704                          natid victim, int subs)
705 {
706     char *what = subs ? "subs" : "ships";
707     int wantflags = subs ? M_SUB : 0;
708     int nowantflags = subs ? 0 : M_SUB;
709     int mission = subs ? MI_SINTERDICT : MI_INTERDICT;
710     int dam;
711
712     dam = unit_interdict(x, y, victim, what,
713                          shp_easiest_target(list, wantflags, nowantflags),
714                          mission);
715     if (dam >= 0)
716         shp_damage(list, dam, wantflags, nowantflags, x, y);
717     return dam >= 0;
718 }
719
720 static int
721 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
722 {
723     int stopping = 0;
724
725     if (shp_contains(list, newx, newy, 0, M_SUB)) {
726         stopping |= shp_fort_interdiction(list, newx, newy, victim);
727
728         if (shp_contains(list, newx, newy, 0, M_SUB)) {
729             stopping |= shp_mission_interdiction(list, newx, newy, victim, 0);
730             stopping |= shp_missile_interdiction(list, newx, newy, victim);
731         }
732     }
733     if (shp_contains(list, newx, newy, M_SUB, 0))
734         stopping |= shp_mission_interdiction(list, newx, newy, victim, 1);
735     return stopping;
736 }
737
738 /* high value of hardtarget is harder to hit */
739 int
740 shp_hardtarget(struct shpstr *sp)
741 {
742     struct sctstr sect;
743     int vis, onsea;
744     struct mchrstr *mcp = mchr + sp->shp_type;
745
746     vis = shp_visib(sp);
747     getsect(sp->shp_x, sp->shp_y, &sect);
748     onsea = sect.sct_type == SCT_WATER;
749     if (mcp->m_flags & M_SUB)
750         vis *= 4;
751     return (int)((sp->shp_effic / 100.0) *
752                  (20 + shp_speed(sp) * onsea / 2.0 - vis));
753 }
754
755 static int
756 shp_hit_mine(struct shpstr *sp)
757 {
758     double m;
759
760     mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
761         xyas(sp->shp_x, sp->shp_y, sp->shp_own));
762
763     nreport(sp->shp_own, N_HIT_MINE, 0, 1);
764
765     m = MINE_DAMAGE();
766     if (mchr[sp->shp_type].m_flags & M_SWEEP)
767         m /= 2.0;
768
769     shipdamage(sp, ldround(m, 1));
770
771     return (int)m;
772 }
773
774 int
775 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor)
776 {
777     struct sctstr sect;
778     struct emp_qelem *qp;
779     struct emp_qelem *next;
780     struct ulist *mlp;
781     coord dx;
782     coord dy;
783     coord newx;
784     coord newy;
785     int move;
786     enum shp_stuck stuck;
787     int stopping = 0;
788     double mobcost;
789     char dp[80];
790
791     if (CANT_HAPPEN(QEMPTY(list)))
792         return 1;
793
794     if (dir <= DIR_STOP || dir >= DIR_VIEW) {
795         shp_nav_put(list, actor);
796         return 1;
797     }
798     dx = diroff[dir][0];
799     dy = diroff[dir][1];
800
801     mlp = (struct ulist *)list->q_back;
802     newx = xnorm(mlp->unit.ship.shp_x + dx);
803     newy = ynorm(mlp->unit.ship.shp_y + dy);
804     getsect(newx, newy, &sect);
805
806     if (sect.sct_own && relations_with(sect.sct_own, actor) < FRIENDLY) {
807         mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
808         return 1;
809     }
810
811     move = 0;
812     for (qp = list->q_back; qp != list; qp = next) {
813         next = qp->q_back;
814         mlp = (struct ulist *)qp;
815         switch (shp_check_nav(&mlp->unit.ship, &sect)) {
816         case SHP_STUCK_NOT:
817             move = 1;
818             break;
819         case SHP_STUCK_CANAL:
820             break;
821         default:
822             CANT_REACH();
823             /* fall through */
824         case SHP_STUCK_CONSTRUCTION:
825         case SHP_STUCK_IMPASSABLE:
826             mpr(actor, "can't go to %s\n", xyas(newx, newy, actor));
827             return 1;
828         }
829     }
830     if (!move) {
831         mpr(actor, "is too large to fit into the canal system at %s\n",
832             xyas(newx, newy, actor));
833         return 1;
834     }
835
836     for (qp = list->q_back; qp != list; qp = next) {
837         next = qp->q_back;
838         mlp = (struct ulist *)qp;
839         stuck = shp_check_nav(&mlp->unit.ship, &sect);
840         if (stuck == SHP_STUCK_CANAL) {
841             sprintf(dp,
842                     "is too large to fit into the canal system at %s",
843                     xyas(newx, newy, actor));
844             shp_stays(actor, dp, mlp);
845             continue;
846         } else if (CANT_HAPPEN(stuck != SHP_STUCK_NOT)) {
847             sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
848             shp_stays(actor, dp, mlp);
849             continue;
850         }
851
852         if (mlp->mobil <= 0.0) {
853             shp_stays(actor, "is out of mobility", mlp);
854             continue;
855         }
856         mobcost = shp_mobcost(&mlp->unit.ship);
857         mlp->unit.ship.shp_x = newx;
858         mlp->unit.ship.shp_y = newy;
859         if (mlp->mobil - mobcost < -127) {
860             mlp->mobil = -127;
861         } else {
862             mlp->mobil -= mobcost;
863         }
864         mlp->unit.ship.shp_mobil = (int)mlp->mobil;
865         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
866
867         /* Now update the map for this ship */
868         rad_map_set(mlp->unit.ship.shp_own,
869                     mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
870                     mlp->unit.ship.shp_effic, mlp->unit.ship.shp_tech,
871                     mchr[mlp->unit.ship.shp_type].m_vrnge);
872     }
873     if (QEMPTY(list))
874         return stopping;
875     stopping |= shp_sweep(list, 0, 0, actor);
876     if (QEMPTY(list))
877         return stopping;
878     stopping |= shp_check_mines(list);
879     if (QEMPTY(list))
880         return stopping;
881     stopping |= shp_interdict(list, newx, newy, actor);
882
883     return stopping;
884 }
885
886 /*
887  * shp_miss_defence
888  * Check for incoming missiles with a P_MAR flag.
889  * Return True=1 if the missile was shotdown.
890  * Or False=0
891  *
892  * Chad Zabel, July 95
893  */
894
895 int
896 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
897 {
898     struct nstr_item ni;
899     struct shpstr ship;
900     int hitchance, hit;
901     double gun, eff, teff;
902
903     snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
904
905     while (nxtitem(&ni, &ship)) {
906         if (!ship.shp_own)
907             continue;
908
909         if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
910             continue;
911
912         if (relations_with(ship.shp_own, bombown) >= NEUTRAL)
913             continue;
914
915         if (ship.shp_effic < 60)
916             continue;
917
918         if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
919             continue;
920         if (ship.shp_item[I_GUN] < 1)   /* we need at least 1 gun */
921             continue;
922         if (!shp_supply(&ship, I_SHELL, 2))
923             continue;
924         ship.shp_item[I_SHELL] -= 2;
925         putship(ship.shp_uid, &ship);
926
927         /* now calculate the odds */
928         gun = shp_usable_guns(&ship);
929         eff = ship.shp_effic / 100.0;
930         teff = ship.shp_tech / (ship.shp_tech + 200.0);
931         /* raise 4.5 for better interception -KHS */
932         hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
933         hitchance = LIMIT_TO(hitchance, 0, 100);
934         hit = pct_chance(hitchance);
935
936         mpr(bombown, "%s anti-missile system activated...%s\n",
937             cname(ship.shp_own),
938             hit ? "KABOOOM!! Missile destroyed\n"
939             : "SWOOSH!!  anti-missile system failed!!");
940         mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
941             ship.shp_uid);
942         mpr(ship.shp_own, "%d%% hitchance...%s\n", hitchance,
943             hit ? "KABOOOM!!  Incoming missile destroyed!\n"
944             : "SWOOSH!!  Missile evades anti-missile systems\n");
945
946         if (hit)
947             return 1;
948     }
949     return 0;                   /* all attempts failed */
950 }
951
952
953 /* Fire missiles at a ship which has fired shells */
954 void
955 shp_missdef(struct shpstr *sp, natid victim)
956 {
957     struct emp_qelem list;
958     struct ulist *mlp;
959     int eff;
960     char buf[512];
961
962     emp_initque(&list);
963     mlp = shp_insque(sp, &list);
964     sprintf(buf, "%s", prship(&mlp->unit.ship));
965
966     eff = sp->shp_effic;
967     shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
968     getship(sp->shp_uid, sp);
969
970     if (!sp->shp_own) {
971         wu(0, victim,
972            "missiles launched in defense did 100%% damage to %s\n",
973            buf);
974         wu(0, victim, "%s sunk!\n", buf);
975     } else if (eff > 0 && sp->shp_effic < eff) {
976         wu(0, victim,
977            "missiles launched in defense did %d%% damage to %s\n",
978            100 * (eff - sp->shp_effic) / eff, buf);
979     }
980     if (!QEMPTY(&list))
981         free(mlp);
982 }
983
984 double
985 shp_mobcost(struct shpstr *sp)
986 {
987     return speed_factor(sp->shp_effic * 0.01 * shp_speed(sp),
988                         sp->shp_tech);
989 }
990
991 /*
992  * Set SP's tech to TLEV along with everything else that depends on it.
993  */
994 void
995 shp_set_tech(struct shpstr *sp, int tlev)
996 {
997     struct mchrstr *mcp = mchr + sp->shp_type;
998
999     if (CANT_HAPPEN(tlev < mcp->m_tech))
1000         tlev = mcp->m_tech;
1001
1002     sp->shp_tech = tlev;
1003 }