]> granicus.if.org Git - nethack/commitdiff
melting ice timeout issues
authornhmall <nhmall@nethack.org>
Sun, 17 Oct 2021 02:22:33 +0000 (22:22 -0400)
committernhmall <nhmall@nethack.org>
Sun, 17 Oct 2021 02:22:33 +0000 (22:22 -0400)
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.

doc/fixes37.0
include/extern.h
src/dig.c
src/hack.c
src/timeout.c
src/trap.c
src/zap.c

index 280c591d9a8a109aed12b6cc0f633ffa24f2a171..20f2ebfb6c5f1218e985e73ddaf07fc8f21d346a 100644 (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
index cc85dca67a42014b0270e0172ecfc20779ff0695..f59df229c448e2fc10fa5090ebd1462f0cb25d4e 100644 (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 ### */
 
index 1c21f7e4e8eedb2226bc73ff240dc65e81cfe0ea..46d3c3b8542822ff66b850c9db28aec657639ae0 100644 (file)
--- 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
index f5f4e309e6aa090f2d8bd9ff0466284eb737011d..5fd152c91b84ddce5e5c53bbf7fc33a759ccdbdd 100644 (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*/
index a76c4e0ed8c4a41645804a3459205a029c3bcabf..630b1b4346cb3cd73c0fe51cb0f5f990f5921269 100644 (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);
+            }
         }
+    }
 }
 
 /*
index 67bd04dbd0976bc21a1634351dd2945791bb0be2..05fe3c3e93a9f37dc17224c34bae3b1724ba6ea0 100644 (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*/
index e1c40f4cbf8fe0dcda993757a7a8589125d2fdfe..301b655ddaff6f8e3f4528730e0e0d6cde4076b0 100644 (file)
--- 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)