/*
* 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-2018, 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,
* 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-2016
*/
#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;
multifire(void)
{
static int ef_with_guns[] = { EF_SECTOR, EF_SHIP, EF_LAND, EF_BAD };
- char vbuf[20];
char *ptr;
double range;
int trange, range2;
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;
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) {
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))
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;
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)
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;
- }
- 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");
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;
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;
}
x = vsect.sct_x;
y = vsect.sct_y;
}
+
+ trange = mapdist(x, y, fx, fy);
+
if (type == EF_SHIP) {
- if (fship.shp_own != player->cnum) {
- pr("Not your ship!\n");
- continue;
- }
- if (target == targ_sub || target == targ_ship) {
+ if (!check_ship_ok(&fship))
+ return RET_FAIL;
+ 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 (opt_NOMOBCOST == 0) {
fship.shp_mobil = MAX(fship.shp_mobil - 15, -100);
putship(fship.shp_uid, &fship);
}
} else if (type == EF_LAND) {
- if (fland.lnd_own != player->cnum) {
- pr("Not your unit!\n");
- continue;
- }
-
+ if (!check_land_ok(&fland))
+ return RET_FAIL;
if (target == targ_land) {
if (fland.lnd_x == vsect.sct_x
&& fland.lnd_y == vsect.sct_y) {
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) {
- pr("Klick! ...\n");
- continue;
- }
if (target == targ_ship) {
if (chance(lnd_acc(&fland) / 100.0))
dam = ldround(dam / 2.0, 1);
}
} else {
- 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 (!check_sect_ok(&fsect))
+ return RET_FAIL;
if (target == targ_land) {
if (fsect.sct_x == vsect.sct_x
&& fsect.sct_y == vsect.sct_y) {
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) {
- 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;
- }
- }
+ if (CANT_HAPPEN(dam < 0)) {
+ pr("Jammed!\n");
+ continue;
}
+ pr("range is %d.00 (%.2f)\n", range2, range);
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:
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, 0);
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),
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,
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));
+ 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');
putship(vship.shp_uid, &vship);
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();
else
odds = 1.0;
do_defdam(&fired, odds);
+ free_flist(&fired);
return RET_OK;
}
if (vict)
wu(0, vict,
"Return fire hit %s in %s for %d damage.\n",
- prsub(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
+ prship(&ship), xyas(ship.shp_x, ship.shp_y, vict), dam);
shipdamage(&ship, dam);
putship(ship.shp_uid, &ship);
} else {
vict = fp->victim;
pr("Return fire hit sector %s for %d damage.\n",
xyas(fp->x, fp->y, player->cnum), dam);
- sectdamage(§, dam, 0);
+ sectdamage(§, dam);
putsect(§);
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(fp);
}
}
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;
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)
}
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);
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;
}
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;
}
static int
-uid_eq(struct emp_qelem *elem, void *key)
+flist_eq(struct emp_qelem *elem, void *key)
{
- return ((struct flist *)elem)->uid == ((struct empobj *)key)->uid;
+ struct flist *e = (struct flist *)elem;
+ struct flist *k = key;
+
+ return e->type == k->type && e->uid == k->uid;
}
static struct flist *
search_flist(struct emp_qelem *list, struct empobj *gp)
{
- return (struct flist *)emp_searchque(list, gp, uid_eq);
+ return (struct flist *)emp_searchque(list, gp, flist_eq);
}