Skip to content

Commit

Permalink
Monsters can see player resistances
Browse files Browse the repository at this point in the history
If monsters see you resist something, generally elemental or magical
attack, or if they see you reflect an attack, they learn that and
will adjust their attack accordingly.

Originally from SporkHack, but this version comes via EvilHack with
some minor changes.
  • Loading branch information
paxed committed May 17, 2021
1 parent 2a10b30 commit 2288452
Show file tree
Hide file tree
Showing 19 changed files with 164 additions and 21 deletions.
1 change: 1 addition & 0 deletions doc/fixes37.0
Expand Up @@ -990,6 +990,7 @@ using 'f' while quiver is empty and 'autoquiver' is Off when wielding a
the quiver (inspired by xNetHack)
3.6's tribute: add one new passage to Sourcery, three to Small Gods, one to
Lords and Ladies, two to Soul Music
monsters can see and remember hero resistances


Platform- and/or Interface-Specific New Features
Expand Down
2 changes: 2 additions & 0 deletions include/extern.h
Expand Up @@ -1491,6 +1491,8 @@ extern const char *stagger(const struct permonst *, const char *);
extern const char *on_fire(struct permonst *, struct attack *);
extern const struct permonst *raceptr(struct monst *);
extern boolean olfaction(struct permonst *);
unsigned long cvt_adtyp_to_mseenres(uchar);
extern void monstseesu(unsigned long);

/* ### monmove.c ### */

Expand Down
18 changes: 18 additions & 0 deletions include/monst.h
Expand Up @@ -68,6 +68,23 @@ enum m_ap_types {
#define M_AP_TYPE(m) ((m)->m_ap_type & M_AP_TYPMASK)
#define M_AP_FLAG(m) ((m)->m_ap_type & ~M_AP_TYPMASK)

enum m_seen_resistance {
M_SEEN_NOTHING = 0x0000,
M_SEEN_MAGR = 0x0001, /* Antimagic, AD_MAGM */
M_SEEN_FIRE = 0x0002, /* Fire_resistance, AD_FIRE */
M_SEEN_COLD = 0x0004, /* Cold_resistance, AD_COLD */
M_SEEN_SLEEP = 0x0008, /* Sleep_resistance, AD_SLEE */
M_SEEN_DISINT = 0x0010, /* Disint_resistance, AD_DISN */
M_SEEN_ELEC = 0x0020, /* Shock_resistance, AD_ELEC */
M_SEEN_POISON = 0x0040, /* AD_DRST */
M_SEEN_ACID = 0x0080, /* Acid_resistance, AD_ACID */
M_SEEN_REFL = 0x0100, /* reflection, no corresponding AD_foo */
};

#define m_seenres(mon, mask) ((mon)->seen_resistance & (mask))
#define m_setseenres(mon, mask) ((mon)->seen_resistance |= (mask))
#define monstseesu_ad(adtyp) monstseesu(cvt_adtyp_to_mseenres(adtyp))

struct monst {
struct monst *nmon;
struct permonst *data;
Expand All @@ -90,6 +107,7 @@ struct monst {

schar mtame; /* level of tameness, implies peaceful */
unsigned short mextrinsics; /* low 8 correspond to mresists */
unsigned long seen_resistance; /* M_SEEN_x; saw you resist an effect */
int mspec_used; /* monster's special ability attack timeout */

Bitfield(female, 1); /* is female */
Expand Down
2 changes: 1 addition & 1 deletion include/patchlevel.h
Expand Up @@ -17,7 +17,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
#define EDITLEVEL 32
#define EDITLEVEL 33

/*
* Development status possibilities.
Expand Down
4 changes: 4 additions & 0 deletions src/explode.c
Expand Up @@ -529,6 +529,10 @@ explode(
g.context.botl = 1;
}

/* You resisted the damage, lets not keep that to ourselves */
if (uhurt == 1)
monstseesu_ad(adtyp);

if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
if (Upolyd) {
rehumanize();
Expand Down
5 changes: 3 additions & 2 deletions src/fountain.c
Expand Up @@ -542,9 +542,10 @@ drinksink(void)
break;
case 2:
You("take a sip of scalding hot %s.", hliquid("water"));
if (Fire_resistance)
if (Fire_resistance) {
pline("It seems quite tasty.");
else
monstseesu(M_SEEN_FIRE);
} else
losehp(rnd(6), "sipping boiling water", KILLED_BY);
/* boiling water burns considered fire damage */
break;
Expand Down
1 change: 1 addition & 0 deletions src/makemon.c
Expand Up @@ -1237,6 +1237,7 @@ makemon(register struct permonst *ptr,

place_monster(mtmp, x, y);
mtmp->mcansee = mtmp->mcanmove = TRUE;
mtmp->seen_resistance = M_SEEN_NOTHING;
mtmp->mpeaceful = (mmflags & MM_ANGRY) ? FALSE : peace_minded(ptr);

switch (ptr->mlet) {
Expand Down
20 changes: 18 additions & 2 deletions src/mcastu.c
Expand Up @@ -292,6 +292,7 @@ castmu(register struct monst *mtmp,
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
pline("But you resist the effects.");
monstseesu(M_SEEN_FIRE);
dmg = 0;
}
burn_away_slime();
Expand All @@ -301,6 +302,7 @@ castmu(register struct monst *mtmp,
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
pline("But you resist the effects.");
monstseesu(M_SEEN_COLD);
dmg = 0;
}
break;
Expand All @@ -309,6 +311,7 @@ castmu(register struct monst *mtmp,
if (Antimagic) {
shieldeff(u.ux, u.uy);
pline_The("missiles bounce off!");
monstseesu(M_SEEN_MAGR);
dmg = 0;
} else
dmg = d((int) mtmp->m_lev / 2 + 1, 6);
Expand Down Expand Up @@ -375,8 +378,10 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
done(DIED);
}
} else {
if (Antimagic)
if (Antimagic) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
}
pline("Lucky for you, it didn't work!");
}
dmg = 0;
Expand Down Expand Up @@ -425,6 +430,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
case MGC_DESTRY_ARMR:
if (Antimagic) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
pline("A field of force surrounds you!");
} else if (!destroy_arm(some_armor(&g.youmonst))) {
Your("skin itches.");
Expand All @@ -434,6 +440,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
case MGC_WEAKEN_YOU: /* drain strength */
if (Antimagic) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
You_feel("momentarily weakened.");
} else {
You("suddenly feel weaker!");
Expand Down Expand Up @@ -461,6 +468,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
case MGC_STUN_YOU:
if (Antimagic || Free_action) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
if (!Stunned)
You_feel("momentarily disoriented.");
make_stunned(1L, FALSE);
Expand All @@ -485,6 +493,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
made the spell virtually harmless to players with magic res. */
if (Antimagic) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
dmg = (dmg + 1) / 2;
}
if (dmg <= 5)
Expand Down Expand Up @@ -528,6 +537,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum)
pline("A pillar of fire strikes all around you!");
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_FIRE);
dmg = 0;
} else
dmg = d(8, 6);
Expand All @@ -549,8 +559,11 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum)
if (reflects || Shock_resistance) {
shieldeff(u.ux, u.uy);
dmg = 0;
if (reflects)
if (reflects) {
monstseesu(M_SEEN_REFL);
break;
}
monstseesu(M_SEEN_ELEC);
} else
dmg = d(8, 6);
if (Half_spell_damage)
Expand Down Expand Up @@ -660,6 +673,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum)
case CLC_PARALYZE:
if (Antimagic || Free_action) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
if (g.multi >= 0)
You("stiffen briefly.");
nomul(-1);
Expand All @@ -679,6 +693,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum)
case CLC_CONFUSE_YOU:
if (Antimagic) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
You_feel("momentarily dizzy.");
} else {
boolean oldprop = !!Confusion;
Expand All @@ -700,6 +715,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum)
case CLC_OPEN_WOUNDS:
if (Antimagic) {
shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR);
dmg = (dmg + 1) / 2;
}
if (dmg <= 5)
Expand Down
8 changes: 7 additions & 1 deletion src/mhitu.c
Expand Up @@ -1233,6 +1233,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
case AD_ACID:
if (Acid_resistance) {
You("are covered with a seemingly harmless goo.");
monstseesu(M_SEEN_ACID);
tmp = 0;
} else {
if (Hallucination)
Expand Down Expand Up @@ -1264,6 +1265,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
if (Shock_resistance) {
shieldeff(u.ux, u.uy);
You("seem unhurt.");
monstseesu(M_SEEN_ELEC);
ugolemeffects(AD_ELEC, tmp);
tmp = 0;
}
Expand All @@ -1275,6 +1277,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
You_feel("mildly chilly.");
monstseesu(M_SEEN_COLD);
ugolemeffects(AD_COLD, tmp);
tmp = 0;
} else
Expand All @@ -1287,6 +1290,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
You_feel("mildly hot.");
monstseesu(M_SEEN_FIRE);
ugolemeffects(AD_FIRE, tmp);
tmp = 0;
} else
Expand Down Expand Up @@ -1393,7 +1397,8 @@ explmu(struct monst *mtmp, struct attack *mattk, boolean ufound)
if (physical_damage)
tmp = Maybe_Half_Phys(tmp);
mdamageu(mtmp, tmp);
}
} else
monstseesu_ad(mattk->adtyp);
break;

case AD_BLND:
Expand Down Expand Up @@ -1596,6 +1601,7 @@ gazemu(struct monst *mtmp, struct attack *mattk)
stop_occupation();
if (Fire_resistance) {
pline_The("fire doesn't feel hot!");
monstseesu(M_SEEN_FIRE);
dmg = 0;
}
burn_away_slime();
Expand Down
32 changes: 32 additions & 0 deletions src/mondata.c
Expand Up @@ -1223,4 +1223,36 @@ olfaction(struct permonst* mdat)
return TRUE;
}

/* Convert attack damage type AD_foo to M_SEEN_bar */
unsigned long
cvt_adtyp_to_mseenres(uchar adtyp)
{
switch (adtyp) {
case AD_MAGM: return M_SEEN_MAGR;
case AD_FIRE: return M_SEEN_FIRE;
case AD_COLD: return M_SEEN_COLD;
case AD_SLEE: return M_SEEN_SLEEP;
case AD_DISN: return M_SEEN_DISINT;
case AD_ELEC: return M_SEEN_ELEC;
case AD_DRST: return M_SEEN_POISON;
case AD_ACID: return M_SEEN_ACID;
/* M_SEEN_REFL has no corresponding AD_foo type */
default: return M_SEEN_NOTHING;
}
}

/* Monsters remember hero resisting effect M_SEEN_foo */
void
monstseesu(unsigned long seenres)
{
struct monst *mtmp;

if (seenres == M_SEEN_NOTHING)
return;

for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
if (!DEADMONSTER(mtmp) && m_canseeu(mtmp))
m_setseenres(mtmp, seenres);
}

/*mondata.c*/
9 changes: 9 additions & 0 deletions src/mthrowu.c
Expand Up @@ -89,6 +89,7 @@ thitu(

if (is_acid && Acid_resistance) {
pline("It doesn't seem to hurt you.");
monstseesu(M_SEEN_ACID);
} else if (obj && obj->oclass == POTION_CLASS) {
/* an explosion which scatters objects might hit hero with one
(potions deliberately thrown at hero are handled by m_throw) */
Expand Down Expand Up @@ -821,6 +822,14 @@ breamm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg)
}
return MM_MISS;
}

/* if we've seen the actual resistance, don't bother, or
* if we're close by and they reflect, just jump the player */
if (m_seenres(mtmp, cvt_adtyp_to_mseenres(typ))
|| (m_seenres(mtmp, M_SEEN_REFL)
&& monnear(mtmp, mtmp->mux, mtmp->muy)))
return MM_HIT;

if (!mtmp->mspec_used && rn2(3)) {
if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
boolean utarget = (mtarg == &g.youmonst);
Expand Down

0 comments on commit 2288452

Please sign in to comment.