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