]> git.pond.sub.org Git - empserver/blob - src/lib/subs/shpsub.c
Import of Empire 4.2.12
[empserver] / src / lib / subs / shpsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-1999, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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  */
34
35 #include "misc.h"
36 #include "queue.h"
37 #include "player.h"
38 #include "var.h"
39 #include "sect.h"
40 #include "ship.h"
41 #include "plane.h"
42 #include "land.h"
43 #include "news.h"
44 #include "item.h"
45 #include "xy.h"
46 #include "nsc.h"
47 #include "file.h"
48 #include "nat.h"
49 #include "path.h"
50 #include "mission.h"
51 #include "optlist.h"
52 #include "damage.h"
53 #include "prototypes.h"
54
55 extern double tfactfire(natid, double);
56
57 extern int etu_per_update;
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      mlist *mlp;
70     
71     emp_initque(list);
72     while (nxtitem(ni, (s_char *)&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, (int *)&ship)) {
83                 pr("ship #%d inelligible - it's for sale.\n", ship.shp_uid);
84                 continue;
85             }
86         }
87         /* This abuse is better fixed by building a ship with the normal negative
88            mobility that everything else is built with */
89 /*
90         if (opt_MOB_ACCESS) {
91           if (ship.shp_effic < 21 &&
92             ship.shp_mobil < etu_per_update) {
93             pr("%s needs at least %d mob to navigate.\n",
94                prship(&ship), etu_per_update);
95             continue;
96           }
97         }
98 */
99         ship.shp_mission = 0;
100         ship.shp_rflags = 0;
101         bzero(ship.shp_rpath,RET_LEN);
102         putship(ship.shp_uid, &ship);
103         mlp = (struct mlist *) malloc(sizeof(struct mlist));
104         mlp->mcp = mcp;
105         bcopy((s_char *)&ship, (s_char *)&mlp->ship,
106               sizeof(struct shpstr));
107         mlp->mobil = (double)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, int *togetherp, natid actor)
115 {
116     extern int update_pending;
117     struct      emp_qelem *qp;
118     struct      emp_qelem *next;
119     struct      mlist *mlp;
120     struct      sctstr sect;
121     struct      shpstr ship;
122     int vec[I_MAX+1];
123     coord   allx;
124     coord   ally;
125     int first=1;
126     
127     *minmobp = 9876.0;
128     *maxmobp = -9876.0;
129     *togetherp = 1;
130     for (qp = list->q_back; qp != list; qp = next) {
131         next = qp->q_back;
132         mlp = (struct mlist *) qp;
133         getship(mlp->ship.shp_uid, &ship);
134         if (ship.shp_own != actor) {
135             mpr(actor, "%s was sunk at %s\n",
136                 prship(&ship),
137                 xyas(ship.shp_x, ship.shp_y, actor));
138             emp_remque((struct emp_qelem *)mlp);
139             free((s_char *)mlp);
140             continue;
141         }
142         if (opt_SAIL) {
143             if (*ship.shp_path && !update_pending) {
144                 shp_mess("has a sail path", mlp);
145                 mpr(actor, "Use `sail <#> -' to reset\n");
146                 continue;
147             }
148         }
149         /* check crew - uws don't count */
150         getvec(VT_ITEM, vec, (s_char *)&ship, EF_SHIP);
151         if (vec[I_MILIT] == 0 && vec[I_CIVIL] == 0) {
152             shp_mess("is crewless", mlp);
153             continue;
154         }
155         if (!getsect(ship.shp_x, ship.shp_y, &sect)) {
156             shp_mess("was sucked into the sky by a strange looking spaceship", mlp); /* heh -KHS */
157             continue;
158         }
159         switch (shp_check_nav(&sect)) {
160         case CN_CONSTRUCTION:
161             shp_mess("is caught in a construction zone", mlp);
162             continue;
163         case CN_LANDLOCKED:
164             shp_mess("is landlocked", mlp);
165             break;
166         case CN_NAVIGABLE:
167             break;
168         case CN_ERROR:
169         default:
170             shp_mess("was just swallowed by a big green worm", mlp);
171             continue;
172         }
173         if (first){
174             allx = ship.shp_x;
175             ally = ship.shp_y;
176             first = 0;
177         }
178         if (ship.shp_x != allx ||
179             ship.shp_y != ally)
180             *togetherp = 0;
181         if (ship.shp_mobil + 1 < (int)mlp->mobil) {
182             mlp->mobil = (double)ship.shp_mobil;
183         }
184         if (mlp->mobil < *minmobp)
185             *minmobp = mlp->mobil;
186         if (mlp->mobil > *maxmobp)
187             *maxmobp = mlp->mobil;
188         bcopy((s_char *)&ship, (s_char *)&mlp->ship,
189               sizeof(struct shpstr));
190     }
191 }
192
193 void
194 shp_put(struct emp_qelem *list, natid actor)
195 {
196     register struct emp_qelem *qp;
197     register struct emp_qelem *newqp;
198     struct      mlist *mlp;
199     
200     qp = list->q_back;
201     while (qp != list) { 
202         mlp = (struct mlist *) qp;
203         mpr(actor, "%s stopped at %s\n", prship(&mlp->ship),
204             xyas(mlp->ship.shp_x, mlp->ship.shp_y, mlp->ship.shp_own));
205         mlp->ship.shp_mobil = (int)mlp->mobil;
206         putship(mlp->ship.shp_uid, &mlp->ship);
207         newqp = qp->q_back;
208         emp_remque(qp);
209         free((s_char *)qp);
210         qp = newqp;
211     }
212 }
213
214 int
215 shp_sweep(struct emp_qelem *ship_list, int verbose, natid actor)
216 {
217     struct  emp_qelem   *qp;
218     struct  emp_qelem   *next;
219     struct  mlist   *mlp;
220     struct  sctstr  sect;
221     int mines, m, max, shells;
222     int changed = 0;
223     int stopping = 0;
224     double      mobcost;
225     
226     for (qp=ship_list->q_back;qp!=ship_list;qp=next) {
227         next = qp->q_back;
228         mlp = (struct mlist *) qp;
229         if (!(mlp->mcp->m_flags & M_SWEEP)) {
230             if (verbose)
231                 mpr(actor, "%s doesn't have minesweeping capability!\n", prship(&mlp->ship));
232             continue;
233         }
234         if (mlp->mobil <= 0.0) {
235             if (verbose)
236                 mpr(actor, "%s is out of mobility!\n", prship(&mlp->ship));
237             continue;
238         }
239         getsect(mlp->ship.shp_x,mlp->ship.shp_y,&sect);
240         if (sect.sct_type != SCT_WATER) {
241             if (verbose)
242                 mpr(actor, "%s is not at sea.  No mines there!\n", prship(&mlp->ship));
243             continue;
244         }               
245         mobcost = mlp->ship.shp_effic * 0.01 * mlp->ship.shp_speed;
246         mobcost = 480.0 / (mobcost +
247                            techfact(mlp->ship.shp_tech,mobcost));
248         mlp->mobil -= mobcost;
249         mlp->ship.shp_mobil = (int)mlp->mobil;
250         putship(mlp->ship.shp_uid, &mlp->ship);
251         if (!(mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR)))
252             continue;
253         max = vl_find(V_SHELL, mlp->mcp->m_vtype,
254                       mlp->mcp->m_vamt, (int)mlp->mcp->m_nv);
255         shells = getvar(V_SHELL, (s_char *)&mlp->ship, EF_SHIP);
256         for (m=0; mines > 0 && m < 5; m++) {
257             if (chance(0.66)) {
258                 mpr(actor, "Sweep...\n");
259                 mines--;
260                 shells = min(max, shells + 1);
261                 changed |= map_set(actor,
262                                    sect.sct_x,
263                                    sect.sct_y,
264                                    'X', 0);
265             }
266         }
267         putvar(V_MINE, mines, (s_char *)&sect, EF_SECTOR);
268         putvar(V_SHELL, shells, (s_char *)&mlp->ship, EF_SHIP);
269         if (shp_check_one_mines(mlp)) {
270           stopping = 1;
271           emp_remque(qp);
272           free((s_char *)qp);
273         }
274         putship(mlp->ship.shp_uid, &mlp->ship);
275         putsect(&sect);
276     }
277     if (changed)
278         writemap(actor);
279     return stopping;
280 }
281
282 int
283 shp_check_one_mines(struct mlist *mlp)
284 {
285   struct sctstr sect;
286   int mines;
287   int changed = 0;
288   int actor;
289
290   getsect(mlp->ship.shp_x, mlp->ship.shp_y, &sect);
291   if (sect.sct_type != SCT_WATER)
292     return 0;
293   if (!(mines = getvar(V_MINE, (s_char *)&sect, EF_SECTOR)))
294     return 0;
295   if (chance(DMINE_HITCHANCE(mines))) {
296     actor = mlp->ship.shp_own;
297     shp_hit_mine(&mlp->ship, mlp->mcp);
298     mines--;
299     changed |= map_set(actor, sect.sct_x, sect.sct_y, 'X', 0);
300     if (changed)
301         writemap(actor);
302     putvar(V_MINE, mines, (s_char *)&sect, EF_SECTOR);
303     putsect(&sect);
304     putship(mlp->ship.shp_uid, (s_char *)&mlp->ship);
305     if (!mlp->ship.shp_own)
306       return 1;
307   }
308   return 0;
309 }
310
311 int
312 shp_check_mines(struct emp_qelem *ship_list)
313 {
314     struct  emp_qelem   *qp;
315     struct  emp_qelem   *next;
316     struct  mlist   *mlp;
317     int stopping = 0;
318     
319     for (qp=ship_list->q_back;qp!=ship_list;qp=next) {
320         next = qp->q_back;
321         mlp = (struct mlist *) qp;
322         if (shp_check_one_mines(mlp)) {
323           stopping = 1;
324           emp_remque(qp);
325           free((s_char *)qp);
326         }
327     }
328     return stopping;
329 }
330
331 void
332 shp_list(struct emp_qelem *ship_list)
333 {
334     struct  emp_qelem   *qp;
335     struct  emp_qelem   *next;
336     struct  mlist   *mlp;
337     struct      shpstr  *shp;
338     int vec[I_MAX+1];
339     
340     pr("shp#     ship type       x,y   fl  eff mil  sh gun pn he xl ln mob tech\n");
341     
342     for (qp=ship_list->q_back;qp!=ship_list;qp=next) {
343         next = qp->q_back;
344         mlp = (struct mlist *) qp;
345         shp = &mlp->ship;
346         pr("%4d ", shp->shp_uid);
347         pr("%-16.16s ", mlp->mcp->m_name);
348         prxy("%4d,%-4d ", shp->shp_x, shp->shp_y, mlp->ship.shp_own);
349         pr("%1c", shp->shp_fleet);
350         pr("%4d%%", shp->shp_effic);
351         getvec(VT_ITEM, vec, (s_char *)shp, EF_SHIP);
352         pr("%4d", vec[I_MILIT]);
353         pr("%4d", vec[I_SHELL]);
354         pr("%4d", vec[I_GUN]);
355         count_planes(shp);
356         pr("%3d", shp->shp_nplane);
357         pr("%3d",shp->shp_nchoppers);
358         pr("%3d",shp->shp_nxlight);
359         count_units(shp);
360         pr("%3d", shp->shp_nland);
361         pr("%4d", shp->shp_mobil);
362         pr("%4d\n", shp->shp_tech);
363     }
364 }
365
366 void 
367 shp_mess(s_char *str, struct mlist *mlp)
368 {
369     mpr(mlp->ship.shp_own, "%s %s & stays in %s\n",
370         prship(&mlp->ship),
371         str, xyas(mlp->ship.shp_x, mlp->ship.shp_y, mlp->ship.shp_own));
372     mlp->ship.shp_mobil = (int)mlp->mobil;
373     putship(mlp->ship.shp_uid, &mlp->ship);
374     emp_remque((struct emp_qelem *)mlp);
375     free((s_char *)mlp);
376 }
377
378 int
379 shp_check_nav(struct sctstr *sect)
380 {
381     extern struct dchrstr dchr[];
382     
383     switch (dchr[sect->sct_type].d_flg & 03) {
384     case NAVOK:
385         break;
386         
387     case NAV_02:
388         if (sect->sct_effic < 2)
389             return CN_CONSTRUCTION;
390         break;
391     case NAV_60:
392         if (sect->sct_effic < 60)
393             return CN_CONSTRUCTION;
394         break;
395     default:
396         return CN_LANDLOCKED;
397     }
398     return CN_NAVIGABLE;
399 }
400
401 static int
402 shp_count(struct emp_qelem *list, int wantflags, int nowantflags, int x, int y)
403 {
404     struct      emp_qelem *qp;
405     struct      emp_qelem *next;
406     struct      mlist *mlp;
407     int count = 0;
408     
409     for (qp = list->q_back; qp != list; qp = next) {
410                 next = qp->q_back;
411                 mlp = (struct mlist *) qp;
412                 if (mlp->ship.shp_x != x || mlp->ship.shp_y != y)
413                         continue;
414                 if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
415                         continue;
416                 if (nowantflags && mlp->mcp->m_flags & nowantflags)
417                         continue;
418                 ++count;
419     }
420     return count;
421 }
422
423 static void
424 shp_damage_one(struct mlist *mlp, int dam)
425 {
426     shipdamage(&mlp->ship, dam);
427     putship(mlp->ship.shp_uid, &mlp->ship);
428     if (!mlp->ship.shp_own) {
429         emp_remque((struct emp_qelem *)mlp);
430         free((s_char *)mlp);
431     }
432 }
433
434 static int
435 shp_damage(struct emp_qelem *list, int totdam, int wantflags, int nowantflags, int x, int y)
436 {
437     struct      emp_qelem *qp;
438     struct      emp_qelem *next;
439     struct      mlist *mlp;
440     int dam;
441     int count;
442     
443     if (!totdam || !(count = shp_count(list, wantflags, nowantflags, x, y)))
444                 return 0;
445     dam = ldround(((double)totdam/(double)count),1);
446     for (qp = list->q_back; qp != list; qp = next) {
447                 next = qp->q_back;
448                 mlp = (struct mlist *) qp;
449                 if (mlp->ship.shp_x != x || mlp->ship.shp_y != y)
450                         continue;
451                 if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
452                         continue;
453                 if (nowantflags && mlp->mcp->m_flags & nowantflags)
454                         continue;
455                 shp_damage_one(mlp, dam);
456     }
457     return dam;
458 }
459
460 static int
461 shp_contains(struct emp_qelem *list, int newx, int newy, int wantflags, int nowantflags)
462 {
463     struct      emp_qelem *qp;
464     struct      emp_qelem *next;
465     struct      mlist *mlp;
466     
467     for (qp = list->q_back; qp != list; qp = next) {
468         next = qp->q_back;
469         mlp = (struct mlist *) qp;
470 /* If the ship isn't in the requested sector, then continue */
471         if (newx != mlp->ship.shp_x || newy != mlp->ship.shp_y)
472             continue;
473         if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
474             continue;
475         if (nowantflags && mlp->mcp->m_flags & nowantflags)
476             continue;
477         return 1;
478     }
479     return 0;
480 }
481
482 static struct mlist *
483 most_valuable_ship(struct emp_qelem *list)
484 {
485     struct      emp_qelem *qp;
486     struct      emp_qelem *next;
487     struct      mlist *mlp;
488     struct      mlist *mvs = 0;
489     
490     for (qp = list->q_back; qp != list; qp = next) {
491         next = qp->q_back;
492         mlp = (struct mlist *) qp;
493         if (mlp->mcp->m_flags & M_SUB)
494             continue;
495         if (!mlp->mcp->m_nxlight &&
496             !mlp->mcp->m_nchoppers &&
497             mlp->mcp->m_cost < 1000 &&
498             !mlp->mcp->m_nplanes &&
499             !mlp->mcp->m_nland)
500             continue;
501         if (!mvs) {
502             mvs = mlp;
503             continue;
504         }
505         if (mlp->mcp->m_cost * mlp->ship.shp_effic >
506             mvs->mcp->m_cost * mvs->ship.shp_effic)
507             mvs = mlp;
508     }
509     return mvs;
510 }
511
512 static int shp_easiest_target(struct emp_qelem *list, int wantflags, int nowantflags)
513 {
514     struct      emp_qelem *qp;
515     struct      emp_qelem *next;
516     struct      mlist *mlp;
517     int hard;
518     int easiest = 9876; /* things start great for victim */
519     int count = 0;
520     
521     for (qp = list->q_back; qp != list; qp = next) {
522         next = qp->q_back;
523         mlp = (struct mlist *) qp;
524         if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
525             continue;
526         if (nowantflags && mlp->mcp->m_flags & nowantflags)
527             continue;
528         hard = shp_hardtarget(&mlp->ship);
529         if (hard < easiest)
530             easiest = hard; /* things get worse for victim */
531         ++count;
532     }
533     return easiest - count;
534 }
535
536 static int
537 shp_missile_interdiction(struct emp_qelem *list, coord newx, coord newy, natid victim)
538 {
539     int     dam;
540     int twotries;
541     int stopping = 0;
542     struct      emp_qelem msl_list, *qp, *newqp;
543     struct      mlist *mvs;
544     s_char      what[512];
545     
546     msl_sel(&msl_list, newx, newy, victim, P_T|P_MAR, 0, MI_INTERDICT);
547     
548     twotries = 0;
549     while (!QEMPTY(&msl_list) && (mvs = most_valuable_ship(list))) {
550         sprintf(what, "%s", prship(&mvs->ship));
551         dam = msl_launch_mindam(&msl_list, newx, newy,
552                                 shp_hardtarget(&mvs->ship),
553                                 EF_SHIP,
554                                 1,
555                                 what, victim, MI_INTERDICT);
556         if (dam) {
557             mpr(victim, "missile interdiction mission does %d damage to %s!\n", dam, what);
558             shp_damage_one(mvs, dam);
559             twotries = 0;
560             stopping |= 1;
561         } else if (++twotries >= 2) {
562             break;
563         }
564     }
565     qp = msl_list.q_forw;
566     while(qp != msl_list.q_forw) {
567         newqp=qp->q_forw;
568         emp_remque(qp);
569         free(qp);
570         qp = newqp;
571     }
572     
573     return stopping;
574 }
575
576 /* Note that this function has a side effect - it uses coastwatch
577  * ranges to see if it should fire upon a ship.  So, this function
578  * is expected to return positive if a ship is in range, and 0 if a
579  * ship is not in range. */
580 static int
581 notify_coastguard(struct emp_qelem *list, int trange, struct sctstr *sectp)
582 {
583     struct      emp_qelem *qp;
584     struct      emp_qelem *next;
585     struct      mlist *mlp;
586     struct      natstr *natp;
587     int vrange;
588     
589         natp = getnatp(sectp->sct_own);
590     
591     vrange = sectp->sct_type==SCT_RADAR?14:4;
592     vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;
593     
594     if (vrange < 1)
595                 vrange = 1;
596     
597     if (vrange < trange)
598                 return 0;
599     
600         /* We got here, so we could theoretically see the ship.  Now,
601          * do we want to see it in our telebox? If not, return positive
602          * since we could see the ship and want forts to fire. */
603         if (!(natp->nat_flags & NF_COASTWATCH))
604                 return 1;
605
606     for (qp = list->q_back; qp != list; qp = next) {
607                 next = qp->q_back;
608                 mlp = (struct mlist *) qp;
609                 if (mlp->mcp->m_flags & M_SUB)
610                         continue;
611                 wu(0, sectp->sct_own,
612                    "%s %s sighted at %s\n",
613                    cname(mlp->ship.shp_own),
614                    prship(&mlp->ship),
615                    xyas(mlp->ship.shp_x, mlp->ship.shp_y, sectp->sct_own));
616                 if (opt_HIDDEN) {
617                         setcont(sectp->sct_own, mlp->ship.shp_own, FOUND_LOOK);
618                 }
619     }
620     
621     return 1;
622 }
623
624 static int
625 shp_fort_interdiction(struct emp_qelem *list, coord newx, coord newy, natid victim)
626 {
627     extern      int fort_max_interdiction_range;
628     struct      nstr_sect ns;
629     struct      sctstr fsect;
630     int trange;
631     double      range, range2, guneff;
632     int shell, gun;
633     int dam;
634     int totdam = 0;
635     s_char      notified[MAXNOC];
636     int i;
637     
638     /* Inform neutral and worse */
639     for (i = 0; i < MAXNOC; ++i) {
640                 if (getrel(getnatp(i),victim) <= NEUTRAL)
641                         notified[i] = 0;
642                 else
643                         notified[i] = 1;
644     }
645     
646     snxtsct_dist(&ns,newx,newy,fort_max_interdiction_range);
647     while (nxtsct(&ns,&fsect)){
648                 if (!fsect.sct_own)
649                         continue;
650                 if (fsect.sct_own == victim)
651                         continue;
652                 if (notified[fsect.sct_own])
653                         continue;
654                 trange = mapdist(newx, newy,
655                                                  fsect.sct_x, fsect.sct_y);
656                 if (notify_coastguard(list, trange, &fsect))
657                         notified[fsect.sct_own] = 1;
658     }
659     if (opt_NO_FORT_FIRE) return 0; /* Only coastwatch notify in nofortfire */
660     /* Only fire at Hostile ships */
661     for (i = 0; i < MAXNOC; ++i) {
662                 if (getrel(getnatp(i),victim) >= NEUTRAL)
663                         notified[i] = 0;
664         }
665     snxtsct_dist(&ns,newx,newy,fort_max_interdiction_range);
666     while (nxtsct(&ns,&fsect)){
667                 if (!notified[fsect.sct_own])
668                         continue;
669                 if (fsect.sct_type != SCT_FORTR)
670                         continue;
671                 gun=getvar(V_GUN,(s_char *)&fsect,EF_SECTOR);
672                 if (gun < 1)
673                         continue;
674                 range = tfactfire(fsect.sct_own,(double)min(gun,7));
675                 if (fsect.sct_effic > 59)
676                         range++;
677                 range2 = roundrange(range);
678                 trange = mapdist(newx, newy,
679                                                  fsect.sct_x, fsect.sct_y);
680                 if (trange > range2)
681                         continue;
682                 if (getvar(V_MILIT,(s_char *)&fsect,EF_SECTOR)<5)
683                         continue;
684                 shell = getvar(V_SHELL,(s_char *)&fsect,EF_SECTOR);
685                 if (shell < 1)
686                         shell += supply_commod(fsect.sct_own,
687                                                                    fsect.sct_x,fsect.sct_y,I_SHELL,
688                                                                    1);
689                 if (shell < 1)
690                         continue;
691                 shell--;
692                 putvar(V_SHELL,shell,(s_char *)&fsect,EF_SECTOR);
693                 putsect(&fsect);
694                 if (gun > 7)
695                         gun = 7;
696                 guneff = landgun((int)fsect.sct_effic, gun);
697                 dam = (int)guneff;
698                 totdam += dam;
699                 mpr(victim, "Incoming fire does %d damage!\n", dam);
700 /*
701   mpr(victim, "%s fires at you for %d!\n",
702   xyas(fsect.sct_x,fsect.sct_y,victim),
703   dam);
704 */
705                 wu(0,fsect.sct_own,
706                    "%s fires at %s ships in %s for %d!\n",
707                    xyas(fsect.sct_x,fsect.sct_y,
708                                 fsect.sct_own),
709                    cname(victim),
710                    xyas(newx,newy,fsect.sct_own), dam);
711                 nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
712     }
713     if (totdam > 0)
714                 return shp_damage(list, totdam, 0, M_SUB, newx, newy);
715     return 0;
716 }
717
718 int
719 shp_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
720 {
721     int stopping = 0;
722     
723     if (shp_contains(list, newx, newy, 0, M_SUB)) {
724                 stopping |= shp_fort_interdiction(list, newx, newy, victim);
725
726                 if (shp_contains(list, newx, newy, 0, M_SUB)) {
727                         stopping |= shp_damage(list, unit_interdict(newx,newy,victim,"ships", shp_easiest_target(list, 0, M_SUB), MI_INTERDICT), 0, M_SUB, newx, newy);
728                         if (most_valuable_ship(list)) {
729                                 stopping |= shp_missile_interdiction(list,newx,newy,victim);
730                         }
731                 }
732         }
733         if (shp_contains(list, newx, newy, M_SUB, 0)) {
734                 stopping |= shp_damage(list, unit_interdict(newx, newy, victim, "subs", shp_easiest_target(list, M_SUB, 0), MI_SINTERDICT), M_SUB, 0, newx, newy);
735         }
736         return stopping;
737 }
738
739 /* high value of hardtarget is harder to hit */
740 int shp_hardtarget(struct shpstr *sp)
741 {
742     struct      sctstr  sect;
743     int vis, onsea;
744     struct      mchrstr *mcp = mchr + sp->shp_type;
745     
746     vis = sp->shp_visib;
747     getsect(sp->shp_x,sp->shp_y, &sect);
748     onsea = (sect.sct_type == SCT_WATER);
749     if (mcp->m_flags & M_SUB)
750         vis *= 4;
751     return (int)(((double)sp->shp_effic/100.0) *
752                  (20 + (double)sp->shp_speed * onsea/2.0 - vis));
753 }
754
755 int
756 shp_hit_mine(struct shpstr *sp, struct mchrstr *mcp)
757 {
758     double      m;
759     
760     mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
761         xyas(sp->shp_x, sp->shp_y, sp->shp_own));
762     
763     nreport(sp->shp_own, N_HIT_MINE, 0, 1);
764     
765     m = 22.0 + (double)(random() % 21);
766     if (mcp->m_flags & M_SWEEP)
767         m /= 2.0;
768     
769     shipdamage(sp, ldround(m,1));
770     
771     return (int)m;
772 }
773
774 void
775 shp_view(struct emp_qelem *list)
776 {
777     struct      sctstr  sect;
778     struct  emp_qelem   *qp;
779     struct  emp_qelem   *next;
780     struct  mlist   *mlp;
781     
782     for (qp=list->q_back;qp!=list;qp=next) {
783         next = qp->q_back;
784         mlp = (struct mlist *)qp;
785         getsect(mlp->ship.shp_x,mlp->ship.shp_y,&sect);
786         if (mlp->mcp->m_flags & M_FOOD)
787             mpr(mlp->ship.shp_own, "[fert:%d] ", sect.sct_fertil);
788         if (mlp->mcp->m_flags & M_OIL)
789             mpr(mlp->ship.shp_own, "[oil:%d] ", sect.sct_oil);
790         mpr(mlp->ship.shp_own, "%s @ %s %d%% %s\n",
791             prship(&mlp->ship),
792             xyas(mlp->ship.shp_x, mlp->ship.shp_y, player->cnum),
793             sect.sct_effic,
794             dchr[sect.sct_type].d_name);
795     }
796 }
797
798 int
799 shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor, int together)
800 {
801     struct      sctstr  sect;
802     struct  emp_qelem   *qp;
803     struct  emp_qelem   *next;
804     struct  emp_qelem   *nqp;
805     struct  emp_qelem   *nnext;
806     struct  mlist   *mlp;
807     coord       dx;
808     coord       dy;
809     coord   newx;
810     coord   newy;
811     int stopping = 0;
812     double      mobcost;
813     double      tech; /* for mapping */
814     double      tf;   /* for mapping */
815     s_char      dp[80];
816     
817     if (dir <= DIR_STOP || dir >= DIR_VIEW) {
818                 shp_put(list, actor);
819                 return 1;
820     }
821     dx = diroff[dir][0];
822     dy = diroff[dir][1];
823     for (qp=list->q_back;qp!=list;qp=next) {
824                 next = qp->q_back;
825                 mlp = (struct mlist *)qp;
826                 newx = xnorm(mlp->ship.shp_x + dx);
827                 newy = ynorm(mlp->ship.shp_y + dy);
828                 getsect(newx, newy, &sect);
829                 if (shp_check_nav(&sect) != CN_NAVIGABLE ||
830                         (sect.sct_own && actor != sect.sct_own &&
831                          getrel(getnatp(sect.sct_own), actor) < FRIENDLY)) {
832                         if (together) {
833                                 mpr(actor,"can't go to %s\n",xyas(newx, newy, actor));
834                                 return 2;
835                         } else {
836                                 sprintf(dp, "can't go to %s",
837                                                 xyas(newx, newy, actor));
838                                 shp_mess(dp, mlp);
839                                 continue;
840                         }
841                 }
842                 if (opt_BIG_CITY && sect.sct_type == SCT_CAPIT) {
843                         if (mlp->mcp->m_lcm + 2 * mlp->mcp->m_hcm >= 60) {
844                                 sprintf(dp, "is too large to fit into the canal system at %s", xyas(newx, newy, actor));
845                                 shp_mess(dp, mlp);
846                                 continue;
847                         }
848                 }
849                 if (mlp->mobil <= 0.0) {
850                         shp_mess("is out of mobility", mlp);
851                         continue;
852                 }
853                 mobcost = mlp->ship.shp_effic * 0.01 * mlp->ship.shp_speed;
854                 mobcost = 480.0 /
855                         (mobcost + techfact(mlp->ship.shp_tech, mobcost));
856                 mlp->ship.shp_x = newx;
857                 mlp->ship.shp_y = newy;
858                 if (mlp->mobil - mobcost < -127) {
859                         mlp->mobil = -127;
860                 } else {
861                         mlp->mobil -= mobcost;
862                 }
863                 mlp->ship.shp_mobil = (int)mlp->mobil;
864                 putship(mlp->ship.shp_uid, &mlp->ship);
865                 mlp->done = 0; /* We haven't interdicted this ship yet */
866                 
867                 /* Now update the map for this ship */
868                 tech = techfact(mlp->ship.shp_tech, (double)mlp->mcp->m_vrnge);
869                 if (mlp->mcp->m_flags & M_SONAR)
870                         tf = techfact(mlp->ship.shp_tech, 1.0);
871                 else
872                         tf = 0.0;
873                 radmapupd(mlp->ship.shp_own, mlp->ship.shp_x, mlp->ship.shp_y,
874                                   (int)mlp->ship.shp_effic, (int)tech, tf);
875     }
876     if (QEMPTY(list))
877                 return stopping;
878     stopping |= shp_sweep(list, 0, actor);
879     if (QEMPTY(list))
880                 return stopping;
881     stopping |= shp_check_mines(list);
882     if (QEMPTY(list))
883                 return stopping;
884         
885 /* Ok, run through each ship and interdict each coordinate */
886     for (qp = list->q_back; qp != list; qp = next) {
887                 next = qp->q_back;
888                 mlp = (struct mlist *) qp;
889 /* Has this ship been interdicted yet? */
890                 if (mlp->done)
891                         continue;
892                 newx = mlp->ship.shp_x;
893                 newy = mlp->ship.shp_y;
894                 stopping |= shp_interdict(list, newx, newy, actor);
895                 if (QEMPTY(list))
896                         return stopping;
897 /* Now, set all ships in this coordinate to done */
898                 for (nqp = list->q_back; nqp != list; nqp = nnext) {
899                         nnext = nqp->q_back;
900                         mlp = (struct mlist *)nqp;
901                         if (mlp->ship.shp_x == newx && mlp->ship.shp_y == newy)
902                                 mlp->done = 1;
903                 }
904     }
905     
906     return stopping;
907 }
908
909 /*
910  * shp_miss_defence 
911  * Check for incoming missiles with a P_MAR flag. 
912  * Return True=1 if the missile was shotdown.
913  * Or False=0
914  * 
915  * Chad Zabel, July 95
916  */
917
918 int
919 shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget)
920 {
921     struct nstr_item ni;
922     struct shpstr       ship;
923     int         hitchance;
924     int         vec[I_MAX+1];
925     double              gun,eff,teff;
926     
927     snxtitem_dist(&ni,EF_SHIP,dx,dy,1);
928     
929     while (nxtitem(&ni,(caddr_t)&ship)) {
930         if (!ship.shp_own)
931             continue;
932         
933         if (!(mchr[(int)ship.shp_type].m_flags & M_ANTIMISSILE))
934             continue;
935         
936         if (getrel(getnatp(ship.shp_own), bombown) >= NEUTRAL)
937             continue;
938         
939         if (ship.shp_effic < 60)
940             continue;
941         
942         if (getvec(VT_ITEM,vec,(caddr_t)&ship,EF_SHIP) < 0)
943             continue;
944         if (vec[I_MILIT] < 1)   /* do we have mil? */
945             continue;
946         if (vec[I_SHELL] < 2) {     /* do we need shells */
947             if (vec[I_SHELL] += supply_commod(ship.shp_own,
948                                               ship.shp_x,
949                                               ship.shp_y,
950                                               I_SHELL,2) < 2)
951                 continue;
952         }
953         if (vec[I_GUN] < 1)      /* we need at least 1 gun */
954             continue;
955         
956         /* now calculate the odds */
957         gun  =  ((double)min(vec[I_GUN],ship.shp_glim));
958         eff  =  (double)ship.shp_effic / 100.0;
959         teff =  (((double)ship.shp_tech)/(((double)ship.shp_tech)+200.0));
960         /* raise 4.5 for better interception -KHS */
961         hitchance = (int)(gun*eff*teff*4.5) - hardtarget;
962         if (hitchance < 0)
963             hitchance = 0;
964         if (hitchance > 100)
965             hitchance = 100;
966         
967         mpr(bombown,"%s anti-missile system activated...",cname(ship.shp_own));
968         mpr(ship.shp_own,
969             "Ship #%i anti-missile system activated!\n",ship.shp_uid);
970         mpr(ship.shp_own,
971             "%d%% hitchance...", hitchance);
972         /* use ammo */
973         putvar (V_SHELL, vec[I_SHELL]-2,(caddr_t)&ship, EF_SHIP);
974         putship(ship.shp_uid,&ship);
975         
976         if (roll(100) <= hitchance) {
977             mpr(bombown,"KABOOOM!! Missile destroyed\n\n");
978             mpr(ship.shp_own,
979                 "KABOOOM!!  Incoming missile destroyed!\n\n");
980             return 1;
981         } else {
982             mpr(bombown, "SWOOSH!!  anti-missile system failed!!\n");
983             mpr(ship.shp_own,
984                 "SWOOSH!!  Missile evades anti-missile systems\n\n"); 
985         }
986     }
987     return 0;   /* all attempts failed */
988 }
989
990 s_char *
991 shp_path(int together, struct shpstr *shp, s_char *buf)
992 {
993     coord   destx;
994     coord   desty;
995     struct      sctstr d_sect;
996     s_char      *cp;
997     
998     if (!sarg_xy(buf, &destx, &desty))
999         return 0;
1000     if (!together){
1001         mpr(shp->shp_own, "Cannot go to a destination sector if not all starting in the same sector\n");
1002         return 0;
1003     }
1004     if (!getsect(destx, desty, &d_sect)) {
1005         mpr(shp->shp_own, "%d,%d is not a sector\n", destx, desty);
1006         return 0;
1007     }
1008     
1009     cp = (s_char *)BestShipPath(buf, shp->shp_x, shp->shp_y,
1010                                 d_sect.sct_x, d_sect.sct_y, player->cnum);
1011     if (!cp || shp->shp_mobil <= 0) {
1012         mpr(shp->shp_own, "Can't get to '%s' right now.\n",
1013             xyas(d_sect.sct_x, d_sect.sct_y, player->cnum));
1014         return 0;
1015     }
1016     return cp;
1017 }
1018
1019 /* Fire missiles at a ship which has fired shells */
1020 void
1021 shp_missdef(struct shpstr *sp, natid victim)
1022 {
1023     struct      emp_qelem       list;
1024     struct      mlist   *mlp;
1025     int eff;
1026     s_char      buf[512];
1027     
1028     emp_initque(&list);
1029     
1030     mlp = (struct mlist *) malloc(sizeof(struct mlist));
1031     mlp->mcp = &mchr[(int)sp->shp_type];
1032     bcopy((s_char *)sp, (s_char *)&mlp->ship, sizeof(struct shpstr));
1033     mlp->mobil = (double)sp->shp_mobil;
1034     emp_insque(&mlp->queue, &list);
1035     sprintf(buf, "%s", prship(&mlp->ship));
1036     
1037     eff = sp->shp_effic;
1038     if (most_valuable_ship(&list)) {
1039         shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
1040         getship(sp->shp_uid, sp);
1041         
1042         if (!sp->shp_own) {
1043             wu(0, victim, "missiles launched in defense did 100%% damage to %s\n", buf);
1044             wu(0, victim, "%s sunk!\n", buf);
1045         } else if (eff > 0 && sp->shp_effic < eff) {
1046             wu(0, victim, "missiles launched in defense did %d%% damage to %s\n", 100*(eff - sp->shp_effic)/eff, buf);
1047         }
1048     }
1049     if (!QEMPTY(&list))
1050         free(mlp);
1051 }
1052