]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
The previous revision is wrong, back it out.
[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     s_char vbuf[20];
92     s_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     s_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     s_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", 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 < 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             if (chance(prb)) {
557                 pr("Wind deflects shell%s.\n", splur(shots));
558 /*                      dam = (int)((double)dam / 2.0);*/
559                 dam =
560                     (int)((double)dam *
561                           (double)((double)(90 - (random() % 11)) /
562                                    100.0));
563                 if (dam < 0)
564                     dam = 0;
565             }
566             break;
567         }
568         switch (target) {
569         case targ_bogus:
570         case targ_land:
571             nreport(player->cnum, N_SCT_SHELL, vict, 1);
572             if (vict && vict != player->cnum)
573                 wu(0, vict,
574                    "Country #%d shelled sector %s for %d damage.\n",
575                    player->cnum, xyas(x, y, vict), dam);
576             pr("Shell%s hit sector %s for %d damage.\n",
577                splur(shots), xyas(x, y, player->cnum), dam);
578             /* Ok, it wasn't a bogus target, so do damage. */
579             if (target != targ_bogus)
580                 sectdamage(&vsect, dam, 0);
581             break;
582         case targ_ship:
583             nreport(player->cnum, N_SHP_SHELL, vict, 1);
584             /* fall through */
585         default:
586             if ((target != targ_sub) ||
587                 ((vship.shp_rflags & RET_DCHRGED) == 0))
588                 check_retreat_and_do_shipdamage(&vship, dam);
589             else
590                 shipdamage(&vship, dam);
591             if (vict) {
592                 wu(0, vict,
593                    "Country #%d shelled %s in %s for %d damage.\n",
594                    player->cnum, prship(&vship),
595                    xyas(vship.shp_x, vship.shp_y, vict), dam);
596             }
597             pr("Shell%s hit %s in %s for %d damage.\n",
598                splur(shots), prsub(&vship),
599                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
600
601             if (vship.shp_effic < SHIP_MINEFF)
602                 pr("%s sunk!\n", prsub(&vship));
603
604             break;
605         }
606         /*  Ok, now, check if we had a bogus target.  If so,
607            just continue on, since there is no defender. */
608         if (target == targ_bogus)
609             continue;
610         if (attacker == targ_unit) {
611             attacker = targ_land;
612             getsect(fland.lnd_x, fland.lnd_y, &fsect);
613         }
614         totaldefdam =
615             defend(&fired, &defended, target, attacker, &vsect, &fsect,
616                    &vship, &fship, fx, fy, &ndefending);
617         switch (target) {
618         case targ_land:
619             putsect(&vsect);
620             break;
621         default:
622             putship(vship.shp_uid, &vship);
623             break;
624         }
625         if ((totaldefdam == 0) && (target == targ_ship))
626             if (vship.shp_rflags & RET_INJURED)
627                 retreat_ship(&vship, 'h');
628         switch (attacker) {
629         case targ_land:
630             putsect(&fsect);
631             break;
632         default:
633             if ((target == targ_ship) || (target == targ_sub)) {
634                 if (fship.shp_effic > SHIP_MINEFF) {
635                     shp_missdef(&fship, vict);
636                 };
637             };
638             putship(fship.shp_uid, &fship);
639             break;
640         }
641     }
642
643     use_ammo(&defended);
644     if (nfiring)
645         odds = ((double)ndefending) / ((double)nfiring);
646     else
647         odds = 1.0;
648     do_defdam(&fired, odds);
649     return RET_OK;
650 }
651
652 static int
653 defend(struct emp_qelem *al, struct emp_qelem *dl, enum targ_type target,
654        enum targ_type attacker, struct sctstr *vsect, struct sctstr *fsect,
655        struct shpstr *vship, struct shpstr *fship, int fx, int fy, int *nd)
656 {
657
658     int dam;
659     int vict, nfiring = 0;
660     struct flist *fp;
661     int aown;
662
663     if (attacker == targ_land)
664         aown = fsect->sct_own;
665     else
666         aown = fship->shp_own;
667
668     if (target == targ_land)
669         vict = vsect->sct_own;
670     else
671         vict = vship->shp_own;
672
673     if (0 !=
674         (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
675         if (nfiring > *nd)
676             *nd = nfiring;
677         fp = malloc(sizeof(struct flist));
678         memset(fp, 0, sizeof(struct flist));
679         fp->defdam = dam;
680         fp->victim = vict;
681         switch (attacker) {
682         case targ_land:
683             fp->x = fsect->sct_x;
684             fp->y = fsect->sct_y;
685             fp->type = targ_land;
686             break;
687         default:
688             fp->type = targ_ship;
689             fp->uid = fship->shp_uid;
690             break;
691         }
692         emp_insque(&fp->queue, al);
693     }
694
695     return dam;
696 }
697
698 static void
699 do_defdam(struct emp_qelem *list, double odds)
700 {
701
702     int dam, vict, first = 1;
703     struct flist *fp;
704     struct shpstr ship;
705     struct sctstr sect;
706     struct emp_qelem *qp, *next;
707
708     for (qp = list->q_forw; qp != list; qp = next) {
709         next = qp->q_forw;
710         fp = (struct flist *)qp;
711         if (fp->type == targ_ship) {
712             if (!getship(fp->uid, &ship) || !ship.shp_own)
713                 continue;
714         }
715         if (first) {
716             pr_beep();
717             pr("\nDefenders fire back!\n");
718             first = 0;
719         }
720         dam = (odds * (double)fp->defdam);
721
722         if (fp->type == targ_ship) {
723             vict = fp->victim;
724             pr("Return fire hit %s in %s for %d damage.\n",
725                prship(&ship),
726                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
727             if (vict)
728                 wu(0, vict,
729                    "Return fire hit %s in %s for %d damage.\n",
730                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
731             shipdamage(&ship, dam);
732             putship(ship.shp_uid, &ship);
733         } else {
734             getsect(fp->x, fp->y, &sect);
735             vict = fp->victim;
736             pr("Return fire hit sector %s for %d damage.\n",
737                xyas(fp->x, fp->y, player->cnum), dam);
738             sectdamage(&sect, dam, 0);
739             putsect(&sect);
740             if (vict)
741                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
742                    xyas(fp->x, fp->y, vict), dam);
743         }
744         emp_remque(&fp->queue);
745         free(fp);
746     }
747 }
748
749 static int
750 quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
751              coord ax, coord ay, int *nfiring)
752 {
753     int nshot;
754     double range, erange, hitchance;
755     struct shpstr ship;
756     struct lndstr land;
757     struct nstr_item ni;
758     int dam, dam2, rel, rel2;
759     double tech;
760     struct sctstr firing;
761     struct nstr_sect ns;
762     struct flist *fp;
763     int gun, shell;
764
765     if (own == 0)
766         return 0;
767     dam = 0;
768     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
769     while (nxtitem(&ni, &ship)) {
770         if (ship.shp_own == 0)
771             continue;
772
773         if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
774             (attacker == targ_land))
775             continue;
776
777         rel = getrel(getnatp(ship.shp_own), own);
778         rel2 = getrel(getnatp(ship.shp_own), aown);
779         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
780             continue;
781         /* Don't shoot yourself */
782         if (ship.shp_own == aown)
783             continue;
784         if (ship.shp_effic < 60)
785             continue;
786
787         gun = ship.shp_item[I_GUN];
788         shell = ship.shp_item[I_SHELL];
789
790         if (ship.shp_item[I_MILIT] < 1)
791             continue;
792
793         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
794             if (shell < SHP_TORP_SHELLS)
795                 shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
796                                        I_SHELL, SHP_TORP_SHELLS - shell);
797             if (shell < SHP_TORP_SHELLS)
798                 continue;
799             if (gun < 1)
800                 continue;
801 /*
802   if (ship.shp_mobil <= 0)
803   continue;
804 */
805             erange = ship.shp_effic *
806                 techfact(ship.shp_tech, ((double)ship.shp_frnge))
807                 / 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 }