]> git.pond.sub.org Git - empserver/blobdiff - src/lib/commands/mfir.c
Revert "Permit ships that can drop depth charges, but not fire"
[empserver] / src / lib / commands / mfir.c
index 58542b49a041a40da6e37ff59e62c1410eea41be..e679074efa7c3b0d9fcd79c1717acda762f9368f 100644 (file)
@@ -1,11 +1,11 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
+ *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                Ken Stevens, Steve McClure, Markus Armbruster
  *
- *  This program is free software; you can redistribute it and/or modify
+ *  Empire is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
+ *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  *  ---
  *
  *  ---
  *
  *  multifire.c: Fire at other sectors/ships
- * 
+ *
  *  Known contributors to this file:
  *     Steve McClure, 2000
+ *     Markus Armbruster, 2004-2015
  */
 
 #include <config.h>
 
+#include "chance.h"
 #include "commands.h"
 #include "empobj.h"
+#include "news.h"
 #include "optlist.h"
 #include "retreat.h"
 
-enum targ_type {
-    targ_land, targ_ship, targ_sub, targ_bogus
+enum targ_type {       /* Targeting... */
+    targ_land,         /* a sector with guns */
+    targ_ship,         /* a ship with guns */
+    targ_sub,          /* a submarine with depth charges */
+    targ_bogus         /* a bogus sector with guns */
 };
 
 struct flist {
     struct emp_qelem queue;    /* list of fired things */
     short type;                        /* EF_SECTOR, EF_SHIP or EF_LAND */
-    short uid;
+    int uid;
     coord x, y;
     int defdam;                        /* damage defenders did */
     natid victim;
@@ -71,20 +76,17 @@ multifire(void)
     coord fy;
     coord x;
     coord y;
-    int mil;
     int dam;
     int totaldefdam = 0;
     int vshipno;
-    double prb;
     natid vict;
     struct shpstr fship;
     struct lndstr fland;
     struct sctstr fsect;
+    char *sep = "";
     struct shpstr vship;
     struct sctstr vsect;
     enum targ_type target;
-    int rel;
-    struct natstr *natp;
     struct nstr_item nbst;
     int type;
     struct empobj *attgp;
@@ -98,9 +100,9 @@ multifire(void)
 
     emp_initque(&fired);
     emp_initque(&defended);
-    if (!(p = getstarg(player->argp[1],
-                      "Firing from ship(s), sect(s), or land unit(s)? ",
-                      buf)))
+    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) {
@@ -111,17 +113,9 @@ multifire(void)
        pr("Ships, land units or sectors only!\n");
        return RET_SYN;
     }
-    if ((ptr = getstarg(player->argp[2], "Firing from? ", buf)) == 0
-       || *ptr == '\0')
-       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, &item)) {
        if (type == EF_LAND) {
            if (!getland(item.land.lnd_uid, &fland))
@@ -155,10 +149,18 @@ multifire(void)
                   fland.lnd_uid, LAND_MINFIREEFF);
                continue;
            }
+           if (fland.lnd_item[I_GUN] == 0) {
+               pr("%s -- not enough guns\n", prland(&fland));
+               continue;
+           }
+
            if (fland.lnd_item[I_SHELL] == 0) {
                pr("%s -- not enough shells\n", prland(&fland));
                continue;
            }
+           pr("%s%s ready to fire\n", sep, prland(&fland));
+           fx = fland.lnd_x;
+           fy = fland.lnd_y;
        } else if (type == EF_SHIP) {
            if (!getship(item.ship.shp_uid, &fship))
                continue;
@@ -184,7 +186,10 @@ multifire(void)
                pr("Ship #%d is crippled!\n", item.ship.shp_uid);
                continue;
            }
-       } else if (type == EF_SECTOR) {
+           pr("%s%s ready to fire\n", sep, prship(&fship));
+           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)
@@ -210,16 +215,18 @@ multifire(void)
                   xyas(item.sect.sct_x, item.sect.sct_y, player->cnum));
                continue;
            }
-           pr("\nSector %s firing\n",
+           pr("%sSector %s ready to fire\n", sep,
               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");
+       sep = "\n";
+
+       ptr = getstarg(player->argp[3], "Firing at? ", buf);
+       if (!ptr)
+           return RET_FAIL;
+       if (!*ptr)
            continue;
-       }
        if (!issector(ptr)) {
            vshipno = atoi(ptr);
            if (vshipno < 0 || !getship(vshipno, &vship) ||
@@ -227,8 +234,7 @@ multifire(void)
                pr("No such ship exists!\n");
                continue;
            }
-           target = (mchr[(int)vship.shp_type].m_flags & M_SUB) ?
-               targ_sub : targ_ship;
+           target = targ_ship; /* targ_ship vs. targ_sub decided below */
            vict = vship.shp_own;
            x = vship.shp_x;
            y = vship.shp_y;
@@ -252,55 +258,35 @@ multifire(void)
            x = vsect.sct_x;
            y = vsect.sct_y;
        }
+
+       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;
-           }
-           if (target == targ_sub || target == targ_ship) {
+           if (target == targ_ship) {
                if (fship.shp_uid == vship.shp_uid) {
                    pr("You can't fire upon yourself!\n");
                    continue;
                }
            }
-           fx = fship.shp_x;
-           fy = fship.shp_y;
-           if ((mil = fship.shp_item[I_MILIT]) < 1) {
-               pr("Not enough military for firing crew.\n");
-               continue;
-           }
-           if (fship.shp_effic < 60) {
-               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
-               && (mchr[(int)fship.shp_type].m_flags & M_DCH)) {
+           /* Use depth charges against subs, but only when in range */
+           if (target == targ_ship && trange <= range2
+               && (mchr[vship.shp_type].m_flags & M_SUB)
+               && (mchr[fship.shp_type].m_flags & M_DCH))
+               target = targ_sub;
+           if (target == targ_sub)
                dam = shp_dchrg(&fship);
-               putship(fship.shp_uid, &fship);
-               if (dam < 0) {
-                   pr("Not enough shells for depth charge!\n");
-                   continue;
-               }
-           } 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;
-               }
+           else
                dam = shp_fire(&fship);
-               putship(fship.shp_uid, &fship);
-               if (dam <= 0) {
-                   pr("Klick!     ...\n");
-                   continue;
-               }
+           fship.shp_mission = 0;
+           putship(fship.shp_uid, &fship);
+           if (CANT_HAPPEN(dam < 0)) {
+               pr("Klick!     ...\n");
+               continue;
            }
            if (opt_NOMOBCOST == 0) {
                fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
@@ -309,11 +295,6 @@ multifire(void)
        } 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;
-           }
-
            if (target == targ_land) {
                if (fland.lnd_x == vsect.sct_x
                    && fland.lnd_y == vsect.sct_y) {
@@ -321,30 +302,13 @@ multifire(void)
                    continue;
                }
            }
-
-           fx = fland.lnd_x;
-           fy = fland.lnd_y;
-
-           if (lchr[fland.lnd_type].l_dam == 0) {
-               pr("Unit %d cannot fire!\n", fland.lnd_uid);
-               continue;
-           }
-           if (fland.lnd_item[I_GUN] == 0) {
-               pr("%s -- not enough guns\n", prland(&fland));
-               continue;
-           }
-
            range = lnd_fire_range(&fland);
            range2 = roundrange(range);
            pr("range is %d.00 (%.2f)\n", range2, range);
-           if (target == targ_sub) {
-               /* Don't tell it's a sub */
-               range2 = -1;
-           }
-
            dam = lnd_fire(&fland);
+           fland.lnd_mission = 0;
            putland(fland.lnd_uid, &fland);
-           if (dam < 0) {
+           if (CANT_HAPPEN(dam < 0)) {
                pr("Klick!     ...\n");
                continue;
            }
@@ -355,14 +319,6 @@ multifire(void)
        } else {
            if (!check_sect_ok(&fsect))
                return RET_FAIL;
-           fx = fsect.sct_x;
-           fy = fsect.sct_y;
-           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));
-               continue;
-           }
            if (target == targ_land) {
                if (fsect.sct_x == vsect.sct_x
                    && fsect.sct_y == vsect.sct_y) {
@@ -370,98 +326,40 @@ multifire(void)
                    continue;
                }
            }
-           if (fsect.sct_item[I_GUN] == 0) {
-               pr("Insufficient arms.\n");
-               continue;
-           }
-           if (fsect.sct_item[I_MILIT] < 5) {
-               pr("Not enough military for firing crew.\n");
-               continue;
-           }
            dam = fort_fire(&fsect);
            putsect(&fsect);
-           if (dam < 0) {
+           if (CANT_HAPPEN(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) {
-               /* Don't tell it's a sub */
-               range2 = -1;
-           }
-       }
-       trange = mapdist(x, y, fx, fy);
-       if (trange > range2) {
-           pr("Target out of range.\n");
-           switch (type) {
-           case EF_SECTOR:
-               putsect(&fsect);
-               break;
-           case EF_LAND:
-               fland.lnd_mission = 0;
-               putland(fland.lnd_uid, &fland);
-               break;
-           case EF_SHIP:
-               fship.shp_mission = 0;
-               putship(fship.shp_uid, &fship);
-               break;
-           default:
-               CANT_REACH();
-           }
-           continue;
-       }
-       switch (target) {
-       case targ_ship:
-           if (!trechk(player->cnum, vict, SEAFIR))
-               continue;
-           break;
-       case targ_sub:
-           if (!trechk(player->cnum, vict, SUBFIR))
-               continue;
-           break;
-       case targ_land:
-           if (!trechk(player->cnum, vict, LANFIR))
-               continue;
-           break;
-       default:
-           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 = range2 ? (double)trange / range2 : 1.0;
-           prb *= prb;
-           if (chance(prb)) {
-               pr("Wind deflects shells.\n");
-/*                     dam = (int)(dam / 2.0);*/
-               dam *= (90 - (random() % 11)) / 100.0;
-               if (dam < 0)
-                   dam = 0;
-           }
            break;
        }
+
+       /*
+        * If the player fires guns at a submarine, take care not to
+        * disclose it's a submarine: pretend the target is out of range.
+        */
+       if (target == targ_ship && (mchr[vship.shp_type].m_flags & M_SUB))
+           range2 = -1;
+       if (trange > range2) {
+           pr("Target out of range.\n");
+           continue;
+       }
+
        switch (target) {
        case targ_bogus:
        case targ_land:
@@ -472,19 +370,12 @@ multifire(void)
                   player->cnum, xyas(x, y, vict), dam);
            pr("Shells hit sector %s for %d damage.\n",
               xyas(x, y, player->cnum), dam);
-           if (target != targ_bogus)
-               sectdamage(&vsect, 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) {
+           if (vict && vict != player->cnum) {
                wu(0, vict,
                   "Country #%d shelled %s in %s for %d damage.\n",
                   player->cnum, prship(&vship),
@@ -493,10 +384,6 @@ multifire(void)
            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,
@@ -511,26 +398,33 @@ multifire(void)
        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);
+           shipdamage(&vship, dam);
+           if (vship.shp_effic < SHIP_MINEFF)
+               pr("%s sunk!\n", prsub(&vship));
            putship(vship.shp_uid, &vship);
+           if (dam && (vship.shp_rflags & RET_INJURED))
+               retreat_ship(&vship, vict, 'i');
+           else if (target == targ_sub && (vship.shp_rflags & RET_DCHRGED))
+               retreat_ship(&vship, vict, 'd');
+           else if (totaldefdam == 0 && (vship.shp_rflags & RET_HELPLESS))
+               retreat_ship(&vship, vict, 'h');
            break;
        }
-       if ((totaldefdam == 0) && (target == targ_ship))
-           if (vship.shp_rflags & RET_HELPLESS)
-               retreat_ship(&vship, 'h');
        switch (attgp->ef_type) {
        case EF_SECTOR:
-           putsect(&fsect);
            break;
        case EF_SHIP:
            if ((target == targ_ship) || (target == targ_sub)) {
                if (fship.shp_effic > SHIP_MINEFF) {
                    shp_missdef(&fship, vict);
-               };
-           };
-           putship(fship.shp_uid, &fship);
+               }
+           }
            break;
        default:
            CANT_REACH();
@@ -625,7 +519,7 @@ quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
     struct shpstr ship;
     struct lndstr land;
     struct nstr_item ni;
-    int dam, dam2, rel, rel2;
+    int dam, dam2;
     struct sctstr firing;
     struct nstr_sect ns;
     struct flist *fp;
@@ -635,19 +529,12 @@ quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
     dam = 0;
     snxtitem_dist(&ni, EF_SHIP, ax, ay, 8);
     while (nxtitem(&ni, &ship)) {
-       if (ship.shp_own == 0)
+       if (!feels_like_helping(ship.shp_own, own, aown))
            continue;
 
        if ((mchr[ship.shp_type].m_flags & M_SUB) && type != EF_SHIP)
            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 (mchr[(int)ship.shp_type].m_flags & M_SUB) {
            erange = torprange(&ship);
            if (roundrange(erange) < ni.curdist)
@@ -687,16 +574,7 @@ quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
     }
     snxtitem_dist(&ni, EF_LAND, ax, ay, 8);
     while (nxtitem(&ni, &land)) {
-       if (land.lnd_own == 0)
-           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)))
+       if (!feels_like_helping(land.lnd_own, own, aown))
            continue;
 
        erange = lnd_fire_range(&land);
@@ -717,6 +595,10 @@ quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown,
        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;
     }
 
@@ -730,17 +612,9 @@ quiet_bigdef(int type, 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_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;
            erange = fortrange(&firing);
            if (roundrange(erange) < ns.curdist)
                continue;