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