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