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