]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Indentation fixes; suspect indent-emp is to blame.
[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     int gun;
784
785     if (own == 0)
786         return 0;
787     dam = 0;
788     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
789     while (nxtitem(&ni, (caddr_t)&ship)) {
790         if (ship.shp_own == 0)
791             continue;
792
793         if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
794             (attacker == targ_land))
795             continue;
796
797         rel = getrel(getnatp(ship.shp_own), own);
798         rel2 = getrel(getnatp(ship.shp_own), aown);
799         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
800             continue;
801         /* Don't shoot yourself */
802         if (ship.shp_own == aown)
803             continue;
804         if (ship.shp_effic < 60)
805             continue;
806         if (getvec(VT_ITEM, vec, (caddr_t)&ship, EF_SHIP) < 0)
807             continue;
808
809         if (vec[I_MILIT] < 1)
810             continue;
811
812         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
813             if (vec[I_SHELL] < 3)
814                 vec[I_SHELL] += supply_commod(ship.shp_own,
815                                               ship.shp_x, ship.shp_y,
816                                               I_SHELL, 3 - vec[I_SHELL]);
817             if (vec[I_SHELL] < 3)
818                 continue;
819             if (vec[I_GUN] < 1)
820                 continue;
821 /*
822   if (ship.shp_mobil <= 0)
823   continue;
824 */
825             erange = ship.shp_effic *
826                 techfact(ship.shp_tech, ((double)ship.shp_frnge))
827                 / 100.0;
828             erange = (double)roundrange(erange);
829             range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
830             if (range > erange)
831                 continue;
832             if (!line_of_sight((s_char **)0, ship.shp_x, ship.shp_y, ax, ay))
833                 continue;
834
835             (*nfiring)++;
836             fp = (struct flist *)malloc(sizeof(struct flist));
837             memset(fp, 0, sizeof(struct flist));
838             fp->type = targ_ship;
839             fp->uid = ship.shp_uid;
840             add_to_fired_queue(&fp->queue, list);
841 /*
842   nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
843 */
844             hitchance = DTORP_HITCHANCE(range, ship.shp_visib);
845             if (!chance(hitchance))
846                 continue;
847
848             dam += TORP_DAMAGE();
849         } else {
850             range = techfact(ship.shp_tech,
851                              ship.shp_frnge * ship.shp_effic / 200.0);
852             range = (double)roundrange(range);
853             if (range < ni.curdist)
854                 continue;
855             /* must have gun, shell, and milit to fire */
856             if (vec[I_SHELL] < 1)
857                 vec[I_SHELL] += supply_commod(ship.shp_own,
858                                               ship.shp_x, ship.shp_y,
859                                               I_SHELL, 1);
860             /* only need 1 shell, so don't check that */
861             if (vec[I_SHELL] < 1)
862                 continue;
863             nshot = min(vec[I_GUN], vec[I_MILIT]);
864             nshot = min(nshot, ship.shp_glim);
865             if (nshot == 0)
866                 continue;
867             (*nfiring)++;
868             fp = (struct flist *)malloc(sizeof(struct flist));
869             memset(fp, 0, sizeof(struct flist));
870             fp->type = targ_ship;
871             fp->uid = ship.shp_uid;
872             add_to_fired_queue(&fp->queue, list);
873             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
874             dam += seagun(ship.shp_effic, nshot);
875         }
876     }
877     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
878     while (nxtitem(&ni, (caddr_t)&land)) {
879         if (land.lnd_own == 0)
880             continue;
881         if (land.lnd_effic < LAND_MINFIREEFF)
882             continue;
883         /* Can't fire if on a ship */
884         if (land.lnd_ship >= 0)
885             continue;
886         if (land.lnd_land >= 0)
887             continue;
888         /* Gotta have military */
889         if (lnd_getmil(&land) < 1)
890             continue;
891         /* Don't shoot yourself */
892         if (land.lnd_own == aown)
893             continue;
894
895         rel = getrel(getnatp(land.lnd_own), own);
896         rel2 = getrel(getnatp(land.lnd_own), aown);
897
898         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
899             continue;
900
901
902         range = techfact((int)land.lnd_tech, (double)land.lnd_frg / 2.0);
903         range = (double)roundrange(range);
904         if (range < ni.curdist)
905             continue;
906
907         resupply_all(&land);
908         if (!has_supply(&land))
909             continue;
910
911         if (getvec(VT_ITEM, vec, (caddr_t)&land, EF_LAND) < 0)
912             continue;
913
914         if (vec[I_MILIT] == 0 || vec[I_SHELL] == 0 || vec[I_GUN] == 0)
915             continue;
916
917         dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, vec[I_GUN],
918                                 land.lnd_ammo, vec[I_SHELL]);
919
920         (*nfiring)++;
921         fp = (struct flist *)malloc(sizeof(struct flist));
922         memset(fp, 0, sizeof(struct flist));
923         fp->type = targ_unit;
924         fp->uid = land.lnd_uid;
925         add_to_fired_queue(&fp->queue, list);
926         use_supply(&land);
927         putland(land.lnd_uid, &land);
928         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
929         dam += dam2;
930     }
931
932     /*
933      * Determine if any nearby gun-equipped sectors are within
934      * range and able to fire at an attacker.  Firing sectors
935      * need to have guns, shells, and military.  Sector being
936      * attacked is x,y -- attacker is at ax,ay.
937      */
938
939     if (!opt_NO_FORT_FIRE) {
940         snxtsct_dist(&ns, ax, ay, 8);
941         while (nxtsct(&ns, &firing)) {
942
943             if (firing.sct_type != SCT_FORTR)
944                 continue;
945             if (firing.sct_own == 0)
946                 continue;
947             rel = getrel(getnatp(firing.sct_own), own);
948             rel2 = getrel(getnatp(firing.sct_own), aown);
949
950             if ((firing.sct_own != own) &&
951                 ((rel != ALLIED) || (rel2 != AT_WAR)))
952                 continue;
953             /* Don't shoot yourself */
954             if (firing.sct_own == aown)
955                 continue;
956             tech = tfactfire(firing.sct_own, 1.0);
957             range = tech * 7.0;
958             if (firing.sct_effic > 59)  /* fort bonus */
959                 range++;
960             range = (double)roundrange(range);
961             if (range < ns.curdist)
962                 continue;
963             if (getvec(VT_ITEM, vec, (caddr_t)&firing, EF_SECTOR) < 0)
964                 continue;
965             if (vec[I_SHELL] < 1)
966                 vec[I_SHELL] += supply_commod(firing.sct_own,
967                                               firing.sct_x, firing.sct_y,
968                                               I_SHELL, 1);
969             if (vec[I_GUN] == 0 || vec[I_MILIT] < 5 || vec[I_SHELL] == 0)
970                 continue;
971             (*nfiring)++;
972             fp = (struct flist *)malloc(sizeof(struct flist));
973             memset(fp, 0, sizeof(struct flist));
974             fp->x = firing.sct_x;
975             fp->y = firing.sct_y;
976             fp->type = targ_land;
977             add_to_fired_queue(&fp->queue, list);
978             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
979             gun = vec[I_GUN];
980             if (gun > 7)
981                 gun = 7;
982             dam += landgun((int)firing.sct_effic, gun);
983         }
984     }
985
986     return ((*nfiring) == 0 ? 0 : (dam / (*nfiring)));
987 }
988
989 static void
990 use_ammo(struct emp_qelem *list)
991 {
992     struct emp_qelem *qp, *next;
993     struct flist *fp;
994     struct shpstr ship;
995     struct lndstr land;
996     struct sctstr sect;
997     int shell, type;
998     s_char *ptr;
999     double mobcost;
1000     struct mchrstr *mcp;
1001
1002     /* use 1 shell from everyone */
1003     for (qp = list->q_forw; qp != list; qp = next) {
1004         next = qp->q_forw;
1005         fp = (struct flist *)qp;
1006         if (fp->type == targ_ship) {
1007             getship(fp->uid, &ship);
1008             ptr = (s_char *)&ship;
1009             type = EF_SHIP;
1010             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
1011                 shell = getvar(V_SHELL, ptr, type);
1012                 shell--;
1013                 if (shell < 0)
1014                     shell = 0;
1015                 putvar(V_SHELL, shell, ptr, type);
1016                 putship(ship.shp_uid, &ship);
1017                 mcp = &mchr[(int)ship.shp_type];
1018                 mobcost = ship.shp_effic * 0.01 * ship.shp_speed;
1019                 mobcost = (480.0 / (mobcost +
1020                                     techfact(ship.shp_tech, mobcost)));
1021                 /* mob cost = 1/2 a sect's mob */
1022                 mobcost /= 2.0;
1023                 ship.shp_mobil -= mobcost;
1024             }
1025         } else if (fp->type == targ_land) {
1026             getsect(fp->x, fp->y, &sect);
1027             ptr = (s_char *)&sect;
1028             type = EF_SECTOR;
1029         } else {
1030             getland(fp->uid, &land);
1031             ptr = (s_char *)&land;
1032             type = EF_LAND;
1033         }
1034         shell = getvar(V_SHELL, ptr, type);
1035         shell--;
1036         if (shell < 0)
1037             shell = 0;
1038         putvar(V_SHELL, shell, ptr, type);
1039         if (fp->type == targ_ship)
1040             putship(ship.shp_uid, &ship);
1041         else if (fp->type == targ_land)
1042             putsect(&sect);
1043         else
1044             putland(land.lnd_uid, &land);
1045
1046         emp_remque(&fp->queue);
1047         free((s_char *)fp);
1048     }
1049
1050 }
1051
1052 static void
1053 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
1054 {
1055     struct emp_qelem *qp;
1056     struct flist *fp, *ep;
1057     int bad = 0;
1058
1059     ep = (struct flist *)elem;
1060
1061     /* Don't put them on the list if they're already there */
1062     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
1063         fp = (struct flist *)qp;
1064         if ((fp->type == targ_ship) && (fp->uid == ep->uid))
1065             bad = 1;
1066         if ((fp->type != targ_ship) && (fp->x == ep->x) &&
1067             (fp->y == ep->y))
1068             bad = 1;
1069     }
1070
1071     if (!bad)
1072         emp_insque(elem, list);
1073 }