]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Simplify quiet_bigdef()
[empserver] / src / lib / commands / mfir.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  *  multifire.c: Fire at other sectors/ships
29  * 
30  *  Known contributors to this file:
31  *     Steve McClure, 2000
32  */
33
34 #include <config.h>
35
36 #include "commands.h"
37 #include "empobj.h"
38 #include "optlist.h"
39 #include "retreat.h"
40
41 enum targ_type {
42     targ_land, targ_ship, targ_sub, targ_unit, targ_bogus
43 };
44
45 struct flist {
46     struct emp_qelem queue;     /* list of fired things */
47     int type;                   /* ship? otherwise sector */
48     int uid;                    /* ship uid */
49     coord x, y;                 /* sector coords */
50     int defdam;                 /* damage defenders did */
51     int victim;                 /* who I was shooting at */
52 };
53
54 static void add_to_fired_queue(struct emp_qelem *, struct emp_qelem *);
55 static int defend(struct emp_qelem *al,
56                   struct emp_qelem *dl,
57                   enum targ_type target,
58                   enum targ_type attacker,
59                   struct sctstr *vsect,
60                   struct sctstr *fsect,
61                   struct shpstr *vship,
62                   struct shpstr *fship, int fx, int fy, int *nd);
63 static void do_defdam(struct emp_qelem *, double);
64 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
65                         coord, int *);
66 static void use_ammo(struct emp_qelem *);
67
68 int
69 multifire(void)
70 {
71     static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
72     char vbuf[20];
73     char *ptr;
74     double range;
75     int trange, range2;
76     coord fx;
77     coord fy;
78     coord x;
79     coord y;
80     int mil;
81     int shots;
82     int dam;
83     int totaldefdam = 0;
84     int fshipno;
85     int vshipno;
86     double prb;
87     natid vict;
88     struct shpstr fship;
89     struct lndstr fland;
90     struct sctstr fsect;
91     struct shpstr vship;
92     struct sctstr vsect;
93     enum targ_type target, attacker, orig_attacker;
94     int rel;
95     struct natstr *natp;
96     struct nstr_item nbst;
97     int type;
98     char *p;
99     int nfiring = 0;
100     int ndefending = 0;
101     union empobj_storage item;
102     struct emp_qelem fired, defended;
103     double odds;
104     char buf[1024];
105
106     emp_initque(&fired);
107     emp_initque(&defended);
108     if (!(p = getstarg(player->argp[1],
109                        "Firing from ship(s), sect(s), or land unit(s)? ",
110                        buf)))
111         return RET_SYN;
112     type = ef_byname_from(p, ef_with_guns);
113     if (type == EF_SECTOR) {
114         if (opt_NO_FORT_FIRE) {
115             pr("Fort firing is disabled.\n");
116             return RET_FAIL;
117         }
118         orig_attacker = attacker = targ_land;
119         shots = 1;
120     } else if (type == EF_SHIP) {
121         orig_attacker = attacker = targ_ship;
122     } else if (type == EF_LAND) {
123         orig_attacker = attacker = targ_unit;
124     } else {
125         pr("Ships, land units or sectors only!\n");
126         return RET_SYN;
127     }
128     if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
129         || *ptr == '\0')
130         return RET_SYN;
131
132     if (!snxtitem(&nbst, type, ptr))
133         return RET_SYN;
134
135     if (player->aborted) {
136         pr("Fire aborted.\n");
137         return RET_OK;
138     }
139     while (nxtitem(&nbst, &item)) {
140         attacker = orig_attacker;
141         if (attacker == targ_unit) {
142             if (!getland(item.land.lnd_uid, &fland))
143                 continue;
144             if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
145                 continue;
146             if (item.land.lnd_own != player->cnum)
147                 continue;
148
149             if (lchr[fland.lnd_type].l_dam == 0) {
150                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
151                 continue;
152             }
153             if (fland.lnd_item[I_MILIT] < 1) {
154                 pr("Unit %d cannot fire because it has no military!\n",
155                    fland.lnd_uid);
156                 continue;
157             }
158             if (fland.lnd_ship >= 0) {
159                 pr("Unit %d cannot fire because it is on a ship!\n",
160                    fland.lnd_uid);
161                 continue;
162             }
163             if (fland.lnd_land >= 0) {
164                 pr("Unit %d cannot fire because it is on a land unit!\n",
165                    fland.lnd_uid);
166                 continue;
167             }
168             if (fland.lnd_effic < LAND_MINFIREEFF) {
169                 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
170                    fland.lnd_uid, LAND_MINFIREEFF);
171                 continue;
172             }
173             if (fland.lnd_item[I_SHELL] == 0) {
174                 pr("%s -- not enough shells\n", prland(&fland));
175                 continue;
176             }
177         } else if (attacker == targ_ship) {
178             if (!getship(item.ship.shp_uid, &fship))
179                 continue;
180             if (item.ship.shp_own != player->cnum)
181                 continue;
182             if (item.ship.shp_item[I_MILIT] < 1) {
183                 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
184                 continue;
185             }
186             if (mchr[item.ship.shp_type].m_glim == 0) {
187                 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
188                 continue;
189             }
190             if (item.ship.shp_item[I_GUN] == 0) {
191                 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
192                 continue;
193             }
194             if (item.ship.shp_item[I_SHELL] == 0) {
195                 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
196                 continue;
197             }
198             if (item.ship.shp_effic < 60) {
199                 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
200                 continue;
201             }
202             fshipno = fship.shp_uid;
203         } else if (attacker == targ_land) {
204             if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
205                 continue;
206             if (item.sect.sct_own != player->cnum)
207                 continue;
208             if (item.sect.sct_type != SCT_FORTR)
209                 continue;
210             if (item.sect.sct_effic < FORTEFF) {
211                 pr("Fort not efficient enough to fire!\n");
212                 continue;
213             }
214             if (item.sect.sct_item[I_GUN] == 0) {
215                 pr("Not enough guns in sector %s!\n",
216                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
217                 continue;
218             }
219             if (item.sect.sct_item[I_SHELL] == 0) {
220                 pr("Not enough shells in sector %s!\n",
221                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
222                 continue;
223             }
224             if (item.sect.sct_item[I_MILIT] < 5) {
225                 pr("Not enough military in sector %s!\n",
226                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
227                 continue;
228             }
229             pr("\nSector %s firing\n",
230                xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
231         }
232         if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
233             || *ptr == '\0')
234             continue;
235         if (player->aborted) {
236             pr("Fire aborted.\n");
237             continue;
238         }
239         ptr[19] = 0;
240         (void)strcpy(vbuf, ptr);
241         if (issector(vbuf))
242             target = targ_land;
243         else
244             target = targ_ship;
245         if (target == targ_ship) {
246             vshipno = atoi(vbuf);
247             if (vshipno < 0 || !getship(vshipno, &vship) ||
248                 (!vship.shp_own)) {
249                 pr("No such ship exists!\n");
250                 continue;
251             }
252             target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
253                 targ_sub : targ_ship;
254             vict = vship.shp_own;
255             x = vship.shp_x;
256             y = vship.shp_y;
257             if (!getsect(x, y, &vsect)) {
258                 pr("No such sector exists!\n");
259                 continue;
260             }
261         } else {
262             if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
263                 pr("No such sector exists!\n");
264                 continue;
265             }
266             /* We check the sector type, but we only use it for damage, not
267                reporting.  That way, you don't get extra information you wouldn't
268                normally get.  Besides, what if they want to slam water? :)  */
269             if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
270                 target = targ_bogus;
271             else
272                 target = targ_land;
273             vict = vsect.sct_own;
274             x = vsect.sct_x;
275             y = vsect.sct_y;
276         }
277         if (attacker == targ_ship) {
278             if (fship.shp_own != player->cnum) {
279                 pr("Not your ship!\n");
280                 continue;
281             }
282             if (target == targ_sub || target == targ_ship) {
283                 if (fship.shp_uid == vship.shp_uid) {
284                     pr("You can't fire upon yourself!\n");
285                     continue;
286                 }
287             }
288             fx = fship.shp_x;
289             fy = fship.shp_y;
290 /*
291   attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
292   targ_sub : targ_ship;
293   if (attacker == targ_sub){
294   pr("Subs may not fire normally.. use torpedo.\n");
295   continue;
296   }
297 */
298             attacker = targ_ship;
299             if ((mil = fship.shp_item[I_MILIT]) < 1) {
300                 pr("Not enough military for firing crew.\n");
301                 continue;
302             }
303             if (fship.shp_effic < 60) {
304                 pr("Ship #%d is crippled (%d%%)\n",
305                    fshipno, fship.shp_effic);
306                 continue;
307             }
308             range = shp_fire_range(&fship);
309             range2 = roundrange(range);
310             pr("range is %d.00 (%.2f)\n", range2, range);
311             if (target == targ_sub
312                 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
313                 dam = shp_dchrg(&fship);
314                 putship(fship.shp_uid, &fship);
315                 if (dam < 0) {
316                     pr("Not enough shells for depth charge!\n");
317                     continue;
318                 }
319             } else {
320                 if (target == targ_sub)
321                     /* Don't tell it's a sub */
322                     range2 = -1;
323                 if (fship.shp_item[I_GUN] == 0) {
324                     pr("Insufficient arms.\n");
325                     continue;
326                 }
327                 dam = shp_fire(&fship);
328                 putship(fship.shp_uid, &fship);
329                 if (dam <= 0) {
330                     pr("Klick!     ...\n");
331                     continue;
332                 }
333             }
334             if (opt_NOMOBCOST == 0) {
335                 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
336                 putship(fship.shp_uid, &fship);
337             }
338         } else if (attacker == targ_unit) {
339             if (fland.lnd_own != player->cnum) {
340                 pr("Not your unit!\n");
341                 continue;
342             }
343
344             if (target == targ_land) {
345                 if (fland.lnd_x == vsect.sct_x
346                     && fland.lnd_y == vsect.sct_y) {
347                     pr("You can't fire upon yourself!\n");
348                     continue;
349                 }
350             }
351
352             fx = fland.lnd_x;
353             fy = fland.lnd_y;
354
355             if (lchr[fland.lnd_type].l_dam == 0) {
356                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
357                 continue;
358             }
359             if (fland.lnd_item[I_GUN] == 0) {
360                 pr("%s -- not enough guns\n", prland(&fland));
361                 continue;
362             }
363
364             range = lnd_fire_range(&fland);
365             range2 = roundrange(range);
366             pr("range is %d.00 (%.2f)\n", range2, range);
367             if (target == targ_sub) {
368                 /* Don't tell it's a sub */
369                 range2 = -1;
370             }
371
372             dam = lnd_fire(&fland);
373             putland(fland.lnd_uid, &fland);
374             if (dam < 0) {
375                 pr("Klick!     ...\n");
376                 continue;
377             }
378             if (target == targ_ship) {
379                 if (chance(lnd_acc(&fland) / 100.0))
380                     dam = ldround(dam / 2.0, 1);
381             }
382         } else {
383             fx = fsect.sct_x;
384             fy = fsect.sct_y;
385             if (fsect.sct_own != player->cnum ||
386                 fsect.sct_type != SCT_FORTR) {
387                 pr("No fortress at %s\n",
388                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
389                 continue;
390             }
391             if (target == targ_land) {
392                 if (fsect.sct_x == vsect.sct_x
393                     && fsect.sct_y == vsect.sct_y) {
394                     pr("You can't fire upon yourself!\n");
395                     continue;
396                 }
397             }
398             attacker = targ_land;
399             if (fsect.sct_item[I_GUN] == 0) {
400                 pr("Insufficient arms.\n");
401                 continue;
402             }
403             if (fsect.sct_item[I_MILIT] < 5) {
404                 pr("Not enough military for firing crew.\n");
405                 continue;
406             }
407             dam = fort_fire(&fsect);
408             putsect(&fsect);
409             if (dam < 0) {
410                 pr("Klick!     ...\n");
411                 continue;
412             }
413             range = fortrange(&fsect);
414             range2 = roundrange(range);
415             pr("range is %d.00 (%.2f)\n", range2, range);
416             if (target == targ_sub) {
417                 /* Don't tell it's a sub */
418                 range2 = -1;
419             }
420         }
421         trange = mapdist(x, y, fx, fy);
422         if (trange > range2) {
423             pr("Target out of range.\n");
424 /*
425                         switch (target) {
426                         case targ_land:
427                         case targ_bogus:
428                                 pr("Target out of range.  Thud.\n");
429                                 break ;
430                         default:
431                                 pr("Target ship out of range.  Splash.\n");
432                                 break ;
433                         }       
434  */
435             switch (attacker) {
436             case targ_land:
437                 putsect(&fsect);
438                 break;
439             case targ_unit:
440                 fland.lnd_mission = 0;
441                 putland(fland.lnd_uid, &fland);
442                 break;
443             default:
444                 fship.shp_mission = 0;
445                 putship(fship.shp_uid, &fship);
446             }
447             continue;
448         }
449 /*
450                 if (target == targ_bogus) {
451                         if (vsect.sct_type == SCT_SANCT) {
452                                 pr("%s is a %s!!\n", vbuf,
453                                    dchr[SCT_SANCT].d_name);
454                                 continue;
455                         } else if (vsect.sct_type == SCT_WATER) {
456                                 pr("You must specify a ship in sector %s!\n",
457                                    vbuf);
458                                 continue;
459                         }
460                 }
461 */
462         switch (target) {
463         case targ_ship:
464             if (!trechk(player->cnum, vict, SEAFIR))
465                 continue;
466             break;
467         case targ_sub:
468             if (!trechk(player->cnum, vict, SUBFIR))
469                 continue;
470             break;
471         case targ_unit:
472         case targ_land:
473             if (!trechk(player->cnum, vict, LANFIR))
474                 continue;
475             break;
476         default:
477             break;
478         }
479
480         if (opt_SLOW_WAR) {
481             if (target == targ_land) {
482                 natp = getnatp(player->cnum);
483                 rel = getrel(natp, vict);
484                 if ((rel != AT_WAR) && (player->cnum != vict) &&
485                     (vict) && (vsect.sct_oldown != player->cnum)) {
486                     pr("You're not at war with them!\n");
487                     continue;
488                 }
489             }
490         }
491         nfiring++;
492         switch (target) {
493         case targ_sub:
494             pr_beep();
495             pr("Kawhomp!!!\n");
496             if (vship.shp_rflags & RET_DCHRGED)
497                 retreat_ship(&vship, 'd');
498             break;
499         default:
500             pr_beep();
501             pr("Kaboom!!!\n");
502             prb = range2 ? (double)trange / range2 : 1.0;
503             prb *= prb;
504             if (chance(prb)) {
505                 pr("Wind deflects shell%s.\n", splur(shots));
506 /*                      dam = (int)(dam / 2.0);*/
507                 dam *= (90 - (random() % 11)) / 100.0;
508                 if (dam < 0)
509                     dam = 0;
510             }
511             break;
512         }
513         switch (target) {
514         case targ_bogus:
515         case targ_land:
516             nreport(player->cnum, N_SCT_SHELL, vict, 1);
517             if (vict && vict != player->cnum)
518                 wu(0, vict,
519                    "Country #%d shelled sector %s for %d damage.\n",
520                    player->cnum, xyas(x, y, vict), dam);
521             pr("Shell%s hit sector %s for %d damage.\n",
522                splur(shots), xyas(x, y, player->cnum), dam);
523             /* Ok, it wasn't a bogus target, so do damage. */
524             if (target != targ_bogus)
525                 sectdamage(&vsect, dam, 0);
526             break;
527         case targ_ship:
528             nreport(player->cnum, N_SHP_SHELL, vict, 1);
529             /* fall through */
530         default:
531             if ((target != targ_sub) ||
532                 ((vship.shp_rflags & RET_DCHRGED) == 0))
533                 check_retreat_and_do_shipdamage(&vship, dam);
534             else
535                 shipdamage(&vship, dam);
536             if (vict) {
537                 wu(0, vict,
538                    "Country #%d shelled %s in %s for %d damage.\n",
539                    player->cnum, prship(&vship),
540                    xyas(vship.shp_x, vship.shp_y, vict), dam);
541             }
542             pr("Shell%s hit %s in %s for %d damage.\n",
543                splur(shots), prsub(&vship),
544                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
545
546             if (vship.shp_effic < SHIP_MINEFF)
547                 pr("%s sunk!\n", prsub(&vship));
548
549             break;
550         }
551         /*  Ok, now, check if we had a bogus target.  If so,
552            just continue on, since there is no defender. */
553         if (target == targ_bogus)
554             continue;
555         if (attacker == targ_unit) {
556             attacker = targ_land;
557             getsect(fland.lnd_x, fland.lnd_y, &fsect);
558         }
559         totaldefdam =
560             defend(&fired, &defended, target, attacker, &vsect, &fsect,
561                    &vship, &fship, fx, fy, &ndefending);
562         switch (target) {
563         case targ_land:
564             putsect(&vsect);
565             break;
566         default:
567             putship(vship.shp_uid, &vship);
568             break;
569         }
570         if ((totaldefdam == 0) && (target == targ_ship))
571             if (vship.shp_rflags & RET_HELPLESS)
572                 retreat_ship(&vship, 'h');
573         switch (attacker) {
574         case targ_land:
575             putsect(&fsect);
576             break;
577         default:
578             if ((target == targ_ship) || (target == targ_sub)) {
579                 if (fship.shp_effic > SHIP_MINEFF) {
580                     shp_missdef(&fship, vict);
581                 };
582             };
583             putship(fship.shp_uid, &fship);
584             break;
585         }
586     }
587
588     use_ammo(&defended);
589     if (nfiring)
590         odds = ((double)ndefending) / ((double)nfiring);
591     else
592         odds = 1.0;
593     do_defdam(&fired, odds);
594     return RET_OK;
595 }
596
597 static int
598 defend(struct emp_qelem *al, struct emp_qelem *dl, enum targ_type target,
599        enum targ_type attacker, struct sctstr *vsect, struct sctstr *fsect,
600        struct shpstr *vship, struct shpstr *fship, int fx, int fy, int *nd)
601 {
602
603     int dam;
604     int vict, nfiring = 0;
605     struct flist *fp;
606     int aown;
607
608     if (attacker == targ_land)
609         aown = fsect->sct_own;
610     else
611         aown = fship->shp_own;
612
613     if (target == targ_land)
614         vict = vsect->sct_own;
615     else
616         vict = vship->shp_own;
617
618     if (0 !=
619         (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
620         if (nfiring > *nd)
621             *nd = nfiring;
622         fp = malloc(sizeof(struct flist));
623         memset(fp, 0, sizeof(struct flist));
624         fp->defdam = dam;
625         fp->victim = vict;
626         switch (attacker) {
627         case targ_land:
628             fp->x = fsect->sct_x;
629             fp->y = fsect->sct_y;
630             fp->type = targ_land;
631             break;
632         default:
633             fp->type = targ_ship;
634             fp->uid = fship->shp_uid;
635             break;
636         }
637         emp_insque(&fp->queue, al);
638     }
639
640     return dam;
641 }
642
643 static void
644 do_defdam(struct emp_qelem *list, double odds)
645 {
646
647     int dam, vict, first = 1;
648     struct flist *fp;
649     struct shpstr ship;
650     struct sctstr sect;
651     struct emp_qelem *qp, *next;
652
653     for (qp = list->q_forw; qp != list; qp = next) {
654         next = qp->q_forw;
655         fp = (struct flist *)qp;
656         if (fp->type == targ_ship) {
657             if (!getship(fp->uid, &ship) || !ship.shp_own)
658                 continue;
659         }
660         if (first) {
661             pr_beep();
662             pr("\nDefenders fire back!\n");
663             first = 0;
664         }
665         dam = odds * fp->defdam;
666
667         if (fp->type == targ_ship) {
668             vict = fp->victim;
669             pr("Return fire hit %s in %s for %d damage.\n",
670                prship(&ship),
671                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
672             if (vict)
673                 wu(0, vict,
674                    "Return fire hit %s in %s for %d damage.\n",
675                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
676             shipdamage(&ship, dam);
677             putship(ship.shp_uid, &ship);
678         } else {
679             getsect(fp->x, fp->y, &sect);
680             vict = fp->victim;
681             pr("Return fire hit sector %s for %d damage.\n",
682                xyas(fp->x, fp->y, player->cnum), dam);
683             sectdamage(&sect, dam, 0);
684             putsect(&sect);
685             if (vict)
686                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
687                    xyas(fp->x, fp->y, vict), dam);
688         }
689         emp_remque(&fp->queue);
690         free(fp);
691     }
692 }
693
694 static int
695 quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
696              coord ax, coord ay, int *nfiring)
697 {
698     double erange;
699     struct shpstr ship;
700     struct lndstr land;
701     struct nstr_item ni;
702     int dam, dam2, rel, rel2;
703     struct sctstr firing;
704     struct nstr_sect ns;
705     struct flist *fp;
706
707     if (own == 0)
708         return 0;
709     dam = 0;
710     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
711     while (nxtitem(&ni, &ship)) {
712         if (ship.shp_own == 0)
713             continue;
714
715         if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
716             (attacker == targ_land))
717             continue;
718
719         rel = getrel(getnatp(ship.shp_own), own);
720         rel2 = getrel(getnatp(ship.shp_own), aown);
721         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
722             continue;
723         /* Don't shoot yourself */
724         if (ship.shp_own == aown)
725             continue;
726         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
727             erange = torprange(&ship);
728             if (roundrange(erange) < ni.curdist)
729                 continue;
730             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
731                 continue;
732             dam2 = shp_torp(&ship, 0);
733             /* no putship(&ship) because ammo is charged in use_ammo() */
734             if (dam2 < 0)
735                 continue;
736             if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
737                 dam2 = 0;
738         } else {
739             erange = shp_fire_range(&ship);
740             if (roundrange(erange) < ni.curdist)
741                 continue;
742             dam2 = shp_fire(&ship);
743             /* no putship(&ship) because ammo is charged in use_ammo() */
744             if (dam2 < 0)
745                 continue;
746             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
747         }
748         (*nfiring)++;
749         fp = malloc(sizeof(struct flist));
750         memset(fp, 0, sizeof(struct flist));
751         fp->type = targ_ship;
752         fp->uid = ship.shp_uid;
753         add_to_fired_queue(&fp->queue, list);
754         dam += dam2;
755     }
756     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
757     while (nxtitem(&ni, &land)) {
758         if (land.lnd_own == 0)
759             continue;
760         /* Don't shoot yourself */
761         if (land.lnd_own == aown)
762             continue;
763
764         rel = getrel(getnatp(land.lnd_own), own);
765         rel2 = getrel(getnatp(land.lnd_own), aown);
766
767         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
768             continue;
769
770         erange = lnd_fire_range(&land);
771         if (roundrange(erange) < ni.curdist)
772             continue;
773
774         dam2 = lnd_fire(&land);
775         /* no putland(&land) because ammo is charged in use_ammo() */
776         if (dam2 < 0)
777             continue;
778
779         (*nfiring)++;
780         fp = malloc(sizeof(struct flist));
781         memset(fp, 0, sizeof(struct flist));
782         fp->type = targ_unit;
783         fp->uid = land.lnd_uid;
784         add_to_fired_queue(&fp->queue, list);
785         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
786         dam += dam2;
787     }
788
789     /*
790      * Determine if any nearby gun-equipped sectors are within
791      * range and able to fire at an attacker.  Firing sectors
792      * need to have guns, shells, and military.  Sector being
793      * attacked is x,y -- attacker is at ax,ay.
794      */
795
796     if (!opt_NO_FORT_FIRE) {
797         snxtsct_dist(&ns, ax, ay, 8);
798         while (nxtsct(&ns, &firing)) {
799             if (firing.sct_own == 0)
800                 continue;
801             rel = getrel(getnatp(firing.sct_own), own);
802             rel2 = getrel(getnatp(firing.sct_own), aown);
803
804             if ((firing.sct_own != own) &&
805                 ((rel != ALLIED) || (rel2 != AT_WAR)))
806                 continue;
807             /* Don't shoot yourself */
808             if (firing.sct_own == aown)
809                 continue;
810             erange = fortrange(&firing);
811             if (roundrange(erange) < ns.curdist)
812                 continue;
813
814             dam2 = fort_fire(&firing);
815             /* no putsect(&firing) because ammo is charged in use_ammo() */
816             if (dam2 < 0)
817                 continue;
818             (*nfiring)++;
819             fp = malloc(sizeof(struct flist));
820             memset(fp, 0, sizeof(struct flist));
821             fp->x = firing.sct_x;
822             fp->y = firing.sct_y;
823             fp->type = targ_land;
824             add_to_fired_queue(&fp->queue, list);
825             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
826             dam += dam2;
827         }
828     }
829
830     return *nfiring == 0 ? 0 : dam / *nfiring;
831 }
832
833 static void
834 use_ammo(struct emp_qelem *list)
835 {
836     struct emp_qelem *qp, *next;
837     struct flist *fp;
838     struct shpstr ship;
839     struct lndstr land;
840     struct sctstr sect;
841     int shell;
842     short *item;
843
844     /* use 1 shell from everyone */
845     for (qp = list->q_forw; qp != list; qp = next) {
846         next = qp->q_forw;
847         fp = (struct flist *)qp;
848         if (fp->type == targ_ship) {
849             getship(fp->uid, &ship);
850             item = ship.shp_item;
851             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
852                 shell = item[I_SHELL];
853                 shell -= SHP_TORP_SHELLS - 1;
854                 if (shell < 0)
855                     shell = 0;
856                 item[I_SHELL] = shell;
857                 putship(ship.shp_uid, &ship);
858                 /* mob cost = 1/2 a sect's mob */
859                 ship.shp_mobil -= shp_mobcost(&ship) / 2.0;
860             }
861         } else if (fp->type == targ_land) {
862             getsect(fp->x, fp->y, &sect);
863             item = sect.sct_item;
864         } else {
865             getland(fp->uid, &land);
866             item = land.lnd_item;
867         }
868         shell = item[I_SHELL];
869         shell--;
870         if (shell < 0)
871             shell = 0;
872         item[I_SHELL] = shell;
873         if (fp->type == targ_ship)
874             putship(ship.shp_uid, &ship);
875         else if (fp->type == targ_land)
876             putsect(&sect);
877         else
878             putland(land.lnd_uid, &land);
879
880         emp_remque(&fp->queue);
881         free(fp);
882     }
883
884 }
885
886 static void
887 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
888 {
889     struct emp_qelem *qp;
890     struct flist *fp;
891     struct flist *ep = (struct flist *)elem;
892
893     /* Don't put them on the list if they're already there */
894     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
895         fp = (struct flist *)qp;
896         if (fp->type == targ_land
897             ? fp->x == ep->x && fp->y == ep->y
898             : fp->uid == ep->uid) {
899             free(ep);
900             return;
901         }
902     }
903     emp_insque(elem, list);
904 }