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