]> git.pond.sub.org Git - empserver/blob - src/lib/subs/shpsub.c
4cfce330b1d9e515a853e99d930c73366ad51d06
[empserver] / src / lib / subs / shpsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  shpsub.c: Ship subroutine stuff
29  * 
30  *  Known contributors to this file:
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1996-2000
33  *     Markus Armbruster, 2006
34  */
35
36 #include <config.h>
37
38 #include <math.h>
39 #include "misc.h"
40 #include "queue.h"
41 #include "player.h"
42 #include "sect.h"
43 #include "ship.h"
44 #include "plane.h"
45 #include "land.h"
46 #include "news.h"
47 #include "item.h"
48 #include "xy.h"
49 #include "nsc.h"
50 #include "file.h"
51 #include "nat.h"
52 #include "path.h"
53 #include "mission.h"
54 #include "optlist.h"
55 #include "damage.h"
56 #include "server.h"
57 #include "prototypes.h"
58
59 static int shp_check_one_mines(struct mlist *);
60 static int shp_hit_mine(struct shpstr *, struct mchrstr *);
61 static void shp_mess(char *, struct mlist *);
62
63 void
64 shp_sel(struct nstr_item *ni, struct emp_qelem *list)
65
66
67     /*  int     wantflags;
68        int      nowantflags;
69      */
70 {
71     struct shpstr ship;
72     struct mchrstr *mcp;
73     struct mlist *mlp;
74
75     emp_initque(list);
76     while (nxtitem(ni, &ship)) {
77         if (!player->owner)
78             continue;
79         mcp = &mchr[(int)ship.shp_type];
80         /* if (wantflags && (mcp->m_flags & wantflags) != wantflags)
81            continue;
82            if (nowantflags && mcp->m_flags & nowantflags)
83            continue;
84          */
85         if (opt_MARKET) {
86             if (ontradingblock(EF_SHIP, &ship)) {
87                 pr("ship #%d inelligible - it's for sale.\n",
88                    ship.shp_uid);
89                 continue;
90             }
91         }
92         /* This abuse is better fixed by building a ship with the normal negative
93            mobility that everything else is built with */
94 /*
95         if (opt_MOB_ACCESS) {
96           if (ship.shp_effic < 21 &&
97             ship.shp_mobil < etu_per_update) {
98             pr("%s needs at least %d mob to navigate.\n",
99                prship(&ship), etu_per_update);
100             continue;
101           }
102         }
103 */
104         ship.shp_mission = 0;
105         ship.shp_rflags = 0;
106         memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
107         putship(ship.shp_uid, &ship);
108         mlp = malloc(sizeof(struct mlist));
109         mlp->mcp = mcp;
110         mlp->ship = ship;
111         mlp->mobil = ship.shp_mobil;
112         emp_insque(&mlp->queue, list);
113     }
114 }
115
116 /* This function assumes that the list was created by shp_sel */
117 void
118 shp_nav(struct emp_qelem *list, double *minmobp, double *maxmobp,
119         int *togetherp, natid actor)
120 {
121     struct emp_qelem *qp;
122     struct emp_qelem *next;
123     struct mlist *mlp;
124     struct sctstr sect;
125     struct shpstr ship;
126     coord allx;
127     coord ally;
128     int first = 1;
129
130     *minmobp = 9876.0;
131     *maxmobp = -9876.0;
132     *togetherp = 1;
133     for (qp = list->q_back; qp != list; qp = next) {
134         next = qp->q_back;
135         mlp = (struct mlist *)qp;
136         getship(mlp->ship.shp_uid, &ship);
137         if (ship.shp_own != actor) {
138             mpr(actor, "%s was sunk at %s\n",
139                 prship(&ship), xyas(ship.shp_x, ship.shp_y, actor));
140             emp_remque((struct emp_qelem *)mlp);
141             free(mlp);
142             continue;
143         }
144         if (opt_SAIL) {
145             if (*ship.shp_path && !update_pending) {
146                 shp_mess("has a sail path", mlp);
147                 mpr(actor, "Use `sail <#> -' to reset\n");
148                 continue;
149             }
150         }
151         /* check crew - uws don't count */
152         if (ship.shp_item[I_MILIT] == 0 && ship.shp_item[I_CIVIL] == 0) {
153             shp_mess("is crewless", mlp);
154             continue;
155         }
156         if (!getsect(ship.shp_x, ship.shp_y, &sect)) {
157             shp_mess("was sucked into the sky by a strange looking spaceship", mlp);    /* heh -KHS */
158             continue;
159         }
160         switch (shp_check_nav(&sect, &ship)) {
161         case CN_CONSTRUCTION:
162             shp_mess("is caught in a construction zone", mlp);
163             continue;
164         case CN_LANDLOCKED:
165             shp_mess("is landlocked", mlp);
166             continue;
167         case CN_NAVIGABLE:
168             break;
169         case CN_ERROR:
170         default:
171             shp_mess("was just swallowed by a big green worm", mlp);
172             continue;
173         }
174         if (first) {
175             allx = ship.shp_x;
176             ally = ship.shp_y;
177             first = 0;
178         }
179         if (ship.shp_x != allx || ship.shp_y != ally)
180             *togetherp = 0;
181         if (ship.shp_mobil + 1 < (int)mlp->mobil) {
182             mlp->mobil = ship.shp_mobil;
183         }
184         if (mlp->mobil < *minmobp)
185             *minmobp = mlp->mobil;
186         if (mlp->mobil > *maxmobp)
187             *maxmobp = mlp->mobil;
188         mlp->ship = ship;
189     }
190 }
191
192 void
193 shp_put(struct emp_qelem *list, natid actor)
194 {
195     struct emp_qelem *qp;
196     struct emp_qelem *newqp;
197     struct mlist *mlp;
198
199     qp = list->q_back;
200     while (qp != list) {
201         mlp = (struct mlist *)qp;
202         mpr(actor, "%s stopped at %s\n", prship(&mlp->ship),
203             xyas(mlp->ship.shp_x, mlp->ship.shp_y, mlp->ship.shp_own));
204         mlp->ship.shp_mobil = (int)mlp->mobil;
205         putship(mlp->ship.shp_uid, &mlp->ship);
206         newqp = qp->q_back;
207         emp_remque(qp);
208         free(qp);
209         qp = newqp;
210     }
211 }
212
213 int
214 shp_sweep(struct emp_qelem *ship_list, int verbose, int takemob, natid actor)
215 {
216     struct emp_qelem *qp;
217     struct emp_qelem *next;
218     struct mlist *mlp;
219     struct sctstr sect;
220     int mines, m, max, shells;
221     int changed = 0;
222     int stopping = 0;
223
224     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
225         next = qp->q_back;
226         mlp = (struct mlist *)qp;
227         if (!(mlp->mcp->m_flags & M_SWEEP)) {
228             if (verbose)
229                 mpr(actor, "%s doesn't have minesweeping capability!\n",
230                     prship(&mlp->ship));
231             continue;
232         }
233         if (takemob && mlp->mobil <= 0.0) {
234             if (verbose)
235                 mpr(actor, "%s is out of mobility!\n", prship(&mlp->ship));
236             continue;
237         }
238         getsect(mlp->ship.shp_x, mlp->ship.shp_y, &sect);
239         if (sect.sct_type != SCT_WATER) {
240             if (verbose)
241                 mpr(actor, "%s is not at sea.  No mines there!\n",
242                     prship(&mlp->ship));
243             continue;
244         }
245         if (takemob) {
246             mlp->mobil -= shp_mobcost(&mlp->ship);
247             mlp->ship.shp_mobil = (int)mlp->mobil;
248         }
249         putship(mlp->ship.shp_uid, &mlp->ship);
250         if (!(mines = sect.sct_mines))
251             continue;
252         max = mlp->mcp->m_item[I_SHELL];
253         shells = mlp->ship.shp_item[I_SHELL];
254         for (m = 0; mines > 0 && m < 5; m++) {
255             if (chance(0.66)) {
256                 mpr(actor, "Sweep...\n");
257                 mines--;
258                 shells = MIN(max, shells + 1);
259                 changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
260             }
261         }
262         sect.sct_mines = mines;
263         mlp->ship.shp_item[I_SHELL] = shells;
264         if (shp_check_one_mines(mlp)) {
265             stopping = 1;
266             emp_remque(qp);
267             free(qp);
268         }
269         putship(mlp->ship.shp_uid, &mlp->ship);
270         putsect(&sect);
271     }
272     if (changed)
273         writemap(actor);
274     return stopping;
275 }
276
277 static int
278 shp_check_one_mines(struct mlist *mlp)
279 {
280     struct sctstr sect;
281     int actor;
282
283     getsect(mlp->ship.shp_x, mlp->ship.shp_y, &sect);
284     if (sect.sct_type != SCT_WATER)
285         return 0;
286     if (!sect.sct_mines)
287         return 0;
288     if (chance(DMINE_HITCHANCE(sect.sct_mines))) {
289         actor = mlp->ship.shp_own;
290         shp_hit_mine(&mlp->ship, mlp->mcp);
291         sect.sct_mines--;
292         if (map_set(actor, sect.sct_x, sect.sct_y, 'X', 0))
293             writemap(actor);
294         putsect(&sect);
295         putship(mlp->ship.shp_uid, &mlp->ship);
296         if (!mlp->ship.shp_own)
297             return 1;
298     }
299     return 0;
300 }
301
302 static int
303 shp_check_mines(struct emp_qelem *ship_list)
304 {
305     struct emp_qelem *qp;
306     struct emp_qelem *next;
307     struct mlist *mlp;
308     int stopping = 0;
309
310     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
311         next = qp->q_back;
312         mlp = (struct mlist *)qp;
313         if (shp_check_one_mines(mlp)) {
314             stopping = 1;
315             emp_remque(qp);
316             free(qp);
317         }
318     }
319     return stopping;
320 }
321
322 void
323 shp_list(struct emp_qelem *ship_list)
324 {
325     struct emp_qelem *qp;
326     struct emp_qelem *next;
327     struct mlist *mlp;
328     struct shpstr *shp;
329
330     pr("shp#     ship type       x,y   fl  eff mil  sh gun pn he xl ln mob tech\n");
331
332     for (qp = ship_list->q_back; qp != ship_list; qp = next) {
333         next = qp->q_back;
334         mlp = (struct mlist *)qp;
335         shp = &mlp->ship;
336         pr("%4d ", shp->shp_uid);
337         pr("%-16.16s ", mlp->mcp->m_name);
338         prxy("%4d,%-4d ", shp->shp_x, shp->shp_y, mlp->ship.shp_own);
339         pr("%1.1s", &shp->shp_fleet);
340         pr("%4d%%", shp->shp_effic);
341         pr("%4d", shp->shp_item[I_MILIT]);
342         pr("%4d", shp->shp_item[I_SHELL]);
343         pr("%4d", shp->shp_item[I_GUN]);
344         count_planes(shp);
345         pr("%3d", shp->shp_nplane);
346         pr("%3d", shp->shp_nchoppers);
347         pr("%3d", shp->shp_nxlight);
348         count_units(shp);
349         pr("%3d", shp->shp_nland);
350         pr("%4d", shp->shp_mobil);
351         pr("%4d\n", shp->shp_tech);
352     }
353 }
354
355 static void
356 shp_mess(char *str, struct mlist *mlp)
357 {
358     mpr(mlp->ship.shp_own, "%s %s & stays in %s\n",
359         prship(&mlp->ship),
360         str, xyas(mlp->ship.shp_x, mlp->ship.shp_y, mlp->ship.shp_own));
361     mlp->ship.shp_mobil = (int)mlp->mobil;
362     putship(mlp->ship.shp_uid, &mlp->ship);
363     emp_remque((struct emp_qelem *)mlp);
364     free(mlp);
365 }
366
367 int
368 shp_check_nav(struct sctstr *sect, struct shpstr *shp)
369 {
370     switch (dchr[sect->sct_type].d_nav) {
371     case NAVOK:
372         break;
373     case NAV_CANAL:
374         if (mchr[(int)shp->shp_type].m_flags & M_CANAL) {
375             if (sect->sct_effic < 2)
376                 return CN_CONSTRUCTION;
377         } else
378             return CN_LANDLOCKED;
379         break;
380     case NAV_02:
381         if (sect->sct_effic < 2)
382             return CN_CONSTRUCTION;
383         break;
384     case NAV_60:
385         if (sect->sct_effic < 60)
386             return CN_CONSTRUCTION;
387         break;
388     default:
389         return CN_LANDLOCKED;
390     }
391     return CN_NAVIGABLE;
392 }
393
394 int
395 sect_has_dock(struct sctstr *sect)
396 {
397     switch (dchr[sect->sct_type].d_nav) {
398     case NAV_02:
399         return 1;
400     default:
401         return 0;
402     }
403 }
404
405 static int
406 shp_count(struct emp_qelem *list, int wantflags, int nowantflags,
407           int x, int y)
408 {
409     struct emp_qelem *qp;
410     struct emp_qelem *next;
411     struct mlist *mlp;
412     int count = 0;
413
414     for (qp = list->q_back; qp != list; qp = next) {
415         next = qp->q_back;
416         mlp = (struct mlist *)qp;
417         if (mlp->ship.shp_x != x || mlp->ship.shp_y != y)
418             continue;
419         if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
420             continue;
421         if (nowantflags && mlp->mcp->m_flags & nowantflags)
422             continue;
423         ++count;
424     }
425     return count;
426 }
427
428 static void
429 shp_damage_one(struct mlist *mlp, int dam)
430 {
431     shipdamage(&mlp->ship, dam);
432     putship(mlp->ship.shp_uid, &mlp->ship);
433     if (!mlp->ship.shp_own) {
434         emp_remque((struct emp_qelem *)mlp);
435         free(mlp);
436     }
437 }
438
439 static int
440 shp_damage(struct emp_qelem *list, int totdam, int wantflags,
441            int nowantflags, int x, int y)
442 {
443     struct emp_qelem *qp;
444     struct emp_qelem *next;
445     struct mlist *mlp;
446     int dam;
447     int count;
448
449     if (!totdam
450         || !(count = shp_count(list, wantflags, nowantflags, x, y)))
451         return 0;
452     dam = ldround((double)totdam / count, 1);
453     for (qp = list->q_back; qp != list; qp = next) {
454         next = qp->q_back;
455         mlp = (struct mlist *)qp;
456         if (mlp->ship.shp_x != x || mlp->ship.shp_y != y)
457             continue;
458         if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
459             continue;
460         if (nowantflags && mlp->mcp->m_flags & nowantflags)
461             continue;
462         shp_damage_one(mlp, dam);
463     }
464     return dam;
465 }
466
467 static int
468 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags,
469              int nowantflags)
470 {
471     struct emp_qelem *qp;
472     struct emp_qelem *next;
473     struct mlist *mlp;
474
475     for (qp = list->q_back; qp != list; qp = next) {
476         next = qp->q_back;
477         mlp = (struct mlist *)qp;
478 /* If the ship isn't in the requested sector, then continue */
479         if (newx != mlp->ship.shp_x || newy != mlp->ship.shp_y)
480             continue;
481         if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
482             continue;
483         if (nowantflags && mlp->mcp->m_flags & nowantflags)
484             continue;
485         return 1;
486     }
487     return 0;
488 }
489
490 static struct mlist *
491 most_valuable_ship(struct emp_qelem *list)
492 {
493     struct emp_qelem *qp;
494     struct emp_qelem *next;
495     struct mlist *mlp;
496     struct mlist *mvs = 0;
497
498     for (qp = list->q_back; qp != list; qp = next) {
499         next = qp->q_back;
500         mlp = (struct mlist *)qp;
501         if (mlp->mcp->m_flags & M_SUB)
502             continue;
503         if (!mlp->mcp->m_nxlight &&
504             !mlp->mcp->m_nchoppers &&
505             mlp->mcp->m_cost < 1000 &&
506             !mlp->mcp->m_nplanes && !mlp->mcp->m_nland)
507             continue;
508         if (!mvs) {
509             mvs = mlp;
510             continue;
511         }
512         if (mlp->mcp->m_cost * mlp->ship.shp_effic >
513             mvs->mcp->m_cost * mvs->ship.shp_effic)
514             mvs = mlp;
515     }
516     return mvs;
517 }
518
519 static int
520 shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
521 {
522     struct emp_qelem *qp;
523     struct emp_qelem *next;
524     struct mlist *mlp;
525     int hard;
526     int easiest = 9876;         /* things start great for victim */
527     int count = 0;
528
529     for (qp = list->q_back; qp != list; qp = next) {
530         next = qp->q_back;
531         mlp = (struct mlist *)qp;
532         if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
533             continue;
534         if (nowantflags && mlp->mcp->m_flags & nowantflags)
535             continue;
536         hard = shp_hardtarget(&mlp->ship);
537         if (hard < easiest)
538             easiest = hard;     /* things get worse for victim */
539         ++count;
540     }
541     return easiest - count;
542 }
543
544 static int
545 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
546                          natid victim)
547 {
548     int dam;
549     int twotries;
550     int stopping = 0;
551     struct emp_qelem msl_list, *qp, *newqp;
552     struct mlist *mvs;
553     char what[512];
554
555     msl_sel(&msl_list, newx, newy, victim, P_T | P_MAR, 0, MI_INTERDICT);
556
557     twotries = 0;
558     while (!QEMPTY(&msl_list) && (mvs = most_valuable_ship(list))) {
559         sprintf(what, "%s", prship(&mvs->ship));
560         dam = msl_launch_mindam(&msl_list, newx, newy,
561                                 shp_hardtarget(&mvs->ship),
562                                 EF_SHIP, 1, what, victim, MI_INTERDICT);
563         if (dam) {
564             mpr(victim,
565                 "missile interdiction mission does %d damage to %s!\n",
566                 dam, what);
567             shp_damage_one(mvs, dam);
568             twotries = 0;
569             stopping |= 1;
570         } else if (++twotries >= 2) {
571             break;
572         }
573     }
574     qp = msl_list.q_forw;
575     while (qp != msl_list.q_forw) {
576         newqp = qp->q_forw;
577         emp_remque(qp);
578         free(qp);
579         qp = newqp;
580     }
581
582     return stopping;
583 }
584
585 /* Note that this function has a side effect - it uses coastwatch
586  * ranges to see if it should fire upon a ship.  So, this function
587  * is expected to return positive if a ship is in range, and 0 if a
588  * ship is not in range. */
589 static int
590 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
591 {
592     struct emp_qelem *qp;
593     struct emp_qelem *next;
594     struct mlist *mlp;
595     struct natstr *natp;
596     int vrange;
597
598     natp = getnatp(sectp->sct_own);
599
600     vrange = sectp->sct_type == SCT_RADAR ? 14 : 4;
601     vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
602
603     if (vrange < 1)
604         vrange = 1;
605
606     if (vrange < trange)
607         return 0;
608
609     for (qp = list->q_back; qp != list; qp = next) {
610         next = qp->q_back;
611         mlp = (struct mlist *)qp;
612         if (mlp->mcp->m_flags & M_SUB)
613             continue;
614         if (natp->nat_flags & NF_COASTWATCH)
615             wu(0, sectp->sct_own,
616                "%s %s sighted at %s\n",
617                cname(mlp->ship.shp_own),
618                prship(&mlp->ship),
619                xyas(mlp->ship.shp_x, mlp->ship.shp_y, sectp->sct_own));
620         if (opt_HIDDEN)
621             setcont(sectp->sct_own, mlp->ship.shp_own, FOUND_COAST);
622     }
623
624     return 1;
625 }
626
627 static int
628 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy,
629                       natid victim)
630 {
631     struct nstr_sect ns;
632     struct sctstr fsect;
633     int trange, range;
634     double guneff;
635     int shell, gun;
636     int dam;
637     int totdam = 0;
638     signed char notified[MAXNOC];
639     int i;
640
641     /* Inform neutral and worse */
642     for (i = 0; i < MAXNOC; ++i) {
643         if (getrel(getnatp(i), victim) <= NEUTRAL)
644             notified[i] = 0;
645         else
646             notified[i] = 1;
647     }
648
649     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
650     while (nxtsct(&ns, &fsect)) {
651         if (!fsect.sct_own)
652             continue;
653         if (fsect.sct_own == victim)
654             continue;
655         if (notified[fsect.sct_own])
656             continue;
657         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
658         if (notify_coastguard(list, trange, &fsect))
659             notified[fsect.sct_own] = 1;
660     }
661     if (opt_NO_FORT_FIRE)
662         return 0;               /* Only coastwatch notify in nofortfire */
663     /* Only fire at Hostile ships */
664     for (i = 0; i < MAXNOC; ++i) {
665         if (getrel(getnatp(i), victim) >= NEUTRAL)
666             notified[i] = 0;
667     }
668     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
669     while (nxtsct(&ns, &fsect)) {
670         if (!notified[fsect.sct_own])
671             continue;
672         gun = fsect.sct_item[I_GUN];
673         if (gun < 1)
674             continue;
675         range = roundrange(fortrange(&fsect));
676         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
677         if (trange > range)
678             continue;
679         if (fsect.sct_item[I_MILIT] < 5)
680             continue;
681         shell = fsect.sct_item[I_SHELL];
682         if (shell < 1)
683             shell += supply_commod(fsect.sct_own, fsect.sct_x, fsect.sct_y,
684                                    I_SHELL, 1);
685         if (shell < 1)
686             continue;
687         shell--;
688         fsect.sct_item[I_SHELL] = shell;
689         putsect(&fsect);
690         if (gun > 7)
691             gun = 7;
692         guneff = landgun((int)fsect.sct_effic, gun);
693         dam = (int)guneff;
694         totdam += dam;
695         mpr(victim, "Incoming fire does %d damage!\n", dam);
696 /*
697   mpr(victim, "%s fires at you for %d!\n",
698   xyas(fsect.sct_x,fsect.sct_y,victim),
699   dam);
700 */
701         wu(0, fsect.sct_own,
702            "%s fires at %s ships in %s for %d!\n",
703            xyas(fsect.sct_x, fsect.sct_y,
704                 fsect.sct_own),
705            cname(victim), xyas(newx, newy, fsect.sct_own), dam);
706         nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
707     }
708     if (totdam > 0)
709         return shp_damage(list, totdam, 0, M_SUB, newx, newy);
710     return 0;
711 }
712
713 static int
714 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
715 {
716     int stopping = 0;
717
718     if (shp_contains(list, newx, newy, 0, M_SUB)) {
719         stopping |= shp_fort_interdiction(list, newx, newy, victim);
720
721         if (shp_contains(list, newx, newy, 0, M_SUB)) {
722             stopping |=
723                 shp_damage(list,
724                            unit_interdict(newx, newy, victim, "ships",
725                                           shp_easiest_target(list, 0, M_SUB),
726                                           MI_INTERDICT),
727                            0, M_SUB, newx, newy);
728             if (most_valuable_ship(list)) {
729                 stopping |=
730                     shp_missile_interdiction(list, newx, newy, victim);
731             }
732         }
733     }
734     if (shp_contains(list, newx, newy, M_SUB, 0)) {
735         stopping |=
736             shp_damage(list,
737                        unit_interdict(newx, newy, victim, "subs",
738                                       shp_easiest_target(list, M_SUB, 0),
739                                       MI_SINTERDICT),
740                        M_SUB, 0, newx, newy);
741     }
742     return stopping;
743 }
744
745 /* high value of hardtarget is harder to hit */
746 int
747 shp_hardtarget(struct shpstr *sp)
748 {
749     struct sctstr sect;
750     int vis, onsea;
751     struct mchrstr *mcp = mchr + sp->shp_type;
752
753     vis = sp->shp_visib;
754     getsect(sp->shp_x, sp->shp_y, &sect);
755     onsea = sect.sct_type == SCT_WATER;
756     if (mcp->m_flags & M_SUB)
757         vis *= 4;
758     return (int)((sp->shp_effic / 100.0) *
759                  (20 + sp->shp_speed * onsea / 2.0 - vis));
760 }
761
762 static int
763 shp_hit_mine(struct shpstr *sp, struct mchrstr *mcp)
764 {
765     double m;
766
767     mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
768         xyas(sp->shp_x, sp->shp_y, sp->shp_own));
769
770     nreport(sp->shp_own, N_HIT_MINE, 0, 1);
771
772     m = MINE_DAMAGE();
773     if (mcp->m_flags & M_SWEEP)
774         m /= 2.0;
775
776     shipdamage(sp, ldround(m, 1));
777
778     return (int)m;
779 }
780
781 void
782 shp_view(struct emp_qelem *list)
783 {
784     struct sctstr sect;
785     struct emp_qelem *qp;
786     struct emp_qelem *next;
787     struct mlist *mlp;
788
789     for (qp = list->q_back; qp != list; qp = next) {
790         next = qp->q_back;
791         mlp = (struct mlist *)qp;
792         getsect(mlp->ship.shp_x, mlp->ship.shp_y, &sect);
793         if (mlp->mcp->m_flags & M_FOOD)
794             mpr(mlp->ship.shp_own, "[fert:%d] ", sect.sct_fertil);
795         if (mlp->mcp->m_flags & M_OIL)
796             mpr(mlp->ship.shp_own, "[oil:%d] ", sect.sct_oil);
797         mpr(mlp->ship.shp_own, "%s @ %s %d%% %s\n",
798             prship(&mlp->ship),
799             xyas(mlp->ship.shp_x, mlp->ship.shp_y, player->cnum),
800             sect.sct_effic, dchr[sect.sct_type].d_name);
801     }
802 }
803
804 int
805 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor,
806                    int together)
807 {
808     struct sctstr sect;
809     struct emp_qelem *qp;
810     struct emp_qelem *next;
811     struct mlist *mlp;
812     struct emp_qelem done;
813     coord dx;
814     coord dy;
815     coord newx;
816     coord newy;
817     int stopping = 0;
818     double mobcost;
819     double tech;                /* for mapping */
820     double tf;                  /* for mapping */
821     char dp[80];
822     int navigate;
823
824     if (dir <= DIR_STOP || dir >= DIR_VIEW) {
825         shp_put(list, actor);
826         return 1;
827     }
828     dx = diroff[dir][0];
829     dy = diroff[dir][1];
830     for (qp = list->q_back; qp != list; qp = next) {
831         next = qp->q_back;
832         mlp = (struct mlist *)qp;
833         newx = xnorm(mlp->ship.shp_x + dx);
834         newy = ynorm(mlp->ship.shp_y + dy);
835         getsect(newx, newy, &sect);
836         navigate = shp_check_nav(&sect, &mlp->ship);
837         if (navigate != CN_NAVIGABLE ||
838             (sect.sct_own && actor != sect.sct_own &&
839              getrel(getnatp(sect.sct_own), actor) < FRIENDLY)) {
840             if (dchr[sect.sct_type].d_nav == NAV_CANAL &&
841                 !(mlp->mcp->m_flags & M_CANAL) &&
842                 navigate == CN_LANDLOCKED)
843                 sprintf(dp,
844                         "is too large to fit into the canal system at %s",
845                         xyas(newx, newy, actor));
846             else
847                 sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
848             if (together) {
849                 mpr(actor, "%s\n", dp);
850                 return 2;
851             } else {
852                 shp_mess(dp, mlp);
853                 continue;
854             }
855         }
856
857         if (mlp->mobil <= 0.0) {
858             shp_mess("is out of mobility", mlp);
859             continue;
860         }
861         mobcost = shp_mobcost(&mlp->ship);
862         mlp->ship.shp_x = newx;
863         mlp->ship.shp_y = newy;
864         if (mlp->mobil - mobcost < -127) {
865             mlp->mobil = -127;
866         } else {
867             mlp->mobil -= mobcost;
868         }
869         mlp->ship.shp_mobil = (int)mlp->mobil;
870         putship(mlp->ship.shp_uid, &mlp->ship);
871
872         /* Now update the map for this ship */
873         tech = techfact(mlp->ship.shp_tech, mlp->mcp->m_vrnge);
874         if (mlp->mcp->m_flags & M_SONAR)
875             tf = techfact(mlp->ship.shp_tech, 1.0);
876         else
877             tf = 0.0;
878         radmapupd(mlp->ship.shp_own, mlp->ship.shp_x, mlp->ship.shp_y,
879                   (int)mlp->ship.shp_effic, (int)tech, tf);
880     }
881     if (QEMPTY(list))
882         return stopping;
883     stopping |= shp_sweep(list, 0, 0, actor);
884     if (QEMPTY(list))
885         return stopping;
886     stopping |= shp_check_mines(list);
887     if (QEMPTY(list))
888         return stopping;
889
890     /* interdict ships sector by sector */
891     emp_initque(&done);
892     while (!QEMPTY(list)) {
893         mlp = (struct mlist *)list->q_back;
894         newx = mlp->ship.shp_x;
895         newy = mlp->ship.shp_y;
896         stopping |= shp_interdict(list, newx, newy, actor);
897         /* move survivors in this sector to done */
898         for (qp = list->q_back; qp != list; qp = next) {
899             next = qp->q_back;
900             mlp = (struct mlist *)qp;
901             if (mlp->ship.shp_x == newx && mlp->ship.shp_y == newy) {
902                 emp_remque(qp);
903                 emp_insque(qp, &done);
904             }
905         }
906     }
907     /* assign surviving ships back to list */
908     emp_insque(list, &done);
909     emp_remque(&done);
910
911     return stopping;
912 }
913
914 /*
915  * shp_miss_defence 
916  * Check for incoming missiles with a P_MAR flag. 
917  * Return True=1 if the missile was shotdown.
918  * Or False=0
919  * 
920  * Chad Zabel, July 95
921  */
922
923 int
924 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
925 {
926     struct nstr_item ni;
927     struct shpstr ship;
928     int hitchance;
929     int shell;
930     double gun, eff, teff;
931
932     snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
933
934     while (nxtitem(&ni, &ship)) {
935         if (!ship.shp_own)
936             continue;
937
938         if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
939             continue;
940
941         if (getrel(getnatp(ship.shp_own), bombown) >= NEUTRAL)
942             continue;
943
944         if (ship.shp_effic < 60)
945             continue;
946
947         shell = ship.shp_item[I_SHELL];
948         if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
949             continue;
950         if (shell < 2) {        /* do we need shells */
951             shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
952                                    I_SHELL, 2);
953             if (shell < 2)
954                 continue;
955         }
956         if (ship.shp_item[I_GUN] < 1)   /* we need at least 1 gun */
957             continue;
958
959         /* now calculate the odds */
960         gun = MIN(ship.shp_item[I_GUN], ship.shp_glim);
961         eff = ship.shp_effic / 100.0;
962         teff = ship.shp_tech / (ship.shp_tech + 200.0);
963         /* raise 4.5 for better interception -KHS */
964         hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
965         if (hitchance < 0)
966             hitchance = 0;
967         if (hitchance > 100)
968             hitchance = 100;
969
970         mpr(bombown, "%s anti-missile system activated...",
971             cname(ship.shp_own));
972         mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
973             ship.shp_uid);
974         mpr(ship.shp_own, "%d%% hitchance...", hitchance);
975         /* use ammo */
976         ship.shp_item[I_SHELL] = shell - 2;
977         putship(ship.shp_uid, &ship);
978
979         if (roll(100) <= hitchance) {
980             mpr(bombown, "KABOOOM!! Missile destroyed\n\n");
981             mpr(ship.shp_own,
982                 "KABOOOM!!  Incoming missile destroyed!\n\n");
983             return 1;
984         } else {
985             mpr(bombown, "SWOOSH!!  anti-missile system failed!!\n");
986             mpr(ship.shp_own,
987                 "SWOOSH!!  Missile evades anti-missile systems\n\n");
988         }
989     }
990     return 0;                   /* all attempts failed */
991 }
992
993 char *
994 shp_path(int together, struct shpstr *shp, char *buf)
995 {
996     coord destx;
997     coord desty;
998     struct sctstr d_sect;
999     char *cp;
1000
1001     if (!sarg_xy(buf, &destx, &desty))
1002         return 0;
1003     if (!together) {
1004         mpr(shp->shp_own,
1005             "Cannot go to a destination sector if not all starting in the same sector\n");
1006         return 0;
1007     }
1008     if (!getsect(destx, desty, &d_sect)) {
1009         mpr(shp->shp_own, "%d,%d is not a sector\n", destx, desty);
1010         return 0;
1011     }
1012
1013     cp = BestShipPath(buf, shp->shp_x, shp->shp_y,
1014                       d_sect.sct_x, d_sect.sct_y, player->cnum);
1015     if (!cp || shp->shp_mobil <= 0) {
1016         mpr(shp->shp_own, "Can't get to '%s' right now.\n",
1017             xyas(d_sect.sct_x, d_sect.sct_y, player->cnum));
1018         return 0;
1019     }
1020     return cp;
1021 }
1022
1023 /* Fire missiles at a ship which has fired shells */
1024 void
1025 shp_missdef(struct shpstr *sp, natid victim)
1026 {
1027     struct emp_qelem list;
1028     struct mlist *mlp;
1029     int eff;
1030     char buf[512];
1031
1032     emp_initque(&list);
1033
1034     mlp = malloc(sizeof(struct mlist));
1035     mlp->mcp = &mchr[(int)sp->shp_type];
1036     mlp->ship = *sp;
1037     mlp->mobil = sp->shp_mobil;
1038     emp_insque(&mlp->queue, &list);
1039     sprintf(buf, "%s", prship(&mlp->ship));
1040
1041     eff = sp->shp_effic;
1042     if (most_valuable_ship(&list)) {
1043         shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
1044         getship(sp->shp_uid, sp);
1045
1046         if (!sp->shp_own) {
1047             wu(0, victim,
1048                "missiles launched in defense did 100%% damage to %s\n",
1049                buf);
1050             wu(0, victim, "%s sunk!\n", buf);
1051         } else if (eff > 0 && sp->shp_effic < eff) {
1052             wu(0, victim,
1053                "missiles launched in defense did %d%% damage to %s\n",
1054                100 * (eff - sp->shp_effic) / eff, buf);
1055         }
1056     }
1057     if (!QEMPTY(&list))
1058         free(mlp);
1059 }
1060
1061 double
1062 shp_mobcost(struct shpstr *sp)
1063 {
1064     return speed_factor(sp->shp_effic * 0.01 * sp->shp_speed,
1065                         sp->shp_tech);
1066 }
1067
1068 /*
1069  * Set SP's tech to TLEV along with everything else that depends on it.
1070  */
1071 void
1072 shp_set_tech(struct shpstr *sp, int tlev)
1073 {
1074     struct mchrstr *mcp = mchr + sp->shp_type;
1075     int tech_diff = tlev - mcp->m_tech;
1076
1077     if (CANT_HAPPEN(tech_diff < 0)) {
1078       tlev -= tech_diff;
1079       tech_diff = 0;
1080     }
1081
1082     sp->shp_tech = tlev;
1083     sp->shp_armor = (short)SHP_DEF(mcp->m_armor, tech_diff);
1084     sp->shp_speed = (short)SHP_SPD(mcp->m_speed, tech_diff);
1085     sp->shp_visib = (short)SHP_VIS(mcp->m_visib, tech_diff);
1086     sp->shp_frnge = (short)SHP_RNG(mcp->m_frnge, tech_diff);
1087     sp->shp_glim  = (short)SHP_FIR(mcp->m_glim, tech_diff);
1088 }