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