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