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