/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2009, Dave Pare, Jeff Bailey, Thomas Ruschak,
- * Ken Stevens, Steve McClure
+ * Copyright (C) 1986-2016, 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/>.
*
* ---
*
* Known contributors to this file:
* Ken Stevens, 1995
* Steve McClure, 1996-2000
- * Markus Armbruster, 2003-2009
+ * Markus Armbruster, 2003-2015
*/
#include <config.h>
#include <stdlib.h>
+#include "chance.h"
#include "empobj.h"
#include "file.h"
#include "item.h"
#include "misc.h"
#include "mission.h"
+#include "news.h"
#include "nsc.h"
#include "optlist.h"
#include "path.h"
-#include "player.h"
+#include "plague.h"
#include "prototypes.h"
#include "queue.h"
#include "xy.h"
struct genlist {
struct emp_qelem queue; /* list of units */
- void *cp; /* pointer to desc of thing */
struct empobj *thing; /* thing's struct */
};
static void add_airport(struct emp_qelem *, coord, coord);
static int air_damage(struct emp_qelem *, coord, coord, int, natid,
char *, int);
-static void build_mission_list(struct genlist *, coord, coord, int, natid);
-static void build_mission_list_type(struct genlist *, coord, coord, int,
- int, natid);
+static void build_mission_list(struct genlist[],
+ unsigned char[], unsigned char[],
+ coord, coord, int);
+static void build_mission_list_type(struct genlist[], unsigned char[],
+ coord, coord, int, int);
static void divide(struct emp_qelem *, struct emp_qelem *, coord, coord);
-static int dosupport(struct genlist *, coord, coord, natid, natid);
+static int dosupport(coord, coord, natid, natid, int);
static int find_airport(struct emp_qelem *, coord, coord);
static void mission_pln_arm(struct emp_qelem *, coord, coord, int,
int, struct ichrstr *);
natid, int);
static int perform_mission_bomb(int, struct emp_qelem *, coord, coord,
natid, int, char *, int, int);
-static int perform_mission(coord, coord, natid, struct emp_qelem *, int,
- char *, int);
+static int perform_mission(coord, coord, natid, struct emp_qelem *,
+ int, char *, int);
+
+static int
+tally_dam(int dam, int newdam)
+{
+ return dam < 0 ? newdam : dam + newdam;
+}
/*
* Interdict commodities & transported planes
ground_interdict(coord x, coord y, natid victim, char *s)
{
int cn;
- int dam = 0, newdam, rel;
+ int dam = 0, newdam;
+ unsigned char act[MAXNOC];
struct genlist mi[MAXNOC];
- int z;
memset(mi, 0, sizeof(mi));
- for (z = 1; z < MAXNOC; z++)
- emp_initque((struct emp_qelem *)&mi[z]);
+ act[0] = 0;
+ for (cn = 1; cn < MAXNOC; cn++) {
+ act[cn] = relations_with(cn, victim) <= HOSTILE;
+ emp_initque(&mi[cn].queue);
+ }
- build_mission_list(mi, x, y, MI_INTERDICT, victim);
+ build_mission_list(mi, act, act, x, y, MI_INTERDICT);
for (cn = 1; cn < MAXNOC; cn++) {
- rel = getrel(getnatp(cn), victim);
- if (rel > HOSTILE)
- continue;
-
if (QEMPTY(&mi[cn].queue))
continue;
newdam = perform_mission(x, y, victim, &mi[cn].queue,
MI_INTERDICT, s, SECT_HARDTARGET);
- dam += newdam;
- if (newdam)
+ if (newdam > 0) {
+ dam += newdam;
mpr(victim, "%s interdiction mission does %d damage!\n",
cname(cn), newdam);
+ }
}
if (dam) {
collateral_damage(x, y, dam);
{
struct emp_qelem *qp;
struct genlist *glp;
- struct mchrstr *mcp;
for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
glp = (struct genlist *)qp;
if (glp->thing->ef_type != EF_SHIP)
return 0;
- mcp = glp->cp;
- if (!(mcp->m_flags & M_SUB))
+ if (!(mchr[glp->thing->type].m_flags & M_SUB))
return 0;
/* It's a sub! */
}
unit_interdict(coord x, coord y, natid victim, char *s, int hardtarget,
int mission)
{
- int cn;
- int dam = 0, newdam;
+ int cn, rel, newdam, osubs;
+ int dam = -1;
+ unsigned char plane_act[MAXNOC], other_act[MAXNOC];
struct genlist mi[MAXNOC];
- int z;
- int osubs;
memset(mi, 0, sizeof(mi));
- for (z = 1; z < MAXNOC; z++)
- emp_initque((struct emp_qelem *)&mi[z]);
+ other_act[0] = plane_act[0] = 0;
+ for (cn = 1; cn < MAXNOC; cn++) {
+ rel = relations_with(cn, victim);
+ other_act[cn] = rel <= HOSTILE;
+ plane_act[cn] = mission == MI_SINTERDICT
+ ? rel <= NEUTRAL : other_act[cn];
+ emp_initque(&mi[cn].queue);
+ }
- build_mission_list(mi, x, y, mission, victim);
+ build_mission_list(mi, other_act, plane_act, x, y, mission);
for (cn = 1; cn < MAXNOC; cn++) {
- if (cn == victim)
- continue;
- if (mission == MI_SINTERDICT) {
- if (getrel(getnatp(cn), victim) >= FRIENDLY)
- continue;
- } else if (getrel(getnatp(cn), victim) > HOSTILE)
- continue;
-
if (QEMPTY(&mi[cn].queue))
continue;
osubs = only_subs(&mi[cn].queue);
newdam = perform_mission(x, y, victim, &mi[cn].queue,
mission, s, hardtarget);
- dam += newdam;
- if (newdam) {
- /* If only subs responded, then we don't know who's
- subs they are */
+ dam = tally_dam(dam, newdam);
+ if (newdam > 0)
mpr(victim, "%s interdiction mission does %d damage!\n",
osubs ? "Enemy" : cname(cn), newdam);
- }
}
- if (dam) {
+ if (dam > 0)
collateral_damage(x, y, dam);
- }
return dam;
}
int
off_support(coord x, coord y, natid victim, natid actee)
{
- int dam = 0;
- struct genlist mi[MAXNOC];
- int z;
-
- memset(mi, 0, sizeof(mi));
- for (z = 1; z < MAXNOC; z++)
- emp_initque((struct emp_qelem *)&mi[z]);
-
- build_mission_list(mi, x, y, MI_SUPPORT, victim);
- build_mission_list(mi, x, y, MI_OSUPPORT, victim);
-
- dam = dosupport(mi, x, y, victim, actee);
- return dam;
+ return dosupport(x, y, victim, actee, MI_OSUPPORT);
}
/*
int
def_support(coord x, coord y, natid victim, natid actee)
{
- int dam = 0;
- struct genlist mi[MAXNOC];
- int z;
-
- memset(mi, 0, sizeof(mi));
- for (z = 1; z < MAXNOC; z++)
- emp_initque((struct emp_qelem *)&mi[z]);
-
- build_mission_list(mi, x, y, MI_SUPPORT, victim);
- build_mission_list(mi, x, y, MI_DSUPPORT, victim);
-
- dam = dosupport(mi, x, y, victim, actee);
- return dam;
+ return dosupport(x, y, victim, actee, MI_DSUPPORT);
}
+/*
+ * Perform support missions in @x,@y against @victim for @actee.
+ * @mission is either MI_OSUPPORT or MI_DSUPPORT.
+ * Return total damage.
+ */
static int
-dosupport(struct genlist *mi, coord x, coord y, natid victim, natid actee)
+dosupport(coord x, coord y, natid victim, natid actee, int mission)
{
int cn;
- int rel;
+ unsigned char act[MAXNOC];
+ struct genlist mi[MAXNOC];
+ int newdam;
int dam = 0;
+ memset(mi, 0, sizeof(mi));
+ act[0] = 0;
for (cn = 1; cn < MAXNOC; cn++) {
- rel = getrel(getnatp(cn), actee);
- if ((cn != actee) && (rel != ALLIED))
- continue;
- rel = getrel(getnatp(cn), victim);
- if ((cn != actee) && (rel != AT_WAR))
- continue;
+ act[cn] = feels_like_helping(cn, actee, victim);
+ emp_initque(&mi[cn].queue);
+ }
+
+ build_mission_list(mi, act, act, x, y, MI_SUPPORT);
+ build_mission_list(mi, act, act, x, y, mission);
+ for (cn = 1; cn < MAXNOC; cn++) {
if (QEMPTY(&mi[cn].queue))
continue;
- dam += perform_mission(x, y, victim, &mi[cn].queue, MI_SUPPORT,
- "", SECT_HARDTARGET);
+ newdam = perform_mission(x, y, victim, &mi[cn].queue, MI_SUPPORT,
+ "", SECT_HARDTARGET);
+ if (newdam > 0)
+ dam += newdam;
}
return dam;
}
static void
-build_mission_list(struct genlist *mi, coord x, coord y, int mission,
- natid victim)
+build_mission_list(struct genlist mi[],
+ unsigned char other_act[], unsigned char plane_act[],
+ coord x, coord y, int mission)
{
- build_mission_list_type(mi, x, y, mission, EF_LAND, victim);
- build_mission_list_type(mi, x, y, mission, EF_SHIP, victim);
- build_mission_list_type(mi, x, y, mission, EF_PLANE, victim);
+ build_mission_list_type(mi, other_act, x, y, mission, EF_LAND);
+ build_mission_list_type(mi, other_act, x, y, mission, EF_SHIP);
+ build_mission_list_type(mi, plane_act, x, y, mission, EF_PLANE);
}
static void
-build_mission_list_type(struct genlist *mi, coord x, coord y, int mission,
- int type, natid victim)
+build_mission_list_type(struct genlist mi[], unsigned char act[],
+ coord x, coord y, int mission, int type)
{
struct nstr_item ni;
struct genlist *glp;
struct empobj *gp;
union empobj_storage item;
- int relat;
- struct sctstr sect;
snxtitem_all(&ni, type);
while (nxtitem(&ni, &item)) {
- gp = (struct empobj *)&item;
+ gp = &item.gen;
- if (gp->own == 0)
+ if (!act[gp->own])
continue;
if (gp->mobil < 1)
(gp->mission != MI_INTERDICT))
continue;
- relat = getrel(getnatp(gp->own), victim);
- if (mission == MI_SINTERDICT) {
- if (relat >= FRIENDLY)
- continue;
- else if (type != EF_PLANE && relat > HOSTILE)
- continue;
- } else if (relat > HOSTILE)
- continue;
-
if (!in_oparea(gp, x, y))
continue;
- if (opt_SLOW_WAR) {
- if (mission != MI_AIR_DEFENSE) {
- getsect(x, y, §);
- if (getrel(getnatp(gp->own), sect.sct_own) > AT_WAR) {
-
- /*
- * If the owner of the unit isn't at war
- * with the victim, and doesn't own the
- * sect being acted upon, and isn't the
- * old owner of that sect, bounce them.
- */
- if (sect.sct_type != SCT_WATER &&
- sect.sct_own != gp->own &&
- sect.sct_oldown != gp->own)
- continue;
- }
- }
- }
-
glp = malloc(sizeof(struct genlist));
memset(glp, 0, sizeof(struct genlist));
- glp->cp = get_empobj_chr(gp);
glp->thing = malloc(sizeof(item));
memcpy(glp->thing, &item, sizeof(item));
emp_insque(&glp->queue, &mi[gp->own].queue);
if (!in_oparea((struct empobj *)&plane, x, y))
continue;
plp = malloc(sizeof(struct plist));
- memset(plp, 0, sizeof(struct plist));
+ plp->load = 0;
+ plp->pstage = PLG_HEALTHY;
plp->pcp = &plchr[(int)plane.pln_type];
plp->plane = plane;
emp_insque(&plp->queue, escorts);
struct emp_qelem *qp, missiles, bombers;
struct genlist *glp;
struct plist *plp;
- struct sctstr sect;
struct plchrstr *pcp;
- int dam = 0;
+ int dam = -1;
int targeting_ships = *s == 's'; /* "subs" or "ships" FIXME gross! */
- getsect(x, y, §);
-
emp_initque(&missiles);
emp_initque(&bombers);
- for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
+ for (qp = list->q_forw; qp != list; ) {
glp = (struct genlist *)qp;
+ qp = qp->q_forw;
if (glp->thing->ef_type == EF_LAND) {
dam = perform_mission_land(dam, (struct lndstr *)glp->thing,
x, y, victim, mission, s,
targeting_ships);
} else if (glp->thing->ef_type == EF_PLANE) {
- pcp = glp->cp;
- if (pcp->pl_flags & P_M)
- /* units have their own missile interdiction */
- if (hardtarget != SECT_HARDTARGET || pcp->pl_flags & P_MAR)
- continue;
-
- /* save planes for later */
- plp = malloc(sizeof(struct plist));
-
- memset(plp, 0, sizeof(struct plist));
- plp->pcp = pcp;
- memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
- if (plp->pcp->pl_flags & P_M)
- emp_insque(&plp->queue, &missiles);
- else
- emp_insque(&plp->queue, &bombers);
+ pcp = &plchr[glp->thing->type];
+ if ((pcp->pl_flags & P_M)
+ && (hardtarget != SECT_HARDTARGET
+ || (pcp->pl_flags & P_MAR)))
+ ; /* units have their own missile interdiction */
+ else {
+ /* save planes for later */
+ plp = malloc(sizeof(struct plist));
+ plp->load = 0;
+ plp->pstage = PLG_HEALTHY;
+ plp->pcp = pcp;
+ memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
+ if (plp->pcp->pl_flags & P_M)
+ emp_insque(&plp->queue, &missiles);
+ else
+ emp_insque(&plp->queue, &bombers);
+ }
} else {
CANT_REACH();
break;
}
+ free(glp->thing);
+ free(glp);
}
dam = perform_mission_msl(dam, &missiles, x, y, victim, hardtarget);
dam = perform_mission_bomb(dam, &bombers, x, y, victim, mission, s,
hardtarget, targeting_ships);
-
- qp = list->q_forw;
- while (qp != list) {
- glp = (struct genlist *)qp;
- qp = qp->q_forw;
-
- free(glp->thing);
- free(glp);
- }
-
return dam;
}
mpr(victim, "%s %s fires at you at %s\n",
cname(lp->lnd_own), prland(lp), xyas(x, y, victim));
- return dam + dam2;
+ return tally_dam(dam, dam2);
}
static int
}
if (mcp->m_flags & M_SUB) {
if (!targeting_ships)
- return dam; /* subs interdict only ships */
+ return dam; /* subs interdict only ships */
range = roundrange(torprange(sp));
if (md > range)
return dam;
wu(0, sp->shp_own,
"\tEffective torpedo range is %d.0\n", range);
wu(0, sp->shp_own,
- "\tWhooosh... Hitchance = %d%%\n",
- (int)(hitchance * 100));
+ "\tWhooosh... Hitchance = %.0f%%\n", hitchance * 100);
if (!chance(hitchance)) {
wu(0, sp->shp_own, "\tMissed\n");
mpr(victim,
"Incoming torpedo sighted @ %s missed (whew)!\n",
xyas(x, y, victim));
- return dam;
+ return tally_dam(dam, 0);
}
wu(0, sp->shp_own, "\tBOOM!...\n");
nreport(victim, N_TORP_SHIP, 0, 1);
cname(sp->shp_own), prship(sp), xyas(x, y, victim));
}
- return dam + dam2;
+ return tally_dam(dam, dam2);
}
static int
perform_mission_msl(int dam, struct emp_qelem *missiles, coord x, coord y,
natid victim, int hardtarget)
{
- int air_dam;
+ int performed, air_dam, sublaunch, dam2;
struct emp_qelem *qp, *newqp;
struct plist *plp;
- int sublaunch, dam2;
/*
* Missiles, except for interdiction of ships or land units,
* because that happens elsewhere, in shp_missile_interdiction()
* and lnd_missile_interdiction().
*/
- air_dam = 0;
+ performed = air_dam = 0;
for (qp = missiles->q_back; qp != missiles; qp = newqp) {
newqp = qp->q_back;
plp = (struct plist *)qp;
if (msl_launch(&plp->plane, EF_SECTOR, "sector", x, y, victim,
&sublaunch) < 0)
goto use_up_msl;
+ performed = 1;
if (!msl_hit(&plp->plane, SECT_HARDTARGET, EF_SECTOR,
N_SCT_MISS, N_SCT_SMISS, sublaunch, victim))
CANT_REACH();
- dam2 = pln_damage(&plp->plane, 'p', 1);
+ dam2 = pln_damage(&plp->plane, 'p', "");
air_dam += dam2;
use_up_msl:
plp->plane.pln_effic = 0;
putplane(plp->plane.pln_uid, &plp->plane);
- }
+ }
emp_remque(qp);
free(qp);
}
- return dam + air_dam;
+
+ return performed ? tally_dam(dam, air_dam) : dam;
}
static int
{
struct emp_qelem *qp, *newqp, escorts, airp, b, e;
struct plist *plp;
- int plane_owner, air_dam, md;
+ int plane_owner, performed, air_dam;
+ size_t md;
emp_initque(&escorts);
emp_initque(&airp);
add_airport(&airp, plp->plane.pln_x, plp->plane.pln_y);
}
- air_dam = 0;
+ performed = air_dam = 0;
for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
struct airport *air;
- char buf[512];
- char *pp;
+ char buf[1024];
air = (struct airport *)qp;
- md = mapdist(x, y, air->x, air->y);
emp_initque(&b);
emp_initque(&e);
/* Split off the escorts at this base into e */
divide(&escorts, &e, air->x, air->y);
- mission_pln_arm(&b, air->x, air->y, 2 * md, 'p', NULL);
+ if (path_find(air->x, air->y, x, y, plane_owner, MOB_FLY) < 0)
+ continue;
+ md = path_find_route(buf, sizeof(buf), air->x, air->y, x, y);
+ if (md >= sizeof(buf))
+ continue;
+ mission_pln_arm(&b, air->x, air->y, 2 * md, 'p', NULL);
if (QEMPTY(&b))
continue;
-
mission_pln_arm(&e, air->x, air->y, 2 * md, 'e', NULL);
- pp = BestAirPath(buf, air->x, air->y, x, y);
- if (CANT_HAPPEN(!pp))
- continue;
+ performed = 1;
wu(0, plane_owner, "Flying %s mission from %s to %s\n",
mission_name(mission),
xyas(air->x, air->y, plane_owner),
xyas(x, y, air->own));
}
- ac_encounter(&b, &e, air->x, air->y, pp, 0);
+ ac_encounter(&b, &e, air->x, air->y, buf, 0);
if (!QEMPTY(&b))
air_dam +=
qp = newqp;
}
- return dam + air_dam;
+ return performed ? tally_dam(dam, air_dam) : dam;
}
int
}
/*
- * Maximum distance GP can perform its mission.
+ * Maximum distance @gp can perform its mission.
* Note: this has nothing to do with the radius of the op-area.
* oprange() governs where the unit *can* strike, the op-area governs
* where the player wants it to strike.
}
/*
- * Does GP's mission op area cover X,Y?
+ * Does @gp's mission op area cover @x,@y?
*/
int
in_oparea(struct empobj *gp, coord x, coord y)
}
if (pcp->pl_flags & P_A) {
- if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
+ if (!pct_chance(pln_identchance(pp, hardtarget, EF_SHIP))) {
emp_remque(qp);
free(qp);
continue;
pcp = plp->pcp;
if (pp->pln_ship >= 0) {
getship(pp->pln_ship, &ship);
+ plp->pstage = ship.shp_pstage;
item = ship.shp_item;
} else if (pp->pln_land >= 0) {
getland(pp->pln_land, &land);
+ plp->pstage = land.lnd_pstage;
item = land.lnd_item;
} else {
getsect(pp->pln_x, pp->pln_y, §);
+ plp->pstage = sect.sct_pstage;
item = sect.sct_item;
}
if (pcp->pl_fuel > item[I_PETROL]) {
getsect(x, y, §);
a->own = sect.sct_own;
- emp_insque((struct emp_qelem *)a, airp);
+ emp_insque(&a->queue, airp);
}
/*
struct plist *plp;
struct plnstr *pp;
int newdam, dam = 0;
+ char buf[32];
int hitchance;
for (qp = bombers->q_forw; qp != bombers; qp = qp->q_forw) {
if (!plp->load)
continue;
- newdam = 0;
if (plp->pcp->pl_flags & P_A) {
- if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
+ if (!pct_chance(pln_identchance(pp, hardtarget, EF_SHIP))) {
wu(0, pp->pln_own,
"\t%s detects sub movement in %s\n",
prplane(pp), xyas(x, y, pp->pln_own));
continue;
}
- if (getrel(getnatp(pp->pln_own), victim) > HOSTILE) {
+ if (relations_with(pp->pln_own, victim) > HOSTILE) {
wu(0, pp->pln_own,
"\t%s tracks %s %s at %s\n",
prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
"\t%s pinbombing %s %s in %s\n",
prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
}
- hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
+
+ buf[0] = 0;
if (nuk_on_plane(&plp->plane) >= 0)
hitchance = 100;
- else if (hardtarget != SECT_HARDTARGET)
- wu(0, pp->pln_own, "\t\t%d%% hitchance...", hitchance);
- if (roll(100) <= hitchance) {
- newdam = pln_damage(&plp->plane, 'p', 1);
+ else {
+ hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
+ if (hardtarget != SECT_HARDTARGET)
+ snprintf(buf, sizeof(buf), "\t\t%d%% hitchance...",
+ hitchance);
+ }
+ if (pct_chance(hitchance)) {
+ newdam = pln_damage(&plp->plane, 'p', buf);
wu(0, pp->pln_own,
"\t\thit %s %s for %d damage\n",
cname(victim), s, newdam);
dam += newdam;
} else {
- newdam = pln_damage(&plp->plane, 'p', 0);
- wu(0, pp->pln_own, "missed\n");
+ newdam = pln_damage(&plp->plane, 'p', NULL);
+ wu(0, pp->pln_own, "%smissed\n", buf);
if (mission == MI_SINTERDICT) {
mpr(victim,
"RUMBLE... your sub in %s hears a depth-charge explode nearby\n",