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:
copperwater
2023-04-15 20:25:26 -04:00
committed by Pasi Kallinen
parent fe39a2b08d
commit dad1c3f8b7
10 changed files with 21 additions and 23 deletions

View File

@@ -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));

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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)) {

View File

@@ -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);
}

View File

@@ -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)) {

View File

@@ -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));
}

View File

@@ -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!";