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