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
+1
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 having already been created so it won't be created if/when the time
for that arrives (the doppelganger will change its shape normally, for that arrives (the doppelganger will change its shape normally,
not be stuck in the unique monster's form) 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 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
+163 -108
View File
@@ -4,12 +4,115 @@
#include "hack.h" #include "hack.h"
static int explosionmask(struct monst *, uchar, char);
/* Note: Arrays are column first, while the screen is row first */ /* Note: Arrays are column first, while the screen is row first */
static const int explosion[3][3] = { static const int explosion[3][3] = {
{ S_expl_tl, S_expl_ml, S_expl_bl }, { S_expl_tl, S_expl_ml, S_expl_bl },
{ S_expl_tc, S_expl_mc, S_expl_bc }, { S_expl_tc, S_expl_mc, S_expl_bc },
{ S_expl_tr, S_expl_mr, S_expl_br } }; { 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 /* 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. * 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 * Wand types get complex because they must be converted to adtyps for
@@ -29,7 +132,8 @@ static const int explosion[3][3] = {
*/ */
void void
explode( 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 type, /* same as in zap.c; -(wand typ) for some WAND_CLASS */
int dam, /* damage amount */ int dam, /* damage amount */
char olet, /* object class or BURNING_OIL or MON_EXPLODE */ char olet, /* object class or BURNING_OIL or MON_EXPLODE */
@@ -44,7 +148,8 @@ explode(
struct monst *mtmp, *mdef = 0; struct monst *mtmp, *mdef = 0;
uchar adtyp; uchar adtyp;
int explmask[3][3]; /* 0=normal explosion, 1=do shieldeff, 2=do nothing */ 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; do_hallu = FALSE, inside_engulfer, grabbed, grabbing;
coord grabxy; coord grabxy;
char hallu_buf[BUFSZ], killr_buf[BUFSZ]; char hallu_buf[BUFSZ], killr_buf[BUFSZ];
@@ -183,95 +288,34 @@ explode(
any_shield = visible = FALSE; any_shield = visible = FALSE;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
if (!isok(i + x - 1, j + y - 1)) { xx = x + i - 1;
explmask[i][j] = 2; yy = y + j - 1;
if (!isok(xx, yy)) {
explmask[i][j] = EXPL_SKIP;
continue; continue;
} else }
explmask[i][j] = 0; explmask[i][j] = EXPL_NONE;
if (u_at(i + x - 1, j + y - 1)) { if (u_at(xx, yy)) {
switch (adtyp) { explmask[i][j] = explosionmask(&g.youmonst, adtyp, olet);
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;
}
} }
/* can be both you and mtmp if you're swallowed or riding */ /* can be both you and mtmp if you're swallowed or riding */
mtmp = m_at(i + x - 1, j + y - 1); mtmp = m_at(xx, yy);
if (!mtmp && u_at(i + x - 1, j + y - 1)) if (!mtmp && u_at(xx, yy))
mtmp = u.usteed; mtmp = u.usteed;
if (mtmp && DEADMONSTER(mtmp))
mtmp = 0;
if (mtmp) { if (mtmp) {
if (DEADMONSTER(mtmp)) explmask[i][j] |= explosionmask(mtmp, adtyp, olet);
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;
}
} }
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) else if (!mtmp)
(void) unmap_invisible(i + x - 1, j + y - 1); (void) unmap_invisible(xx, yy);
if (cansee(i + x - 1, j + y - 1)) if (cansee(xx, yy))
visible = TRUE; visible = TRUE;
if (explmask[i][j] == 1) if ((explmask[i][j] & (EXPL_MON | EXPL_HERO)) != 0)
any_shield = TRUE; any_shield = TRUE;
} }
@@ -279,11 +323,13 @@ explode(
/* Start the explosion */ /* Start the explosion */
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
if (explmask[i][j] == 2) if (explmask[i][j] == EXPL_SKIP)
continue; continue;
xx = x + i - 1;
yy = y + j - 1;
tmp_at(starting ? DISP_BEAM : DISP_CHANGE, tmp_at(starting ? DISP_BEAM : DISP_CHANGE,
explosion_to_glyph(expltype, explosion[i][j])); explosion_to_glyph(expltype, explosion[i][j]));
tmp_at(i + x - 1, j + y - 1); tmp_at(xx, yy);
starting = 0; starting = 0;
} }
curs_on_u(); /* will flush screen and output */ curs_on_u(); /* will flush screen and output */
@@ -292,13 +338,15 @@ explode(
for (k = 0; k < SHIELD_COUNT; k++) { for (k = 0; k < SHIELD_COUNT; k++) {
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) { 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 * Bypass tmp_at() and send the shield glyphs
* directly to the buffered screen. tmp_at() * directly to the buffered screen. tmp_at()
* will clean up the location for us later. * 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])); cmap_to_glyph(shield_static[k]));
} }
curs_on_u(); /* will flush screen and output */ curs_on_u(); /* will flush screen and output */
@@ -308,10 +356,12 @@ explode(
/* Cover last shield glyph with blast symbol. */ /* Cover last shield glyph with blast symbol. */
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
if (explmask[i][j] == 1) xx = x + i - 1;
show_glyph( yy = y + j - 1;
i + x - 1, j + y - 1, if ((explmask[i][j] & (EXPL_MON | EXPL_HERO)) != 0)
explosion_to_glyph(expltype, explosion[i][j])); show_glyph(xx, yy,
explosion_to_glyph(expltype,
explosion[i][j]));
} }
} else { /* delay a little bit. */ } else { /* delay a little bit. */
@@ -334,37 +384,42 @@ explode(
if (!Deaf && !didmsg) if (!Deaf && !didmsg)
pline("Boom!"); pline("Boom!");
/* apply effects to monsters and floor objects first, in case the
damage to the hero is fatal and leaves bones */
if (dam) { if (dam) {
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
if (explmask[i][j] == 2) if (explmask[i][j] == EXPL_SKIP)
continue; continue;
if (u_at(i + x - 1, j + y - 1)) { xx = x + i - 1;
uhurt = (explmask[i][j] == 1) ? 1 : 2; 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 /* If the player is attacking via polyself into something
* with an explosion attack, leave them (and their gear) * with an explosion attack, leave them (and their gear)
* unharmed, to avoid punishing them from using such * unharmed, to avoid punishing them from using such
* polyforms creatively */ * polyforms creatively */
if (!g.context.mon_moving && you_exploding) if (!g.context.mon_moving && you_exploding)
uhurt = 0; uhurt = 0;
} } else if (inside_engulfer) {
/* for inside_engulfer, only <u.ux,u.uy> is affected */ /* for inside_engulfer, only <u.ux,u.uy> is affected */
else if (inside_engulfer)
continue; continue;
}
idamres = idamnonres = 0; idamres = idamnonres = 0;
/* Affect the floor unless the player caused the explosion from /* Affect the floor unless the player caused the explosion
* inside their engulfer. */ * from inside their engulfer. */
if (!(u.uswallow && !g.context.mon_moving)) if (!(u.uswallow && !g.context.mon_moving))
(void) zap_over_floor((coordxy) (i + x - 1), (void) zap_over_floor(xx, yy, type,
(coordxy) (j + y - 1), type,
&shopdamage, exploding_wand_typ); &shopdamage, exploding_wand_typ);
mtmp = m_at(i + x - 1, j + y - 1); mtmp = m_at(xx, yy);
if (!mtmp && u_at(i + x - 1, j + y - 1)) if (!mtmp && u_at(xx, yy))
mtmp = u.usteed; mtmp = u.usteed;
if (!mtmp) if (!mtmp)
continue; continue;
if (do_hallu) { if (do_hallu) {
int tryct = 0;
/* replace "gas spore" with a different description /* replace "gas spore" with a different description
for each target (we can't distinguish personal names for each target (we can't distinguish personal names
like "Barney" here in order to suppress "the" below, like "Barney" here in order to suppress "the" below,
@@ -372,7 +427,7 @@ explode(
do { do {
Sprintf(hallu_buf, "%s explosion", Sprintf(hallu_buf, "%s explosion",
s_suffix(rndmonnam((char *) 0))); s_suffix(rndmonnam((char *) 0)));
} while (*hallu_buf != lowc(*hallu_buf)); } while (*hallu_buf != lowc(*hallu_buf) && ++tryct < 20);
str = hallu_buf; str = hallu_buf;
} }
if (engulfing_u(mtmp)) { if (engulfing_u(mtmp)) {
@@ -435,7 +490,7 @@ explode(
} }
pline("%s gets slightly %s!", Monnam(u.ustuck), adj); 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) if (mtmp->m_ap_type)
seemimic(mtmp); seemimic(mtmp);
pline("%s is caught in the %s!", Monnam(mtmp), str); 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, RING_CLASS, (int) adtyp);
idamnonres += destroy_mitem(mtmp, WAND_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); golemeffects(mtmp, (int) adtyp, dam + idamres);
mtmp->mhp -= idamnonres; mtmp->mhp -= idamnonres;
} else { } else {
@@ -462,8 +517,8 @@ explode(
int mdam = dam; int mdam = dam;
if (resist(mtmp, olet, 0, FALSE)) { if (resist(mtmp, olet, 0, FALSE)) {
/* inside_engulfer: <i+x-1,j+y-1> == <u.ux,u.uy> */ /* inside_engulfer: <xx,yy> == <u.ux,u.uy> */
if (cansee(i + x - 1, j + y - 1) || inside_engulfer) if (cansee(xx, yy) || inside_engulfer)
pline("%s resists the %s!", Monnam(mtmp), str); pline("%s resists the %s!", Monnam(mtmp), str);
mdam = (dam + 1) / 2; mdam = (dam + 1) / 2;
} }
@@ -539,7 +594,7 @@ explode(
if (Invulnerable) { if (Invulnerable) {
damu = 0; damu = 0;
You("are unharmed!"); You("are unharmed!");
} else if (adtyp == AD_PHYS || physical_dmg) } else if (adtyp == AD_PHYS || adtyp == AD_ACID)
damu = Maybe_Half_Phys(damu); damu = Maybe_Half_Phys(damu);
if (adtyp == AD_FIRE) { if (adtyp == AD_FIRE) {
(void) burnarmor(&g.youmonst); (void) burnarmor(&g.youmonst);