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