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