]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
abdf4026c1b4dbaafcfada39305800d6860001a4
[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             && (vship.shp_rflags & RET_HELPLESS)
498             && !(vship.shp_rflags & RET_INJURED))
499             retreat_ship(&vship, 'h');
500         switch (attgp->ef_type) {
501         case EF_SECTOR:
502             putsect(&fsect);
503             break;
504         case EF_SHIP:
505             if ((target == targ_ship) || (target == targ_sub)) {
506                 if (fship.shp_effic > SHIP_MINEFF) {
507                     shp_missdef(&fship, vict);
508                 };
509             };
510             putship(fship.shp_uid, &fship);
511             break;
512         default:
513             CANT_REACH();
514         }
515     }
516
517     free_flist(&defended);
518     if (nfiring)
519         odds = ((double)ndefending) / ((double)nfiring);
520     else
521         odds = 1.0;
522     do_defdam(&fired, odds);
523     return RET_OK;
524 }
525
526 static int
527 defend(struct emp_qelem *al, struct emp_qelem *dl,
528        struct empobj *attgp, natid vict, int *nd)
529 {
530     int dam;
531     int nfiring = 0;
532
533     dam = quiet_bigdef(attgp->ef_type, dl, vict,
534                        attgp->own, attgp->x, attgp->y, &nfiring);
535     if (dam) {
536         if (nfiring > *nd)
537             *nd = nfiring;
538         add_to_flist(al, attgp, dam, vict);
539     }
540
541     return dam;
542 }
543
544 static void
545 do_defdam(struct emp_qelem *list, double odds)
546 {
547
548     int dam, first = 1;
549     natid vict;
550     struct flist *fp;
551     struct shpstr ship;
552     struct sctstr sect;
553     struct emp_qelem *qp, *next;
554
555     for (qp = list->q_forw; qp != list; qp = next) {
556         next = qp->q_forw;
557         fp = (struct flist *)qp;
558         if (fp->type == EF_SHIP) {
559             if (!getship(fp->uid, &ship) || !ship.shp_own)
560                 continue;
561         }
562         if (first) {
563             pr_beep();
564             pr("\nDefenders fire back!\n");
565             first = 0;
566         }
567         dam = odds * fp->defdam;
568
569         if (fp->type == EF_SHIP) {
570             vict = fp->victim;
571             pr("Return fire hit %s in %s for %d damage.\n",
572                prship(&ship),
573                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
574             if (vict)
575                 wu(0, vict,
576                    "Return fire hit %s in %s for %d damage.\n",
577                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
578             shipdamage(&ship, dam);
579             putship(ship.shp_uid, &ship);
580         } else {
581             CANT_HAPPEN(fp->type != EF_SECTOR);
582             getsect(fp->x, fp->y, &sect);
583             vict = fp->victim;
584             pr("Return fire hit sector %s for %d damage.\n",
585                xyas(fp->x, fp->y, player->cnum), dam);
586             sectdamage(&sect, dam);
587             putsect(&sect);
588             if (vict)
589                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
590                    xyas(fp->x, fp->y, vict), dam);
591         }
592         emp_remque(&fp->queue);
593         free(fp);
594     }
595 }
596
597 static int
598 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
599              coord ax, coord ay, int *nfiring)
600 {
601     double erange;
602     struct shpstr ship;
603     struct lndstr land;
604     struct nstr_item ni;
605     int dam, dam2, rel, rel2;
606     struct sctstr firing;
607     struct nstr_sect ns;
608     struct flist *fp;
609
610     if (own == 0)
611         return 0;
612     dam = 0;
613     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
614     while (nxtitem(&ni, &ship)) {
615         if (ship.shp_own == 0)
616             continue;
617
618         if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
619             continue;
620
621         rel = getrel(getnatp(ship.shp_own), own);
622         rel2 = getrel(getnatp(ship.shp_own), aown);
623         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
624             continue;
625         /* Don't shoot yourself */
626         if (ship.shp_own == aown)
627             continue;
628         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
629             erange = torprange(&ship);
630             if (roundrange(erange) < ni.curdist)
631                 continue;
632             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
633                 continue;
634             fp = search_flist(list, (struct empobj *)&ship);
635             if (fp)
636                 dam2 = fp->defdam;
637             else {
638                 dam2 = shp_torp(&ship, 0);
639                 putship(ship.shp_uid, &ship);
640             }
641             if (dam2 < 0)
642                 continue;
643             if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
644                 dam2 = 0;
645         } else {
646             erange = shp_fire_range(&ship);
647             if (roundrange(erange) < ni.curdist)
648                 continue;
649             fp = search_flist(list, (struct empobj *)&ship);
650             if (fp)
651                 dam2 = fp->defdam;
652             else {
653                 dam2 = shp_fire(&ship);
654                 putship(ship.shp_uid, &ship);
655             }
656             if (dam2 < 0)
657                 continue;
658             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
659         }
660         (*nfiring)++;
661         if (!fp)
662             add_to_flist(list, (struct empobj *)&ship, dam2, 0);
663         dam += dam2;
664     }
665     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
666     while (nxtitem(&ni, &land)) {
667         if (land.lnd_own == 0)
668             continue;
669         /* Don't shoot yourself */
670         if (land.lnd_own == aown)
671             continue;
672
673         rel = getrel(getnatp(land.lnd_own), own);
674         rel2 = getrel(getnatp(land.lnd_own), aown);
675
676         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
677             continue;
678
679         erange = lnd_fire_range(&land);
680         if (roundrange(erange) < ni.curdist)
681             continue;
682
683         fp = search_flist(list, (struct empobj *)&land);
684         if (fp)
685             dam2 = fp->defdam;
686         else {
687             dam2 = lnd_fire(&land);
688             putland(land.lnd_uid, &land);
689         }
690         if (dam2 < 0)
691             continue;
692
693         (*nfiring)++;
694         if (!fp)
695             add_to_flist(list, (struct empobj *)&land, dam2, 0);
696         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
697         if (type == EF_SHIP) {
698             if (chance(lnd_acc(&land) / 100.0))
699                 dam2 = ldround(dam2 / 2.0, 1);
700         }
701         dam += dam2;
702     }
703
704     /*
705      * Determine if any nearby gun-equipped sectors are within
706      * range and able to fire at an attacker.  Firing sectors
707      * need to have guns, shells, and military.  Sector being
708      * attacked is x,y -- attacker is at ax,ay.
709      */
710
711     if (!opt_NO_FORT_FIRE) {
712         snxtsct_dist(&ns, ax, ay, 8);
713         while (nxtsct(&ns, &firing)) {
714             if (firing.sct_own == 0)
715                 continue;
716             rel = getrel(getnatp(firing.sct_own), own);
717             rel2 = getrel(getnatp(firing.sct_own), aown);
718
719             if ((firing.sct_own != own) &&
720                 ((rel != ALLIED) || (rel2 != AT_WAR)))
721                 continue;
722             /* Don't shoot yourself */
723             if (firing.sct_own == aown)
724                 continue;
725             erange = fortrange(&firing);
726             if (roundrange(erange) < ns.curdist)
727                 continue;
728
729             fp = search_flist(list, (struct empobj *)&firing);
730             if (fp)
731                 dam2 = fp->defdam;
732             else {
733                 dam2 = fort_fire(&firing);
734                 putsect(&firing);
735             }
736             if (dam2 < 0)
737                 continue;
738             (*nfiring)++;
739             if (!fp)
740                 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
741             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
742             dam += dam2;
743         }
744     }
745
746     return *nfiring == 0 ? 0 : dam / *nfiring;
747 }
748
749 static void
750 add_to_flist(struct emp_qelem *list,
751              struct empobj *gp, int dam, natid victim)
752 {
753     struct flist *fp;
754
755     fp = malloc(sizeof(struct flist));
756     fp->type = gp->ef_type;
757     fp->uid = gp->uid;
758     fp->x = gp->x;
759     fp->y = gp->y;
760     fp->defdam = dam;
761     fp->victim = victim;
762     emp_insque(&fp->queue, list);
763 }
764
765 static void
766 free_flist(struct emp_qelem *list)
767 {
768     struct emp_qelem *qp, *next;
769     struct flist *fp;
770
771     for (qp = list->q_forw; qp != list; qp = next) {
772         next = qp->q_forw;
773         fp = (struct flist *)qp;
774         emp_remque(&fp->queue);
775         free(fp);
776     }
777 }
778
779 static int
780 uid_eq(struct emp_qelem *elem, void *key)
781 {
782     return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
783 }
784
785 static struct flist *
786 search_flist(struct emp_qelem *list, struct empobj *gp)
787 {
788     return (struct flist *)emp_searchque(list, gp, uid_eq);
789 }