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