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