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