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