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