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