]> git.pond.sub.org Git - empserver/blobdiff - src/lib/commands/mfir.c
Factor feels_like_helping() out of quiet_bigdef(), sd(), dd()
[empserver] / src / lib / commands / mfir.c
index 93bfb13430d50b036b2d0f33ad87549abd104669..1d8925ff176527e3b73ac89dbead62480c1f9002 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *  Copyright (C) 1986-2010, Dave Pare, Jeff Bailey, Thomas Ruschak,
  *                           Ken Stevens, Steve McClure
  *
  *  This program is free software; you can redistribute it and/or modify
  *
  *  ---
  *
- *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
- *  related information and legal notices. It is expected that any future
- *  projects/authors will amend these files as needed.
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
  *
  *  ---
  *
  *  multifire.c: Fire at other sectors/ships
- * 
+ *
  *  Known contributors to this file:
  *     Steve McClure, 2000
- *     
+ *     Markus Armbruster, 2004-2009
  */
 
-#include "misc.h"
-#include "player.h"
-#include "var.h"
-#include "xy.h"
-#include "treaty.h"
-#include "nat.h"
-#include "ship.h"
-#include "land.h"
-#include "sect.h"
-#include "retreat.h"
-#include "news.h"
-#include "nsc.h"
-#include "file.h"
-#include "queue.h"
-#include <ctype.h>
-#include "options.h"
-#include "optlist.h"
-#include "damage.h"
+#include <config.h>
+
 #include "commands.h"
+#include "empobj.h"
+#include "optlist.h"
+#include "retreat.h"
 
 enum targ_type {
-    targ_land, targ_ship, targ_sub, targ_unit, targ_bogus
+    targ_land, targ_ship, targ_sub, targ_bogus
 };
 
 struct flist {
     struct emp_qelem queue;    /* list of fired things */
-    int type;                  /* ship? otherwise sector */
-    int uid;                   /* ship uid */
-    coord x, y;                        /* sector coords */
+    short type;                        /* EF_SECTOR, EF_SHIP or EF_LAND */
+    int uid;
+    coord x, y;
     int defdam;                        /* damage defenders did */
-    int victim;                        /* who I was shooting at */
+    natid victim;
 };
 
-union item_u {
-    struct shpstr ship;
-    struct sctstr sect;
-    struct lndstr land;
-};
-
-static void add_to_fired_queue(struct emp_qelem *, struct emp_qelem *);
-static int 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);
+static int defend(struct emp_qelem *, struct emp_qelem *,
+                 struct empobj *, natid, int *);
 static void do_defdam(struct emp_qelem *, double);
 static int quiet_bigdef(int, struct emp_qelem *, natid, natid, coord,
                        coord, int *);
-static void use_ammo(struct emp_qelem *);
+static void add_to_flist(struct emp_qelem *, struct empobj *, int, natid);
+static void free_flist(struct emp_qelem *);
+static struct flist *search_flist(struct emp_qelem *, struct empobj *);
 
 int
 multifire(void)
 {
-    s_char vbuf[20];
-    s_char *ptr;
-    double range2, range;
-    int trange;
+    static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
+    char *ptr;
+    double range;
+    int trange, range2;
     coord fx;
     coord fy;
     coord x;
     coord y;
-    int mil;
-    int gun;
-    int shell;
-    int shots;
-    double guneff;
     int dam;
     int totaldefdam = 0;
-    int fshipno;
     int vshipno;
-    double prb;
     natid vict;
     struct shpstr fship;
     struct lndstr fland;
     struct sctstr fsect;
     struct shpstr vship;
     struct sctstr vsect;
-    enum targ_type target, attacker, orig_attacker;
-    int rel;
-    struct natstr *natp;
+    enum targ_type target;
     struct nstr_item nbst;
     int type;
-    s_char *p;
+    struct empobj *attgp;
+    char *p;
     int nfiring = 0;
     int ndefending = 0;
-    union item_u item;
+    union empobj_storage item;
     struct emp_qelem fired, defended;
     double odds;
-    s_char buf[1024];
-#if defined(_WIN32)
-    time_t now;
-#endif
+    char buf[1024];
 
     emp_initque(&fired);
     emp_initque(&defended);
-    type = (-1);
-    while ((type != EF_SECTOR) && (type != EF_SHIP) && (type != EF_LAND)) {
-       if (!(p = getstarg(player->argp[1],
-                          "Firing from ship(s), sect(s), or land unit(s)? ",
-                          buf)))
-           return RET_SYN;
-       player->argp[1] = 0;
-       type = ef_byname(p);
-       if (type == EF_SECTOR) {
-           if (opt_NO_FORT_FIRE) {
-               pr("Fort firing is disabled.\n");
-               return RET_FAIL;
-           }
-           orig_attacker = attacker = targ_land;
-           shots = 1;
-       } else if (type == EF_SHIP) {
-           orig_attacker = attacker = targ_ship;
-       } else if (type == EF_LAND) {
-           orig_attacker = attacker = targ_unit;
-       } else
-           pr("Please type 'sh', 'l', or 'se'!\n");
+    p = getstarg(player->argp[1],
+                "Firing from ship(s), sect(s), or land unit(s)? ", buf);
+    if (!p)
+       return RET_SYN;
+    type = ef_byname_from(p, ef_with_guns);
+    if (opt_NO_FORT_FIRE && type == EF_SECTOR) {
+       pr("Fort firing is disabled.\n");
+       return RET_FAIL;
     }
-    if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
-       || *ptr == '\0')
+    if (type < 0) {
+       pr("Ships, land units or sectors only!\n");
        return RET_SYN;
-
-    if (!snxtitem(&nbst, type, ptr))
+    }
+    if (!snxtitem(&nbst, type, player->argp[2], "Firing from? "))
        return RET_SYN;
 
-    if (player->aborted) {
-       pr("Fire aborted.\n");
-       return RET_OK;
-    }
-    while (nxtitem(&nbst, (s_char *)&item)) {
-       attacker = orig_attacker;
-       if (attacker == targ_unit) {
+    while (nxtitem(&nbst, &item)) {
+       if (type == EF_LAND) {
            if (!getland(item.land.lnd_uid, &fland))
                continue;
            if (!getsect(item.land.lnd_x, item.land.lnd_y, &fsect))
@@ -173,11 +120,11 @@ multifire(void)
            if (item.land.lnd_own != player->cnum)
                continue;
 
-           if (fland.lnd_frg == 0) {
+           if (lchr[fland.lnd_type].l_dam == 0) {
                pr("Unit %d cannot fire!\n", fland.lnd_uid);
                continue;
            }
-           if (lnd_getmil(&fland) < 1) {
+           if (fland.lnd_item[I_MILIT] < 1) {
                pr("Unit %d cannot fire because it has no military!\n",
                   fland.lnd_uid);
                continue;
@@ -193,35 +140,34 @@ multifire(void)
                continue;
            }
            if (fland.lnd_effic < LAND_MINFIREEFF) {
-               pr("Unit %d cannot fire because it is less than %d%% efficient\n", fland.lnd_uid, LAND_MINFIREEFF);
+               pr("Unit %d cannot fire because it is less than %d%% efficient\n",
+                  fland.lnd_uid, LAND_MINFIREEFF);
                continue;
            }
-           resupply_commod(&fland, I_SHELL);   /* Get more shells */
-           putland(fland.lnd_uid, &fland);
-           if (getvar(V_SHELL, (s_char *)&fland, EF_LAND) == 0) {
+           if (fland.lnd_item[I_SHELL] == 0) {
                pr("%s -- not enough shells\n", prland(&fland));
                continue;
            }
-       } else if (attacker == targ_ship) {
+           fx = fland.lnd_x;
+           fy = fland.lnd_y;
+       } else if (type == EF_SHIP) {
            if (!getship(item.ship.shp_uid, &fship))
                continue;
            if (item.ship.shp_own != player->cnum)
                continue;
-           if (getvar(V_MILIT, (s_char *)&item.ship, EF_SHIP) < 1) {
+           if (item.ship.shp_item[I_MILIT] < 1) {
                pr("Not enough mil on ship #%d\n", item.ship.shp_uid);
                continue;
            }
-           gun = getvar(V_GUN, (s_char *)&item.ship, EF_SHIP);
-           gun = min(gun, item.ship.shp_glim);
-           if (item.ship.shp_frnge == 0) {
+           if (mchr[item.ship.shp_type].m_glim == 0) {
                pr("Ships %d cannot fire guns!\n", item.ship.shp_uid);
                continue;
            }
-           if (gun == 0) {
+           if (item.ship.shp_item[I_GUN] == 0) {
                pr("Not enough guns on ship #%d\n", item.ship.shp_uid);
                continue;
            }
-           if (getvar(V_SHELL, (s_char *)&item.ship, EF_SHIP) == 0) {
+           if (item.ship.shp_item[I_SHELL] == 0) {
                pr("Not enough shells on ship #%d\n", item.ship.shp_uid);
                continue;
            }
@@ -229,51 +175,47 @@ multifire(void)
                pr("Ship #%d is crippled!\n", item.ship.shp_uid);
                continue;
            }
-           fshipno = fship.shp_uid;
-       } else if (attacker == targ_land) {
+           fx = fship.shp_x;
+           fy = fship.shp_y;
+       } else {
            if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect))
                continue;
            if (item.sect.sct_own != player->cnum)
                continue;
            if (item.sect.sct_type != SCT_FORTR)
                continue;
-           if (item.sect.sct_effic < ((u_char)FORTEFF)) {
+           if (item.sect.sct_effic < FORTEFF) {
                pr("Fort not efficient enough to fire!\n");
                continue;
            }
-           if (getvar(V_GUN, (s_char *)&item.sect, EF_SECTOR) == 0) {
+           if (item.sect.sct_item[I_GUN] == 0) {
                pr("Not enough guns in sector %s!\n",
                   xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
                continue;
            }
-           if (getvar(V_SHELL, (s_char *)&item.sect, EF_SECTOR) == 0) {
+           if (item.sect.sct_item[I_SHELL] == 0) {
                pr("Not enough shells in sector %s!\n",
                   xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
                continue;
            }
-           if (getvar(V_MILIT, (s_char *)&item.sect, EF_SECTOR) < 5) {
+           if (item.sect.sct_item[I_MILIT] < 5) {
                pr("Not enough military in sector %s!\n",
                   xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
                continue;
            }
            pr("\nSector %s firing\n",
               xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
+           fx = fsect.sct_x;
+           fy = fsect.sct_y;
        }
-       if ((ptr = getstarg(player->argp[3], "Firing at? ", buf)) == 0
-           || *ptr == '\0')
-           continue;
-       if (player->aborted) {
-           pr("Fire aborted.\n");
+
+       ptr = getstarg(player->argp[3], "Firing at? ", buf);
+       if (!ptr)
+           return RET_FAIL;
+       if (!*ptr)
            continue;
-       }
-       ptr[19] = 0;
-       (void)strcpy(vbuf, ptr);
-       if (issector(vbuf))
-           target = targ_land;
-       else
-           target = targ_ship;
-       if (target == targ_ship) {
-           vshipno = atoi(vbuf);
+       if (!issector(ptr)) {
+           vshipno = atoi(ptr);
            if (vshipno < 0 || !getship(vshipno, &vship) ||
                (!vship.shp_own)) {
                pr("No such ship exists!\n");
@@ -289,7 +231,7 @@ multifire(void)
                continue;
            }
        } else {
-           if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
+           if (!sarg_xy(ptr, &x, &y) || !getsect(x, y, &vsect)) {
                pr("No such sector exists!\n");
                continue;
            }
@@ -304,8 +246,12 @@ multifire(void)
            x = vsect.sct_x;
            y = vsect.sct_y;
        }
-       if (attacker == targ_ship) {
-           shots = -1;         /* convert to max later */
+
+       trange = mapdist(x, y, fx, fy);
+
+       if (type == EF_SHIP) {
+           if (!check_ship_ok(&fship))
+               return RET_FAIL;
            if (fship.shp_own != player->cnum) {
                pr("Not your ship!\n");
                continue;
@@ -316,72 +262,44 @@ multifire(void)
                    continue;
                }
            }
-           fx = fship.shp_x;
-           fy = fship.shp_y;
-/*
-  attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
-  targ_sub : targ_ship;
-  if (attacker == targ_sub){
-  pr("Subs may not fire normally.. use torpedo.\n");
-  continue;
-  }
-*/
-           attacker = targ_ship;
-           if ((mil = getvar(V_MILIT, (s_char *)&fship, EF_SHIP)) < 1) {
+           if (fship.shp_item[I_MILIT] < 1) {
                pr("Not enough military for firing crew.\n");
                continue;
            }
-           gun = getvar(V_GUN, (s_char *)&fship, EF_SHIP);
-           gun = min(gun, fship.shp_glim);
-           if (fship.shp_frnge == 0 || gun == 0) {
-               pr("Insufficient arms.\n");
-               continue;
-           }
-           shell = getvar(V_SHELL, (s_char *)&fship, EF_SHIP);
-           if (shell < 2)
-               shell += supply_commod(fship.shp_own, fship.shp_x,
-                                      fship.shp_y, I_SHELL, 2 - shell);
-           if (shell <= 0) {
-               pr("Klick!     ...\n");
-               continue;
-           }
            if (fship.shp_effic < 60) {
-               pr("Ship #%d is crippled (%d%%)\n", fshipno,
-                  fship.shp_effic);
-               continue;
-           }
-           range = techfact(fship.shp_tech,
-                            (double)fship.shp_frnge / 2.0);
-           range2 = (double)roundrange(range);
-           pr("range is %.2f (%.2f)\n", range2, range);
-           if (target == targ_sub) {
-               if ((mchr[(int)fship.shp_type].m_flags & M_DCH) == 0) {
-                   pr("A %s can't drop depth charges!\n",
-                      mchr[(int)fship.shp_type].m_name);
-                   continue;
-               }
-               if (shell < 2) {
-                   pr("Not enough shells for depth charge!\n");
+               pr("Ship #%d is crippled (%d%%)\n",
+                  fship.shp_uid, fship.shp_effic);
+               continue;
+           }
+           range = shp_fire_range(&fship);
+           range2 = roundrange(range);
+           pr("range is %d.00 (%.2f)\n", range2, range);
+           if (target == targ_sub
+               && trange <= range2
+               && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
+               dam = shp_dchrg(&fship);
+           } else {
+               if (target == targ_sub)
+                   /* Don't tell it's a sub */
+                   range2 = -1;
+               if (fship.shp_item[I_GUN] == 0) {
+                   pr("Insufficient arms.\n");
                    continue;
                }
+               dam = shp_fire(&fship);
            }
-           gun = min(gun, shell * 2);
-           gun = min(gun, mil / 2);
-           gun = max(gun, 1);
-           if (shots > gun || shots < 0)
-               shots = gun;
-           else if (shots == 0) {
-               pr("No shots fired.\n");
+           putship(fship.shp_uid, &fship);
+           if (dam <= 0) {
+               pr("Klick!     ...\n");
                continue;
            }
-           guneff = seagun(fship.shp_effic, shots);
-           dam = (int)guneff;
-           shell -= ldround(((double)shots) / 2.0, 1);
-           putvar(V_SHELL, shell, (s_char *)&fship, EF_SHIP);
-           putship(fship.shp_uid, &fship);
-           if (opt_NOMOBCOST == 0)
-               fship.shp_mobil = max(fship.shp_mobil - 15, -100);
-       } else if (attacker == targ_unit) {
+           if (opt_NOMOBCOST == 0) {
+               fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
+               putship(fship.shp_uid, &fship);
+           }
+       } else if (type == EF_LAND) {
+           if (!check_land_ok(&fland))
+               return RET_FAIL;
            if (fland.lnd_own != player->cnum) {
                pr("Not your unit!\n");
                continue;
@@ -395,52 +313,40 @@ multifire(void)
                }
            }
 
-           fx = fland.lnd_x;
-           fy = fland.lnd_y;
-
-           if (fland.lnd_frg == 0) {
+           if (lchr[fland.lnd_type].l_dam == 0) {
                pr("Unit %d cannot fire!\n", fland.lnd_uid);
                continue;
            }
-           if (getvar(V_SHELL, (s_char *)&fland, EF_LAND) == 0) {
-               pr("%s -- not enough shells\n", prland(&fland));
+           if (fland.lnd_item[I_GUN] == 0) {
+               pr("%s -- not enough guns\n", prland(&fland));
                continue;
            }
 
-           shell = getvar(V_SHELL, (s_char *)&fland, EF_LAND);
-
-           range = techfact((int)fland.lnd_tech,
-                            (double)fland.lnd_frg / 2.0);
-           range2 = (double)roundrange(range);
-           pr("range is %.2f (%.2f)\n", range2, range);
+           range = lnd_fire_range(&fland);
+           range2 = roundrange(range);
+           pr("range is %d.00 (%.2f)\n", range2, range);
            if (target == targ_sub) {
-               pr("A %s can't drop depth charges!\n",
-                  lchr[(int)fland.lnd_type].l_name);
-               continue;
+               /* Don't tell it's a sub */
+               range2 = -1;
            }
 
-           gun = getvar(V_GUN, (s_char *)&fland, EF_LAND);
-           if (gun <= 0) {
-               pr("%s -- not enough guns\n", prland(&fland));
+           dam = lnd_fire(&fland);
+           putland(fland.lnd_uid, &fland);
+           if (dam < 0) {
+               pr("Klick!     ...\n");
                continue;
            }
-
-           dam = (int)landunitgun(fland.lnd_effic, fland.lnd_dam, gun,
-                                  fland.lnd_ammo, shell);
            if (target == targ_ship) {
-               if (chance(((double)fland.lnd_acc) / 100.0))
-                   dam = ldround(((double)dam / 2.0), 1);
+               if (chance(lnd_acc(&fland) / 100.0))
+                   dam = ldround(dam / 2.0, 1);
            }
-           use_supply(&fland);
-           resupply_commod(&fland, I_SHELL);   /* Get more shells */
-           putland(fland.lnd_uid, &fland);
        } else {
-           fx = fsect.sct_x;
-           fy = fsect.sct_y;
+           if (!check_sect_ok(&fsect))
+               return RET_FAIL;
            if (fsect.sct_own != player->cnum ||
                fsect.sct_type != SCT_FORTR) {
-               pr("No fortress at %s\n", xyas(fsect.sct_x,
-                                              fsect.sct_y, player->cnum));
+               pr("No fortress at %s\n",
+                  xyas(fsect.sct_x, fsect.sct_y, player->cnum));
                continue;
            }
            if (target == targ_land) {
@@ -450,81 +356,47 @@ multifire(void)
                    continue;
                }
            }
-           attacker = targ_land;
-           if ((gun = getvar(V_GUN, (s_char *)&fsect, EF_SECTOR)) == 0) {
+           if (fsect.sct_item[I_GUN] == 0) {
                pr("Insufficient arms.\n");
                continue;
            }
-           shell = getvar(V_SHELL, (s_char *)&fsect, EF_SECTOR);
-           if (shell <= 0)
-               shell += supply_commod(fsect.sct_own, fsect.sct_x,
-                                      fsect.sct_y, I_SHELL, 1);
-           if (shell <= 0) {
-               pr("Klick!     ...\n");
+           if (fsect.sct_item[I_MILIT] < 5) {
+               pr("Not enough military for firing crew.\n");
                continue;
            }
-           if (getvar(V_MILIT, (s_char *)&fsect, EF_SECTOR) < 5) {
-               pr("Not enough military for firing crew.\n");
+           dam = fort_fire(&fsect);
+           putsect(&fsect);
+           if (dam < 0) {
+               pr("Klick!     ...\n");
                continue;
            }
+           range = fortrange(&fsect);
+           range2 = roundrange(range);
+           pr("range is %d.00 (%.2f)\n", range2, range);
            if (target == targ_sub) {
-               pr("Target ship not sighted!\n");
-               continue;
+               /* Don't tell it's a sub */
+               range2 = -1;
            }
-           if (gun > 7)
-               gun = 7;
-           range = tfactfire(player->cnum, 7.0);
-           if (fsect.sct_effic > 59)
-               range++;
-           range2 = (double)roundrange(range);
-           pr("range is %.2f (%.2f)\n", range2, range);
-           guneff = landgun((int)fsect.sct_effic, gun);
-           dam = (int)guneff;
-           shell--;
-           putvar(V_SHELL, shell, (s_char *)&fsect, EF_SECTOR);
-           putsect(&fsect);
        }
-       trange = mapdist(x, y, fx, fy);
        if (trange > range2) {
            pr("Target out of range.\n");
-/*
-                       switch (target) {
-                       case targ_land:
-                       case targ_bogus:
-                               pr("Target out of range.  Thud.\n");
-                               break ;
-                       default:
-                               pr("Target ship out of range.  Splash.\n");
-                               break ;
-                       }       
- */
-           switch (attacker) {
-           case targ_land:
+           switch (type) {
+           case EF_SECTOR:
                putsect(&fsect);
                break;
-           case targ_unit:
+           case EF_LAND:
                fland.lnd_mission = 0;
                putland(fland.lnd_uid, &fland);
                break;
-           default:
+           case EF_SHIP:
                fship.shp_mission = 0;
                putship(fship.shp_uid, &fship);
+               break;
+           default:
+               CANT_REACH();
            }
            continue;
        }
-/*
-               if (target == targ_bogus) {
-                       if (vsect.sct_type == SCT_SANCT) {
-                               pr("%s is a %s!!\n", vbuf,
-                                  dchr[SCT_SANCT].d_name);
-                               continue;
-                       } else if (vsect.sct_type == SCT_WATER) {
-                               pr("You must specify a ship in sector %s!\n",
-                                  vbuf);
-                               continue;
-                       }
-               }
-*/
        switch (target) {
        case targ_ship:
            if (!trechk(player->cnum, vict, SEAFIR))
@@ -534,7 +406,6 @@ multifire(void)
            if (!trechk(player->cnum, vict, SUBFIR))
                continue;
            break;
-       case targ_unit:
        case targ_land:
            if (!trechk(player->cnum, vict, LANFIR))
                continue;
@@ -543,46 +414,15 @@ multifire(void)
            break;
        }
 
-       if (opt_SLOW_WAR) {
-           if (target == targ_land) {
-               natp = getnatp(player->cnum);
-               rel = getrel(natp, vict);
-               if ((rel != AT_WAR) && (player->cnum != vict) &&
-                   (vict) && (vsect.sct_oldown != player->cnum)) {
-                   pr("You're not at war with them!\n");
-                   continue;
-               }
-           }
-       }
        nfiring++;
        switch (target) {
        case targ_sub:
            pr_beep();
            pr("Kawhomp!!!\n");
-           if (vship.shp_rflags & RET_DCHRGED)
-               retreat_ship(&vship, 'd');
            break;
        default:
            pr_beep();
            pr("Kaboom!!!\n");
-           prb = (double)(range2 ? (trange / range2) : 1.0);
-           prb *= prb;
-#if !defined(_WIN32)
-           srandom(random());
-#else
-           (void)time(&now);
-           (void)srand(now);
-#endif
-           if (chance(prb)) {
-               pr("Wind deflects shell%s.\n", splur(shots));
-/*                     dam = (int)((double)dam / 2.0);*/
-               dam =
-                   (int)((double)dam *
-                         (double)((double)(90 - (random() % 11)) /
-                                  100.0));
-               if (dam < 0)
-                   dam = 0;
-           }
            break;
        }
        switch (target) {
@@ -593,62 +433,61 @@ multifire(void)
                wu(0, vict,
                   "Country #%d shelled sector %s for %d damage.\n",
                   player->cnum, xyas(x, y, vict), dam);
-           pr("Shell%s hit sector %s for %d damage.\n",
-              splur(shots), xyas(x, y, player->cnum), dam);
-           /* Ok, it wasn't a bogus target, so do damage. */
-           if (target != targ_bogus)
-               sectdamage(&vsect, dam, 0);
+           pr("Shells hit sector %s for %d damage.\n",
+              xyas(x, y, player->cnum), dam);
            break;
        case targ_ship:
            nreport(player->cnum, N_SHP_SHELL, vict, 1);
+           /* fall through */
        default:
-           if ((target != targ_sub) ||
-               ((vship.shp_rflags & RET_DCHRGED) == 0))
-               check_retreat_and_do_shipdamage(&vship, dam);
-           else
-               shipdamage(&vship, dam);
            if (vict) {
                wu(0, vict,
                   "Country #%d shelled %s in %s for %d damage.\n",
                   player->cnum, prship(&vship),
                   xyas(vship.shp_x, vship.shp_y, vict), dam);
            }
-           pr("Shell%s hit %s in %s for %d damage.\n",
-              splur(shots), prsub(&vship),
+           pr("Shells hit %s in %s for %d damage.\n",
+              prsub(&vship),
               xyas(vship.shp_x, vship.shp_y, player->cnum), dam);
-
-           if (vship.shp_effic < SHIP_MINEFF)
-               pr("%s sunk!\n", prsub(&vship));
-
            break;
        }
        /*  Ok, now, check if we had a bogus target.  If so,
           just continue on, since there is no defender. */
        if (target == targ_bogus)
            continue;
-       if (attacker == targ_unit) {
-           attacker = targ_land;
+       attgp = &item.gen;
+       if (type == EF_LAND) {
            getsect(fland.lnd_x, fland.lnd_y, &fsect);
+           attgp = (struct empobj *)&fsect;
        }
-       totaldefdam =
-           defend(&fired, &defended, target, attacker, &vsect, &fsect,
-                  &vship, &fship, fx, fy, &ndefending);
+       totaldefdam = defend(&fired, &defended, attgp, vict, &ndefending);
        switch (target) {
        case targ_land:
+           getsect(x, y, &vsect);
+           sectdamage(&vsect, dam);
            putsect(&vsect);
            break;
        default:
+           getship(vshipno, &vship);
+           check_retreat_and_do_shipdamage(&vship, dam);
+           if (vship.shp_effic < SHIP_MINEFF)
+               pr("%s sunk!\n", prsub(&vship));
+           else if (target == targ_sub
+               && (vship.shp_rflags & RET_DCHRGED)
+               && !(vship.shp_rflags & RET_INJURED))
+               retreat_ship(&vship, 'd');
            putship(vship.shp_uid, &vship);
            break;
        }
-       if ((totaldefdam == 0) && (target == targ_ship))
-           if (vship.shp_rflags & RET_INJURED)
-               retreat_ship(&vship, 'h');
-       switch (attacker) {
-       case targ_land:
+       if (totaldefdam == 0 && target == targ_ship
+           && (vship.shp_rflags & RET_HELPLESS)
+           && !(vship.shp_rflags & RET_INJURED))
+           retreat_ship(&vship, 'h');
+       switch (attgp->ef_type) {
+       case EF_SECTOR:
            putsect(&fsect);
            break;
-       default:
+       case EF_SHIP:
            if ((target == targ_ship) || (target == targ_sub)) {
                if (fship.shp_effic > SHIP_MINEFF) {
                    shp_missdef(&fship, vict);
@@ -656,10 +495,12 @@ multifire(void)
            };
            putship(fship.shp_uid, &fship);
            break;
+       default:
+           CANT_REACH();
        }
     }
 
-    use_ammo(&defended);
+    free_flist(&defended);
     if (nfiring)
        odds = ((double)ndefending) / ((double)nfiring);
     else
@@ -669,56 +510,29 @@ multifire(void)
 }
 
 static int
-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)
+defend(struct emp_qelem *al, struct emp_qelem *dl,
+       struct empobj *attgp, natid vict, int *nd)
 {
-
     int dam;
-    int vict, nfiring = 0;
-    struct flist *fp;
-    int aown;
-
-    if (attacker == targ_land)
-       aown = fsect->sct_own;
-    else
-       aown = fship->shp_own;
-
-    if (target == targ_land)
-       vict = vsect->sct_own;
-    else
-       vict = vship->shp_own;
+    int nfiring = 0;
 
-    if (0 !=
-       (dam = quiet_bigdef(attacker, dl, vict, aown, fx, fy, &nfiring))) {
+    dam = quiet_bigdef(attgp->ef_type, dl, vict,
+                      attgp->own, attgp->x, attgp->y, &nfiring);
+    if (dam) {
        if (nfiring > *nd)
            *nd = nfiring;
-       fp = (struct flist *)malloc(sizeof(struct flist));
-       memset(fp, 0, sizeof(struct flist));
-       fp->defdam = dam;
-       fp->victim = vict;
-       switch (attacker) {
-       case targ_land:
-           fp->x = fsect->sct_x;
-           fp->y = fsect->sct_y;
-           fp->type = targ_land;
-           break;
-       default:
-           fp->type = targ_ship;
-           fp->uid = fship->shp_uid;
-           break;
-       }
-       emp_insque(&fp->queue, al);
+       add_to_flist(al, attgp, dam, vict);
     }
 
-    return (dam);
+    return dam;
 }
 
 static void
 do_defdam(struct emp_qelem *list, double odds)
 {
 
-    int dam, vict, first = 1;
+    int dam, first = 1;
+    natid vict;
     struct flist *fp;
     struct shpstr ship;
     struct sctstr sect;
@@ -727,7 +541,7 @@ do_defdam(struct emp_qelem *list, double odds)
     for (qp = list->q_forw; qp != list; qp = next) {
        next = qp->q_forw;
        fp = (struct flist *)qp;
-       if (fp->type == targ_ship) {
+       if (fp->type == EF_SHIP) {
            if (!getship(fp->uid, &ship) || !ship.shp_own)
                continue;
        }
@@ -736,9 +550,9 @@ do_defdam(struct emp_qelem *list, double odds)
            pr("\nDefenders fire back!\n");
            first = 0;
        }
-       dam = (odds * (double)fp->defdam);
+       dam = odds * fp->defdam;
 
-       if (fp->type == targ_ship) {
+       if (fp->type == EF_SHIP) {
            vict = fp->victim;
            pr("Return fire hit %s in %s for %d damage.\n",
               prship(&ship),
@@ -750,182 +564,110 @@ do_defdam(struct emp_qelem *list, double odds)
            shipdamage(&ship, dam);
            putship(ship.shp_uid, &ship);
        } else {
+           CANT_HAPPEN(fp->type != EF_SECTOR);
            getsect(fp->x, fp->y, &sect);
            vict = fp->victim;
            pr("Return fire hit sector %s for %d damage.\n",
               xyas(fp->x, fp->y, player->cnum), dam);
-           sectdamage(&sect, dam, 0);
+           sectdamage(&sect, dam);
            putsect(&sect);
            if (vict)
                wu(0, vict, "Return fire hit sector %s for %d damage.\n",
                   xyas(fp->x, fp->y, vict), dam);
        }
        emp_remque(&fp->queue);
-       free((s_char *)fp);
+       free(fp);
     }
 }
 
 static int
-quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
+quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
             coord ax, coord ay, int *nfiring)
 {
-    int nshot;
-    double range, erange, hitchance;
+    double erange;
     struct shpstr ship;
     struct lndstr land;
     struct nstr_item ni;
-    int vec[I_MAX + 1];
-    int dam, dam2, rel, rel2;
-    double tech;
+    int dam, dam2;
     struct sctstr firing;
     struct nstr_sect ns;
     struct flist *fp;
-    int gun;
 
     if (own == 0)
        return 0;
     dam = 0;
     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
-    while (nxtitem(&ni, (caddr_t)&ship)) {
-       if (ship.shp_own == 0)
-           continue;
-
-       if ((mchr[(int)ship.shp_type].m_flags & M_SUB) &&
-           (attacker == targ_land))
-           continue;
-
-       rel = getrel(getnatp(ship.shp_own), own);
-       rel2 = getrel(getnatp(ship.shp_own), aown);
-       if ((ship.shp_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
-           continue;
-       /* Don't shoot yourself */
-       if (ship.shp_own == aown)
-           continue;
-       if (ship.shp_effic < 60)
-           continue;
-       if (getvec(VT_ITEM, vec, (caddr_t)&ship, EF_SHIP) < 0)
+    while (nxtitem(&ni, &ship)) {
+       if (!feels_like_helping(ship.shp_own, own, aown))
            continue;
 
-       if (vec[I_MILIT] < 1)
+       if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
            continue;
 
        if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
-           if (vec[I_SHELL] < 3)
-               vec[I_SHELL] += supply_commod(ship.shp_own,
-                                             ship.shp_x, ship.shp_y,
-                                             I_SHELL, 3 - vec[I_SHELL]);
-           if (vec[I_SHELL] < 3)
-               continue;
-           if (vec[I_GUN] < 1)
-               continue;
-/*
-  if (ship.shp_mobil <= 0)
-  continue;
-*/
-           erange = ship.shp_effic *
-               techfact(ship.shp_tech, ((double)ship.shp_frnge))
-               / 100.0;
-           erange = (double)roundrange(erange);
-           range = mapdist(ship.shp_x, ship.shp_y, ax, ay);
-           if (range > erange)
+           erange = torprange(&ship);
+           if (roundrange(erange) < ni.curdist)
                continue;
-           if (!line_of_sight((s_char **)0, ship.shp_x, ship.shp_y, ax, ay))
+           if (!line_of_sight(NULL, ship.shp_x, ship.shp_y, ax, ay))
                continue;
-
-           (*nfiring)++;
-           fp = (struct flist *)malloc(sizeof(struct flist));
-           memset(fp, 0, sizeof(struct flist));
-           fp->type = targ_ship;
-           fp->uid = ship.shp_uid;
-           add_to_fired_queue(&fp->queue, list);
-/*
-  nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
-*/
-           hitchance = DTORP_HITCHANCE(range, ship.shp_visib);
-           if (!chance(hitchance))
+           fp = search_flist(list, (struct empobj *)&ship);
+           if (fp)
+               dam2 = fp->defdam;
+           else {
+               dam2 = shp_torp(&ship, 0);
+               putship(ship.shp_uid, &ship);
+           }
+           if (dam2 < 0)
                continue;
-
-           dam += TORP_DAMAGE();
+           if (!chance(shp_torp_hitchance(&ship, ni.curdist)))
+               dam2 = 0;
        } else {
-           range = techfact(ship.shp_tech,
-                            ship.shp_frnge * ship.shp_effic / 200.0);
-           range = (double)roundrange(range);
-           if (range < ni.curdist)
-               continue;
-           /* must have gun, shell, and milit to fire */
-           if (vec[I_SHELL] < 1)
-               vec[I_SHELL] += supply_commod(ship.shp_own,
-                                             ship.shp_x, ship.shp_y,
-                                             I_SHELL, 1);
-           /* only need 1 shell, so don't check that */
-           if (vec[I_SHELL] < 1)
-               continue;
-           nshot = min(vec[I_GUN], vec[I_MILIT]);
-           nshot = min(nshot, ship.shp_glim);
-           if (nshot == 0)
+           erange = shp_fire_range(&ship);
+           if (roundrange(erange) < ni.curdist)
+               continue;
+           fp = search_flist(list, (struct empobj *)&ship);
+           if (fp)
+               dam2 = fp->defdam;
+           else {
+               dam2 = shp_fire(&ship);
+               putship(ship.shp_uid, &ship);
+           }
+           if (dam2 < 0)
                continue;
-           (*nfiring)++;
-           fp = (struct flist *)malloc(sizeof(struct flist));
-           memset(fp, 0, sizeof(struct flist));
-           fp->type = targ_ship;
-           fp->uid = ship.shp_uid;
-           add_to_fired_queue(&fp->queue, list);
            nreport(ship.shp_own, N_FIRE_BACK, player->cnum, 1);
-           dam += seagun(ship.shp_effic, nshot);
        }
+       (*nfiring)++;
+       if (!fp)
+           add_to_flist(list, (struct empobj *)&ship, dam2, 0);
+       dam += dam2;
     }
     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
-    while (nxtitem(&ni, (caddr_t)&land)) {
-       if (land.lnd_own == 0)
-           continue;
-       if (land.lnd_effic < LAND_MINFIREEFF)
-           continue;
-       /* Can't fire if on a ship */
-       if (land.lnd_ship >= 0)
-           continue;
-       if (land.lnd_land >= 0)
-           continue;
-       /* Gotta have military */
-       if (lnd_getmil(&land) < 1)
-           continue;
-       /* Don't shoot yourself */
-       if (land.lnd_own == aown)
-           continue;
-
-       rel = getrel(getnatp(land.lnd_own), own);
-       rel2 = getrel(getnatp(land.lnd_own), aown);
-
-       if ((land.lnd_own != own) && ((rel != ALLIED) || (rel2 != AT_WAR)))
-           continue;
-
-
-       range = techfact((int)land.lnd_tech, (double)land.lnd_frg / 2.0);
-       range = (double)roundrange(range);
-       if (range < ni.curdist)
-           continue;
-
-       resupply_all(&land);
-       if (!has_supply(&land))
+    while (nxtitem(&ni, &land)) {
+       if (!feels_like_helping(land.lnd_own, own, aown))
            continue;
 
-       if (getvec(VT_ITEM, vec, (caddr_t)&land, EF_LAND) < 0)
+       erange = lnd_fire_range(&land);
+       if (roundrange(erange) < ni.curdist)
            continue;
 
-       if (vec[I_MILIT] == 0 || vec[I_SHELL] == 0 || vec[I_GUN] == 0)
+       fp = search_flist(list, (struct empobj *)&land);
+       if (fp)
+           dam2 = fp->defdam;
+       else {
+           dam2 = lnd_fire(&land);
+           putland(land.lnd_uid, &land);
+       }
+       if (dam2 < 0)
            continue;
 
-       dam2 = (int)landunitgun(land.lnd_effic, land.lnd_dam, vec[I_GUN],
-                               land.lnd_ammo, vec[I_SHELL]);
-
        (*nfiring)++;
-       fp = (struct flist *)malloc(sizeof(struct flist));
-       memset(fp, 0, sizeof(struct flist));
-       fp->type = targ_unit;
-       fp->uid = land.lnd_uid;
-       add_to_fired_queue(&fp->queue, list);
-       use_supply(&land);
-       putland(land.lnd_uid, &land);
+       if (!fp)
+           add_to_flist(list, (struct empobj *)&land, dam2, 0);
        nreport(land.lnd_own, N_FIRE_BACK, player->cnum, 1);
+       if (type == EF_SHIP) {
+           if (chance(lnd_acc(&land) / 100.0))
+               dam2 = ldround(dam2 / 2.0, 1);
+       }
        dam += dam2;
     }
 
@@ -939,135 +681,71 @@ quiet_bigdef(int attacker, struct emp_qelem *list, natid own, natid aown,
     if (!opt_NO_FORT_FIRE) {
        snxtsct_dist(&ns, ax, ay, 8);
        while (nxtsct(&ns, &firing)) {
-
-           if (firing.sct_type != SCT_FORTR)
-               continue;
-           if (firing.sct_own == 0)
+           if (!feels_like_helping(firing.sct_own, own, aown))
                continue;
-           rel = getrel(getnatp(firing.sct_own), own);
-           rel2 = getrel(getnatp(firing.sct_own), aown);
 
-           if ((firing.sct_own != own) &&
-               ((rel != ALLIED) || (rel2 != AT_WAR)))
-               continue;
-           /* Don't shoot yourself */
-           if (firing.sct_own == aown)
-               continue;
-           tech = tfactfire(firing.sct_own, 1.0);
-           range = tech * 7.0;
-           if (firing.sct_effic > 59)  /* fort bonus */
-               range++;
-           range = (double)roundrange(range);
-           if (range < ns.curdist)
-               continue;
-           if (getvec(VT_ITEM, vec, (caddr_t)&firing, EF_SECTOR) < 0)
+           erange = fortrange(&firing);
+           if (roundrange(erange) < ns.curdist)
                continue;
-           if (vec[I_SHELL] < 1)
-               vec[I_SHELL] += supply_commod(firing.sct_own,
-                                             firing.sct_x, firing.sct_y,
-                                             I_SHELL, 1);
-           if (vec[I_GUN] == 0 || vec[I_MILIT] < 5 || vec[I_SHELL] == 0)
+
+           fp = search_flist(list, (struct empobj *)&firing);
+           if (fp)
+               dam2 = fp->defdam;
+           else {
+               dam2 = fort_fire(&firing);
+               putsect(&firing);
+           }
+           if (dam2 < 0)
                continue;
            (*nfiring)++;
-           fp = (struct flist *)malloc(sizeof(struct flist));
-           memset(fp, 0, sizeof(struct flist));
-           fp->x = firing.sct_x;
-           fp->y = firing.sct_y;
-           fp->type = targ_land;
-           add_to_fired_queue(&fp->queue, list);
+           if (!fp)
+               add_to_flist(list, (struct empobj *)&firing, dam2, 0);
            nreport(firing.sct_own, N_FIRE_BACK, player->cnum, 1);
-           gun = vec[I_GUN];
-           if (gun > 7)
-               gun = 7;
-           dam += landgun((int)firing.sct_effic, gun);
+           dam += dam2;
        }
     }
 
-    return ((*nfiring) == 0 ? 0 : (dam / (*nfiring)));
+    return *nfiring == 0 ? 0 : dam / *nfiring;
 }
 
 static void
-use_ammo(struct emp_qelem *list)
+add_to_flist(struct emp_qelem *list,
+            struct empobj *gp, int dam, natid victim)
+{
+    struct flist *fp;
+
+    fp = malloc(sizeof(struct flist));
+    fp->type = gp->ef_type;
+    fp->uid = gp->uid;
+    fp->x = gp->x;
+    fp->y = gp->y;
+    fp->defdam = dam;
+    fp->victim = victim;
+    emp_insque(&fp->queue, list);
+}
+
+static void
+free_flist(struct emp_qelem *list)
 {
     struct emp_qelem *qp, *next;
     struct flist *fp;
-    struct shpstr ship;
-    struct lndstr land;
-    struct sctstr sect;
-    int shell, type;
-    s_char *ptr;
-    double mobcost;
-    struct mchrstr *mcp;
 
-    /* use 1 shell from everyone */
     for (qp = list->q_forw; qp != list; qp = next) {
        next = qp->q_forw;
        fp = (struct flist *)qp;
-       if (fp->type == targ_ship) {
-           getship(fp->uid, &ship);
-           ptr = (s_char *)&ship;
-           type = EF_SHIP;
-           if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
-               shell = getvar(V_SHELL, ptr, type);
-               shell--;
-               if (shell < 0)
-                   shell = 0;
-               putvar(V_SHELL, shell, ptr, type);
-               putship(ship.shp_uid, &ship);
-               mcp = &mchr[(int)ship.shp_type];
-               mobcost = ship.shp_effic * 0.01 * ship.shp_speed;
-               mobcost = (480.0 / (mobcost +
-                                   techfact(ship.shp_tech, mobcost)));
-               /* mob cost = 1/2 a sect's mob */
-               mobcost /= 2.0;
-               ship.shp_mobil -= mobcost;
-           }
-       } else if (fp->type == targ_land) {
-           getsect(fp->x, fp->y, &sect);
-           ptr = (s_char *)&sect;
-           type = EF_SECTOR;
-       } else {
-           getland(fp->uid, &land);
-           ptr = (s_char *)&land;
-           type = EF_LAND;
-       }
-       shell = getvar(V_SHELL, ptr, type);
-       shell--;
-       if (shell < 0)
-           shell = 0;
-       putvar(V_SHELL, shell, ptr, type);
-       if (fp->type == targ_ship)
-           putship(ship.shp_uid, &ship);
-       else if (fp->type == targ_land)
-           putsect(&sect);
-       else
-           putland(land.lnd_uid, &land);
-
        emp_remque(&fp->queue);
-       free((s_char *)fp);
+       free(fp);
     }
-
 }
 
-static void
-add_to_fired_queue(struct emp_qelem *elem, struct emp_qelem *list)
+static int
+uid_eq(struct emp_qelem *elem, void *key)
 {
-    struct emp_qelem *qp;
-    struct flist *fp, *ep;
-    int bad = 0;
-
-    ep = (struct flist *)elem;
-
-    /* Don't put them on the list if they're already there */
-    for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
-       fp = (struct flist *)qp;
-       if ((fp->type == targ_ship) && (fp->uid == ep->uid))
-           bad = 1;
-       if ((fp->type != targ_ship) && (fp->x == ep->x) &&
-           (fp->y == ep->y))
-           bad = 1;
-    }
+    return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
+}
 
-    if (!bad)
-       emp_insque(elem, list);
+static struct flist *
+search_flist(struct emp_qelem *list, struct empobj *gp)
+{
+    return (struct flist *)emp_searchque(list, gp, uid_eq);
 }