Fix: monster hurtling and liquid

A monster hurtling over liquid would drown immediately the instant it
touched the first square of water, even if normally it would have kept
moving (e.g. hurtling over a short moat).  Additionally, its placement
on liquid would not take into consideration other monsters, so it could
overwrite an existing monster on that spot and lead to an impossible,
and/or two monsters occupying a single position.

Fix these issues, so that liquid effects like drowning only happen if
the monster ends up in liquid at the end of the hurtle, and so that
other monsters in the way will stop it early even if they're floating
over or swimming on a pool/water/lava square.

Also use canspotmon instead of canseemon for the wiztelekinesis debug
command.
This commit is contained in:
Michael Meyer
2022-02-23 12:58:43 -05:00
committed by Pasi Kallinen
parent 8e91320d2f
commit 1e951db9bc
4 changed files with 12 additions and 16 deletions

View File

@@ -297,6 +297,7 @@ typedef struct sortloot_item Loot;
* to make an extra call to goodpos()] */
#define GP_ALLOW_U 0x100000L /* don't reject hero's location */
#define MM_NOEXCLAM 0x200000L /* more sedate "<mon> appears." mesg for ^G */
#define MM_IGNORELAVA 0x400000L /* ignore lava when positioning */
/* flags for make_corpse() and mkcorpstat(); 0..7 are recorded in obj->spe */
#define CORPSTAT_NONE 0x00

View File

@@ -1236,7 +1236,7 @@ wiz_telekinesis(void)
pline("Pick a monster to hurtle.");
do {
if (mtmp && !DEADMONSTER(mtmp) && canseemon(mtmp)) {
if (mtmp && !DEADMONSTER(mtmp) && canspotmon(mtmp)) {
cc.x = mtmp->mx;
cc.y = mtmp->my;
}
@@ -1245,7 +1245,7 @@ wiz_telekinesis(void)
if (ans < 0 || cc.x < 0)
return ECMD_CANCEL;
if (((mtmp = m_at(cc.x, cc.y)) != 0) && canseemon(mtmp)) {
if (((mtmp = m_at(cc.x, cc.y)) != 0) && canspotmon(mtmp)) {
if (!getdir("which direction?"))
return ECMD_CANCEL;

View File

@@ -907,21 +907,11 @@ mhurtle_step(genericptr_t arg, int x, int y)
struct monst *mon = (struct monst *) arg;
struct monst *mtmp;
if (is_pool(x, y) || is_lava(x, y)) {
remove_monster(mon->mx, mon->my);
newsym(mon->mx, mon->my);
place_monster(mon, x, y);
newsym(mon->mx, mon->my);
set_apparxy(mon);
if (minliquid(mon))
return FALSE;
return is_waterwall(x, y) ? FALSE : TRUE;
}
/* TODO: Treat walls, doors, iron bars, etc. specially
* rather than just stopping before.
*/
if (goodpos(x, y, mon, 0) && m_in_out_region(mon, x, y)) {
if (goodpos(x, y, mon, MM_IGNOREWATER | MM_IGNORELAVA)
&& m_in_out_region(mon, x, y)) {
int res;
remove_monster(mon->mx, mon->my);
@@ -929,6 +919,8 @@ mhurtle_step(genericptr_t arg, int x, int y)
place_monster(mon, x, y);
newsym(mon->mx, mon->my);
set_apparxy(mon);
if (is_waterwall(x, y))
return FALSE;
++g.force_mintrap;
res = mintrap(mon);
--g.force_mintrap;
@@ -1047,6 +1039,7 @@ mhurtle(struct monst *mon, int dx, int dy, int range)
cc.x = mon->mx + (dx * range);
cc.y = mon->my + (dy * range);
(void) walk_path(&mc, &cc, mhurtle_step, (genericptr_t) mon);
(void) minliquid(mon);
return;
}

View File

@@ -43,6 +43,7 @@ goodpos(int x, int y, struct monst* mtmp, long gpflags)
{
struct permonst *mdat = (struct permonst *) 0;
boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0),
ignorelava = ((gpflags & MM_IGNORELAVA) != 0),
allow_u = ((gpflags & GP_ALLOW_U) != 0);
if (!isok(x, y))
@@ -94,7 +95,7 @@ goodpos(int x, int y, struct monst* mtmp, long gpflags)
&& !grounded(mdat)));
} else if (mdat->mlet == S_EEL && rn2(13) && !ignorewater) {
return FALSE;
} else if (is_lava(x, y)) {
} else if (is_lava(x, y) && !ignorelava) {
/* 3.6.3: floating eye can levitate over lava but it avoids
that due the effect of the heat causing it to dry out */
if (mdat == &mons[PM_FLOATING_EYE])
@@ -114,7 +115,8 @@ goodpos(int x, int y, struct monst* mtmp, long gpflags)
return TRUE;
}
if (!accessible(x, y)) {
if (!(is_pool(x, y) && ignorewater))
if (!(is_pool(x, y) && ignorewater)
&& !(is_lava(x, y) && ignorelava))
return FALSE;
}