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