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