]> git.pond.sub.org Git - empserver/blob - src/lib/commands/mfir.c
Update copyright notice.
[empserver] / src / lib / commands / mfir.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2004, 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             if (opt_NOMOBCOST == 0)
382                 fship.shp_mobil = max(fship.shp_mobil - 15, -100);
383             putship(fship.shp_uid, &fship);
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 dam, dam2, rel, rel2;
779     double tech;
780     struct sctstr firing;
781     struct nstr_sect ns;
782     struct flist *fp;
783     int gun, shell;
784
785     if (own == 0)
786         return 0;
787     dam = 0;
788     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
789     while (nxtitem(&ni, &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
807         gun = ship.shp_item[I_GUN];
808         shell = ship.shp_item[I_SHELL];
809
810         if (ship.shp_item[I_MILIT] < 1)
811             continue;
812
813         if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
814             if (shell < SHP_TORP_SHELLS)
815                 shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
816                                        I_SHELL, SHP_TORP_SHELLS - shell);
817             if (shell < SHP_TORP_SHELLS)
818                 continue;
819             if (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 (shell < 1)
857                 shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
858                                        I_SHELL, 1);
859             /* only need 1 shell, so don't check that */
860             if (shell < 1)
861                 continue;
862             nshot = min(gun, ship.shp_item[I_MILIT]);
863             nshot = min(nshot, ship.shp_glim);
864             if (nshot == 0)
865                 continue;
866             (*nfiring)++;
867             fp = (struct flist *)malloc(sizeof(struct flist));
868             memset(fp, 0, sizeof(struct flist));
869             fp->type = targ_ship;
870             fp->uid = ship.shp_uid;
871             add_to_fired_queue(&fp->queue, list);
872             nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
873             dam += seagun(ship.shp_effic, nshot);
874         }
875     }
876     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
877     while (nxtitem(&ni, &land)) {
878         if (land.lnd_own == 0)
879             continue;
880         if (land.lnd_effic < LAND_MINFIREEFF)
881             continue;
882         /* Can't fire if on a ship */
883         if (land.lnd_ship >= 0)
884             continue;
885         if (land.lnd_land >= 0)
886             continue;
887         /* Gotta have military */
888         if (lnd_getmil(&land) < 1)
889             continue;
890         /* Don't shoot yourself */
891         if (land.lnd_own == aown)
892             continue;
893
894         rel = getrel(getnatp(land.lnd_own), own);
895         rel2 = getrel(getnatp(land.lnd_own), aown);
896
897         if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
898             continue;
899
900
901         range = techfact((int)land.lnd_tech, (double)land.lnd_frg / 2.0);
902         range = (double)roundrange(range);
903         if (range < ni.curdist)
904             continue;
905
906         resupply_all(&land);
907         if (!has_supply(&land))
908             continue;
909
910         gun = land.lnd_item[I_GUN];
911         shell = land.lnd_item[I_SHELL];
912
913         if (land.lnd_item[I_MILIT] == 0 || shell == 0 || gun == 0)
914             continue;
915
916         dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, gun,
917                                 land.lnd_ammo, shell);
918
919         (*nfiring)++;
920         fp = (struct flist *)malloc(sizeof(struct flist));
921         memset(fp, 0, sizeof(struct flist));
922         fp->type = targ_unit;
923         fp->uid = land.lnd_uid;
924         add_to_fired_queue(&fp->queue, list);
925         use_supply(&land);
926         putland(land.lnd_uid, &land);
927         nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
928         dam += dam2;
929     }
930
931     /*
932      * Determine if any nearby gun-equipped sectors are within
933      * range and able to fire at an attacker.  Firing sectors
934      * need to have guns, shells, and military.  Sector being
935      * attacked is x,y -- attacker is at ax,ay.
936      */
937
938     if (!opt_NO_FORT_FIRE) {
939         snxtsct_dist(&ns, ax, ay, 8);
940         while (nxtsct(&ns, &firing)) {
941
942             if (firing.sct_type != SCT_FORTR)
943                 continue;
944             if (firing.sct_own == 0)
945                 continue;
946             rel = getrel(getnatp(firing.sct_own), own);
947             rel2 = getrel(getnatp(firing.sct_own), aown);
948
949             if ((firing.sct_own != own) &&
950                 ((rel != ALLIED) || (rel2 != AT_WAR)))
951                 continue;
952             /* Don't shoot yourself */
953             if (firing.sct_own == aown)
954                 continue;
955             tech = tfactfire(firing.sct_own, 1.0);
956             range = tech * 7.0;
957             if (firing.sct_effic > 59)  /* fort bonus */
958                 range++;
959             range = (double)roundrange(range);
960             if (range < ns.curdist)
961                 continue;
962
963             gun = firing.sct_item[I_GUN];
964             shell = firing.sct_item[I_SHELL];
965
966             if (shell < 1)
967                 shell += supply_commod(firing.sct_own,
968                                        firing.sct_x, firing.sct_y,
969                                        I_SHELL, 1);
970             if (gun == 0 || firing.sct_item[I_MILIT] < 5 || 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             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;
998     short *item;
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             item = ship.shp_item;
1009             if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
1010                 shell = item[I_SHELL];
1011                 shell -= SHP_TORP_SHELLS - 1;
1012                 if (shell < 0)
1013                     shell = 0;
1014                 item[I_SHELL] = shell;
1015                 putship(ship.shp_uid, &ship);
1016                 mcp = &mchr[(int)ship.shp_type];
1017                 mobcost = ship.shp_effic * 0.01 * ship.shp_speed;
1018                 mobcost = (480.0 / (mobcost +
1019                                     techfact(ship.shp_tech, mobcost)));
1020                 /* mob cost = 1/2 a sect's mob */
1021                 mobcost /= 2.0;
1022                 ship.shp_mobil -= mobcost;
1023             }
1024         } else if (fp->type == targ_land) {
1025             getsect(fp->x, fp->y, &sect);
1026             item = sect.sct_item;
1027         } else {
1028             getland(fp->uid, &land);
1029             item = land.lnd_item;
1030         }
1031         shell = item[I_SHELL];
1032         shell--;
1033         if (shell < 0)
1034             shell = 0;
1035         item[I_SHELL] = shell;
1036         if (fp->type == targ_ship)
1037             putship(ship.shp_uid, &ship);
1038         else if (fp->type == targ_land)
1039             putsect(&sect);
1040         else
1041             putland(land.lnd_uid, &land);
1042
1043         emp_remque(&fp->queue);
1044         free((s_char *)fp);
1045     }
1046
1047 }
1048
1049 static void
1050 add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
1051 {
1052     struct emp_qelem *qp;
1053     struct flist *fp, *ep;
1054     int bad = 0;
1055
1056     ep = (struct flist *)elem;
1057
1058     /* Don't put them on the list if they're already there */
1059     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
1060         fp = (struct flist *)qp;
1061         if ((fp->type == targ_ship) && (fp->uid == ep->uid))
1062             bad = 1;
1063         if ((fp->type != targ_ship) && (fp->x == ep->x) &&
1064             (fp->y == ep->y))
1065             bad = 1;
1066     }
1067
1068     if (!bad)
1069         emp_insque(elem, list);
1070 }