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