]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Clean up fire command's enum targ_type
[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 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_land:
426             if (!trechk(player->cnum, vict, LANFIR))
427                 continue;
428             break;
429         default:
430             break;
431         }
432
433         if (opt_SLOW_WAR) {
434             if (target == targ_land) {
435                 natp = getnatp(player->cnum);
436                 rel = getrel(natp, vict);
437                 if ((rel != AT_WAR) && (player->cnum != vict) &&
438                     (vict) && (vsect.sct_oldown != player->cnum)) {
439                     pr("You're not at war with them!\n");
440                     continue;
441                 }
442             }
443         }
444         nfiring++;
445         switch (target) {
446         case targ_sub:
447             pr_beep();
448             pr("Kawhomp!!!\n");
449             if (vship.shp_rflags & RET_DCHRGED)
450                 retreat_ship(&vship, 'd');
451             break;
452         default:
453             pr_beep();
454             pr("Kaboom!!!\n");
455             prb = range2 ? (double)trange / range2 : 1.0;
456             prb *= prb;
457             if (chance(prb)) {
458                 pr("Wind deflects shells.\n");
459 /*                      dam = (int)(dam / 2.0);*/
460                 dam *= (90 - (random() % 11)) / 100.0;
461                 if (dam < 0)
462                     dam = 0;
463             }
464             break;
465         }
466         switch (target) {
467         case targ_bogus:
468         case targ_land:
469             nreport(player->cnum, N_SCT_SHELL, vict, 1);
470             if (vict && vict != player->cnum)
471                 wu(0, vict,
472                    "Country #%d shelled sector %s for %d damage.\n",
473                    player->cnum, xyas(x, y, vict), dam);
474             pr("Shells hit sector %s for %d damage.\n",
475                xyas(x, y, player->cnum), dam);
476             if (target != targ_bogus)
477                 sectdamage(&vsect, dam, 0);
478             break;
479         case targ_ship:
480             nreport(player->cnum, N_SHP_SHELL, vict, 1);
481             /* fall through */
482         default:
483             if ((target != targ_sub) ||
484                 ((vship.shp_rflags & RET_DCHRGED) == 0))
485                 check_retreat_and_do_shipdamage(&vship, dam);
486             else
487                 shipdamage(&vship, dam);
488             if (vict) {
489                 wu(0, vict,
490                    "Country #%d shelled %s in %s for %d damage.\n",
491                    player->cnum, prship(&vship),
492                    xyas(vship.shp_x, vship.shp_y, vict), dam);
493             }
494             pr("Shells hit %s in %s for %d damage.\n",
495                prsub(&vship),
496                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
497
498             if (vship.shp_effic < SHIP_MINEFF)
499                 pr("%s sunk!\n", prsub(&vship));
500
501             break;
502         }
503         /*  Ok, now, check if we had a bogus target.  If so,
504            just continue on, since there is no defender. */
505         if (target == targ_bogus)
506             continue;
507         attgp = &item.gen;
508         if (type == EF_LAND) {
509             getsect(fland.lnd_x, fland.lnd_y, &fsect);
510             attgp = (struct empobj *)&fsect;
511         }
512         totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
513         switch (target) {
514         case targ_land:
515             putsect(&vsect);
516             break;
517         default:
518             putship(vship.shp_uid, &vship);
519             break;
520         }
521         if ((totaldefdam == 0) && (target == targ_ship))
522             if (vship.shp_rflags & RET_HELPLESS)
523                 retreat_ship(&vship, 'h');
524         switch (attgp->ef_type) {
525         case EF_SECTOR:
526             putsect(&fsect);
527             break;
528         case EF_SHIP:
529             if ((target == targ_ship) || (target == targ_sub)) {
530                 if (fship.shp_effic > SHIP_MINEFF) {
531                     shp_missdef(&fship, vict);
532                 };
533             };
534             putship(fship.shp_uid, &fship);
535             break;
536         default:
537             CANT_REACH();
538         }
539     }
540
541     free_flist(&defended);
542     if (nfiring)
543         odds = ((double)ndefending) / ((double)nfiring);
544     else
545         odds = 1.0;
546     do_defdam(&fired, odds);
547     return RET_OK;
548 }
549
550 static int
551 defend(struct emp_qelem *al, struct emp_qelem *dl,
552        struct empobj *attgp, natid vict, int *nd)
553 {
554     int dam;
555     int nfiring = 0;
556
557     dam = quiet_bigdef(attgp->ef_type, dl, vict,
558                        attgp->own, attgp->x, attgp->y, &nfiring);
559     if (dam) {
560         if (nfiring > *nd)
561             *nd = nfiring;
562         add_to_flist(al, attgp, dam, vict);
563     }
564
565     return dam;
566 }
567
568 static void
569 do_defdam(struct emp_qelem *list, double odds)
570 {
571
572     int dam, first = 1;
573     natid vict;
574     struct flist *fp;
575     struct shpstr ship;
576     struct sctstr sect;
577     struct emp_qelem *qp, *next;
578
579     for (qp = list->q_forw; qp != list; qp = next) {
580         next = qp->q_forw;
581         fp = (struct flist *)qp;
582         if (fp->type == EF_SHIP) {
583             if (!getship(fp->uid, &ship) || !ship.shp_own)
584                 continue;
585         }
586         if (first) {
587             pr_beep();
588             pr("\nDefenders fire back!\n");
589             first = 0;
590         }
591         dam = odds * fp->defdam;
592
593         if (fp->type == EF_SHIP) {
594             vict = fp->victim;
595             pr("Return fire hit %s in %s for %d damage.\n",
596                prship(&ship),
597                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
598             if (vict)
599                 wu(0, vict,
600                    "Return fire hit %s in %s for %d damage.\n",
601                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
602             shipdamage(&ship, dam);
603             putship(ship.shp_uid, &ship);
604         } else {
605             CANT_HAPPEN(fp->type != EF_SECTOR);
606             getsect(fp->x, fp->y, &sect);
607             vict = fp->victim;
608             pr("Return fire hit sector %s for %d damage.\n",
609                xyas(fp->x, fp->y, player->cnum), dam);
610             sectdamage(&sect, dam, 0);
611             putsect(&sect);
612             if (vict)
613                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
614                    xyas(fp->x, fp->y, vict), dam);
615         }
616         emp_remque(&fp->queue);
617         free(fp);
618     }
619 }
620
621 static int
622 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
623              coord ax, coord ay, int *nfiring)
624 {
625     double erange;
626     struct shpstr ship;
627     struct lndstr land;
628     struct nstr_item ni;
629     int dam, dam2, rel, rel2;
630     struct sctstr firing;
631     struct nstr_sect ns;
632     struct flist *fp;
633
634     if (own == 0)
635         return 0;
636     dam = 0;
637     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
638     while (nxtitem(&ni, &ship)) {
639         if (ship.shp_own == 0)
640             continue;
641
642         if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
643             continue;
644
645         rel = getrel(getnatp(ship.shp_own), own);
646         rel2 = getrel(getnatp(ship.shp_own), aown);
647         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
648             continue;
649         /* Don't shoot yourself */
650         if (ship.shp_own == aown)
651             continue;
652         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
653             erange = torprange(&ship);
654             if (roundrange(erange) < ni.curdist)
655                 continue;
656             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
657                 continue;
658             fp = search_flist(list, (struct empobj *)&ship);
659             if (fp)
660                 dam2 = fp->defdam;
661             else {
662                 dam2 = shp_torp(&ship, 0);
663                 putship(ship.shp_uid, &ship);
664             }
665             if (dam2 < 0)
666                 continue;
667             if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
668                 dam2 = 0;
669         } else {
670             erange = shp_fire_range(&ship);
671             if (roundrange(erange) < ni.curdist)
672                 continue;
673             fp = search_flist(list, (struct empobj *)&ship);
674             if (fp)
675                 dam2 = fp->defdam;
676             else {
677                 dam2 = shp_fire(&ship);
678                 putship(ship.shp_uid, &ship);
679             }
680             if (dam2 < 0)
681                 continue;
682             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
683         }
684         (*nfiring)++;
685         if (!fp)
686             add_to_flist(list, (struct empobj *)&ship, dam2, 0);
687         dam += dam2;
688     }
689     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
690     while (nxtitem(&ni, &land)) {
691         if (land.lnd_own == 0)
692             continue;
693         /* Don't shoot yourself */
694         if (land.lnd_own == aown)
695             continue;
696
697         rel = getrel(getnatp(land.lnd_own), own);
698         rel2 = getrel(getnatp(land.lnd_own), aown);
699
700         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
701             continue;
702
703         erange = lnd_fire_range(&land);
704         if (roundrange(erange) < ni.curdist)
705             continue;
706
707         fp = search_flist(list, (struct empobj *)&land);
708         if (fp)
709             dam2 = fp->defdam;
710         else {
711             dam2 = lnd_fire(&land);
712             putland(land.lnd_uid, &land);
713         }
714         if (dam2 < 0)
715             continue;
716
717         (*nfiring)++;
718         if (!fp)
719             add_to_flist(list, (struct empobj *)&land, dam2, 0);
720         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
721         dam += dam2;
722     }
723
724     /*
725      * Determine if any nearby gun-equipped sectors are within
726      * range and able to fire at an attacker.  Firing sectors
727      * need to have guns, shells, and military.  Sector being
728      * attacked is x,y -- attacker is at ax,ay.
729      */
730
731     if (!opt_NO_FORT_FIRE) {
732         snxtsct_dist(&ns, ax, ay, 8);
733         while (nxtsct(&ns, &firing)) {
734             if (firing.sct_own == 0)
735                 continue;
736             rel = getrel(getnatp(firing.sct_own), own);
737             rel2 = getrel(getnatp(firing.sct_own), aown);
738
739             if ((firing.sct_own != own) &&
740                 ((rel != ALLIED) || (rel2 != AT_WAR)))
741                 continue;
742             /* Don't shoot yourself */
743             if (firing.sct_own == aown)
744                 continue;
745             erange = fortrange(&firing);
746             if (roundrange(erange) < ns.curdist)
747                 continue;
748
749             fp = search_flist(list, (struct empobj *)&firing);
750             if (fp)
751                 dam2 = fp->defdam;
752             else {
753                 dam2 = fort_fire(&firing);
754                 putsect(&firing);
755             }
756             if (dam2 < 0)
757                 continue;
758             (*nfiring)++;
759             if (!fp)
760                 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
761             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
762             dam += dam2;
763         }
764     }
765
766     return *nfiring == 0 ? 0 : dam / *nfiring;
767 }
768
769 static void
770 add_to_flist(struct emp_qelem *list,
771              struct empobj *gp, int dam, natid victim)
772 {
773     struct flist *fp;
774
775     fp = malloc(sizeof(struct flist));
776     fp->type = gp->ef_type;
777     fp->uid = gp->uid;
778     fp->x = gp->x;
779     fp->y = gp->y;
780     fp->defdam = dam;
781     fp->victim = victim;
782     emp_insque(&fp->queue, list);
783 }
784
785 static void
786 free_flist(struct emp_qelem *list)
787 {
788     struct emp_qelem *qp, *next;
789     struct flist *fp;
790
791     for (qp = list->q_forw; qp != list; qp = next) {
792         next = qp->q_forw;
793         fp = (struct flist *)qp;
794         emp_remque(&fp->queue);
795         free(fp);
796     }
797 }
798
799 static int
800 uid_eq(struct emp_qelem *elem, void *key)
801 {
802     return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
803 }
804
805 static struct flist *
806 search_flist(struct emp_qelem *list, struct empobj *gp)
807 {
808     return (struct flist *)emp_searchque(list, gp, uid_eq);
809 }