]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Merge branch 'master' into hvy-metal-2
[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             lnd_unlimber(&fland);
360             if (target == targ_ship) {
361                 if (chance(lnd_acc(&fland) / 100.0))
362                     dam = ldround(dam / 2.0, 1);
363             }
364         } else {
365             if (!check_sect_ok(&fsect))
366                 return RET_FAIL;
367             if (fsect.sct_own != player->cnum ||
368                 fsect.sct_type != SCT_FORTR) {
369                 pr("No fortress at %s\n",
370                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
371                 continue;
372             }
373             if (target == targ_land) {
374                 if (fsect.sct_x == vsect.sct_x
375                     && fsect.sct_y == vsect.sct_y) {
376                     pr("You can't fire upon yourself!\n");
377                     continue;
378                 }
379             }
380             if (fsect.sct_item[I_GUN] == 0) {
381                 pr("Insufficient arms.\n");
382                 continue;
383             }
384             if (fsect.sct_item[I_MILIT] < 5) {
385                 pr("Not enough military for firing crew.\n");
386                 continue;
387             }
388             dam = fort_fire(&fsect);
389             putsect(&fsect);
390             if (dam < 0) {
391                 pr("Klick!     ...\n");
392                 continue;
393             }
394             range = fortrange(&fsect);
395             range2 = roundrange(range);
396             pr("range is %d.00 (%.2f)\n", range2, range);
397             if (target == targ_sub) {
398                 /* Don't tell it's a sub */
399                 range2 = -1;
400             }
401         }
402         if (trange > range2) {
403             pr("Target out of range.\n");
404             switch (type) {
405             case EF_SECTOR:
406                 putsect(&fsect);
407                 break;
408             case EF_LAND:
409                 fland.lnd_mission = 0;
410                 putland(fland.lnd_uid, &fland);
411                 break;
412             case EF_SHIP:
413                 fship.shp_mission = 0;
414                 putship(fship.shp_uid, &fship);
415                 break;
416             default:
417                 CANT_REACH();
418             }
419             continue;
420         }
421         switch (target) {
422         case targ_ship:
423             if (!trechk(player->cnum, vict, SEAFIR))
424                 continue;
425             break;
426         case targ_sub:
427             if (!trechk(player->cnum, vict, SUBFIR))
428                 continue;
429             break;
430         case targ_land:
431             if (!trechk(player->cnum, vict, LANFIR))
432                 continue;
433             break;
434         default:
435             break;
436         }
437
438         if (opt_SLOW_WAR) {
439             if (target == targ_land) {
440                 natp = getnatp(player->cnum);
441                 rel = getrel(natp, vict);
442                 if ((rel != AT_WAR) && (player->cnum != vict) &&
443                     (vict) && (vsect.sct_oldown != player->cnum)) {
444                     pr("You're not at war with them!\n");
445                     continue;
446                 }
447             }
448         }
449         nfiring++;
450         switch (target) {
451         case targ_sub:
452             pr_beep();
453             pr("Kawhomp!!!\n");
454             break;
455         default:
456             pr_beep();
457             pr("Kaboom!!!\n");
458             prb = range2 ? (double)trange / range2 : 1.0;
459             prb *= prb;
460             if (chance(prb)) {
461                 pr("Wind deflects shells.\n");
462 /*                      dam = (int)(dam / 2.0);*/
463                 dam *= (90 - (random() % 11)) / 100.0;
464                 if (dam < 0)
465                     dam = 0;
466             }
467             break;
468         }
469         switch (target) {
470         case targ_bogus:
471         case targ_land:
472             nreport(player->cnum, N_SCT_SHELL, vict, 1);
473             if (vict && vict != player->cnum)
474                 wu(0, vict,
475                    "Country #%d shelled sector %s for %d damage.\n",
476                    player->cnum, xyas(x, y, vict), dam);
477             pr("Shells hit sector %s for %d damage.\n",
478                xyas(x, y, player->cnum), dam);
479             break;
480         case targ_ship:
481             nreport(player->cnum, N_SHP_SHELL, vict, 1);
482             /* fall through */
483         default:
484             if (vict) {
485                 wu(0, vict,
486                    "Country #%d shelled %s in %s for %d damage.\n",
487                    player->cnum, prship(&vship),
488                    xyas(vship.shp_x, vship.shp_y, vict), dam);
489             }
490             pr("Shells hit %s in %s for %d damage.\n",
491                prsub(&vship),
492                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
493             break;
494         }
495         /*  Ok, now, check if we had a bogus target.  If so,
496            just continue on, since there is no defender. */
497         if (target == targ_bogus)
498             continue;
499         attgp = &item.gen;
500         if (type == EF_LAND) {
501             getsect(fland.lnd_x, fland.lnd_y, &fsect);
502             attgp = (struct empobj *)&fsect;
503         }
504         totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
505         switch (target) {
506         case targ_land:
507             getsect(x, y, &vsect);
508             sectdamage(&vsect, dam);
509             putsect(&vsect);
510             break;
511         default:
512             getship(vshipno, &vship);
513             check_retreat_and_do_shipdamage(&vship, dam);
514             if (vship.shp_effic < SHIP_MINEFF)
515                 pr("%s sunk!\n", prsub(&vship));
516             else if (target == targ_sub
517                 && (vship.shp_rflags & RET_DCHRGED)
518                 && !(vship.shp_rflags & RET_INJURED))
519                 retreat_ship(&vship, 'd');
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);
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         /* Too ponderous for counter-battery fire */
699         if (lchr[(int)land.lnd_type].l_flags & L_HEAVY)
700             continue;
701
702         rel = getrel(getnatp(land.lnd_own), own);
703         rel2 = getrel(getnatp(land.lnd_own), aown);
704
705         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
706             continue;
707
708         erange = lnd_fire_range(&land);
709         if (roundrange(erange) < ni.curdist)
710             continue;
711
712         fp = search_flist(list, (struct empobj *)&land);
713         if (fp)
714             dam2 = fp->defdam;
715         else {
716             dam2 = lnd_fire(&land);
717             putland(land.lnd_uid, &land);
718         }
719         if (dam2 < 0)
720             continue;
721
722         lnd_unlimber(&land);
723         (*nfiring)++;
724         if (!fp)
725             add_to_flist(list, (struct empobj *)&land, dam2, 0);
726         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
727         dam += dam2;
728     }
729
730     /*
731      * Determine if any nearby gun-equipped sectors are within
732      * range and able to fire at an attacker.  Firing sectors
733      * need to have guns, shells, and military.  Sector being
734      * attacked is x,y -- attacker is at ax,ay.
735      */
736
737     if (!opt_NO_FORT_FIRE) {
738         snxtsct_dist(&ns, ax, ay, 8);
739         while (nxtsct(&ns, &firing)) {
740             if (firing.sct_own == 0)
741                 continue;
742             rel = getrel(getnatp(firing.sct_own), own);
743             rel2 = getrel(getnatp(firing.sct_own), aown);
744
745             if ((firing.sct_own != own) &&
746                 ((rel != ALLIED) || (rel2 != AT_WAR)))
747                 continue;
748             /* Don't shoot yourself */
749             if (firing.sct_own == aown)
750                 continue;
751             erange = fortrange(&firing);
752             if (roundrange(erange) < ns.curdist)
753                 continue;
754
755             fp = search_flist(list, (struct empobj *)&firing);
756             if (fp)
757                 dam2 = fp->defdam;
758             else {
759                 dam2 = fort_fire(&firing);
760                 putsect(&firing);
761             }
762             if (dam2 < 0)
763                 continue;
764             (*nfiring)++;
765             if (!fp)
766                 add_to_flist(list, (struct empobj *)&firing, dam2, 0);
767             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
768             dam += dam2;
769         }
770     }
771
772     return *nfiring == 0 ? 0 : dam / *nfiring;
773 }
774
775 static void
776 add_to_flist(struct emp_qelem *list,
777              struct empobj *gp, int dam, natid victim)
778 {
779     struct flist *fp;
780
781     fp = malloc(sizeof(struct flist));
782     fp->type = gp->ef_type;
783     fp->uid = gp->uid;
784     fp->x = gp->x;
785     fp->y = gp->y;
786     fp->defdam = dam;
787     fp->victim = victim;
788     emp_insque(&fp->queue, list);
789 }
790
791 static void
792 free_flist(struct emp_qelem *list)
793 {
794     struct emp_qelem *qp, *next;
795     struct flist *fp;
796
797     for (qp = list->q_forw; qp != list; qp = next) {
798         next = qp->q_forw;
799         fp = (struct flist *)qp;
800         emp_remque(&fp->queue);
801         free(fp);
802     }
803 }
804
805 static int
806 uid_eq(struct emp_qelem *elem, void *key)
807 {
808     return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
809 }
810
811 static struct flist *
812 search_flist(struct emp_qelem *list, struct empobj *gp)
813 {
814     return (struct flist *)emp_searchque(list, gp, uid_eq);
815 }