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