melting ice timeout issues

Reported directly to devteam by entrez via email:
>
> I noticed some potential issues with (melting) ice:
>
>   * Digging down into ice, or setting a land mine on the ice and
>   triggering it, doesn't remove the melt_ice timeout, so it can result
>   in a sequence like dig down -> pit fills with water -> freeze water
>   -> freezing water tries to set melt_ice timeout -> duplicate timeout
>   impossible.  Or if you don't freeze the water again, melt_ice will
>   run on a non-ice surface, which might at least produce strange
>   messages.
>
>   * Setting a land mine on ice: melting ice doesn't do anything with
>   the trap, so there is still a land mine which you can trigger by
>   flying over the water (the land mine's trigger is also still
>   described as being 'in a pile of soil', despite being underwater at
>   this point).  Similar thing happens with bear traps.
>
>   * Not really related to _melting_ ice, but an exploding land mine
>   doesn't reset the typ from ICE to FLOOR (like normal digging does),
>   so it will result in a square with a pit that is also an ice square,
>   where the ice can melt under the pit and produce a combination
>   pit/moat.  If you then freeze the moat, the pit reappears on top of
>   the ice.
This commit is contained in:
nhmall
2021-10-16 22:22:33 -04:00
parent 91b4e4570d
commit 17165e3b92
7 changed files with 94 additions and 42 deletions

View File

@@ -646,6 +646,8 @@ breaching a shop wall, using locking magic to put a door there, then unlocking
that door yielded a situation where subsequent shop damage repair
produced invalid map data which resulted in an impossible() warning
about "wall_angle: unknown" during map display
melting ice timer could persist after the ice was gone from digging or from an
exploding land mine
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository

View File

@@ -897,6 +897,7 @@ extern int max_capacity(void);
extern boolean check_capacity(const char *);
extern int inv_cnt(boolean);
extern long money_cnt(struct obj *);
extern void spot_checks(xchar, xchar, schar);
/* ### hacklib.c ### */
@@ -2685,6 +2686,7 @@ extern void sink_into_lava(void);
extern void sokoban_guilt(void);
extern const char * trapname(int, boolean);
extern void ignite_items(struct obj *);
extern void trap_ice_effects(xchar x, xchar y, boolean ice_is_melting);
/* ### u_init.c ### */

View File

@@ -767,9 +767,9 @@ dighole(boolean pit_only, boolean by_magic, coord *cc)
register struct trap *ttmp;
struct rm *lev;
struct obj *boulder_here;
schar typ;
schar typ, old_typ;
xchar dig_x, dig_y;
boolean nohole;
boolean nohole, retval = TRUE;
if (!cc) {
dig_x = u.ux;
@@ -784,10 +784,11 @@ dighole(boolean pit_only, boolean by_magic, coord *cc)
ttmp = t_at(dig_x, dig_y);
lev = &levl[dig_x][dig_y];
nohole = (!Can_dig_down(&u.uz) && !lev->candig);
old_typ = lev->typ;
if ((ttmp && (ttmp->ttyp == MAGIC_PORTAL
|| ttmp->ttyp == VIBRATING_SQUARE || nohole))
|| (IS_ROCK(lev->typ) && lev->typ != SDOOR
|| (IS_ROCK(old_typ) && old_typ != SDOOR
&& (lev->wall_info & W_NONDIGGABLE) != 0)) {
pline_The("%s %shere is too hard to dig in.", surface(dig_x, dig_y),
(dig_x != u.ux || dig_y != u.uy) ? "t" : "");
@@ -797,20 +798,19 @@ dighole(boolean pit_only, boolean by_magic, coord *cc)
hliquid(is_lava(dig_x, dig_y) ? "lava" : "water"));
wake_nearby(); /* splashing */
} else if (lev->typ == DRAWBRIDGE_DOWN
} else if (old_typ == DRAWBRIDGE_DOWN
|| (is_drawbridge_wall(dig_x, dig_y) >= 0)) {
/* drawbridge_down is the platform crossing the moat when the
bridge is extended; drawbridge_wall is the open "doorway" or
closed "door" where the portcullis/mechanism is located */
if (pit_only) {
pline_The("drawbridge seems too hard to dig through.");
return FALSE;
retval = FALSE;
} else {
int x = dig_x, y = dig_y;
/* if under the portcullis, the bridge is adjacent */
(void) find_drawbridge(&x, &y);
destroy_drawbridge(x, y);
return TRUE;
}
} else if ((boulder_here = sobj_at(BOULDER, dig_x, dig_y)) != 0) {
@@ -828,13 +828,10 @@ dighole(boolean pit_only, boolean by_magic, coord *cc)
(void) delfloortrap(ttmp);
}
delobj(boulder_here);
return TRUE;
} else if (IS_GRAVE(lev->typ)) {
} else if (IS_GRAVE(old_typ)) {
digactualhole(dig_x, dig_y, BY_YOU, PIT);
dig_up_grave(cc);
return TRUE;
} else if (lev->typ == DRAWBRIDGE_UP) {
} else if (old_typ == DRAWBRIDGE_UP) {
/* must be floor or ice, other cases handled above */
/* dig "pit" and let fluid flow in (if possible) */
typ = fillholetyp(dig_x, dig_y, FALSE);
@@ -847,20 +844,19 @@ dighole(boolean pit_only, boolean by_magic, coord *cc)
pline_The("%s %shere is too hard to dig in.",
surface(dig_x, dig_y),
(dig_x != u.ux || dig_y != u.uy) ? "t" : "");
return FALSE;
retval = FALSE;
} else {
lev->drawbridgemask &= ~DB_UNDER;
lev->drawbridgemask |= (typ == LAVAPOOL) ? DB_LAVA : DB_MOAT;
liquid_flow(dig_x, dig_y, typ, ttmp,
"As you dig, the hole fills with %s!");
}
lev->drawbridgemask &= ~DB_UNDER;
lev->drawbridgemask |= (typ == LAVAPOOL) ? DB_LAVA : DB_MOAT;
liquid_flow(dig_x, dig_y, typ, ttmp,
"As you dig, the hole fills with %s!");
return TRUE;
/* the following two are here for the wand of digging */
} else if (IS_THRONE(lev->typ)) {
} else if (IS_THRONE(old_typ)) {
pline_The("throne is too hard to break apart.");
} else if (IS_ALTAR(lev->typ)) {
} else if (IS_ALTAR(old_typ)) {
pline_The("altar is too hard to break apart.");
} else {
@@ -873,28 +869,25 @@ dighole(boolean pit_only, boolean by_magic, coord *cc)
liquid_flow(dig_x, dig_y, typ, ttmp,
"As you dig, the hole fills with %s!");
}
return TRUE;
} else {
/* magical digging disarms settable traps */
if (by_magic && ttmp
&& (ttmp->ttyp == LANDMINE || ttmp->ttyp == BEAR_TRAP)) {
int otyp = (ttmp->ttyp == LANDMINE) ? LAND_MINE : BEARTRAP;
/* convert trap into buried object (deletes trap) */
cnv_trap_obj(otyp, 1, ttmp, TRUE);
}
/* finally we get to make a hole */
if (nohole || pit_only)
digactualhole(dig_x, dig_y, BY_YOU, PIT);
else
digactualhole(dig_x, dig_y, BY_YOU, HOLE);
}
/* magical digging disarms settable traps */
if (by_magic && ttmp
&& (ttmp->ttyp == LANDMINE || ttmp->ttyp == BEAR_TRAP)) {
int otyp = (ttmp->ttyp == LANDMINE) ? LAND_MINE : BEARTRAP;
/* convert trap into buried object (deletes trap) */
cnv_trap_obj(otyp, 1, ttmp, TRUE);
}
/* finally we get to make a hole */
if (nohole || pit_only)
digactualhole(dig_x, dig_y, BY_YOU, PIT);
else
digactualhole(dig_x, dig_y, BY_YOU, HOLE);
return TRUE;
}
return FALSE;
spot_checks(dig_x, dig_y, old_typ);
return retval;
}
static void

View File

@@ -3430,4 +3430,26 @@ money_cnt(struct obj *otmp)
return 0L;
}
void
spot_checks(xchar x, xchar y, schar old_typ)
{
schar new_typ = levl[x][y].typ;
boolean db_ice_now = FALSE;
switch (old_typ) {
case DRAWBRIDGE_UP:
db_ice_now = ((levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE);
/*FALLTHRU*/
case ICE:
if ((new_typ != old_typ) || !db_ice_now) {
/* make sure there's no MELT_ICE_AWAY timer */
if (spot_time_left(x, y, MELT_ICE_AWAY)) {
spot_stop_timers(x, y, MELT_ICE_AWAY);
}
/* adjust things affected by the ice */
obj_ice_effects(x, y, FALSE);
}
break;
}
}
/*hack.c*/

View File

@@ -1860,7 +1860,7 @@ timer_sanity_check(void)
timer_element *curr;
/* this should be much more complete */
for (curr = g.timer_base; curr; curr = curr->next)
for (curr = g.timer_base; curr; curr = curr->next) {
if (curr->kind == TIMER_OBJECT) {
struct obj *obj = curr->arg.a_obj;
@@ -1868,7 +1868,20 @@ timer_sanity_check(void)
impossible("timer sanity: untimed obj %s, timer %ld",
fmt_ptr((genericptr_t) obj), curr->tid);
}
} else if (curr->kind == TIMER_LEVEL) {
long where = curr->arg.a_long;
xchar x = (xchar) ((where >> 16) & 0xFFFF),
y = (xchar) (where & 0xFFFF);
if (!isok(x, y)) {
impossible("timer sanity: spot timer %d at <%d,%d>",
curr->tid, x, y);
} else if (curr->func_index == MELT_ICE_AWAY && !is_ice(x, y)) {
impossible("timer sanity: melt timer %d on non-ice %d <%d,%d>",
curr->tid, levl[x][y].typ, x, y);
}
}
}
}
/*

View File

@@ -2570,8 +2570,9 @@ blow_up_landmine(struct trap* trap)
{
int x = trap->tx, y = trap->ty, dbx, dby;
struct rm *lev = &levl[x][y];
schar typ;
schar old_typ, typ;
old_typ = lev->typ;
(void) scatter(x, y, 4,
MAY_DESTROY | MAY_HIT | MAY_FRACTURE | VIS_EFFECTS,
(struct obj *) 0);
@@ -2607,6 +2608,7 @@ blow_up_landmine(struct trap* trap)
}
}
}
spot_checks(x, y, old_typ);
}
static void
@@ -5996,4 +5998,20 @@ ignite_items(struct obj* objchn)
}
}
void
trap_ice_effects(xchar x, xchar y, boolean ice_is_melting)
{
struct trap *ttmp = t_at(x, y);
if (ttmp && ice_is_melting) {
if (ttmp->ttyp == LANDMINE || ttmp->ttyp == BEAR_TRAP) {
/* landmine or bear trap set on top of the ice falls
into the water */
int otyp = (ttmp->ttyp == LANDMINE) ? LAND_MINE : BEARTRAP;
cnv_trap_obj(otyp, 1, ttmp, TRUE);
} else {
deltrap(ttmp);
}
}
}
/*trap.c*/

View File

@@ -4477,6 +4477,8 @@ melt_ice(xchar x, xchar y, const char *msg)
lev->icedpool = 0;
}
spot_stop_timers(x, y, MELT_ICE_AWAY); /* no more ice to melt away */
if (t_at(x, y))
trap_ice_effects(x, y, TRUE); /* TRUE because ice_is_melting */
obj_ice_effects(x, y, FALSE);
unearth_objs(x, y);
if (Underwater)