From: nhmall Date: Sun, 17 Oct 2021 02:22:33 +0000 (-0400) Subject: melting ice timeout issues X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=17165e3b92ea55d08898350ac9eb61a001228ab5;p=nethack 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. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 280c591d9..20f2ebfb6 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -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 diff --git a/include/extern.h b/include/extern.h index cc85dca67..f59df229c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 ### */ diff --git a/src/dig.c b/src/dig.c index 1c21f7e4e..46d3c3b85 100644 --- a/src/dig.c +++ b/src/dig.c @@ -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; - /* 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); + } - /* 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); } - - /* 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 diff --git a/src/hack.c b/src/hack.c index f5f4e309e..5fd152c91 100644 --- a/src/hack.c +++ b/src/hack.c @@ -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*/ diff --git a/src/timeout.c b/src/timeout.c index a76c4e0ed..630b1b434 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -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); + } } + } } /* diff --git a/src/trap.c b/src/trap.c index 67bd04dbd..05fe3c3e9 100644 --- a/src/trap.c +++ b/src/trap.c @@ -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*/ diff --git a/src/zap.c b/src/zap.c index e1c40f4cb..301b655dd 100644 --- a/src/zap.c +++ b/src/zap.c @@ -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)