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