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
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 ### */
#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;
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 */
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
-#define EDITLEVEL 32
+#define EDITLEVEL 33
/*
* Development status possibilities.
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();
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;
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) {
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
pline("But you resist the effects.");
+ monstseesu(M_SEEN_FIRE);
dmg = 0;
}
burn_away_slime();
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
pline("But you resist the effects.");
+ monstseesu(M_SEEN_COLD);
dmg = 0;
}
break;
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);
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;
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.");
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!");
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);
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)
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);
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)
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);
case CLC_CONFUSE_YOU:
if (Antimagic) {
shieldeff(u.ux, u.uy);
+ monstseesu(M_SEEN_MAGR);
You_feel("momentarily dizzy.");
} else {
boolean oldprop = !!Confusion;
case CLC_OPEN_WOUNDS:
if (Antimagic) {
shieldeff(u.ux, u.uy);
+ monstseesu(M_SEEN_MAGR);
dmg = (dmg + 1) / 2;
}
if (dmg <= 5)
case AD_ACID:
if (Acid_resistance) {
You("are covered with a seemingly harmless goo.");
+ monstseesu(M_SEEN_ACID);
tmp = 0;
} else {
if (Hallucination)
if (Shock_resistance) {
shieldeff(u.ux, u.uy);
You("seem unhurt.");
+ monstseesu(M_SEEN_ELEC);
ugolemeffects(AD_ELEC, tmp);
tmp = 0;
}
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
You_feel("mildly chilly.");
+ monstseesu(M_SEEN_COLD);
ugolemeffects(AD_COLD, tmp);
tmp = 0;
} else
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
You_feel("mildly hot.");
+ monstseesu(M_SEEN_FIRE);
ugolemeffects(AD_FIRE, tmp);
tmp = 0;
} else
if (physical_damage)
tmp = Maybe_Half_Phys(tmp);
mdamageu(mtmp, tmp);
- }
+ } else
+ monstseesu_ad(mattk->adtyp);
break;
case AD_BLND:
stop_occupation();
if (Fire_resistance) {
pline_The("fire doesn't feel hot!");
+ monstseesu(M_SEEN_FIRE);
dmg = 0;
}
burn_away_slime();
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*/
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) */
}
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);
find_offensive(struct monst* mtmp)
{
register struct obj *obj;
- boolean reflection_skip = (Reflecting && rn2(2));
+ boolean reflection_skip = m_seenres(mtmp, M_SEEN_REFL) != 0
+ || monnear(mtmp, mtmp->mux, mtmp->muy);
struct obj *helmet = which_armor(mtmp, W_ARMH);
g.m.offensive = (struct obj *) 0;
for (obj = mtmp->minvent; obj; obj = obj->nobj) {
if (!reflection_skip) {
nomore(MUSE_WAN_DEATH);
- if (obj->otyp == WAN_DEATH && obj->spe > 0) {
+ if (obj->otyp == WAN_DEATH && obj->spe > 0
+ && !m_seenres(mtmp, M_SEEN_MAGR)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_DEATH;
}
nomore(MUSE_WAN_SLEEP);
- if (obj->otyp == WAN_SLEEP && obj->spe > 0 && g.multi >= 0) {
+ if (obj->otyp == WAN_SLEEP && obj->spe > 0 && g.multi >= 0
+ && !m_seenres(mtmp, M_SEEN_SLEEP)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_SLEEP;
}
nomore(MUSE_WAN_FIRE);
- if (obj->otyp == WAN_FIRE && obj->spe > 0) {
+ if (obj->otyp == WAN_FIRE && obj->spe > 0
+ && !m_seenres(mtmp, M_SEEN_FIRE)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_FIRE;
}
nomore(MUSE_FIRE_HORN);
- if (obj->otyp == FIRE_HORN && obj->spe > 0 && can_blow(mtmp)) {
+ if (obj->otyp == FIRE_HORN && obj->spe > 0 && can_blow(mtmp)
+ && !m_seenres(mtmp, M_SEEN_FIRE)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_FIRE_HORN;
}
nomore(MUSE_WAN_COLD);
- if (obj->otyp == WAN_COLD && obj->spe > 0) {
+ if (obj->otyp == WAN_COLD && obj->spe > 0
+ && !m_seenres(mtmp, M_SEEN_COLD)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_COLD;
}
nomore(MUSE_FROST_HORN);
- if (obj->otyp == FROST_HORN && obj->spe > 0 && can_blow(mtmp)) {
+ if (obj->otyp == FROST_HORN && obj->spe > 0 && can_blow(mtmp)
+ && !m_seenres(mtmp, M_SEEN_COLD)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_FROST_HORN;
}
nomore(MUSE_WAN_LIGHTNING);
- if (obj->otyp == WAN_LIGHTNING && obj->spe > 0) {
+ if (obj->otyp == WAN_LIGHTNING && obj->spe > 0
+ && !m_seenres(mtmp, M_SEEN_ELEC)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_LIGHTNING;
}
nomore(MUSE_WAN_MAGIC_MISSILE);
- if (obj->otyp == WAN_MAGIC_MISSILE && obj->spe > 0) {
+ if (obj->otyp == WAN_MAGIC_MISSILE && obj->spe > 0
+ && !m_seenres(mtmp, M_SEEN_MAGR)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_MAGIC_MISSILE;
}
nomore(MUSE_WAN_UNDEAD_TURNING);
m_use_undead_turning(mtmp, obj);
nomore(MUSE_WAN_STRIKING);
- if (obj->otyp == WAN_STRIKING && obj->spe > 0) {
+ if (obj->otyp == WAN_STRIKING && obj->spe > 0
+ && !m_seenres(mtmp, M_SEEN_MAGR)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_WAN_STRIKING;
}
g.m.has_offense = MUSE_POT_CONFUSION;
}
nomore(MUSE_POT_SLEEPING);
- if (obj->otyp == POT_SLEEPING) {
+ if (obj->otyp == POT_SLEEPING
+ && !m_seenres(mtmp, M_SEEN_SLEEP)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_POT_SLEEPING;
}
nomore(MUSE_POT_ACID);
- if (obj->otyp == POT_ACID) {
+ if (obj->otyp == POT_ACID
+ && !m_seenres(mtmp, M_SEEN_ACID)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_POT_ACID;
}
nomore(MUSE_SCR_FIRE);
if (obj->otyp == SCR_FIRE && resists_fire(mtmp)
&& dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 2
- && mtmp->mcansee && haseyes(mtmp->data)) {
+ && mtmp->mcansee && haseyes(mtmp->data)
+ && !m_seenres(mtmp, M_SEEN_FIRE)) {
g.m.offensive = obj;
g.m.has_offense = MUSE_SCR_FIRE;
}
break;
case POT_SLEEPING:
if (Sleep_resistance || Free_action) {
+ monstseesu(M_SEEN_SLEEP);
You("yawn.");
} else {
You("suddenly fall asleep!");
g.multi_reason = "sleeping off a magical draught";
g.nomovemsg = You_can_move_again;
exercise(A_DEX, FALSE);
- } else
+ } else {
You("yawn.");
+ monstseesu(M_SEEN_SLEEP);
+ }
break;
case POT_SPEED:
if (!Fast)
pline("For some reason you're unaffected.");
else
(void) ureflects("%s reflects from your %s.", "It");
+ monstseesu(M_SEEN_REFL);
} else if (Shock_resistance) {
shieldeff(u.ux, u.uy);
pline("It seems not to affect you.");
+ monstseesu(M_SEEN_ELEC);
} else
fry_by_god(resp_god, FALSE);
}
} else {
You("bask in its %s glow for a minute...", NH_BLACK);
godvoice(resp_god, "I believe it not!");
+ monstseesu(M_SEEN_DISINT);
}
if (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)) {
/* one more try for high altars */
}
else if (Fire_resistance) {
shieldeff(u.ux, u.uy);
+ monstseesu(M_SEEN_FIRE);
if (!Blind)
pline("Oh, look, what a pretty fire in your %s.",
makeplural(body_part(HAND)));
return FALSE;
} else {
You("cough!");
+ monstseesu(M_SEEN_POISON);
return FALSE;
}
} else { /* A monster is inside the cloud */
seetrap(trap);
if (Sleep_resistance || breathless(g.youmonst.data)) {
You("are enveloped in a cloud of gas!");
+ monstseesu(M_SEEN_SLEEP);
} else {
pline("A cloud of gas puts you to sleep!");
fall_asleep(-rnd(25), TRUE);
the(box ? xname(box) : surface(u.ux, u.uy)));
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
+ monstseesu(M_SEEN_FIRE);
num = rn2(2);
} else if (Upolyd) {
num = d(2, 4);
if (Shock_resistance) {
shieldeff(u.ux, u.uy);
You("don't seem to be affected.");
+ monstseesu(M_SEEN_ELEC);
dmg = 0;
} else
dmg = d(4, 4);
You("sink into the %s%s!", hliquid("lava"),
!boil_away ? ", but it only burns slightly"
: " and are about to be immolated");
+ if (Fire_resistance)
+ monstseesu(M_SEEN_FIRE);
if (u.uhp > 1)
losehp(!boil_away ? 1 : (u.uhp / 2), lava_killer,
KILLED_BY); /* lava damage */
return;
} else if (Fire_resistance) {
pline_The("fire doesn't feel hot!");
+ monstseesu(M_SEEN_FIRE);
mhm->damage = 0;
}
if ((int) magr->m_lev > rn2(20))
pline("You're covered in frost!");
if (Cold_resistance) {
pline_The("frost doesn't seem cold!");
+ monstseesu(M_SEEN_COLD);
mhm->damage = 0;
}
if ((int) magr->m_lev > rn2(20))
You("get zapped!");
if (Shock_resistance) {
pline_The("zap doesn't shock you!");
+ monstseesu(M_SEEN_ELEC);
mhm->damage = 0;
}
if ((int) magr->m_lev > rn2(20))
if (Acid_resistance) {
pline("You're covered in %s, but it seems harmless.",
hliquid("acid"));
+ monstseesu(M_SEEN_ACID);
mhm->damage = 0;
} else {
pline("You're covered in %s! It burns!", hliquid("acid"));
hitmsg(magr, mattk);
if (uncancelled && g.multi >= 0 && !rn2(5)) {
- if (Sleep_resistance)
+ if (Sleep_resistance) {
+ monstseesu(M_SEEN_SLEEP);
return;
+ }
fall_asleep(-rnd(10), TRUE);
if (Blind)
You("are put to sleep!");
if (!Acid_resistance)
mdamageu(mon, tmp);
+ else
+ monstseesu(M_SEEN_ACID);
if (!rn2(30))
erode_armor(&g.youmonst, ERODE_CORRODE);
}
/* wrath of gods for attacking Oracle */
if (Antimagic) {
shieldeff(u.ux, u.uy);
+ monstseesu(M_SEEN_MAGR);
pline("A hail of magic missiles narrowly misses you!");
} else {
You("are hit by magic missiles appearing from thin air!");
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
You_feel("a mild chill.");
+ monstseesu(M_SEEN_COLD);
ugolemeffects(AD_COLD, tmp);
break;
}
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
You_feel("mildly warm.");
+ monstseesu(M_SEEN_FIRE);
ugolemeffects(AD_FIRE, tmp);
break;
}
if (Shock_resistance) {
shieldeff(u.ux, u.uy);
You_feel("a mild tingle.");
+ monstseesu(M_SEEN_ELEC);
ugolemeffects(AD_ELEC, tmp);
break;
}
if (Antimagic) {
shieldeff(u.ux, u.uy);
pline("Boing!");
+ monstseesu(M_SEEN_MAGR);
} else {
if (ordinary) {
You("bash yourself!");
} else {
shieldeff(u.ux, u.uy);
You("zap yourself, but seem unharmed.");
+ monstseesu(M_SEEN_ELEC);
ugolemeffects(AD_ELEC, d(12, 6));
}
destroy_item(WAND_CLASS, AD_ELEC);
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
You_feel("rather warm.");
+ monstseesu(M_SEEN_FIRE);
ugolemeffects(AD_FIRE, d(12, 6));
} else {
pline("You've set yourself afire!");
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
You_feel("a little chill.");
+ monstseesu(M_SEEN_COLD);
ugolemeffects(AD_COLD, d(12, 6));
} else {
You("imitate a popsicle!");
if (Antimagic) {
shieldeff(u.ux, u.uy);
pline_The("missiles bounce!");
+ monstseesu(M_SEEN_MAGR);
} else {
damage = d(4, 6);
pline("Idiot! You've shot yourself!");
if (Sleep_resistance) {
shieldeff(u.ux, u.uy);
You("don't feel sleepy!");
+ monstseesu(M_SEEN_SLEEP);
} else {
pline_The("sleep ray hits you!");
fall_asleep(-rnd(50), TRUE);
if (Antimagic) {
shieldeff(sx, sy);
pline_The("missiles bounce off!");
+ monstseesu(M_SEEN_MAGR);
} else {
dam = d(nd, 6);
exercise(A_STR, FALSE);
if (Fire_resistance) {
shieldeff(sx, sy);
You("don't feel hot!");
+ monstseesu(M_SEEN_FIRE);
ugolemeffects(AD_FIRE, d(nd, 6));
} else {
dam = d(nd, 6);
if (Cold_resistance) {
shieldeff(sx, sy);
You("don't feel cold.");
+ monstseesu(M_SEEN_COLD);
ugolemeffects(AD_COLD, d(nd, 6));
} else {
dam = d(nd, 6);
if (Sleep_resistance) {
shieldeff(u.ux, u.uy);
You("don't feel sleepy.");
+ monstseesu(M_SEEN_SLEEP);
} else {
fall_asleep(-d(nd, 25), TRUE); /* sleep ray */
}
if (abstyp == ZT_BREATH(ZT_DEATH)) {
if (Disint_resistance) {
You("are not disintegrated.");
+ monstseesu(M_SEEN_DISINT);
break;
} else if (uarms) {
/* destroy shield; other possessions are safe */
break;
} else if (Antimagic) {
shieldeff(sx, sy);
+ monstseesu(M_SEEN_MAGR);
You("aren't affected.");
break;
}
if (Shock_resistance) {
shieldeff(sx, sy);
You("aren't affected.");
+ monstseesu(M_SEEN_ELEC);
ugolemeffects(AD_ELEC, d(nd, 6));
} else {
dam = d(nd, 6);
case ZT_ACID:
if (Acid_resistance) {
pline_The("%s doesn't hurt.", hliquid("acid"));
+ monstseesu(M_SEEN_ACID);
dam = 0;
} else {
pline_The("%s burns!", hliquid("acid"));
"it");
} else
pline("For some reason you are not affected.");
+ monstseesu(M_SEEN_REFL);
dx = -dx;
dy = -dy;
shieldeff(sx, sy);