]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Fix trailing whitespace
[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  *     Markus Armbruster, 2004-2008
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     short 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 mil;
76     int dam;
77     int totaldefdam = 0;
78     int vshipno;
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 (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
115         return RET_SYN;
116
117     while (nxtitem(&nbst, &item)) {
118         if (type == EF_LAND) {
119             if (!getland(item.land.lnd_uid, &fland))
120                 continue;
121             if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
122                 continue;
123             if (item.land.lnd_own != player->cnum)
124                 continue;
125
126             if (lchr[fland.lnd_type].l_dam == 0) {
127                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
128                 continue;
129             }
130             if (fland.lnd_item[I_MILIT] < 1) {
131                 pr("Unit %d cannot fire because it has no military!\n",
132                    fland.lnd_uid);
133                 continue;
134             }
135             if (fland.lnd_ship >= 0) {
136                 pr("Unit %d cannot fire because it is on a ship!\n",
137                    fland.lnd_uid);
138                 continue;
139             }
140             if (fland.lnd_land >= 0) {
141                 pr("Unit %d cannot fire because it is on a land unit!\n",
142                    fland.lnd_uid);
143                 continue;
144             }
145             if (fland.lnd_effic < LAND_MINFIREEFF) {
146                 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
147                    fland.lnd_uid, LAND_MINFIREEFF);
148                 continue;
149             }
150             if (fland.lnd_item[I_SHELL] == 0) {
151                 pr("%s -- not enough shells\n", prland(&fland));
152                 continue;
153             }
154             fx = fland.lnd_x;
155             fy = fland.lnd_y;
156         } else if (type == EF_SHIP) {
157             if (!getship(item.ship.shp_uid, &fship))
158                 continue;
159             if (item.ship.shp_own != player->cnum)
160                 continue;
161             if (item.ship.shp_item[I_MILIT] < 1) {
162                 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
163                 continue;
164             }
165             if (mchr[item.ship.shp_type].m_glim == 0) {
166                 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
167                 continue;
168             }
169             if (item.ship.shp_item[I_GUN] == 0) {
170                 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
171                 continue;
172             }
173             if (item.ship.shp_item[I_SHELL] == 0) {
174                 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
175                 continue;
176             }
177             if (item.ship.shp_effic < 60) {
178                 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
179                 continue;
180             }
181             fx = fship.shp_x;
182             fy = fship.shp_y;
183         } else {
184             if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
185                 continue;
186             if (item.sect.sct_own != player->cnum)
187                 continue;
188             if (item.sect.sct_type != SCT_FORTR)
189                 continue;
190             if (item.sect.sct_effic < FORTEFF) {
191                 pr("Fort not efficient enough to fire!\n");
192                 continue;
193             }
194             if (item.sect.sct_item[I_GUN] == 0) {
195                 pr("Not enough guns in sector %s!\n",
196                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
197                 continue;
198             }
199             if (item.sect.sct_item[I_SHELL] == 0) {
200                 pr("Not enough shells in sector %s!\n",
201                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
202                 continue;
203             }
204             if (item.sect.sct_item[I_MILIT] < 5) {
205                 pr("Not enough military in sector %s!\n",
206                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
207                 continue;
208             }
209             pr("\nSector %s firing\n",
210                xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
211             fx = fsect.sct_x;
212             fy = fsect.sct_y;
213         }
214
215         ptr = getstarg(player->argp[3], "Firing at? ", buf);
216         if (!ptr)
217             return RET_FAIL;
218         if (!*ptr)
219             continue;
220         if (!issector(ptr)) {
221             vshipno = atoi(ptr);
222             if (vshipno < 0 || !getship(vshipno, &vship) ||
223                 (!vship.shp_own)) {
224                 pr("No such ship exists!\n");
225                 continue;
226             }
227             target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
228                 targ_sub : targ_ship;
229             vict = vship.shp_own;
230             x = vship.shp_x;
231             y = vship.shp_y;
232             if (!getsect(x, y, &vsect)) {
233                 pr("No such sector exists!\n");
234                 continue;
235             }
236         } else {
237             if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
238                 pr("No such sector exists!\n");
239                 continue;
240             }
241             /* We check the sector type, but we only use it for damage, not
242                reporting.  That way, you don't get extra information you wouldn't
243                normally get.  Besides, what if they want to slam water? :)  */
244             if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
245                 target = targ_bogus;
246             else
247                 target = targ_land;
248             vict = vsect.sct_own;
249             x = vsect.sct_x;
250             y = vsect.sct_y;
251         }
252
253         trange = mapdist(x, y, fx, fy);
254
255         if (type == EF_SHIP) {
256             if (!check_ship_ok(&fship))
257                 return RET_FAIL;
258             if (fship.shp_own != player->cnum) {
259                 pr("Not your ship!\n");
260                 continue;
261             }
262             if (target == targ_sub || target == targ_ship) {
263                 if (fship.shp_uid == vship.shp_uid) {
264                     pr("You can't fire upon yourself!\n");
265                     continue;
266                 }
267             }
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                 && trange <= range2
282                 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
283                 dam = shp_dchrg(&fship);
284             } else {
285                 if (target == targ_sub)
286                     /* Don't tell it's a sub */
287                     range2 = -1;
288                 if (fship.shp_item[I_GUN] == 0) {
289                     pr("Insufficient arms.\n");
290                     continue;
291                 }
292                 dam = shp_fire(&fship);
293             }
294             putship(fship.shp_uid, &fship);
295             if (dam <= 0) {
296                 pr("Klick!     ...\n");
297                 continue;
298             }
299             if (opt_NOMOBCOST == 0) {
300                 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
301                 putship(fship.shp_uid, &fship);
302             }
303         } else if (type == EF_LAND) {
304             if (!check_land_ok(&fland))
305                 return RET_FAIL;
306             if (fland.lnd_own != player->cnum) {
307                 pr("Not your unit!\n");
308                 continue;
309             }
310
311             if (target == targ_land) {
312                 if (fland.lnd_x == vsect.sct_x
313                     && fland.lnd_y == vsect.sct_y) {
314                     pr("You can't fire upon yourself!\n");
315                     continue;
316                 }
317             }
318
319             if (lchr[fland.lnd_type].l_dam == 0) {
320                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
321                 continue;
322             }
323             if (fland.lnd_item[I_GUN] == 0) {
324                 pr("%s -- not enough guns\n", prland(&fland));
325                 continue;
326             }
327
328             range = lnd_fire_range(&fland);
329             range2 = roundrange(range);
330             pr("range is %d.00 (%.2f)\n", range2, range);
331             if (target == targ_sub) {
332                 /* Don't tell it's a sub */
333                 range2 = -1;
334             }
335
336             dam = lnd_fire(&fland);
337             putland(fland.lnd_uid, &fland);
338             if (dam < 0) {
339                 pr("Klick!     ...\n");
340                 continue;
341             }
342             if (target == targ_ship) {
343                 if (chance(lnd_acc(&fland) / 100.0))
344                     dam = ldround(dam / 2.0, 1);
345             }
346         } else {
347             if (!check_sect_ok(&fsect))
348                 return RET_FAIL;
349             if (fsect.sct_own != player->cnum ||
350                 fsect.sct_type != SCT_FORTR) {
351                 pr("No fortress at %s\n",
352                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
353                 continue;
354             }
355             if (target == targ_land) {
356                 if (fsect.sct_x == vsect.sct_x
357                     && fsect.sct_y == vsect.sct_y) {
358                     pr("You can't fire upon yourself!\n");
359                     continue;
360                 }
361             }
362             if (fsect.sct_item[I_GUN] == 0) {
363                 pr("Insufficient arms.\n");
364                 continue;
365             }
366             if (fsect.sct_item[I_MILIT] < 5) {
367                 pr("Not enough military for firing crew.\n");
368                 continue;
369             }
370             dam = fort_fire(&fsect);
371             putsect(&fsect);
372             if (dam < 0) {
373                 pr("Klick!     ...\n");
374                 continue;
375             }
376             range = fortrange(&fsect);
377             range2 = roundrange(range);
378             pr("range is %d.00 (%.2f)\n", range2, range);
379             if (target == targ_sub) {
380                 /* Don't tell it's a sub */
381                 range2 = -1;
382             }
383         }
384         if (trange > range2) {
385             pr("Target out of range.\n");
386             switch (type) {
387             case EF_SECTOR:
388                 putsect(&fsect);
389                 break;
390             case EF_LAND:
391                 fland.lnd_mission = 0;
392                 putland(fland.lnd_uid, &fland);
393                 break;
394             case EF_SHIP:
395                 fship.shp_mission = 0;
396                 putship(fship.shp_uid, &fship);
397                 break;
398             default:
399                 CANT_REACH();
400             }
401             continue;
402         }
403         switch (target) {
404         case targ_ship:
405             if (!trechk(player->cnum, vict, SEAFIR))
406                 continue;
407             break;
408         case targ_sub:
409             if (!trechk(player->cnum, vict, SUBFIR))
410                 continue;
411             break;
412         case targ_land:
413             if (!trechk(player->cnum, vict, LANFIR))
414                 continue;
415             break;
416         default:
417             break;
418         }
419
420         if (opt_SLOW_WAR) {
421             if (target == targ_land) {
422                 natp = getnatp(player->cnum);
423                 rel = getrel(natp, vict);
424                 if ((rel != AT_WAR) && (player->cnum != vict) &&
425                     (vict) && (vsect.sct_oldown != player->cnum)) {
426                     pr("You're not at war with them!\n");
427                     continue;
428                 }
429             }
430         }
431         nfiring++;
432         switch (target) {
433         case targ_sub:
434             pr_beep();
435             pr("Kawhomp!!!\n");
436             break;
437         default:
438             pr_beep();
439             pr("Kaboom!!!\n");
440             break;
441         }
442         switch (target) {
443         case targ_bogus:
444         case targ_land:
445             nreport(player->cnum, N_SCT_SHELL, vict, 1);
446             if (vict && vict != player->cnum)
447                 wu(0, vict,
448                    "Country #%d shelled sector %s for %d damage.\n",
449                    player->cnum, xyas(x, y, vict), dam);
450             pr("Shells hit sector %s for %d damage.\n",
451                xyas(x, y, player->cnum), dam);
452             break;
453         case targ_ship:
454             nreport(player->cnum, N_SHP_SHELL, vict, 1);
455             /* fall through */
456         default:
457             if (vict) {
458                 wu(0, vict,
459                    "Country #%d shelled %s in %s for %d damage.\n",
460                    player->cnum, prship(&vship),
461                    xyas(vship.shp_x, vship.shp_y, vict), dam);
462             }
463             pr("Shells hit %s in %s for %d damage.\n",
464                prsub(&vship),
465                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
466             break;
467         }
468         /*  Ok, now, check if we had a bogus target.  If so,
469            just continue on, since there is no defender. */
470         if (target == targ_bogus)
471             continue;
472         attgp = &item.gen;
473         if (type == EF_LAND) {
474             getsect(fland.lnd_x, fland.lnd_y, &fsect);
475             attgp = (struct empobj *)&fsect;
476         }
477         totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
478         switch (target) {
479         case targ_land:
480             getsect(x, y, &vsect);
481             sectdamage(&vsect, dam);
482             putsect(&vsect);
483             break;
484         default:
485             getship(vshipno, &vship);
486             check_retreat_and_do_shipdamage(&vship, dam);
487             if (vship.shp_effic < SHIP_MINEFF)
488                 pr("%s sunk!\n", prsub(&vship));
489             else if (target == targ_sub
490                 && (vship.shp_rflags & RET_DCHRGED)
491                 && !(vship.shp_rflags & RET_INJURED))
492                 retreat_ship(&vship, 'd');
493             putship(vship.shp_uid, &vship);
494             break;
495         }
496         if ((totaldefdam == 0) && (target == targ_ship))
497             if (vship.shp_rflags & RET_HELPLESS)
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 }