]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Use `short' instead of `unsigned short' for item storage in places
[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 (fland.lnd_item[I_SHELL] == 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 (item.ship.shp_item[I_MILIT] < 1) {
211                 pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
212                 continue;
213             }
214             gun = item.ship.shp_item[I_GUN];
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 (item.ship.shp_item[I_SHELL] == 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 (item.sect.sct_item[I_GUN] == 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 (item.sect.sct_item[I_SHELL] == 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 (item.sect.sct_item[I_MILIT] < 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 = fship.shp_item[I_MILIT]) < 1) {
331                 pr("Not enough military for firing crew.\n");
332                 continue;
333             }
334             gun = fship.shp_item[I_GUN];
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 = fship.shp_item[I_SHELL];
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             fship.shp_item[I_SHELL] = shell;
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 (fland.lnd_item[I_SHELL] == 0) {
406                 pr("%s -- not enough shells\n", prland(&fland));
407                 continue;
408             }
409
410             shell = fland.lnd_item[I_SHELL];
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 = fland.lnd_item[I_GUN];
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 = fsect.sct_item[I_GUN]) == 0) {
455                 pr("Insufficient arms.\n");
456                 continue;
457             }
458             shell = fsect.sct_item[I_SHELL];
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 (fsect.sct_item[I_MILIT] < 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             fsect.sct_item[I_SHELL] = shell;
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             /* fall through */
605         default:
606             if ((target != targ_sub) ||
607                 ((vship.shp_rflags & RET_DCHRGED) == 0))
608                 check_retreat_and_do_shipdamage(&vship, dam);
609             else
610                 shipdamage(&vship, dam);
611             if (vict) {
612                 wu(0, vict,
613                    "Country #%d shelled %s in %s for %d damage.\n",
614                    player->cnum, prship(&vship),
615                    xyas(vship.shp_x, vship.shp_y, vict), dam);
616             }
617             pr("Shell%s hit %s in %s for %d damage.\n",
618                splur(shots), prsub(&vship),
619                xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
620
621             if (vship.shp_effic < SHIP_MINEFF)
622                 pr("%s sunk!\n", prsub(&vship));
623
624             break;
625         }
626         /*  Ok, now, check if we had a bogus target.  If so,
627            just continue on, since there is no defender. */
628         if (target == targ_bogus)
629             continue;
630         if (attacker == targ_unit) {
631             attacker = targ_land;
632             getsect(fland.lnd_x, fland.lnd_y, &fsect);
633         }
634         totaldefdam =
635             defend(&fired, &defended, target, attacker, &vsect, &fsect,
636                    &vship, &fship, fx, fy, &ndefending);
637         switch (target) {
638         case targ_land:
639             putsect(&vsect);
640             break;
641         default:
642             putship(vship.shp_uid, &vship);
643             break;
644         }
645         if ((totaldefdam == 0) && (target == targ_ship))
646             if (vship.shp_rflags & RET_INJURED)
647                 retreat_ship(&vship, 'h');
648         switch (attacker) {
649         case targ_land:
650             putsect(&fsect);
651             break;
652         default:
653             if ((target == targ_ship) || (target == targ_sub)) {
654                 if (fship.shp_effic > SHIP_MINEFF) {
655                     shp_missdef(&fship, vict);
656                 };
657             };
658             putship(fship.shp_uid, &fship);
659             break;
660         }
661     }
662
663     use_ammo(&defended);
664     if (nfiring)
665         odds = ((double)ndefending) / ((double)nfiring);
666     else
667         odds = 1.0;
668     do_defdam(&fired, odds);
669     return RET_OK;
670 }
671
672 static int
673 defend(struct emp_qelem *al, struct emp_qelem *dl, enum targ_type target,
674        enum targ_type attacker, struct sctstr *vsect, struct sctstr *fsect,
675        struct shpstr *vship, struct shpstr *fship, int fx, int fy, int *nd)
676 {
677
678     int dam;
679     int vict, nfiring = 0;
680     struct flist *fp;
681     int aown;
682
683     if (attacker == targ_land)
684         aown = fsect->sct_own;
685     else
686         aown = fship->shp_own;
687
688     if (target == targ_land)
689         vict = vsect->sct_own;
690     else
691         vict = vship->shp_own;
692
693     if (0 !=
694         (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
695         if (nfiring > *nd)
696             *nd = nfiring;
697         fp = (struct flist *)malloc(sizeof(struct flist));
698         memset(fp, 0, sizeof(struct flist));
699         fp->defdam = dam;
700         fp->victim = vict;
701         switch (attacker) {
702         case targ_land:
703             fp->x = fsect->sct_x;
704             fp->y = fsect->sct_y;
705             fp->type = targ_land;
706             break;
707         default:
708             fp->type = targ_ship;
709             fp->uid = fship->shp_uid;
710             break;
711         }
712         emp_insque(&fp->queue, al);
713     }
714
715     return (dam);
716 }
717
718 static void
719 do_defdam(struct emp_qelem *list, double odds)
720 {
721
722     int dam, vict, first = 1;
723     struct flist *fp;
724     struct shpstr ship;
725     struct sctstr sect;
726     struct emp_qelem *qp, *next;
727
728     for (qp = list->q_forw; qp != list; qp = next) {
729         next = qp->q_forw;
730         fp = (struct flist *)qp;
731         if (fp->type == targ_ship) {
732             if (!getship(fp->uid, &ship) || !ship.shp_own)
733                 continue;
734         }
735         if (first) {
736             pr_beep();
737             pr("\nDefenders fire back!\n");
738             first = 0;
739         }
740         dam = (odds * (double)fp->defdam);
741
742         if (fp->type == targ_ship) {
743             vict = fp->victim;
744             pr("Return fire hit %s in %s for %d damage.\n",
745                prship(&ship),
746                xyas(ship.shp_x, ship.shp_y, player->cnum), dam);
747             if (vict)
748                 wu(0, vict,
749                    "Return fire hit %s in %s for %d damage.\n",
750                    prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
751             shipdamage(&ship, dam);
752             putship(ship.shp_uid, &ship);
753         } else {
754             getsect(fp->x, fp->y, &sect);
755             vict = fp->victim;
756             pr("Return fire hit sector %s for %d damage.\n",
757                xyas(fp->x, fp->y, player->cnum), dam);
758             sectdamage(&sect, dam, 0);
759             putsect(&sect);
760             if (vict)
761                 wu(0, vict, "Return fire hit sector %s for %d damage.\n",
762                    xyas(fp->x, fp->y, vict), dam);
763         }
764         emp_remque(&fp->queue);
765         free((s_char *)fp);
766     }
767 }
768
769 static int
770 quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
771              coord ax, coord ay, int *nfiring)
772 {
773     int nshot;
774     double range, erange, hitchance;
775     struct shpstr ship;
776     struct lndstr land;
777     struct nstr_item ni;
778     int vec[I_MAX + 1];
779     int dam, dam2, rel, rel2;
780     double tech;
781     struct sctstr firing;
782     struct nstr_sect ns;
783     struct flist *fp;
784     int gun;
785
786     if (own == 0)
787         return 0;
788     dam = 0;
789     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
790     while (nxtitem(&ni, (caddr_t)&ship)) {
791         if (ship.shp_own == 0)
792             continue;
793
794         if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
795             (attacker == targ_land))
796             continue;
797
798         rel = getrel(getnatp(ship.shp_own), own);
799         rel2 = getrel(getnatp(ship.shp_own), aown);
800         if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
801             continue;
802         /* Don't shoot yourself */
803         if (ship.shp_own == aown)
804             continue;
805         if (ship.shp_effic < 60)
806             continue;
807         if (getvec(VT_ITEM, vec, (caddr_t)&ship, EF_SHIP) < 0)
808             continue;
809
810         if (vec[I_MILIT] < 1)
811             continue;
812
813         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
814             if (vec[I_SHELL] < 3)
815                 vec[I_SHELL] += supply_commod(ship.shp_own,
816                                               ship.shp_x, ship.shp_y,
817                                               I_SHELL, 3 - vec[I_SHELL]);
818             if (vec[I_SHELL] < 3)
819                 continue;
820             if (vec[I_GUN] < 1)
821                 continue;
822 /*
823   if (ship.shp_mobil <= 0)
824   continue;
825 */
826             erange = ship.shp_effic *
827                 techfact(ship.shp_tech, ((double)ship.shp_frnge))
828                 / 100.0;
829             erange = (double)roundrange(erange);
830             range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
831             if (range > erange)
832                 continue;
833             if (!line_of_sight((s_char **)0, ship.shp_x, ship.shp_y, ax, ay))
834                 continue;
835
836             (*nfiring)++;
837             fp = (struct flist *)malloc(sizeof(struct flist));
838             memset(fp, 0, sizeof(struct flist));
839             fp->type = targ_ship;
840             fp->uid = ship.shp_uid;
841             add_to_fired_queue(&fp->queue, list);
842 /*
843   nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
844 */
845             hitchance = DTORP_HITCHANCE(range, ship.shp_visib);
846             if (!chance(hitchance))
847                 continue;
848
849             dam += TORP_DAMAGE();
850         } else {
851             range = techfact(ship.shp_tech,
852                              ship.shp_frnge * ship.shp_effic / 200.0);
853             range = (double)roundrange(range);
854             if (range < ni.curdist)
855                 continue;
856             /* must have gun, shell, and milit to fire */
857             if (vec[I_SHELL] < 1)
858                 vec[I_SHELL] += supply_commod(ship.shp_own,
859                                               ship.shp_x, ship.shp_y,
860                                               I_SHELL, 1);
861             /* only need 1 shell, so don't check that */
862             if (vec[I_SHELL] < 1)
863                 continue;
864             nshot = min(vec[I_GUN], vec[I_MILIT]);
865             nshot = min(nshot, ship.shp_glim);
866             if (nshot == 0)
867                 continue;
868             (*nfiring)++;
869             fp = (struct flist *)malloc(sizeof(struct flist));
870             memset(fp, 0, sizeof(struct flist));
871             fp->type = targ_ship;
872             fp->uid = ship.shp_uid;
873             add_to_fired_queue(&fp->queue, list);
874             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
875             dam += seagun(ship.shp_effic, nshot);
876         }
877     }
878     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
879     while (nxtitem(&ni, (caddr_t)&land)) {
880         if (land.lnd_own == 0)
881             continue;
882         if (land.lnd_effic < LAND_MINFIREEFF)
883             continue;
884         /* Can't fire if on a ship */
885         if (land.lnd_ship >= 0)
886             continue;
887         if (land.lnd_land >= 0)
888             continue;
889         /* Gotta have military */
890         if (lnd_getmil(&land) < 1)
891             continue;
892         /* Don't shoot yourself */
893         if (land.lnd_own == aown)
894             continue;
895
896         rel = getrel(getnatp(land.lnd_own), own);
897         rel2 = getrel(getnatp(land.lnd_own), aown);
898
899         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
900             continue;
901
902
903         range = techfact((int)land.lnd_tech, (double)land.lnd_frg / 2.0);
904         range = (double)roundrange(range);
905         if (range < ni.curdist)
906             continue;
907
908         resupply_all(&land);
909         if (!has_supply(&land))
910             continue;
911
912         if (getvec(VT_ITEM, vec, (caddr_t)&land, EF_LAND) < 0)
913             continue;
914
915         if (vec[I_MILIT] == 0 || vec[I_SHELL] == 0 || vec[I_GUN] == 0)
916             continue;
917
918         dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, vec[I_GUN],
919                                 land.lnd_ammo, vec[I_SHELL]);
920
921         (*nfiring)++;
922         fp = (struct flist *)malloc(sizeof(struct flist));
923         memset(fp, 0, sizeof(struct flist));
924         fp->type = targ_unit;
925         fp->uid = land.lnd_uid;
926         add_to_fired_queue(&fp->queue, list);
927         use_supply(&land);
928         putland(land.lnd_uid, &land);
929         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
930         dam += dam2;
931     }
932
933     /*
934      * Determine if any nearby gun-equipped sectors are within
935      * range and able to fire at an attacker.  Firing sectors
936      * need to have guns, shells, and military.  Sector being
937      * attacked is x,y -- attacker is at ax,ay.
938      */
939
940     if (!opt_NO_FORT_FIRE) {
941         snxtsct_dist(&ns, ax, ay, 8);
942         while (nxtsct(&ns, &firing)) {
943
944             if (firing.sct_type != SCT_FORTR)
945                 continue;
946             if (firing.sct_own == 0)
947                 continue;
948             rel = getrel(getnatp(firing.sct_own), own);
949             rel2 = getrel(getnatp(firing.sct_own), aown);
950
951             if ((firing.sct_own != own) &&
952                 ((rel != ALLIED) || (rel2 != AT_WAR)))
953                 continue;
954             /* Don't shoot yourself */
955             if (firing.sct_own == aown)
956                 continue;
957             tech = tfactfire(firing.sct_own, 1.0);
958             range = tech * 7.0;
959             if (firing.sct_effic > 59)  /* fort bonus */
960                 range++;
961             range = (double)roundrange(range);
962             if (range < ns.curdist)
963                 continue;
964             if (getvec(VT_ITEM, vec, (caddr_t)&firing, EF_SECTOR) < 0)
965                 continue;
966             if (vec[I_SHELL] < 1)
967                 vec[I_SHELL] += supply_commod(firing.sct_own,
968                                               firing.sct_x, firing.sct_y,
969                                               I_SHELL, 1);
970             if (vec[I_GUN] == 0 || vec[I_MILIT] < 5 || vec[I_SHELL] == 0)
971                 continue;
972             (*nfiring)++;
973             fp = (struct flist *)malloc(sizeof(struct flist));
974             memset(fp, 0, sizeof(struct flist));
975             fp->x = firing.sct_x;
976             fp->y = firing.sct_y;
977             fp->type = targ_land;
978             add_to_fired_queue(&fp->queue, list);
979             nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
980             gun = vec[I_GUN];
981             if (gun > 7)
982                 gun = 7;
983             dam += landgun((int)firing.sct_effic, gun);
984         }
985     }
986
987     return ((*nfiring) == 0 ? 0 : (dam / (*nfiring)));
988 }
989
990 static void
991 use_ammo(struct emp_qelem *list)
992 {
993     struct emp_qelem *qp, *next;
994     struct flist *fp;
995     struct shpstr ship;
996     struct lndstr land;
997     struct sctstr sect;
998     int shell;
999     short *item;
1000     double mobcost;
1001     struct mchrstr *mcp;
1002
1003     /* use 1 shell from everyone */
1004     for (qp = list->q_forw; qp != list; qp = next) {
1005         next = qp->q_forw;
1006         fp = (struct flist *)qp;
1007         if (fp->type == targ_ship) {
1008             getship(fp->uid, &ship);
1009             item = ship.shp_item;
1010             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
1011                 shell = item[I_SHELL];
1012                 shell--;
1013                 if (shell < 0)
1014                     shell = 0;
1015                 item[I_SHELL] = shell;
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             item = sect.sct_item;
1028         } else {
1029             getland(fp->uid, &land);
1030             item = land.lnd_item;
1031         }
1032         shell = item[I_SHELL];
1033         shell--;
1034         if (shell < 0)
1035             shell = 0;
1036         item[I_SHELL] = shell;
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 static 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 }