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