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