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