]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
(multifire): If firing at a submarine with something that can't drop
[empserver] / src / lib / commands / mfir.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2004, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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
35 #include "misc.h"
36 #include "player.h"
37 #include "xy.h"
38 #include "treaty.h"
39 #include "nat.h"
40 #include "ship.h"
41 #include "land.h"
42 #include "sect.h"
43 #include "retreat.h"
44 #include "news.h"
45 #include "nsc.h"
46 #include "file.h"
47 #include "queue.h"
48 #include <ctype.h>
49 #include "optlist.h"
50 #include "damage.h"
51 #include "commands.h"
52
53 enum targ_type {
54     targ_land, targ_ship, targ_sub, targ_unit, targ_bogus
55 };
56
57 struct flist {
58     struct emp_qelem queue;     /* list of fired things */
59     int type;                   /* ship? otherwise sector */
60     int uid;                    /* ship uid */
61     coord x, y;                 /* sector coords */
62     int defdam;                 /* damage defenders did */
63     int victim;                 /* who I was shooting at */
64 };
65
66 union item_u {
67     struct shpstr ship;
68     struct sctstr sect;
69     struct lndstr land;
70 };
71
72 static void add_to_fired_queue(struct emp_qelem *, struct emp_qelem *);
73 static int defend(struct emp_qelem *al,
74                   struct emp_qelem *dl,
75                   enum targ_type target,
76                   enum targ_type attacker,
77                   struct sctstr *vsect,
78                   struct sctstr *fsect,
79                   struct shpstr *vship,
80                   struct shpstr *fship, int fx, int fy, int *nd);
81 static void do_defdam(struct emp_qelem *, double);
82 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
83                         coord, int *);
84 static void use_ammo(struct emp_qelem *);
85
86 int
87 multifire(void)
88 {
89     s_char vbuf[20];
90     s_char *ptr;
91     double range2, range;
92     int trange;
93     coord fx;
94     coord fy;
95     coord x;
96     coord y;
97     int mil;
98     int gun;
99     int shell;
100     int shots;
101     double guneff;
102     int dam;
103     int totaldefdam = 0;
104     int fshipno;
105     int vshipno;
106     double prb;
107     natid vict;
108     struct shpstr fship;
109     struct lndstr fland;
110     struct sctstr fsect;
111     struct shpstr vship;
112     struct sctstr vsect;
113     enum targ_type target, attacker, orig_attacker;
114     int rel;
115     struct natstr *natp;
116     struct nstr_item nbst;
117     int type;
118     s_char *p;
119     int nfiring = 0;
120     int ndefending = 0;
121     union item_u item;
122     struct emp_qelem fired, defended;
123     double odds;
124     s_char buf[1024];
125
126     emp_initque(&fired);
127     emp_initque(&defended);
128     type = (-1);
129     while ((type != EF_SECTOR) && (type != EF_SHIP) && (type != EF_LAND)) {
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         player->argp[1] = 0;
135         type = ef_byname(p);
136         if (type == EF_SECTOR) {
137             if (opt_NO_FORT_FIRE) {
138                 pr("Fort firing is disabled.\n");
139                 return RET_FAIL;
140             }
141             orig_attacker = attacker = targ_land;
142             shots = 1;
143         } else if (type == EF_SHIP) {
144             orig_attacker = attacker = targ_ship;
145         } else if (type == EF_LAND) {
146             orig_attacker = attacker = targ_unit;
147         } else
148             pr("Please type 'sh', 'l', or 'se'!\n");
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, (s_char *)&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", fland.lnd_uid, LAND_MINFIREEFF);
192                 continue;
193             }
194             resupply_commod(&fland, I_SHELL);   /* Get more shells */
195             putland(fland.lnd_uid, &fland);
196             if (fland.lnd_item[I_SHELL] == 0) {
197                 pr("%s -- not enough shells\n", prland(&fland));
198                 continue;
199             }
200         } else if (attacker == targ_ship) {
201             if (!getship(item.ship.shp_uid, &fship))
202                 continue;
203             if (item.ship.shp_own != player->cnum)
204                 continue;
205             if (item.ship.shp_item[I_MILIT] < 1) {
206                 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
207                 continue;
208             }
209             gun = item.ship.shp_item[I_GUN];
210             gun = min(gun, item.ship.shp_glim);
211             if (item.ship.shp_frnge == 0) {
212                 pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
213                 continue;
214             }
215             if (gun == 0) {
216                 pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
217                 continue;
218             }
219             if (item.ship.shp_item[I_SHELL] == 0) {
220                 pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
221                 continue;
222             }
223             if (item.ship.shp_effic < 60) {
224                 pr("Ship #%d is crippled!\n", item.ship.shp_uid);
225                 continue;
226             }
227             fshipno = fship.shp_uid;
228         } else if (attacker == targ_land) {
229             if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
230                 continue;
231             if (item.sect.sct_own != player->cnum)
232                 continue;
233             if (item.sect.sct_type != SCT_FORTR)
234                 continue;
235             if (item.sect.sct_effic < ((u_char)FORTEFF)) {
236                 pr("Fort not efficient enough to fire!\n");
237                 continue;
238             }
239             if (item.sect.sct_item[I_GUN] == 0) {
240                 pr("Not enough guns in sector %s!\n",
241                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
242                 continue;
243             }
244             if (item.sect.sct_item[I_SHELL] == 0) {
245                 pr("Not enough shells in sector %s!\n",
246                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
247                 continue;
248             }
249             if (item.sect.sct_item[I_MILIT] < 5) {
250                 pr("Not enough military in sector %s!\n",
251                    xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
252                 continue;
253             }
254             pr("\nSector %s firing\n",
255                xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
256         }
257         if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
258             || *ptr == '\0')
259             continue;
260         if (player->aborted) {
261             pr("Fire aborted.\n");
262             continue;
263         }
264         ptr[19] = 0;
265         (void)strcpy(vbuf, ptr);
266         if (issector(vbuf))
267             target = targ_land;
268         else
269             target = targ_ship;
270         if (target == targ_ship) {
271             vshipno = atoi(vbuf);
272             if (vshipno < 0 || !getship(vshipno, &vship) ||
273                 (!vship.shp_own)) {
274                 pr("No such ship exists!\n");
275                 continue;
276             }
277             target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
278                 targ_sub : targ_ship;
279             vict = vship.shp_own;
280             x = vship.shp_x;
281             y = vship.shp_y;
282             if (!getsect(x, y, &vsect)) {
283                 pr("No such sector exists!\n");
284                 continue;
285             }
286         } else {
287             if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
288                 pr("No such sector exists!\n");
289                 continue;
290             }
291             /* We check the sector type, but we only use it for damage, not
292                reporting.  That way, you don't get extra information you wouldn't
293                normally get.  Besides, what if they want to slam water? :)  */
294             if (vsect.sct_type == SCT_SANCT || vsect.sct_type == SCT_WATER)
295                 target = targ_bogus;
296             else
297                 target = targ_land;
298             vict = vsect.sct_own;
299             x = vsect.sct_x;
300             y = vsect.sct_y;
301         }
302         if (attacker == targ_ship) {
303             if (fship.shp_own != player->cnum) {
304                 pr("Not your ship!\n");
305                 continue;
306             }
307             if (target == targ_sub || target == targ_ship) {
308                 if (fship.shp_uid == vship.shp_uid) {
309                     pr("You can't fire upon yourself!\n");
310                     continue;
311                 }
312             }
313             fx = fship.shp_x;
314             fy = fship.shp_y;
315 /*
316   attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
317   targ_sub : targ_ship;
318   if (attacker == targ_sub){
319   pr("Subs may not fire normally.. use torpedo.\n");
320   continue;
321   }
322 */
323             attacker = targ_ship;
324             if ((mil = fship.shp_item[I_MILIT]) < 1) {
325                 pr("Not enough military for firing crew.\n");
326                 continue;
327             }
328             gun = fship.shp_item[I_GUN];
329             gun = min(gun, fship.shp_glim);
330             if (fship.shp_frnge == 0 || gun == 0) {
331                 pr("Insufficient arms.\n");
332                 continue;
333             }
334             shell = fship.shp_item[I_SHELL];
335             if (shell < 2)
336                 shell += supply_commod(fship.shp_own, fship.shp_x,
337                                        fship.shp_y, I_SHELL, 2 - shell);
338             if (shell <= 0) {
339                 pr("Klick!     ...\n");
340                 continue;
341             }
342             if (fship.shp_effic < 60) {
343                 pr("Ship #%d is crippled (%d%%)\n", fshipno,
344                    fship.shp_effic);
345                 continue;
346             }
347             range = techfact(fship.shp_tech,
348                              (double)fship.shp_frnge / 2.0);
349             range2 = (double)roundrange(range);
350             pr("range is %.2f (%.2f)\n", range2, range);
351             if (target == targ_sub) {
352                 if ((mchr[(int)fship.shp_type].m_flags & M_DCH) == 0) {
353                     /* Don't tell it's a sub */
354                     range2 = -1;
355                 } else if (shell < 2) {
356                     pr("Not enough shells for depth charge!\n");
357                     continue;
358                 }
359             }
360             gun = min(gun, shell * 2);
361             gun = min(gun, mil / 2);
362             gun = max(gun, 1);
363             shots = gun;
364             guneff = seagun(fship.shp_effic, shots);
365             dam = (int)guneff;
366             shell -= ldround(((double)shots) / 2.0, 1);
367             fship.shp_item[I_SHELL] = shell;
368             if (opt_NOMOBCOST == 0)
369                 fship.shp_mobil = max(fship.shp_mobil - 15, -100);
370             putship(fship.shp_uid, &fship);
371         } else if (attacker == targ_unit) {
372             if (fland.lnd_own != player->cnum) {
373                 pr("Not your unit!\n");
374                 continue;
375             }
376
377             if (target == targ_land) {
378                 if (fland.lnd_x == vsect.sct_x
379                     && fland.lnd_y == vsect.sct_y) {
380                     pr("You can't fire upon yourself!\n");
381                     continue;
382                 }
383             }
384
385             fx = fland.lnd_x;
386             fy = fland.lnd_y;
387
388             if (fland.lnd_frg == 0) {
389                 pr("Unit %d cannot fire!\n", fland.lnd_uid);
390                 continue;
391             }
392             if (fland.lnd_item[I_SHELL] == 0) {
393                 pr("%s -- not enough shells\n", prland(&fland));
394                 continue;
395             }
396
397             shell = fland.lnd_item[I_SHELL];
398
399             range = techfact((int)fland.lnd_tech,
400                              (double)fland.lnd_frg / 2.0);
401             range2 = (double)roundrange(range);
402             pr("range is %.2f (%.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(((double)fland.lnd_acc) / 100.0))
418                     dam = ldround(((double)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", xyas(fsect.sct_x,
429                                                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, fsect.sct_x,
447                                        fsect.sct_y, I_SHELL, 1);
448             if (shell <= 0) {
449                 pr("Klick!     ...\n");
450                 continue;
451             }
452             if (fsect.sct_item[I_MILIT] < 5) {
453                 pr("Not enough military for firing crew.\n");
454                 continue;
455             }
456             if (gun > 7)
457                 gun = 7;
458             range = tfactfire(player->cnum, 7.0);
459             if (fsect.sct_effic > 59)
460                 range++;
461             range2 = (double)roundrange(range);
462             pr("range is %.2f (%.2f)\n", range2, range);
463             if (target == targ_sub) {
464                 /* Don't tell it's a sub */
465                 range2 = -1;
466             }
467             guneff = landgun((int)fsect.sct_effic, gun);
468             dam = (int)guneff;
469             shell--;
470             fsect.sct_item[I_SHELL] = shell;
471             putsect(&fsect);
472         }
473         trange = mapdist(x, y, fx, fy);
474         if (trange > range2) {
475             pr("Target out of range.\n");
476 /*
477                         switch (target) {
478                         case targ_land:
479                         case targ_bogus:
480                                 pr("Target out of range.  Thud.\n");
481                                 break ;
482                         default:
483                                 pr("Target ship out of range.  Splash.\n");
484                                 break ;
485                         }       
486  */
487             switch (attacker) {
488             case targ_land:
489                 putsect(&fsect);
490                 break;
491             case targ_unit:
492                 fland.lnd_mission = 0;
493                 putland(fland.lnd_uid, &fland);
494                 break;
495             default:
496                 fship.shp_mission = 0;
497                 putship(fship.shp_uid, &fship);
498             }
499             continue;
500         }
501 /*
502                 if (target == targ_bogus) {
503                         if (vsect.sct_type == SCT_SANCT) {
504                                 pr("%s is a %s!!\n", vbuf,
505                                    dchr[SCT_SANCT].d_name);
506                                 continue;
507                         } else if (vsect.sct_type == SCT_WATER) {
508                                 pr("You must specify a ship in sector %s!\n",
509                                    vbuf);
510                                 continue;
511                         }
512                 }
513 */
514         switch (target) {
515         case targ_ship:
516             if (!trechk(player->cnum, vict, SEAFIR))
517                 continue;
518             break;
519         case targ_sub:
520             if (!trechk(player->cnum, vict, SUBFIR))
521                 continue;
522             break;
523         case targ_unit:
524         case targ_land:
525             if (!trechk(player->cnum, vict, LANFIR))
526                 continue;
527             break;
528         default:
529             break;
530         }
531
532         if (opt_SLOW_WAR) {
533             if (target == targ_land) {
534                 natp = getnatp(player->cnum);
535                 rel = getrel(natp, vict);
536                 if ((rel != AT_WAR) && (player->cnum != vict) &&
537                     (vict) && (vsect.sct_oldown != player->cnum)) {
538                     pr("You're not at war with them!\n");
539                     continue;
540                 }
541             }
542         }
543         nfiring++;
544         switch (target) {
545         case targ_sub:
546             pr_beep();
547             pr("Kawhomp!!!\n");
548             if (vship.shp_rflags & RET_DCHRGED)
549                 retreat_ship(&vship, 'd');
550             break;
551         default:
552             pr_beep();
553             pr("Kaboom!!!\n");
554             prb = (double)(range2 ? (trange / range2) : 1.0);
555             prb *= prb;
556             srandom(time(NULL));
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_INJURED)
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 = (struct flist *)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((s_char *)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, ((double)ship.shp_frnge))
808                 / 100.0;
809             erange = (double)roundrange(erange);
810             range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
811             if (range > erange)
812                 continue;
813             if (!line_of_sight((s_char **)0, ship.shp_x, ship.shp_y, ax, ay))
814                 continue;
815
816             (*nfiring)++;
817             fp = (struct flist *)malloc(sizeof(struct flist));
818             memset(fp, 0, sizeof(struct flist));
819             fp->type = targ_ship;
820             fp->uid = ship.shp_uid;
821             add_to_fired_queue(&fp->queue, list);
822 /*
823   nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
824 */
825             hitchance = DTORP_HITCHANCE(range, ship.shp_visib);
826             if (!chance(hitchance))
827                 continue;
828
829             dam += TORP_DAMAGE();
830         } else {
831             range = techfact(ship.shp_tech,
832                              ship.shp_frnge * ship.shp_effic / 200.0);
833             range = (double)roundrange(range);
834             if (range < ni.curdist)
835                 continue;
836             /* must have gun, shell, and milit to fire */
837             if (shell < 1)
838                 shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
839                                        I_SHELL, 1);
840             /* only need 1 shell, so don't check that */
841             if (shell < 1)
842                 continue;
843             nshot = min(gun, ship.shp_item[I_MILIT]);
844             nshot = min(nshot, ship.shp_glim);
845             if (nshot == 0)
846                 continue;
847             (*nfiring)++;
848             fp = (struct flist *)malloc(sizeof(struct flist));
849             memset(fp, 0, sizeof(struct flist));
850             fp->type = targ_ship;
851             fp->uid = ship.shp_uid;
852             add_to_fired_queue(&fp->queue, list);
853             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
854             dam += seagun(ship.shp_effic, nshot);
855         }
856     }
857     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
858     while (nxtitem(&ni, &land)) {
859         if (land.lnd_own == 0)
860             continue;
861         if (land.lnd_effic < LAND_MINFIREEFF)
862             continue;
863         /* Can't fire if on a ship */
864         if (land.lnd_ship >= 0)
865             continue;
866         if (land.lnd_land >= 0)
867             continue;
868         /* Gotta have military */
869         if (lnd_getmil(&land) < 1)
870             continue;
871         /* Don't shoot yourself */
872         if (land.lnd_own == aown)
873             continue;
874
875         rel = getrel(getnatp(land.lnd_own), own);
876         rel2 = getrel(getnatp(land.lnd_own), aown);
877
878         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
879             continue;
880
881
882         range = techfact((int)land.lnd_tech, (double)land.lnd_frg / 2.0);
883         range = (double)roundrange(range);
884         if (range < ni.curdist)
885             continue;
886
887         resupply_all(&land);
888         if (!has_supply(&land))
889             continue;
890
891         gun = land.lnd_item[I_GUN];
892         shell = land.lnd_item[I_SHELL];
893
894         if (land.lnd_item[I_MILIT] == 0 || shell == 0 || gun == 0)
895             continue;
896
897         dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, gun,
898                                 land.lnd_ammo, shell);
899
900         (*nfiring)++;
901         fp = (struct flist *)malloc(sizeof(struct flist));
902         memset(fp, 0, sizeof(struct flist));
903         fp->type = targ_unit;
904         fp->uid = land.lnd_uid;
905         add_to_fired_queue(&fp->queue, list);
906         use_supply(&land);
907         putland(land.lnd_uid, &land);
908         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
909         dam += dam2;
910     }
911
912     /*
913      * Determine if any nearby gun-equipped sectors are within
914      * range and able to fire at an attacker.  Firing sectors
915      * need to have guns, shells, and military.  Sector being
916      * attacked is x,y -- attacker is at ax,ay.
917      */
918
919     if (!opt_NO_FORT_FIRE) {
920         snxtsct_dist(&ns, ax, ay, 8);
921         while (nxtsct(&ns, &firing)) {
922
923             if (firing.sct_type != SCT_FORTR)
924                 continue;
925             if (firing.sct_own == 0)
926                 continue;
927             rel = getrel(getnatp(firing.sct_own), own);
928             rel2 = getrel(getnatp(firing.sct_own), aown);
929
930             if ((firing.sct_own != own) &&
931                 ((rel != ALLIED) || (rel2 != AT_WAR)))
932                 continue;
933             /* Don't shoot yourself */
934             if (firing.sct_own == aown)
935                 continue;
936             tech = tfactfire(firing.sct_own, 1.0);
937             range = tech * 7.0;
938             if (firing.sct_effic > 59)  /* fort bonus */
939                 range++;
940             range = (double)roundrange(range);
941             if (range < ns.curdist)
942                 continue;
943
944             gun = firing.sct_item[I_GUN];
945             shell = firing.sct_item[I_SHELL];
946
947             if (shell < 1)
948                 shell += supply_commod(firing.sct_own,
949                                        firing.sct_x, firing.sct_y,
950                                        I_SHELL, 1);
951             if (gun == 0 || firing.sct_item[I_MILIT] < 5 || shell == 0)
952                 continue;
953             (*nfiring)++;
954             fp = (struct flist *)malloc(sizeof(struct flist));
955             memset(fp, 0, sizeof(struct flist));
956             fp->x = firing.sct_x;
957             fp->y = firing.sct_y;
958             fp->type = targ_land;
959             add_to_fired_queue(&fp->queue, list);
960             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
961             if (gun > 7)
962                 gun = 7;
963             dam += landgun((int)firing.sct_effic, gun);
964         }
965     }
966
967     return ((*nfiring) == 0 ? 0 : (dam / (*nfiring)));
968 }
969
970 static void
971 use_ammo(struct emp_qelem *list)
972 {
973     struct emp_qelem *qp, *next;
974     struct flist *fp;
975     struct shpstr ship;
976     struct lndstr land;
977     struct sctstr sect;
978     int shell;
979     short *item;
980     double mobcost;
981     struct mchrstr *mcp;
982
983     /* use 1 shell from everyone */
984     for (qp = list->q_forw; qp != list; qp = next) {
985         next = qp->q_forw;
986         fp = (struct flist *)qp;
987         if (fp->type == targ_ship) {
988             getship(fp->uid, &ship);
989             item = ship.shp_item;
990             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
991                 shell = item[I_SHELL];
992                 shell -= SHP_TORP_SHELLS - 1;
993                 if (shell < 0)
994                     shell = 0;
995                 item[I_SHELL] = shell;
996                 putship(ship.shp_uid, &ship);
997                 mcp = &mchr[(int)ship.shp_type];
998                 mobcost = ship.shp_effic * 0.01 * ship.shp_speed;
999                 mobcost = (480.0 / (mobcost +
1000                                     techfact(ship.shp_tech, mobcost)));
1001                 /* mob cost = 1/2 a sect's mob */
1002                 mobcost /= 2.0;
1003                 ship.shp_mobil -= mobcost;
1004             }
1005         } else if (fp->type == targ_land) {
1006             getsect(fp->x, fp->y, &sect);
1007             item = sect.sct_item;
1008         } else {
1009             getland(fp->uid, &land);
1010             item = land.lnd_item;
1011         }
1012         shell = item[I_SHELL];
1013         shell--;
1014         if (shell < 0)
1015             shell = 0;
1016         item[I_SHELL] = shell;
1017         if (fp->type == targ_ship)
1018             putship(ship.shp_uid, &ship);
1019         else if (fp->type == targ_land)
1020             putsect(&sect);
1021         else
1022             putland(land.lnd_uid, &land);
1023
1024         emp_remque(&fp->queue);
1025         free((s_char *)fp);
1026     }
1027
1028 }
1029
1030 static void
1031 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
1032 {
1033     struct emp_qelem *qp;
1034     struct flist *fp, *ep;
1035     int bad = 0;
1036
1037     ep = (struct flist *)elem;
1038
1039     /* Don't put them on the list if they're already there */
1040     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
1041         fp = (struct flist *)qp;
1042         if ((fp->type == targ_ship) && (fp->uid == ep->uid))
1043             bad = 1;
1044         if ((fp->type != targ_ship) && (fp->x == ep->x) &&
1045             (fp->y == ep->y))
1046             bad = 1;
1047     }
1048
1049     if (!bad)
1050         emp_insque(elem, list);
1051 }