]> git.pond.sub.org Git - empserver/blob - src/lib/subs/shpsub.c
bed6d02f06111b93e998f90da29c101e4c4ab108
[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     double guneff;
591     int shell, gun;
592     int dam;
593     int totdam = 0;
594     signed char notified[MAXNOC];
595     int i;
596
597     /* Inform neutral and worse */
598     for (i = 0; i < MAXNOC; ++i) {
599         if (getrel(getnatp(i), victim) <= NEUTRAL)
600             notified[i] = 0;
601         else
602             notified[i] = 1;
603     }
604
605     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
606     while (nxtsct(&ns, &fsect)) {
607         if (!fsect.sct_own)
608             continue;
609         if (fsect.sct_own == victim)
610             continue;
611         if (notified[fsect.sct_own])
612             continue;
613         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
614         if (notify_coastguard(list, trange, &fsect))
615             notified[fsect.sct_own] = 1;
616     }
617     if (opt_NO_FORT_FIRE)
618         return 0;               /* Only coastwatch notify in nofortfire */
619     /* Only fire at Hostile ships */
620     for (i = 0; i < MAXNOC; ++i) {
621         if (getrel(getnatp(i), victim) >= NEUTRAL)
622             notified[i] = 0;
623     }
624     snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
625     while (nxtsct(&ns, &fsect)) {
626         if (!notified[fsect.sct_own])
627             continue;
628         gun = fsect.sct_item[I_GUN];
629         if (gun < 1)
630             continue;
631         range = roundrange(fortrange(&fsect));
632         trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
633         if (trange > range)
634             continue;
635         if (fsect.sct_item[I_MILIT] < 5)
636             continue;
637         shell = fsect.sct_item[I_SHELL];
638         if (shell < 1)
639             shell += supply_commod(fsect.sct_own, fsect.sct_x, fsect.sct_y,
640                                    I_SHELL, 1);
641         if (shell < 1)
642             continue;
643         shell--;
644         fsect.sct_item[I_SHELL] = shell;
645         putsect(&fsect);
646         if (gun > 7)
647             gun = 7;
648         guneff = landgun((int)fsect.sct_effic, gun);
649         dam = (int)guneff;
650         totdam += dam;
651         mpr(victim, "Incoming fire does %d damage!\n", dam);
652 /*
653   mpr(victim, "%s fires at you for %d!\n",
654   xyas(fsect.sct_x,fsect.sct_y,victim),
655   dam);
656 */
657         wu(0, fsect.sct_own,
658            "%s fires at %s ships in %s for %d!\n",
659            xyas(fsect.sct_x, fsect.sct_y,
660                 fsect.sct_own),
661            cname(victim), xyas(newx, newy, fsect.sct_own), dam);
662         nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
663     }
664     if (totdam > 0)
665         return shp_damage(list, totdam, 0, M_SUB, newx, newy);
666     return 0;
667 }
668
669 static int
670 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
671 {
672     int stopping = 0;
673
674     if (shp_contains(list, newx, newy, 0, M_SUB)) {
675         stopping |= shp_fort_interdiction(list, newx, newy, victim);
676
677         if (shp_contains(list, newx, newy, 0, M_SUB)) {
678             stopping |=
679                 shp_damage(list,
680                            unit_interdict(newx, newy, victim, "ships",
681                                           shp_easiest_target(list, 0, M_SUB),
682                                           MI_INTERDICT),
683                            0, M_SUB, newx, newy);
684             if (most_valuable_ship(list)) {
685                 stopping |=
686                     shp_missile_interdiction(list, newx, newy, victim);
687             }
688         }
689     }
690     if (shp_contains(list, newx, newy, M_SUB, 0)) {
691         stopping |=
692             shp_damage(list,
693                        unit_interdict(newx, newy, victim, "subs",
694                                       shp_easiest_target(list, M_SUB, 0),
695                                       MI_SINTERDICT),
696                        M_SUB, 0, newx, newy);
697     }
698     return stopping;
699 }
700
701 /* high value of hardtarget is harder to hit */
702 int
703 shp_hardtarget(struct shpstr *sp)
704 {
705     struct sctstr sect;
706     int vis, onsea;
707     struct mchrstr *mcp = mchr + sp->shp_type;
708
709     vis = sp->shp_visib;
710     getsect(sp->shp_x, sp->shp_y, &sect);
711     onsea = sect.sct_type == SCT_WATER;
712     if (mcp->m_flags & M_SUB)
713         vis *= 4;
714     return (int)((sp->shp_effic / 100.0) *
715                  (20 + sp->shp_speed * onsea / 2.0 - vis));
716 }
717
718 static int
719 shp_hit_mine(struct shpstr *sp, struct mchrstr *mcp)
720 {
721     double m;
722
723     mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
724         xyas(sp->shp_x, sp->shp_y, sp->shp_own));
725
726     nreport(sp->shp_own, N_HIT_MINE, 0, 1);
727
728     m = MINE_DAMAGE();
729     if (mcp->m_flags & M_SWEEP)
730         m /= 2.0;
731
732     shipdamage(sp, ldround(m, 1));
733
734     return (int)m;
735 }
736
737 int
738 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor,
739                    int together)
740 {
741     struct sctstr sect;
742     struct emp_qelem *qp;
743     struct emp_qelem *next;
744     struct ulist *mlp;
745     struct emp_qelem done;
746     coord dx;
747     coord dy;
748     coord newx;
749     coord newy;
750     int stopping = 0;
751     double mobcost;
752     double tech;                /* for mapping */
753     double tf;                  /* for mapping */
754     char dp[80];
755     int navigate;
756
757     if (dir <= DIR_STOP || dir >= DIR_VIEW) {
758         unit_put(list, actor);
759         return 1;
760     }
761     dx = diroff[dir][0];
762     dy = diroff[dir][1];
763     for (qp = list->q_back; qp != list; qp = next) {
764         next = qp->q_back;
765         mlp = (struct ulist *)qp;
766         newx = xnorm(mlp->unit.ship.shp_x + dx);
767         newy = ynorm(mlp->unit.ship.shp_y + dy);
768         getsect(newx, newy, &sect);
769         navigate = shp_check_nav(&sect, &mlp->unit.ship);
770         if (navigate != CN_NAVIGABLE ||
771             (sect.sct_own && actor != sect.sct_own &&
772              getrel(getnatp(sect.sct_own), actor) < FRIENDLY)) {
773             if (dchr[sect.sct_type].d_nav == NAV_CANAL &&
774                 !(((struct mchrstr *)mlp->chrp)->m_flags & M_CANAL) &&
775                 navigate == CN_LANDLOCKED)
776                 sprintf(dp,
777                         "is too large to fit into the canal system at %s",
778                         xyas(newx, newy, actor));
779             else
780                 sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
781             if (together) {
782                 mpr(actor, "%s\n", dp);
783                 return 2;
784             } else {
785                 shp_mess(dp, mlp);
786                 continue;
787             }
788         }
789
790         if (mlp->mobil <= 0.0) {
791             shp_mess("is out of mobility", mlp);
792             continue;
793         }
794         mobcost = shp_mobcost(&mlp->unit.ship);
795         mlp->unit.ship.shp_x = newx;
796         mlp->unit.ship.shp_y = newy;
797         if (mlp->mobil - mobcost < -127) {
798             mlp->mobil = -127;
799         } else {
800             mlp->mobil -= mobcost;
801         }
802         mlp->unit.ship.shp_mobil = (int)mlp->mobil;
803         putship(mlp->unit.ship.shp_uid, &mlp->unit.ship);
804
805         /* Now update the map for this ship */
806         tech = techfact(mlp->unit.ship.shp_tech,
807                         ((struct mchrstr *)mlp->chrp)->m_vrnge);
808         if (((struct mchrstr *)mlp->chrp)->m_flags & M_SONAR)
809             tf = techfact(mlp->unit.ship.shp_tech, 1.0);
810         else
811             tf = 0.0;
812         radmapupd(mlp->unit.ship.shp_own,
813                   mlp->unit.ship.shp_x, mlp->unit.ship.shp_y,
814                   (int)mlp->unit.ship.shp_effic, (int)tech, tf);
815     }
816     if (QEMPTY(list))
817         return stopping;
818     stopping |= shp_sweep(list, 0, 0, actor);
819     if (QEMPTY(list))
820         return stopping;
821     stopping |= shp_check_mines(list);
822     if (QEMPTY(list))
823         return stopping;
824
825     /* interdict ships sector by sector */
826     emp_initque(&done);
827     while (!QEMPTY(list)) {
828         mlp = (struct ulist *)list->q_back;
829         newx = mlp->unit.ship.shp_x;
830         newy = mlp->unit.ship.shp_y;
831         stopping |= shp_interdict(list, newx, newy, actor);
832         /* move survivors in this sector to done */
833         for (qp = list->q_back; qp != list; qp = next) {
834             next = qp->q_back;
835             mlp = (struct ulist *)qp;
836             if (mlp->unit.ship.shp_x == newx &&
837                 mlp->unit.ship.shp_y == newy) {
838                 emp_remque(qp);
839                 emp_insque(qp, &done);
840             }
841         }
842     }
843     /* assign surviving ships back to list */
844     emp_insque(list, &done);
845     emp_remque(&done);
846
847     return stopping;
848 }
849
850 /*
851  * shp_miss_defence 
852  * Check for incoming missiles with a P_MAR flag. 
853  * Return True=1 if the missile was shotdown.
854  * Or False=0
855  * 
856  * Chad Zabel, July 95
857  */
858
859 int
860 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
861 {
862     struct nstr_item ni;
863     struct shpstr ship;
864     int hitchance;
865     int shell;
866     double gun, eff, teff;
867
868     snxtitem_dist(&ni, EF_SHIP, dx, dy, 1);
869
870     while (nxtitem(&ni, &ship)) {
871         if (!ship.shp_own)
872             continue;
873
874         if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
875             continue;
876
877         if (getrel(getnatp(ship.shp_own), bombown) >= NEUTRAL)
878             continue;
879
880         if (ship.shp_effic < 60)
881             continue;
882
883         shell = ship.shp_item[I_SHELL];
884         if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */
885             continue;
886         if (shell < 2) {        /* do we need shells */
887             shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
888                                    I_SHELL, 2);
889             if (shell < 2)
890                 continue;
891         }
892         if (ship.shp_item[I_GUN] < 1)   /* we need at least 1 gun */
893             continue;
894
895         /* now calculate the odds */
896         gun = MIN(ship.shp_item[I_GUN], ship.shp_glim);
897         eff = ship.shp_effic / 100.0;
898         teff = ship.shp_tech / (ship.shp_tech + 200.0);
899         /* raise 4.5 for better interception -KHS */
900         hitchance = (int)(gun * eff * teff * 4.5) - hardtarget;
901         if (hitchance < 0)
902             hitchance = 0;
903         if (hitchance > 100)
904             hitchance = 100;
905
906         mpr(bombown, "%s anti-missile system activated...",
907             cname(ship.shp_own));
908         mpr(ship.shp_own, "Ship #%i anti-missile system activated!\n",
909             ship.shp_uid);
910         mpr(ship.shp_own, "%d%% hitchance...", hitchance);
911         /* use ammo */
912         ship.shp_item[I_SHELL] = shell - 2;
913         putship(ship.shp_uid, &ship);
914
915         if (roll(100) <= hitchance) {
916             mpr(bombown, "KABOOOM!! Missile destroyed\n\n");
917             mpr(ship.shp_own,
918                 "KABOOOM!!  Incoming missile destroyed!\n\n");
919             return 1;
920         } else {
921             mpr(bombown, "SWOOSH!!  anti-missile system failed!!\n");
922             mpr(ship.shp_own,
923                 "SWOOSH!!  Missile evades anti-missile systems\n\n");
924         }
925     }
926     return 0;                   /* all attempts failed */
927 }
928
929
930 /* Fire missiles at a ship which has fired shells */
931 void
932 shp_missdef(struct shpstr *sp, natid victim)
933 {
934     struct emp_qelem list;
935     struct ulist *mlp;
936     int eff;
937     char buf[512];
938
939     emp_initque(&list);
940
941     mlp = malloc(sizeof(struct ulist));
942     mlp->chrp = (struct empobj_chr *)&mchr[(int)sp->shp_type];
943     mlp->unit.ship = *sp;
944     mlp->mobil = sp->shp_mobil;
945     emp_insque(&mlp->queue, &list);
946     sprintf(buf, "%s", prship(&mlp->unit.ship));
947
948     eff = sp->shp_effic;
949     if (most_valuable_ship(&list)) {
950         shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
951         getship(sp->shp_uid, sp);
952
953         if (!sp->shp_own) {
954             wu(0, victim,
955                "missiles launched in defense did 100%% damage to %s\n",
956                buf);
957             wu(0, victim, "%s sunk!\n", buf);
958         } else if (eff > 0 && sp->shp_effic < eff) {
959             wu(0, victim,
960                "missiles launched in defense did %d%% damage to %s\n",
961                100 * (eff - sp->shp_effic) / eff, buf);
962         }
963     }
964     if (!QEMPTY(&list))
965         free(mlp);
966 }
967
968 double
969 shp_mobcost(struct shpstr *sp)
970 {
971     return speed_factor(sp->shp_effic * 0.01 * sp->shp_speed,
972                         sp->shp_tech);
973 }
974
975 /*
976  * Set SP's tech to TLEV along with everything else that depends on it.
977  */
978 void
979 shp_set_tech(struct shpstr *sp, int tlev)
980 {
981     struct mchrstr *mcp = mchr + sp->shp_type;
982
983     if (CANT_HAPPEN(tlev < mcp->m_tech))
984         tlev = mcp->m_tech;
985
986     sp->shp_tech = tlev;
987     sp->shp_armor = m_armor(mcp, tlev);
988     sp->shp_speed = m_speed(mcp, tlev);
989     sp->shp_visib = m_visib(mcp, tlev);
990     sp->shp_frnge = m_frnge(mcp, tlev);
991     sp->shp_glim  = m_glim(mcp, tlev);
992 }