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