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