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