]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
e73882ccb7063865c2a5a13dc3857206827fc08d
[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_unit, targ_bogus
43 };
44
45 struct flist {
46     struct emp_qelem queue;     /* list of fired things */
47     int type;                   /* ship? otherwise sector */
48     int uid;                    /* ship uid */
49     coord x, y;                 /* sector coords */
50     int defdam;                 /* damage defenders did */
51     int victim;                 /* who I was shooting at */
52 };
53
54 static void add_to_fired_queue(struct emp_qelem *, struct emp_qelem *);
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 use_ammo(struct emp_qelem *);
61
62 int
63 multifire(void)
64 {
65     static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
66     char vbuf[20];
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 shots;
76     int dam;
77     int totaldefdam = 0;
78     int vshipno;
79     double prb;
80     natid vict;
81     struct shpstr fship;
82     struct lndstr fland;
83     struct sctstr fsect;
84     struct shpstr vship;
85     struct sctstr vsect;
86     enum targ_type target, attacker, orig_attacker;
87     int rel;
88     struct natstr *natp;
89     struct nstr_item nbst;
90     int type;
91     struct empobj *attgp;
92     char *p;
93     int nfiring = 0;
94     int ndefending = 0;
95     union empobj_storage item;
96     struct emp_qelem fired, defended;
97     double odds;
98     char buf[1024];
99
100     emp_initque(&fired);
101     emp_initque(&defended);
102     if (!(p = getstarg(player->argp[1],
103                        "Firing from ship(s), sect(s), or land unit(s)? ",
104                        buf)))
105         return RET_SYN;
106     type = ef_byname_from(p, ef_with_guns);
107     if (type == EF_SECTOR) {
108         if (opt_NO_FORT_FIRE) {
109             pr("Fort firing is disabled.\n");
110             return RET_FAIL;
111         }
112         orig_attacker = attacker = targ_land;
113         shots = 1;
114     } else if (type == EF_SHIP) {
115         orig_attacker = attacker = targ_ship;
116     } else if (type == EF_LAND) {
117         orig_attacker = attacker = targ_unit;
118     } else {
119         pr("Ships, land units or sectors only!\n");
120         return RET_SYN;
121     }
122     if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
123         || *ptr == '\0')
124         return RET_SYN;
125
126     if (!snxtitem(&nbst, type, ptr))
127         return RET_SYN;
128
129     if (player->aborted) {
130         pr("Fire aborted.\n");
131         return RET_OK;
132     }
133     while (nxtitem(&nbst, &item)) {
134         attacker = orig_attacker;
135         if (attacker == targ_unit) {
136             if (!getland(item.land.lnd_uid, &fland))
137                 continue;
138             if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
139                 continue;
140             if (item.land.lnd_own != player->cnum)
141                 continue;
142
143             if (lchr[fland.lnd_type].l_dam == 0) {
144                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
145                 continue;
146             }
147             if (fland.lnd_item[I_MILIT] < 1) {
148                 pr("Unit %d cannot fire because it has no military!\n",
149                    fland.lnd_uid);
150                 continue;
151             }
152             if (fland.lnd_ship >= 0) {
153                 pr("Unit %d cannot fire because it is on a ship!\n",
154                    fland.lnd_uid);
155                 continue;
156             }
157             if (fland.lnd_land >= 0) {
158                 pr("Unit %d cannot fire because it is on a land unit!\n",
159                    fland.lnd_uid);
160                 continue;
161             }
162             if (fland.lnd_effic < LAND_MINFIREEFF) {
163                 pr("Unit %d cannot fire because it is less than %d%% efficient\n",
164                    fland.lnd_uid, LAND_MINFIREEFF);
165                 continue;
166             }
167             if (fland.lnd_item[I_SHELL] == 0) {
168                 pr("%s -- not enough shells\n", prland(&fland));
169                 continue;
170             }
171         } else if (attacker == targ_ship) {
172             if (!getship(item.ship.shp_uid, &fship))
173                 continue;
174             if (item.ship.shp_own != player->cnum)
175                 continue;
176             if (item.ship.shp_item[I_MILIT] < 1) {
177                 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
178                 continue;
179             }
180             if (mchr[item.ship.shp_type].m_glim == 0) {
181                 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
182                 continue;
183             }
184             if (item.ship.shp_item[I_GUN] == 0) {
185                 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
186                 continue;
187             }
188             if (item.ship.shp_item[I_SHELL] == 0) {
189                 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
190                 continue;
191             }
192             if (item.ship.shp_effic < 60) {
193                 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
194                 continue;
195             }
196         } else if (attacker == targ_land) {
197             if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
198                 continue;
199             if (item.sect.sct_own != player->cnum)
200                 continue;
201             if (item.sect.sct_type != SCT_FORTR)
202                 continue;
203             if (item.sect.sct_effic < FORTEFF) {
204                 pr("Fort not efficient enough to fire!\n");
205                 continue;
206             }
207             if (item.sect.sct_item[I_GUN] == 0) {
208                 pr("Not enough guns 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_SHELL] == 0) {
213                 pr("Not enough shells in sector %s!\n",
214                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
215                 continue;
216             }
217             if (item.sect.sct_item[I_MILIT] < 5) {
218                 pr("Not enough military in sector %s!\n",
219                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
220                 continue;
221             }
222             pr("\nSector %s firing\n",
223                xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
224         }
225         if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
226             || *ptr == '\0')
227             continue;
228         if (player->aborted) {
229             pr("Fire aborted.\n");
230             continue;
231         }
232         ptr[19] = 0;
233         (void)strcpy(vbuf, ptr);
234         if (issector(vbuf))
235             target = targ_land;
236         else
237             target = targ_ship;
238         if (target == targ_ship) {
239             vshipno = atoi(vbuf);
240             if (vshipno < 0 || !getship(vshipno, &vship) ||
241                 (!vship.shp_own)) {
242                 pr("No such ship exists!\n");
243                 continue;
244             }
245             target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
246                 targ_sub : targ_ship;
247             vict = vship.shp_own;
248             x = vship.shp_x;
249             y = vship.shp_y;
250             if (!getsect(x, y, &vsect)) {
251                 pr("No such sector exists!\n");
252                 continue;
253             }
254         } else {
255             if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
256                 pr("No such sector exists!\n");
257                 continue;
258             }
259             /* We check the sector type, but we only use it for damage, not
260                reporting.  That way, you don't get extra information you wouldn't
261                normally get.  Besides, what if they want to slam water? :)  */
262             if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
263                 target = targ_bogus;
264             else
265                 target = targ_land;
266             vict = vsect.sct_own;
267             x = vsect.sct_x;
268             y = vsect.sct_y;
269         }
270         if (attacker == targ_ship) {
271             if (fship.shp_own != player->cnum) {
272                 pr("Not your ship!\n");
273                 continue;
274             }
275             if (target == targ_sub || target == targ_ship) {
276                 if (fship.shp_uid == vship.shp_uid) {
277                     pr("You can't fire upon yourself!\n");
278                     continue;
279                 }
280             }
281             fx = fship.shp_x;
282             fy = fship.shp_y;
283 /*
284   attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
285   targ_sub : targ_ship;
286   if (attacker == targ_sub){
287   pr("Subs may not fire normally.. use torpedo.\n");
288   continue;
289   }
290 */
291             attacker = targ_ship;
292             if ((mil = fship.shp_item[I_MILIT]) < 1) {
293                 pr("Not enough military for firing crew.\n");
294                 continue;
295             }
296             if (fship.shp_effic < 60) {
297                 pr("Ship #%d is crippled (%d%%)\n",
298                    fship.shp_uid, fship.shp_effic);
299                 continue;
300             }
301             range = shp_fire_range(&fship);
302             range2 = roundrange(range);
303             pr("range is %d.00 (%.2f)\n", range2, range);
304             if (target == targ_sub
305                 && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
306                 dam = shp_dchrg(&fship);
307                 putship(fship.shp_uid, &fship);
308                 if (dam < 0) {
309                     pr("Not enough shells for depth charge!\n");
310                     continue;
311                 }
312             } else {
313                 if (target == targ_sub)
314                     /* Don't tell it's a sub */
315                     range2 = -1;
316                 if (fship.shp_item[I_GUN] == 0) {
317                     pr("Insufficient arms.\n");
318                     continue;
319                 }
320                 dam = shp_fire(&fship);
321                 putship(fship.shp_uid, &fship);
322                 if (dam <= 0) {
323                     pr("Klick!     ...\n");
324                     continue;
325                 }
326             }
327             if (opt_NOMOBCOST == 0) {
328                 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
329                 putship(fship.shp_uid, &fship);
330             }
331         } else if (attacker == targ_unit) {
332             if (fland.lnd_own != player->cnum) {
333                 pr("Not your unit!\n");
334                 continue;
335             }
336
337             if (target == targ_land) {
338                 if (fland.lnd_x == vsect.sct_x
339                     && fland.lnd_y == vsect.sct_y) {
340                     pr("You can't fire upon yourself!\n");
341                     continue;
342                 }
343             }
344
345             fx = fland.lnd_x;
346             fy = fland.lnd_y;
347
348             if (lchr[fland.lnd_type].l_dam == 0) {
349                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
350                 continue;
351             }
352             if (fland.lnd_item[I_GUN] == 0) {
353                 pr("%s -- not enough guns\n", prland(&fland));
354                 continue;
355             }
356
357             range = lnd_fire_range(&fland);
358             range2 = roundrange(range);
359             pr("range is %d.00 (%.2f)\n", range2, range);
360             if (target == targ_sub) {
361                 /* Don't tell it's a sub */
362                 range2 = -1;
363             }
364
365             dam = lnd_fire(&fland);
366             putland(fland.lnd_uid, &fland);
367             if (dam < 0) {
368                 pr("Klick!     ...\n");
369                 continue;
370             }
371             if (target == targ_ship) {
372                 if (chance(lnd_acc(&fland) / 100.0))
373                     dam = ldround(dam / 2.0, 1);
374             }
375         } else {
376             fx = fsect.sct_x;
377             fy = fsect.sct_y;
378             if (fsect.sct_own != player->cnum ||
379                 fsect.sct_type != SCT_FORTR) {
380                 pr("No fortress at %s\n",
381                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
382                 continue;
383             }
384             if (target == targ_land) {
385                 if (fsect.sct_x == vsect.sct_x
386                     && fsect.sct_y == vsect.sct_y) {
387                     pr("You can't fire upon yourself!\n");
388                     continue;
389                 }
390             }
391             attacker = targ_land;
392             if (fsect.sct_item[I_GUN] == 0) {
393                 pr("Insufficient arms.\n");
394                 continue;
395             }
396             if (fsect.sct_item[I_MILIT] < 5) {
397                 pr("Not enough military for firing crew.\n");
398                 continue;
399             }
400             dam = fort_fire(&fsect);
401             putsect(&fsect);
402             if (dam < 0) {
403                 pr("Klick!     ...\n");
404                 continue;
405             }
406             range = fortrange(&fsect);
407             range2 = roundrange(range);
408             pr("range is %d.00 (%.2f)\n", range2, range);
409             if (target == targ_sub) {
410                 /* Don't tell it's a sub */
411                 range2 = -1;
412             }
413         }
414         trange = mapdist(x, y, fx, fy);
415         if (trange > range2) {
416             pr("Target out of range.\n");
417 /*
418                         switch (target) {
419                         case targ_land:
420                         case targ_bogus:
421                                 pr("Target out of range.  Thud.\n");
422                                 break ;
423                         default:
424                                 pr("Target ship out of range.  Splash.\n");
425                                 break ;
426                         }       
427  */
428             switch (attacker) {
429             case targ_land:
430                 putsect(&fsect);
431                 break;
432             case targ_unit:
433                 fland.lnd_mission = 0;
434                 putland(fland.lnd_uid, &fland);
435                 break;
436             default:
437                 fship.shp_mission = 0;
438                 putship(fship.shp_uid, &fship);
439             }
440             continue;
441         }
442 /*
443                 if (target == targ_bogus) {
444                         if (vsect.sct_type == SCT_SANCT) {
445                                 pr("%s is a %s!!\n", vbuf,
446                                    dchr[SCT_SANCT].d_name);
447                                 continue;
448                         } else if (vsect.sct_type == SCT_WATER) {
449                                 pr("You must specify a ship in sector %s!\n",
450                                    vbuf);
451                                 continue;
452                         }
453                 }
454 */
455         switch (target) {
456         case targ_ship:
457             if (!trechk(player->cnum, vict, SEAFIR))
458                 continue;
459             break;
460         case targ_sub:
461             if (!trechk(player->cnum, vict, SUBFIR))
462                 continue;
463             break;
464         case targ_unit:
465         case targ_land:
466             if (!trechk(player->cnum, vict, LANFIR))
467                 continue;
468             break;
469         default:
470             break;
471         }
472
473         if (opt_SLOW_WAR) {
474             if (target == targ_land) {
475                 natp = getnatp(player->cnum);
476                 rel = getrel(natp, vict);
477                 if ((rel != AT_WAR) && (player->cnum != vict) &&
478                     (vict) && (vsect.sct_oldown != player->cnum)) {
479                     pr("You're not at war with them!\n");
480                     continue;
481                 }
482             }
483         }
484         nfiring++;
485         switch (target) {
486         case targ_sub:
487             pr_beep();
488             pr("Kawhomp!!!\n");
489             if (vship.shp_rflags & RET_DCHRGED)
490                 retreat_ship(&vship, 'd');
491             break;
492         default:
493             pr_beep();
494             pr("Kaboom!!!\n");
495             prb = range2 ? (double)trange / range2 : 1.0;
496             prb *= prb;
497             if (chance(prb)) {
498                 pr("Wind deflects shell%s.\n", splur(shots));
499 /*                      dam = (int)(dam / 2.0);*/
500                 dam *= (90 - (random() % 11)) / 100.0;
501                 if (dam < 0)
502                     dam = 0;
503             }
504             break;
505         }
506         switch (target) {
507         case targ_bogus:
508         case targ_land:
509             nreport(player->cnum, N_SCT_SHELL, vict, 1);
510             if (vict && vict != player->cnum)
511                 wu(0, vict,
512                    "Country #%d shelled sector %s for %d damage.\n",
513                    player->cnum, xyas(x, y, vict), dam);
514             pr("Shell%s hit sector %s for %d damage.\n",
515                splur(shots), xyas(x, y, player->cnum), dam);
516             /* Ok, it wasn't a bogus target, so do damage. */
517             if (target != targ_bogus)
518                 sectdamage(&vsect, dam, 0);
519             break;
520         case targ_ship:
521             nreport(player->cnum, N_SHP_SHELL, vict, 1);
522             /* fall through */
523         default:
524             if ((target != targ_sub) ||
525                 ((vship.shp_rflags & RET_DCHRGED) == 0))
526                 check_retreat_and_do_shipdamage(&vship, dam);
527             else
528                 shipdamage(&vship, dam);
529             if (vict) {
530                 wu(0, vict,
531                    "Country #%d shelled %s in %s for %d damage.\n",
532                    player->cnum, prship(&vship),
533                    xyas(vship.shp_x, vship.shp_y, vict), dam);
534             }
535             pr("Shell%s hit %s in %s for %d damage.\n",
536                splur(shots), prsub(&vship),
537                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
538
539             if (vship.shp_effic < SHIP_MINEFF)
540                 pr("%s sunk!\n", prsub(&vship));
541
542             break;
543         }
544         /*  Ok, now, check if we had a bogus target.  If so,
545            just continue on, since there is no defender. */
546         if (target == targ_bogus)
547             continue;
548         attgp = &item.gen;
549         if (attacker == targ_unit) {
550             attacker = targ_land;
551             getsect(fland.lnd_x, fland.lnd_y, &fsect);
552             attgp = (struct empobj *)&fsect;
553         }
554         totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
555         switch (target) {
556         case targ_land:
557             putsect(&vsect);
558             break;
559         default:
560             putship(vship.shp_uid, &vship);
561             break;
562         }
563         if ((totaldefdam == 0) && (target == targ_ship))
564             if (vship.shp_rflags & RET_HELPLESS)
565                 retreat_ship(&vship, 'h');
566         switch (attacker) {
567         case targ_land:
568             putsect(&fsect);
569             break;
570         default:
571             if ((target == targ_ship) || (target == targ_sub)) {
572                 if (fship.shp_effic > SHIP_MINEFF) {
573                     shp_missdef(&fship, vict);
574                 };
575             };
576             putship(fship.shp_uid, &fship);
577             break;
578         }
579     }
580
581     use_ammo(&defended);
582     if (nfiring)
583         odds = ((double)ndefending) / ((double)nfiring);
584     else
585         odds = 1.0;
586     do_defdam(&fired, odds);
587     return RET_OK;
588 }
589
590 static int
591 defend(struct emp_qelem *al, struct emp_qelem *dl,
592        struct empobj *attgp, natid vict, int *nd)
593 {
594
595     int dam;
596     int nfiring = 0;
597     struct flist *fp;
598
599     dam = quiet_bigdef(attgp->ef_type, dl, vict,
600                        attgp->own, attgp->x, attgp->y, &nfiring);
601     if (dam) {
602         if (nfiring > *nd)
603             *nd = nfiring;
604         fp = malloc(sizeof(struct flist));
605         memset(fp, 0, sizeof(struct flist));
606         fp->defdam = dam;
607         fp->victim = vict;
608         switch (attgp->ef_type) {
609         case EF_SECTOR:
610             fp->x = attgp->x;
611             fp->y = attgp->y;
612             fp->type = targ_land;
613             break;
614         default:
615             fp->type = targ_ship;
616             fp->uid = attgp->uid;
617             break;
618         }
619         emp_insque(&fp->queue, al);
620     }
621
622     return dam;
623 }
624
625 static void
626 do_defdam(struct emp_qelem *list, double odds)
627 {
628
629     int dam, vict, first = 1;
630     struct flist *fp;
631     struct shpstr ship;
632     struct sctstr sect;
633     struct emp_qelem *qp, *next;
634
635     for (qp = list->q_forw; qp != list; qp = next) {
636         next = qp->q_forw;
637         fp = (struct flist *)qp;
638         if (fp->type == targ_ship) {
639             if (!getship(fp->uid, &ship) || !ship.shp_own)
640                 continue;
641         }
642         if (first) {
643             pr_beep();
644             pr("\nDefenders fire back!\n");
645             first = 0;
646         }
647         dam = odds * fp->defdam;
648
649         if (fp->type == targ_ship) {
650             vict = fp->victim;
651             pr("Return fire hit %s in %s for %d damage.\n",
652                prship(&ship),
653                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
654             if (vict)
655                 wu(0, vict,
656                    "Return fire hit %s in %s for %d damage.\n",
657                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
658             shipdamage(&ship, dam);
659             putship(ship.shp_uid, &ship);
660         } else {
661             getsect(fp->x, fp->y, &sect);
662             vict = fp->victim;
663             pr("Return fire hit sector %s for %d damage.\n",
664                xyas(fp->x, fp->y, player->cnum), dam);
665             sectdamage(&sect, dam, 0);
666             putsect(&sect);
667             if (vict)
668                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
669                    xyas(fp->x, fp->y, vict), dam);
670         }
671         emp_remque(&fp->queue);
672         free(fp);
673     }
674 }
675
676 static int
677 quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
678              coord ax, coord ay, int *nfiring)
679 {
680     double erange;
681     struct shpstr ship;
682     struct lndstr land;
683     struct nstr_item ni;
684     int dam, dam2, rel, rel2;
685     struct sctstr firing;
686     struct nstr_sect ns;
687     struct flist *fp;
688
689     if (own == 0)
690         return 0;
691     dam = 0;
692     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
693     while (nxtitem(&ni, &ship)) {
694         if (ship.shp_own == 0)
695             continue;
696
697         if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
698             continue;
699
700         rel = getrel(getnatp(ship.shp_own), own);
701         rel2 = getrel(getnatp(ship.shp_own), aown);
702         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
703             continue;
704         /* Don't shoot yourself */
705         if (ship.shp_own == aown)
706             continue;
707         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
708             erange = torprange(&ship);
709             if (roundrange(erange) < ni.curdist)
710                 continue;
711             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
712                 continue;
713             dam2 = shp_torp(&ship, 0);
714             /* no putship(&ship) because ammo is charged in use_ammo() */
715             if (dam2 < 0)
716                 continue;
717             if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
718                 dam2 = 0;
719         } else {
720             erange = shp_fire_range(&ship);
721             if (roundrange(erange) < ni.curdist)
722                 continue;
723             dam2 = shp_fire(&ship);
724             /* no putship(&ship) because ammo is charged in use_ammo() */
725             if (dam2 < 0)
726                 continue;
727             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
728         }
729         (*nfiring)++;
730         fp = malloc(sizeof(struct flist));
731         memset(fp, 0, sizeof(struct flist));
732         fp->type = targ_ship;
733         fp->uid = ship.shp_uid;
734         add_to_fired_queue(&fp->queue, list);
735         dam += dam2;
736     }
737     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
738     while (nxtitem(&ni, &land)) {
739         if (land.lnd_own == 0)
740             continue;
741         /* Don't shoot yourself */
742         if (land.lnd_own == aown)
743             continue;
744
745         rel = getrel(getnatp(land.lnd_own), own);
746         rel2 = getrel(getnatp(land.lnd_own), aown);
747
748         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
749             continue;
750
751         erange = lnd_fire_range(&land);
752         if (roundrange(erange) < ni.curdist)
753             continue;
754
755         dam2 = lnd_fire(&land);
756         /* no putland(&land) because ammo is charged in use_ammo() */
757         if (dam2 < 0)
758             continue;
759
760         (*nfiring)++;
761         fp = malloc(sizeof(struct flist));
762         memset(fp, 0, sizeof(struct flist));
763         fp->type = targ_unit;
764         fp->uid = land.lnd_uid;
765         add_to_fired_queue(&fp->queue, list);
766         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
767         dam += dam2;
768     }
769
770     /*
771      * Determine if any nearby gun-equipped sectors are within
772      * range and able to fire at an attacker.  Firing sectors
773      * need to have guns, shells, and military.  Sector being
774      * attacked is x,y -- attacker is at ax,ay.
775      */
776
777     if (!opt_NO_FORT_FIRE) {
778         snxtsct_dist(&ns, ax, ay, 8);
779         while (nxtsct(&ns, &firing)) {
780             if (firing.sct_own == 0)
781                 continue;
782             rel = getrel(getnatp(firing.sct_own), own);
783             rel2 = getrel(getnatp(firing.sct_own), aown);
784
785             if ((firing.sct_own != own) &&
786                 ((rel != ALLIED) || (rel2 != AT_WAR)))
787                 continue;
788             /* Don't shoot yourself */
789             if (firing.sct_own == aown)
790                 continue;
791             erange = fortrange(&firing);
792             if (roundrange(erange) < ns.curdist)
793                 continue;
794
795             dam2 = fort_fire(&firing);
796             /* no putsect(&firing) because ammo is charged in use_ammo() */
797             if (dam2 < 0)
798                 continue;
799             (*nfiring)++;
800             fp = malloc(sizeof(struct flist));
801             memset(fp, 0, sizeof(struct flist));
802             fp->x = firing.sct_x;
803             fp->y = firing.sct_y;
804             fp->type = targ_land;
805             add_to_fired_queue(&fp->queue, list);
806             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
807             dam += dam2;
808         }
809     }
810
811     return *nfiring == 0 ? 0 : dam / *nfiring;
812 }
813
814 static void
815 use_ammo(struct emp_qelem *list)
816 {
817     struct emp_qelem *qp, *next;
818     struct flist *fp;
819     struct shpstr ship;
820     struct lndstr land;
821     struct sctstr sect;
822     int shell;
823     short *item;
824
825     /* use 1 shell from everyone */
826     for (qp = list->q_forw; qp != list; qp = next) {
827         next = qp->q_forw;
828         fp = (struct flist *)qp;
829         if (fp->type == targ_ship) {
830             getship(fp->uid, &ship);
831             item = ship.shp_item;
832             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
833                 shell = item[I_SHELL];
834                 shell -= SHP_TORP_SHELLS - 1;
835                 if (shell < 0)
836                     shell = 0;
837                 item[I_SHELL] = shell;
838                 putship(ship.shp_uid, &ship);
839                 /* mob cost = 1/2 a sect's mob */
840                 ship.shp_mobil -= shp_mobcost(&ship) / 2.0;
841             }
842         } else if (fp->type == targ_land) {
843             getsect(fp->x, fp->y, &sect);
844             item = sect.sct_item;
845         } else {
846             getland(fp->uid, &land);
847             item = land.lnd_item;
848         }
849         shell = item[I_SHELL];
850         shell--;
851         if (shell < 0)
852             shell = 0;
853         item[I_SHELL] = shell;
854         if (fp->type == targ_ship)
855             putship(ship.shp_uid, &ship);
856         else if (fp->type == targ_land)
857             putsect(&sect);
858         else
859             putland(land.lnd_uid, &land);
860
861         emp_remque(&fp->queue);
862         free(fp);
863     }
864
865 }
866
867 static void
868 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
869 {
870     struct emp_qelem *qp;
871     struct flist *fp;
872     struct flist *ep = (struct flist *)elem;
873
874     /* Don't put them on the list if they're already there */
875     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
876         fp = (struct flist *)qp;
877         if (fp->type == targ_land
878             ? fp->x == ep->x && fp->y == ep->y
879             : fp->uid == ep->uid) {
880             free(ep);
881             return;
882         }
883     }
884     emp_insque(elem, list);
885 }