Fix: breathless monsters always generate in water in special levels
... unless explicitly specified to generate at a specific point or within a specific area. But if they are permitted to generate anywhere on the level, and it contains water, they always end up in the water. I noticed this when trying to explicitly specify ghouls to generate anywhere on a level with a minimal amount of water. This was due to the definition of "amphibious" being conflated with "breathless", such that all breathless monsters counted as amphibious. There are plenty of breathless monsters in the game that decidedly don't normally inhabit water, such as undead, but they would pass the amphibious() check in pm_to_humidity and thus the game decides that they must generate in wet terrain if there is any available. This fix takes the approach of changing amphibious() so that it no longer checks the M1_BREATHLESS flag and only considers M1_AMPHIBIOUS, then updating the places where amphibious() and Amphibious are used accordingly. I also added a new macro cant_drown() which wraps up swimming, amphibiousness, and breathlessness because these three things are frequently checked together in the context of whether something should drown. Places where amphibious() or Amphibious did NOT have an extra breathless() or Breathless check added on, and thus where behavior has been changed: - The pm_to_humidity function (to fix the bug). - Player vs water in goodpos; it didn't seem like being polymorphed into a breathless non-amphibious monster should make it fair game to randomly teleport into water even though it's technically safe. - Awarding extra experience when killing an eel. (So the hero will get the extra experience if they are polymorphed into a breathless non-amphibious monster and don't have magical breathing. Very much an edge case.)
This commit is contained in:
committed by
Pasi Kallinen
parent
fe39a2b08d
commit
dad1c3f8b7
@@ -378,8 +378,8 @@ e_survives_at(struct entity *etmp, coordxy x, coordxy y)
|
||||
if (noncorporeal(etmp->edata))
|
||||
return TRUE;
|
||||
if (is_pool(x, y))
|
||||
return (boolean) ((is_u(etmp) && (Wwalking || Amphibious || Swimming
|
||||
|| Flying || Levitation))
|
||||
return (boolean) ((is_u(etmp) && (Wwalking || Amphibious || Breathless
|
||||
|| Swimming || Flying || Levitation))
|
||||
|| is_swimmer(etmp->edata)
|
||||
|| is_flyer(etmp->edata)
|
||||
|| is_floater(etmp->edata));
|
||||
|
||||
@@ -1002,8 +1002,7 @@ Amulet_off(void)
|
||||
/* HMagical_breathing must be set off
|
||||
before calling drown() */
|
||||
setworn((struct obj *) 0, W_AMUL);
|
||||
if (!breathless(gy.youmonst.data) && !amphibious(gy.youmonst.data)
|
||||
&& !Swimming) {
|
||||
if (!cant_drown(gy.youmonst.data) && !Swimming) {
|
||||
You("suddenly inhale an unhealthy amount of %s!",
|
||||
hliquid("water"));
|
||||
(void) drown();
|
||||
|
||||
@@ -2825,7 +2825,8 @@ pooleffects(
|
||||
if (lava_effects())
|
||||
return TRUE;
|
||||
} else if ((!Wwalking || is_waterwall(u.ux,u.uy))
|
||||
&& (newspot || !u.uinwater || !(Swimming || Amphibious))) {
|
||||
&& (newspot || !u.uinwater
|
||||
|| !(Swimming || Amphibious || Breathless))) {
|
||||
if (drown())
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1328,8 +1328,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
|
||||
: amphibious(gy.youmonst.data)
|
||||
? "feel comforted."
|
||||
: "can barely breathe!");
|
||||
/* NB: Amphibious includes Breathless */
|
||||
if (Amphibious && !flaming(gy.youmonst.data))
|
||||
if ((Amphibious || Breathless) && !flaming(gy.youmonst.data))
|
||||
tmp = 0;
|
||||
} else {
|
||||
You("are %s!", enfolds(mtmp->data) ? "being squashed"
|
||||
|
||||
@@ -827,8 +827,7 @@ minliquid_core(struct monst* mtmp)
|
||||
* water damage to dead monsters' inventory, but survivors need to
|
||||
* be handled here. Swimmers are able to protect their stuff...
|
||||
*/
|
||||
if ((waterwall || !is_clinger(mtmp->data))
|
||||
&& !is_swimmer(mtmp->data) && !amphibious(mtmp->data)) {
|
||||
if ((waterwall || !is_clinger(mtmp->data)) && !cant_drown(mtmp->data)) {
|
||||
/* like hero with teleport intrinsic or spell, teleport away
|
||||
if possible */
|
||||
if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) {
|
||||
|
||||
@@ -689,7 +689,7 @@ dismount_steed(
|
||||
if (!Underwater)
|
||||
pline("%s falls into the %s!", Monnam(mtmp),
|
||||
surface(u.ux, u.uy));
|
||||
if (!is_swimmer(mdat) && !amphibious(mdat)) {
|
||||
if (!cant_drown(mdat)) {
|
||||
killed(mtmp);
|
||||
adjalign(-1);
|
||||
}
|
||||
|
||||
@@ -4485,7 +4485,7 @@ drown(void)
|
||||
feel_newsym(u.ux, u.uy); /* in case Blind, map the water here */
|
||||
/* happily wading in the same contiguous pool */
|
||||
if (u.uinwater && is_pool(u.ux - u.dx, u.uy - u.dy)
|
||||
&& (Swimming || Amphibious)) {
|
||||
&& (Swimming || Amphibious || Breathless)) {
|
||||
/* water effects on objects every now and then */
|
||||
if (!rn2(5))
|
||||
inpool_ok = TRUE;
|
||||
@@ -4495,7 +4495,8 @@ drown(void)
|
||||
|
||||
if (!u.uinwater) {
|
||||
You("%s into the %s%c", is_solid ? "plunge" : "fall",
|
||||
waterbody_name(u.ux, u.uy), (Amphibious || Swimming) ? '.' : '!');
|
||||
waterbody_name(u.ux, u.uy),
|
||||
(Amphibious || Swimming || Breathless) ? '.' : '!');
|
||||
if (!Swimming && !is_solid)
|
||||
You("sink like %s.", Hallucination ? "the Titanic" : "a rock");
|
||||
}
|
||||
@@ -4520,8 +4521,8 @@ drown(void)
|
||||
unleash_all();
|
||||
}
|
||||
|
||||
if (Amphibious || Swimming) {
|
||||
if (Amphibious) {
|
||||
if (Amphibious || Breathless || Swimming) {
|
||||
if (Amphibious || Breathless) {
|
||||
if (Verbose(3, drown))
|
||||
pline("But you aren't drowning.");
|
||||
if (!Is_waterlevel(&u.uz)) {
|
||||
|
||||
10
src/uhitm.c
10
src/uhitm.c
@@ -3094,8 +3094,7 @@ mhitm_ad_wrap(
|
||||
}
|
||||
} else if (u.ustuck == mdef) {
|
||||
/* Monsters don't wear amulets of magical breathing */
|
||||
if (is_pool(u.ux, u.uy) && !is_swimmer(pd)
|
||||
&& !amphibious(pd)) {
|
||||
if (is_pool(u.ux, u.uy) && !cant_drown(pd)) {
|
||||
You("drown %s...", mon_nam(mdef));
|
||||
mhm->damage = mdef->mhp;
|
||||
} else if (mattk->aatyp == AT_HUGS)
|
||||
@@ -3124,7 +3123,8 @@ mhitm_ad_wrap(
|
||||
Some_Monnam(magr), coil ? "coils" : "swings");
|
||||
}
|
||||
} else if (u.ustuck == magr) {
|
||||
if (is_pool(magr->mx, magr->my) && !Swimming && !Amphibious) {
|
||||
if (is_pool(magr->mx, magr->my) && !Swimming && !Amphibious
|
||||
&& !Breathless) {
|
||||
boolean moat = (levl[magr->mx][magr->my].typ != POOL)
|
||||
&& !is_waterwall(magr->mx, magr->my)
|
||||
&& !Is_medusa_level(&u.uz)
|
||||
@@ -3311,7 +3311,7 @@ mhitm_ad_slim(
|
||||
|
||||
if (gv.vis && canseemon(mdef))
|
||||
ncflags |= NC_SHOW_MSG;
|
||||
if (newcham(mdef, &mons[PM_GREEN_SLIME], ncflags))
|
||||
if (newcham(mdef, &mons[PM_GREEN_SLIME], ncflags))
|
||||
pd = mdef->data;
|
||||
mdef->mstrategy &= ~STRAT_WAITFORU;
|
||||
mhm->hitflags = M_ATTK_HIT;
|
||||
@@ -4769,7 +4769,7 @@ gulpum(struct monst *mdef, struct attack *mattk)
|
||||
case AD_PHYS:
|
||||
if (gy.youmonst.data == &mons[PM_FOG_CLOUD]) {
|
||||
pline("%s is laden with your moisture.", Monnam(mdef));
|
||||
if (amphibious(pd) && !flaming(pd)) {
|
||||
if ((breathless(pd) || amphibious(pd)) && !flaming(pd)) {
|
||||
dam = 0;
|
||||
pline("%s seems unharmed.", Monnam(mdef));
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ static void wishcmdassist(int);
|
||||
|
||||
#define is_hero_spell(type) ((type) >= 10 && (type) < 20)
|
||||
|
||||
#define M_IN_WATER(ptr) \
|
||||
((ptr)->mlet == S_EEL || amphibious(ptr) || is_swimmer(ptr))
|
||||
#define M_IN_WATER(ptr) ((ptr)->mlet == S_EEL || cant_drown(ptr))
|
||||
|
||||
static const char are_blinded_by_the_flash[] = "are blinded by the flash!";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user