From 077606bc0184a85c4574d8ffb68305d842d896fa Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Fri, 6 Mar 2009 02:22:14 +0000 Subject: [PATCH] fix #H1826 - split long worm with 0 hit points From a bug report, a long worm with 0 HP was observed via stethoscope after cutting one or more worms in half many times, followed by an unspecified crash. Cutting a worm doesn't reduce its level below 3, but if a worm is drained to level 0 by some other means and then gets cut in half (and still has at least 2 HP left), cutworm() would give the new level 0 worm 0d8 (hence 0) for current and max HP. That could confuse end-of-move monster cleanup, which thinks 0 HP is a dead monster who has been removed from the map but not yet purged from the fmon list. Purging it would then leave a stale monster pointer on the map. cutworm() should have special cased level 0 to use 1d4 for HP, but instead I've changed it to not produce a cloned worm if the source one is lower than level 3. --- doc/fixes34.4 | 1 + src/worm.c | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/doc/fixes34.4 b/doc/fixes34.4 index 63f635b9e..41281b004 100644 --- a/doc/fixes34.4 +++ b/doc/fixes34.4 @@ -366,6 +366,7 @@ could get "suddenly you cannot see the " while invisible mon remained displayed due to telepathy or extended detection cutting a long worm in half would trigger segfault/accvio crash if the hit took parent down to 1 hit point or if long worms had become extinct +cutting a level 0 long worm in half produced a new worm with 0 hit points blinded invisible hero can't see self as invisible via ';' or '/' a hangup save while picking up gold from shop floor could duplicate that gold jellyfish do not technically have a head diff --git a/src/worm.c b/src/worm.c index aac8ce41f..4f0dcc59b 100644 --- a/src/worm.c +++ b/src/worm.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)worm.c 3.5 2007/07/15 */ +/* SCCS Id: @(#)worm.c 3.5 2009/03/05 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -363,11 +363,12 @@ cutworm(worm, x, y, weap) /* * At this point, the old worm is correct. Any new worm will have - * it's head at "curr" and its tail at "new_tail". + * it's head at "curr" and its tail at "new_tail". The old worm + * must be at least level 3 in order to produce a new worm. */ new_worm = 0; - new_wnum = rn2(3) ? 0 : get_wormno(); + new_wnum = (worm->m_lev >= 3 && !rn2(3)) ? get_wormno() : 0; if (new_wnum) { remove_monster(x, y); /* clone_mon puts new head here */ /* clone_mon() will fail if enough long worms have been @@ -392,19 +393,16 @@ cutworm(worm, x, y, weap) new_worm->wormno = new_wnum; /* affix new worm number */ new_worm->mcloned = 0; /* treat second worm as a normal monster */ - /* Devalue the monster level of both halves of the worm. */ - worm->m_lev = ((unsigned)worm->m_lev <= 3) ? - (unsigned)worm->m_lev : max((unsigned)worm->m_lev - 2, 3); + /* Devalue the monster level of both halves of the worm. + Note: m_lev is always at least 3 in order to get this far. */ + worm->m_lev = max((unsigned)worm->m_lev - 2, 3); new_worm->m_lev = worm->m_lev; - /* Calculate the mhp on the new_worm for the (lower) monster level. */ + /* Calculate the lower-level mhp; use d8 for long worms. + Can't use newmonhp() here because it would reset m_lev. */ new_worm->mhpmax = new_worm->mhp = d((int)new_worm->m_lev, 8); - - /* Calculate the mhp on the old worm for the (lower) monster level. */ - if (worm->m_lev > 3) { - worm->mhpmax = d((int)worm->m_lev, 8); - if (worm->mhpmax < worm->mhp) worm->mhp = worm->mhpmax; - } + worm->mhpmax = d((int)worm->m_lev, 8); /* new maxHP for old worm */ + if (worm->mhpmax < worm->mhp) worm->mhp = worm->mhpmax; wtails[new_wnum] = new_tail; /* We've got all the info right now */ wheads[new_wnum] = curr; /* so we can do this faster than */ -- 2.40.0