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