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