]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
d205618886298e97fa48ef6a69190e1d7ca7fa76
[empserver] / src / lib / commands / mfir.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, 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             gun = fship.shp_item[I_GUN];
312             gun = MIN(gun, fship.shp_glim);
313             if (fship.shp_frnge == 0 || gun == 0) {
314                 pr("Insufficient arms.\n");
315                 continue;
316             }
317             shell = fship.shp_item[I_SHELL];
318             if (shell < 2)
319                 shell += supply_commod(fship.shp_own,
320                                        fship.shp_x, fship.shp_y,
321                                        I_SHELL, 2 - shell);
322             if (shell <= 0) {
323                 pr("Klick!     ...\n");
324                 continue;
325             }
326             if (fship.shp_effic < 60) {
327                 pr("Ship #%d is crippled (%d%%)\n",
328                    fshipno, fship.shp_effic);
329                 continue;
330             }
331             range = effrange(fship.shp_frnge, fship.shp_tech);
332             range2 = roundrange(range);
333             pr("range is %d.00 (%.2f)\n", range2, range);
334             if (target == targ_sub) {
335                 if ((mchr[(int)fship.shp_type].m_flags & M_DCH) == 0) {
336                     /* Don't tell it's a sub */
337                     range2 = -1;
338                 } else if (shell < 2) {
339                     pr("Not enough shells for depth charge!\n");
340                     continue;
341                 }
342             }
343             gun = MIN(gun, shell * 2);
344             gun = MIN(gun, mil / 2);
345             gun = MAX(gun, 1);
346             shots = gun;
347             guneff = seagun(fship.shp_effic, shots);
348             dam = (int)guneff;
349             shell -= ldround(shots / 2.0, 1);
350             fship.shp_item[I_SHELL] = shell;
351             if (opt_NOMOBCOST == 0)
352                 fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
353             putship(fship.shp_uid, &fship);
354         } else if (attacker == targ_unit) {
355             if (fland.lnd_own != player->cnum) {
356                 pr("Not your unit!\n");
357                 continue;
358             }
359
360             if (target == targ_land) {
361                 if (fland.lnd_x == vsect.sct_x
362                     && fland.lnd_y == vsect.sct_y) {
363                     pr("You can't fire upon yourself!\n");
364                     continue;
365                 }
366             }
367
368             fx = fland.lnd_x;
369             fy = fland.lnd_y;
370
371             if (fland.lnd_frg == 0) {
372                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
373                 continue;
374             }
375             if (fland.lnd_item[I_SHELL] == 0) {
376                 pr("%s -- not enough shells\n", prland(&fland));
377                 continue;
378             }
379
380             shell = fland.lnd_item[I_SHELL];
381
382             range = effrange(fland.lnd_frg, fland.lnd_tech);
383             range2 = roundrange(range);
384             pr("range is %d.00 (%.2f)\n", range2, range);
385             if (target == targ_sub) {
386                 /* Don't tell it's a sub */
387                 range2 = -1;
388             }
389
390             gun = fland.lnd_item[I_GUN];
391             if (gun <= 0) {
392                 pr("%s -- not enough guns\n", prland(&fland));
393                 continue;
394             }
395
396             dam = (int)landunitgun(fland.lnd_effic, fland.lnd_dam, gun,
397                                    fland.lnd_ammo, shell);
398             if (target == targ_ship) {
399                 if (chance(fland.lnd_acc / 100.0))
400                     dam = ldround(dam / 2.0, 1);
401             }
402             use_supply(&fland);
403             resupply_commod(&fland, I_SHELL);   /* Get more shells */
404             putland(fland.lnd_uid, &fland);
405         } else {
406             fx = fsect.sct_x;
407             fy = fsect.sct_y;
408             if (fsect.sct_own != player->cnum ||
409                 fsect.sct_type != SCT_FORTR) {
410                 pr("No fortress at %s\n",
411                    xyas(fsect.sct_x, fsect.sct_y, player->cnum));
412                 continue;
413             }
414             if (target == targ_land) {
415                 if (fsect.sct_x == vsect.sct_x
416                     && fsect.sct_y == vsect.sct_y) {
417                     pr("You can't fire upon yourself!\n");
418                     continue;
419                 }
420             }
421             attacker = targ_land;
422             if ((gun = fsect.sct_item[I_GUN]) == 0) {
423                 pr("Insufficient arms.\n");
424                 continue;
425             }
426             shell = fsect.sct_item[I_SHELL];
427             if (shell <= 0)
428                 shell += supply_commod(fsect.sct_own,
429                                        fsect.sct_x, fsect.sct_y,
430                                        I_SHELL, 1);
431             if (shell <= 0) {
432                 pr("Klick!     ...\n");
433                 continue;
434             }
435             if (fsect.sct_item[I_MILIT] < 5) {
436                 pr("Not enough military for firing crew.\n");
437                 continue;
438             }
439             if (gun > 7)
440                 gun = 7;
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             guneff = landgun((int)fsect.sct_effic, gun);
449             dam = (int)guneff;
450             shell--;
451             fsect.sct_item[I_SHELL] = shell;
452             putsect(&fsect);
453         }
454         trange = mapdist(x, y, fx, fy);
455         if (trange > range2) {
456             pr("Target out of range.\n");
457 /*
458                         switch (target) {
459                         case targ_land:
460                         case targ_bogus:
461                                 pr("Target out of range.  Thud.\n");
462                                 break ;
463                         default:
464                                 pr("Target ship out of range.  Splash.\n");
465                                 break ;
466                         }       
467  */
468             switch (attacker) {
469             case targ_land:
470                 putsect(&fsect);
471                 break;
472             case targ_unit:
473                 fland.lnd_mission = 0;
474                 putland(fland.lnd_uid, &fland);
475                 break;
476             default:
477                 fship.shp_mission = 0;
478                 putship(fship.shp_uid, &fship);
479             }
480             continue;
481         }
482 /*
483                 if (target == targ_bogus) {
484                         if (vsect.sct_type == SCT_SANCT) {
485                                 pr("%s is a %s!!\n", vbuf,
486                                    dchr[SCT_SANCT].d_name);
487                                 continue;
488                         } else if (vsect.sct_type == SCT_WATER) {
489                                 pr("You must specify a ship in sector %s!\n",
490                                    vbuf);
491                                 continue;
492                         }
493                 }
494 */
495         switch (target) {
496         case targ_ship:
497             if (!trechk(player->cnum, vict, SEAFIR))
498                 continue;
499             break;
500         case targ_sub:
501             if (!trechk(player->cnum, vict, SUBFIR))
502                 continue;
503             break;
504         case targ_unit:
505         case targ_land:
506             if (!trechk(player->cnum, vict, LANFIR))
507                 continue;
508             break;
509         default:
510             break;
511         }
512
513         if (opt_SLOW_WAR) {
514             if (target == targ_land) {
515                 natp = getnatp(player->cnum);
516                 rel = getrel(natp, vict);
517                 if ((rel != AT_WAR) && (player->cnum != vict) &&
518                     (vict) && (vsect.sct_oldown != player->cnum)) {
519                     pr("You're not at war with them!\n");
520                     continue;
521                 }
522             }
523         }
524         nfiring++;
525         switch (target) {
526         case targ_sub:
527             pr_beep();
528             pr("Kawhomp!!!\n");
529             if (vship.shp_rflags & RET_DCHRGED)
530                 retreat_ship(&vship, 'd');
531             break;
532         default:
533             pr_beep();
534             pr("Kaboom!!!\n");
535             prb = range2 ? (double)trange / range2 : 1.0;
536             prb *= prb;
537             if (chance(prb)) {
538                 pr("Wind deflects shell%s.\n", splur(shots));
539 /*                      dam = (int)(dam / 2.0);*/
540                 dam *= (90 - (random() % 11)) / 100.0;
541                 if (dam < 0)
542                     dam = 0;
543             }
544             break;
545         }
546         switch (target) {
547         case targ_bogus:
548         case targ_land:
549             nreport(player->cnum, N_SCT_SHELL, vict, 1);
550             if (vict && vict != player->cnum)
551                 wu(0, vict,
552                    "Country #%d shelled sector %s for %d damage.\n",
553                    player->cnum, xyas(x, y, vict), dam);
554             pr("Shell%s hit sector %s for %d damage.\n",
555                splur(shots), xyas(x, y, player->cnum), dam);
556             /* Ok, it wasn't a bogus target, so do damage. */
557             if (target != targ_bogus)
558                 sectdamage(&vsect, dam, 0);
559             break;
560         case targ_ship:
561             nreport(player->cnum, N_SHP_SHELL, vict, 1);
562             /* fall through */
563         default:
564             if ((target != targ_sub) ||
565                 ((vship.shp_rflags & RET_DCHRGED) == 0))
566                 check_retreat_and_do_shipdamage(&vship, dam);
567             else
568                 shipdamage(&vship, dam);
569             if (vict) {
570                 wu(0, vict,
571                    "Country #%d shelled %s in %s for %d damage.\n",
572                    player->cnum, prship(&vship),
573                    xyas(vship.shp_x, vship.shp_y, vict), dam);
574             }
575             pr("Shell%s hit %s in %s for %d damage.\n",
576                splur(shots), prsub(&vship),
577                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
578
579             if (vship.shp_effic < SHIP_MINEFF)
580                 pr("%s sunk!\n", prsub(&vship));
581
582             break;
583         }
584         /*  Ok, now, check if we had a bogus target.  If so,
585            just continue on, since there is no defender. */
586         if (target == targ_bogus)
587             continue;
588         if (attacker == targ_unit) {
589             attacker = targ_land;
590             getsect(fland.lnd_x, fland.lnd_y, &fsect);
591         }
592         totaldefdam =
593             defend(&fired, &defended, target, attacker, &vsect, &fsect,
594                    &vship, &fship, fx, fy, &ndefending);
595         switch (target) {
596         case targ_land:
597             putsect(&vsect);
598             break;
599         default:
600             putship(vship.shp_uid, &vship);
601             break;
602         }
603         if ((totaldefdam == 0) && (target == targ_ship))
604             if (vship.shp_rflags & RET_HELPLESS)
605                 retreat_ship(&vship, 'h');
606         switch (attacker) {
607         case targ_land:
608             putsect(&fsect);
609             break;
610         default:
611             if ((target == targ_ship) || (target == targ_sub)) {
612                 if (fship.shp_effic > SHIP_MINEFF) {
613                     shp_missdef(&fship, vict);
614                 };
615             };
616             putship(fship.shp_uid, &fship);
617             break;
618         }
619     }
620
621     use_ammo(&defended);
622     if (nfiring)
623         odds = ((double)ndefending) / ((double)nfiring);
624     else
625         odds = 1.0;
626     do_defdam(&fired, odds);
627     return RET_OK;
628 }
629
630 static int
631 defend(struct emp_qelem *al, struct emp_qelem *dl, enum targ_type target,
632        enum targ_type attacker, struct sctstr *vsect, struct sctstr *fsect,
633        struct shpstr *vship, struct shpstr *fship, int fx, int fy, int *nd)
634 {
635
636     int dam;
637     int vict, nfiring = 0;
638     struct flist *fp;
639     int aown;
640
641     if (attacker == targ_land)
642         aown = fsect->sct_own;
643     else
644         aown = fship->shp_own;
645
646     if (target == targ_land)
647         vict = vsect->sct_own;
648     else
649         vict = vship->shp_own;
650
651     if (0 !=
652         (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
653         if (nfiring > *nd)
654             *nd = nfiring;
655         fp = malloc(sizeof(struct flist));
656         memset(fp, 0, sizeof(struct flist));
657         fp->defdam = dam;
658         fp->victim = vict;
659         switch (attacker) {
660         case targ_land:
661             fp->x = fsect->sct_x;
662             fp->y = fsect->sct_y;
663             fp->type = targ_land;
664             break;
665         default:
666             fp->type = targ_ship;
667             fp->uid = fship->shp_uid;
668             break;
669         }
670         emp_insque(&fp->queue, al);
671     }
672
673     return dam;
674 }
675
676 static void
677 do_defdam(struct emp_qelem *list, double odds)
678 {
679
680     int dam, vict, first = 1;
681     struct flist *fp;
682     struct shpstr ship;
683     struct sctstr sect;
684     struct emp_qelem *qp, *next;
685
686     for (qp = list->q_forw; qp != list; qp = next) {
687         next = qp->q_forw;
688         fp = (struct flist *)qp;
689         if (fp->type == targ_ship) {
690             if (!getship(fp->uid, &ship) || !ship.shp_own)
691                 continue;
692         }
693         if (first) {
694             pr_beep();
695             pr("\nDefenders fire back!\n");
696             first = 0;
697         }
698         dam = odds * fp->defdam;
699
700         if (fp->type == targ_ship) {
701             vict = fp->victim;
702             pr("Return fire hit %s in %s for %d damage.\n",
703                prship(&ship),
704                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
705             if (vict)
706                 wu(0, vict,
707                    "Return fire hit %s in %s for %d damage.\n",
708                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
709             shipdamage(&ship, dam);
710             putship(ship.shp_uid, &ship);
711         } else {
712             getsect(fp->x, fp->y, &sect);
713             vict = fp->victim;
714             pr("Return fire hit sector %s for %d damage.\n",
715                xyas(fp->x, fp->y, player->cnum), dam);
716             sectdamage(&sect, dam, 0);
717             putsect(&sect);
718             if (vict)
719                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
720                    xyas(fp->x, fp->y, vict), dam);
721         }
722         emp_remque(&fp->queue);
723         free(fp);
724     }
725 }
726
727 static int
728 quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
729              coord ax, coord ay, int *nfiring)
730 {
731     int nshot, range;
732     double erange, hitchance;
733     struct shpstr ship;
734     struct lndstr land;
735     struct nstr_item ni;
736     int dam, dam2, rel, rel2;
737     struct sctstr firing;
738     struct nstr_sect ns;
739     struct flist *fp;
740     int gun, shell;
741
742     if (own == 0)
743         return 0;
744     dam = 0;
745     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
746     while (nxtitem(&ni, &ship)) {
747         if (ship.shp_own == 0)
748             continue;
749
750         if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
751             (attacker == targ_land))
752             continue;
753
754         rel = getrel(getnatp(ship.shp_own), own);
755         rel2 = getrel(getnatp(ship.shp_own), aown);
756         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
757             continue;
758         /* Don't shoot yourself */
759         if (ship.shp_own == aown)
760             continue;
761         if (ship.shp_effic < 60)
762             continue;
763
764         gun = ship.shp_item[I_GUN];
765         shell = ship.shp_item[I_SHELL];
766
767         if (ship.shp_item[I_MILIT] < 1)
768             continue;
769
770         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
771             if (shell < SHP_TORP_SHELLS)
772                 shell += supply_commod(ship.shp_own,
773                                        ship.shp_x, ship.shp_y,
774                                        I_SHELL, SHP_TORP_SHELLS - shell);
775             if (shell < SHP_TORP_SHELLS)
776                 continue;
777             if (gun < 1)
778                 continue;
779 /*
780   if (ship.shp_mobil <= 0)
781   continue;
782 */
783             erange = torprange(&ship);
784             range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
785             if (range > roundrange(erange))
786                 continue;
787             if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
788                 continue;
789
790             (*nfiring)++;
791             fp = malloc(sizeof(struct flist));
792             memset(fp, 0, sizeof(struct flist));
793             fp->type = targ_ship;
794             fp->uid = ship.shp_uid;
795             add_to_fired_queue(&fp->queue, list);
796 /*
797   nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
798 */
799             hitchance = DTORP_HITCHANCE(range, ship.shp_visib);
800             if (!chance(hitchance))
801                 continue;
802
803             dam += TORP_DAMAGE();
804         } else {
805             erange = effrange(ship.shp_frnge, ship.shp_tech);
806             if (roundrange(erange) < ni.curdist)
807                 continue;
808             /* must have gun, shell, and milit to fire */
809             if (shell < 1)
810                 shell += supply_commod(ship.shp_own,
811                                        ship.shp_x, ship.shp_y, I_SHELL, 1);
812             /* only need 1 shell, so don't check that */
813             if (shell < 1)
814                 continue;
815             nshot = MIN(gun, ship.shp_item[I_MILIT]);
816             nshot = MIN(nshot, ship.shp_glim);
817             if (nshot == 0)
818                 continue;
819             (*nfiring)++;
820             fp = malloc(sizeof(struct flist));
821             memset(fp, 0, sizeof(struct flist));
822             fp->type = targ_ship;
823             fp->uid = ship.shp_uid;
824             add_to_fired_queue(&fp->queue, list);
825             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
826             dam += seagun(ship.shp_effic, nshot);
827         }
828     }
829     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
830     while (nxtitem(&ni, &land)) {
831         if (land.lnd_own == 0)
832             continue;
833         if (land.lnd_effic < LAND_MINFIREEFF)
834             continue;
835         /* Can't fire if on a ship */
836         if (land.lnd_ship >= 0)
837             continue;
838         if (land.lnd_land >= 0)
839             continue;
840         /* Gotta have military */
841         if (land.lnd_item[I_MILIT] < 1)
842             continue;
843         /* Don't shoot yourself */
844         if (land.lnd_own == aown)
845             continue;
846
847         rel = getrel(getnatp(land.lnd_own), own);
848         rel2 = getrel(getnatp(land.lnd_own), aown);
849
850         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
851             continue;
852
853         erange = effrange(land.lnd_frg, land.lnd_tech);
854         if (roundrange(erange) < ni.curdist)
855             continue;
856
857         resupply_all(&land);
858         if (!has_supply(&land))
859             continue;
860
861         gun = land.lnd_item[I_GUN];
862         shell = land.lnd_item[I_SHELL];
863
864         if (land.lnd_item[I_MILIT] == 0 || shell == 0 || gun == 0)
865             continue;
866
867         dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, gun,
868                                 land.lnd_ammo, shell);
869
870         (*nfiring)++;
871         fp = malloc(sizeof(struct flist));
872         memset(fp, 0, sizeof(struct flist));
873         fp->type = targ_unit;
874         fp->uid = land.lnd_uid;
875         add_to_fired_queue(&fp->queue, list);
876         use_supply(&land);
877         putland(land.lnd_uid, &land);
878         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
879         dam += dam2;
880     }
881
882     /*
883      * Determine if any nearby gun-equipped sectors are within
884      * range and able to fire at an attacker.  Firing sectors
885      * need to have guns, shells, and military.  Sector being
886      * attacked is x,y -- attacker is at ax,ay.
887      */
888
889     if (!opt_NO_FORT_FIRE) {
890         snxtsct_dist(&ns, ax, ay, 8);
891         while (nxtsct(&ns, &firing)) {
892             if (firing.sct_own == 0)
893                 continue;
894             rel = getrel(getnatp(firing.sct_own), own);
895             rel2 = getrel(getnatp(firing.sct_own), aown);
896
897             if ((firing.sct_own != own) &&
898                 ((rel != ALLIED) || (rel2 != AT_WAR)))
899                 continue;
900             /* Don't shoot yourself */
901             if (firing.sct_own == aown)
902                 continue;
903             erange = fortrange(&firing);
904             if (roundrange(erange) < ns.curdist)
905                 continue;
906
907             gun = firing.sct_item[I_GUN];
908             shell = firing.sct_item[I_SHELL];
909
910             if (shell < 1)
911                 shell += supply_commod(firing.sct_own,
912                                        firing.sct_x, firing.sct_y,
913                                        I_SHELL, 1);
914             if (gun == 0 || firing.sct_item[I_MILIT] < 5 || shell == 0)
915                 continue;
916             (*nfiring)++;
917             fp = malloc(sizeof(struct flist));
918             memset(fp, 0, sizeof(struct flist));
919             fp->x = firing.sct_x;
920             fp->y = firing.sct_y;
921             fp->type = targ_land;
922             add_to_fired_queue(&fp->queue, list);
923             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
924             if (gun > 7)
925                 gun = 7;
926             dam += landgun((int)firing.sct_effic, gun);
927         }
928     }
929
930     return *nfiring == 0 ? 0 : dam / *nfiring;
931 }
932
933 static void
934 use_ammo(struct emp_qelem *list)
935 {
936     struct emp_qelem *qp, *next;
937     struct flist *fp;
938     struct shpstr ship;
939     struct lndstr land;
940     struct sctstr sect;
941     int shell;
942     short *item;
943
944     /* use 1 shell from everyone */
945     for (qp = list->q_forw; qp != list; qp = next) {
946         next = qp->q_forw;
947         fp = (struct flist *)qp;
948         if (fp->type == targ_ship) {
949             getship(fp->uid, &ship);
950             item = ship.shp_item;
951             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
952                 shell = item[I_SHELL];
953                 shell -= SHP_TORP_SHELLS - 1;
954                 if (shell < 0)
955                     shell = 0;
956                 item[I_SHELL] = shell;
957                 putship(ship.shp_uid, &ship);
958                 /* mob cost = 1/2 a sect's mob */
959                 ship.shp_mobil -= shp_mobcost(&ship) / 2.0;
960             }
961         } else if (fp->type == targ_land) {
962             getsect(fp->x, fp->y, &sect);
963             item = sect.sct_item;
964         } else {
965             getland(fp->uid, &land);
966             item = land.lnd_item;
967         }
968         shell = item[I_SHELL];
969         shell--;
970         if (shell < 0)
971             shell = 0;
972         item[I_SHELL] = shell;
973         if (fp->type == targ_ship)
974             putship(ship.shp_uid, &ship);
975         else if (fp->type == targ_land)
976             putsect(&sect);
977         else
978             putland(land.lnd_uid, &land);
979
980         emp_remque(&fp->queue);
981         free(fp);
982     }
983
984 }
985
986 static void
987 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
988 {
989     struct emp_qelem *qp;
990     struct flist *fp, *ep;
991     int bad = 0;
992
993     ep = (struct flist *)elem;
994
995     /* Don't put them on the list if they're already there */
996     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
997         fp = (struct flist *)qp;
998         if ((fp->type == targ_ship) && (fp->uid == ep->uid))
999             bad = 1;
1000         if ((fp->type != targ_ship) && (fp->x == ep->x) &&
1001             (fp->y == ep->y))
1002             bad = 1;
1003     }
1004
1005     if (!bad)
1006         emp_insque(elem, list);
1007 }