fix #H5459 - explosions and steeds

Reported nearly five and half years ago:  mounted hero and steed
shared resistances if they got hit by an explosion.
This commit is contained in:
PatR
2022-09-25 01:21:07 -07:00
parent f4e87e8744
commit caf1eeebf9
2 changed files with 164 additions and 108 deletions

View File

@@ -1034,6 +1034,7 @@ if a bones file contains a doppelganger imitating a unique monster, a game
having already been created so it won't be created if/when the time
for that arrives (the doppelganger will change its shape normally,
not be stuck in the unique monster's form)
when mounted hero got hit by an explosion, hero and steed shared resistances
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository

View File

@@ -4,12 +4,115 @@
#include "hack.h"
static int explosionmask(struct monst *, uchar, char);
/* Note: Arrays are column first, while the screen is row first */
static const int explosion[3][3] = {
{ S_expl_tl, S_expl_ml, S_expl_bl },
{ S_expl_tc, S_expl_mc, S_expl_bc },
{ S_expl_tr, S_expl_mr, S_expl_br } };
/* what to do at [x+i][y+j] for i=-1,0,1 and j=-1,0,1 */
enum explode_action {
EXPL_NONE = 0, /* not specified yet or no shield effect needed */
EXPL_MON = 1, /* monster is affected */
EXPL_HERO = 2, /* hero is affected */
EXPL_SKIP = 4 /* don't apply shield effect (out of bounds) */
};
/* check if shield effects are needed for location affected by explosion */
static int
explosionmask(
struct monst *m, /* target monster (might be youmonst) */
uchar adtyp, /* damage type */
char olet) /* object class (only matters for AD_DISN) */
{
int res = EXPL_NONE;
if (m == &g.youmonst) {
switch (adtyp) {
case AD_PHYS:
/* leave 'res' with EXPL_NONE */
break;
case AD_MAGM:
if (Antimagic)
res = EXPL_HERO;
break;
case AD_FIRE:
if (Fire_resistance)
res = EXPL_HERO;
break;
case AD_COLD:
if (Cold_resistance)
res = EXPL_HERO;
break;
case AD_DISN:
if ((olet == WAND_CLASS)
? (nonliving(m->data) || is_demon(m->data))
: Disint_resistance)
res = EXPL_HERO;
break;
case AD_ELEC:
if (Shock_resistance)
res = EXPL_HERO;
break;
case AD_DRST:
if (Poison_resistance)
res = EXPL_HERO;
break;
case AD_ACID:
if (Acid_resistance)
res = EXPL_HERO;
break;
default:
impossible("explosion type %d?", adtyp);
break;
}
} else {
/* 'm' is a monster */
switch (adtyp) {
case AD_PHYS:
break;
case AD_MAGM:
if (resists_magm(m))
res = EXPL_MON;
break;
case AD_FIRE:
if (resists_fire(m))
res = EXPL_MON;
break;
case AD_COLD:
if (resists_cold(m))
res = EXPL_MON;
break;
case AD_DISN:
if ((olet == WAND_CLASS)
? (nonliving(m->data) || is_demon(m->data)
|| is_vampshifter(m))
: !!resists_disint(m))
res = EXPL_MON;
break;
case AD_ELEC:
if (resists_elec(m))
res = EXPL_MON;
break;
case AD_DRST:
if (resists_poison(m))
res = EXPL_MON;
break;
case AD_ACID:
if (resists_acid(m))
res = EXPL_MON;
break;
default:
impossible("explosion type %d?", adtyp);
break;
}
}
return res;
}
/* Note: I had to choose one of three possible kinds of "type" when writing
* this function: a wand type (like in zap.c), an adtyp, or an object type.
* Wand types get complex because they must be converted to adtyps for
@@ -29,7 +132,8 @@ static const int explosion[3][3] = {
*/
void
explode(
coordxy x, coordxy y, /* explosion's location; adjacent spots are also affected */
coordxy x, coordxy y, /* explosion's location;
* adjacent spots are also affected */
int type, /* same as in zap.c; -(wand typ) for some WAND_CLASS */
int dam, /* damage amount */
char olet, /* object class or BURNING_OIL or MON_EXPLODE */
@@ -44,7 +148,8 @@ explode(
struct monst *mtmp, *mdef = 0;
uchar adtyp;
int explmask[3][3]; /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
boolean shopdamage = FALSE, generic = FALSE, physical_dmg = FALSE,
coordxy xx, yy;
boolean shopdamage = FALSE, generic = FALSE,
do_hallu = FALSE, inside_engulfer, grabbed, grabbing;
coord grabxy;
char hallu_buf[BUFSZ], killr_buf[BUFSZ];
@@ -183,95 +288,34 @@ explode(
any_shield = visible = FALSE;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) {
if (!isok(i + x - 1, j + y - 1)) {
explmask[i][j] = 2;
xx = x + i - 1;
yy = y + j - 1;
if (!isok(xx, yy)) {
explmask[i][j] = EXPL_SKIP;
continue;
} else
explmask[i][j] = 0;
}
explmask[i][j] = EXPL_NONE;
if (u_at(i + x - 1, j + y - 1)) {
switch (adtyp) {
case AD_PHYS:
explmask[i][j] = 0;
break;
case AD_MAGM:
explmask[i][j] = !!Antimagic;
break;
case AD_FIRE:
explmask[i][j] = !!Fire_resistance;
break;
case AD_COLD:
explmask[i][j] = !!Cold_resistance;
break;
case AD_DISN:
explmask[i][j] = (olet == WAND_CLASS)
? !!(nonliving(g.youmonst.data)
|| is_demon(g.youmonst.data))
: !!Disint_resistance;
break;
case AD_ELEC:
explmask[i][j] = !!Shock_resistance;
break;
case AD_DRST:
explmask[i][j] = !!Poison_resistance;
break;
case AD_ACID:
explmask[i][j] = !!Acid_resistance;
physical_dmg = TRUE;
break;
default:
impossible("explosion type %d?", adtyp);
break;
}
if (u_at(xx, yy)) {
explmask[i][j] = explosionmask(&g.youmonst, adtyp, olet);
}
/* can be both you and mtmp if you're swallowed or riding */
mtmp = m_at(i + x - 1, j + y - 1);
if (!mtmp && u_at(i + x - 1, j + y - 1))
mtmp = m_at(xx, yy);
if (!mtmp && u_at(xx, yy))
mtmp = u.usteed;
if (mtmp && DEADMONSTER(mtmp))
mtmp = 0;
if (mtmp) {
if (DEADMONSTER(mtmp))
explmask[i][j] = 2;
else
switch (adtyp) {
case AD_PHYS:
break;
case AD_MAGM:
explmask[i][j] |= resists_magm(mtmp);
break;
case AD_FIRE:
explmask[i][j] |= resists_fire(mtmp);
break;
case AD_COLD:
explmask[i][j] |= resists_cold(mtmp);
break;
case AD_DISN:
explmask[i][j] |= (olet == WAND_CLASS)
? (nonliving(mtmp->data)
|| is_demon(mtmp->data)
|| is_vampshifter(mtmp))
: resists_disint(mtmp);
break;
case AD_ELEC:
explmask[i][j] |= resists_elec(mtmp);
break;
case AD_DRST:
explmask[i][j] |= resists_poison(mtmp);
break;
case AD_ACID:
explmask[i][j] |= resists_acid(mtmp);
break;
default:
impossible("explosion type %d?", adtyp);
break;
}
explmask[i][j] |= explosionmask(mtmp, adtyp, olet);
}
if (mtmp && cansee(i + x - 1, j + y - 1) && !canspotmon(mtmp))
map_invisible(i + x - 1, j + y - 1);
if (mtmp && cansee(xx, yy) && !canspotmon(mtmp))
map_invisible(xx, yy);
else if (!mtmp)
(void) unmap_invisible(i + x - 1, j + y - 1);
if (cansee(i + x - 1, j + y - 1))
(void) unmap_invisible(xx, yy);
if (cansee(xx, yy))
visible = TRUE;
if (explmask[i][j] == 1)
if ((explmask[i][j] & (EXPL_MON | EXPL_HERO)) != 0)
any_shield = TRUE;
}
@@ -279,11 +323,13 @@ explode(
/* Start the explosion */
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) {
if (explmask[i][j] == 2)
if (explmask[i][j] == EXPL_SKIP)
continue;
xx = x + i - 1;
yy = y + j - 1;
tmp_at(starting ? DISP_BEAM : DISP_CHANGE,
explosion_to_glyph(expltype, explosion[i][j]));
tmp_at(i + x - 1, j + y - 1);
tmp_at(xx, yy);
starting = 0;
}
curs_on_u(); /* will flush screen and output */
@@ -292,13 +338,15 @@ explode(
for (k = 0; k < SHIELD_COUNT; k++) {
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) {
if (explmask[i][j] == 1)
xx = x + i - 1;
yy = y + j - 1;
if ((explmask[i][j] & (EXPL_MON | EXPL_HERO)) != 0)
/*
* Bypass tmp_at() and send the shield glyphs
* directly to the buffered screen. tmp_at()
* will clean up the location for us later.
*/
show_glyph(i + x - 1, j + y - 1,
show_glyph(xx, yy,
cmap_to_glyph(shield_static[k]));
}
curs_on_u(); /* will flush screen and output */
@@ -308,10 +356,12 @@ explode(
/* Cover last shield glyph with blast symbol. */
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) {
if (explmask[i][j] == 1)
show_glyph(
i + x - 1, j + y - 1,
explosion_to_glyph(expltype, explosion[i][j]));
xx = x + i - 1;
yy = y + j - 1;
if ((explmask[i][j] & (EXPL_MON | EXPL_HERO)) != 0)
show_glyph(xx, yy,
explosion_to_glyph(expltype,
explosion[i][j]));
}
} else { /* delay a little bit. */
@@ -334,37 +384,42 @@ explode(
if (!Deaf && !didmsg)
pline("Boom!");
/* apply effects to monsters and floor objects first, in case the
damage to the hero is fatal and leaves bones */
if (dam) {
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
if (explmask[i][j] == 2)
if (explmask[i][j] == EXPL_SKIP)
continue;
if (u_at(i + x - 1, j + y - 1)) {
uhurt = (explmask[i][j] == 1) ? 1 : 2;
xx = x + i - 1;
yy = y + j - 1;
if (u_at(xx, yy)) {
uhurt = ((explmask[i][j] & EXPL_HERO) != 0) ? 1 : 2;
/* If the player is attacking via polyself into something
* with an explosion attack, leave them (and their gear)
* unharmed, to avoid punishing them from using such
* polyforms creatively */
if (!g.context.mon_moving && you_exploding)
uhurt = 0;
}
/* for inside_engulfer, only <u.ux,u.uy> is affected */
else if (inside_engulfer)
} else if (inside_engulfer) {
/* for inside_engulfer, only <u.ux,u.uy> is affected */
continue;
}
idamres = idamnonres = 0;
/* Affect the floor unless the player caused the explosion from
* inside their engulfer. */
/* Affect the floor unless the player caused the explosion
* from inside their engulfer. */
if (!(u.uswallow && !g.context.mon_moving))
(void) zap_over_floor((coordxy) (i + x - 1),
(coordxy) (j + y - 1), type,
(void) zap_over_floor(xx, yy, type,
&shopdamage, exploding_wand_typ);
mtmp = m_at(i + x - 1, j + y - 1);
if (!mtmp && u_at(i + x - 1, j + y - 1))
mtmp = m_at(xx, yy);
if (!mtmp && u_at(xx, yy))
mtmp = u.usteed;
if (!mtmp)
continue;
if (do_hallu) {
int tryct = 0;
/* replace "gas spore" with a different description
for each target (we can't distinguish personal names
like "Barney" here in order to suppress "the" below,
@@ -372,7 +427,7 @@ explode(
do {
Sprintf(hallu_buf, "%s explosion",
s_suffix(rndmonnam((char *) 0)));
} while (*hallu_buf != lowc(*hallu_buf));
} while (*hallu_buf != lowc(*hallu_buf) && ++tryct < 20);
str = hallu_buf;
}
if (engulfing_u(mtmp)) {
@@ -435,7 +490,7 @@ explode(
}
pline("%s gets slightly %s!", Monnam(u.ustuck), adj);
}
} else if (cansee(i + x - 1, j + y - 1)) {
} else if (cansee(xx, yy)) {
if (mtmp->m_ap_type)
seemimic(mtmp);
pline("%s is caught in the %s!", Monnam(mtmp), str);
@@ -451,7 +506,7 @@ explode(
idamnonres += destroy_mitem(mtmp, RING_CLASS, (int) adtyp);
idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int) adtyp);
if (explmask[i][j] == 1) {
if ((explmask[i][j] & EXPL_MON) != 0) {
golemeffects(mtmp, (int) adtyp, dam + idamres);
mtmp->mhp -= idamnonres;
} else {
@@ -462,8 +517,8 @@ explode(
int mdam = dam;
if (resist(mtmp, olet, 0, FALSE)) {
/* inside_engulfer: <i+x-1,j+y-1> == <u.ux,u.uy> */
if (cansee(i + x - 1, j + y - 1) || inside_engulfer)
/* inside_engulfer: <xx,yy> == <u.ux,u.uy> */
if (cansee(xx, yy) || inside_engulfer)
pline("%s resists the %s!", Monnam(mtmp), str);
mdam = (dam + 1) / 2;
}
@@ -539,7 +594,7 @@ explode(
if (Invulnerable) {
damu = 0;
You("are unharmed!");
} else if (adtyp == AD_PHYS || physical_dmg)
} else if (adtyp == AD_PHYS || adtyp == AD_ACID)
damu = Maybe_Half_Phys(damu);
if (adtyp == AD_FIRE) {
(void) burnarmor(&g.youmonst);