end of game oddities: thrownobj, ball&chain

If you died while Punished but with attached ball and chain temporarily
off the map (changing levels and when swallowed are the cases I looked
at; there may be others), the ball and chain objects would not appear
in bones (for the falling-down-stairs case; bones are never saved if
hero dies while swallowed) and they weren't being freed.  Put them
back on the map so that they'll be included in bones and also freed as
part of normal map cleanup.

This caused a problem if the attached ball had state OBJ_FREE due to
being thrown rather than being temporarily off the map.  'thrownobj'
was being deallocated without first cancelling punishment, so uball
object was freed via thrownobj pointer but stale uball pointer still
referenced it.  Unpunishing would introduce sequencing issues because
that would need to be after attribute disclosure.  So instead of
deallocating thrown or kicked object, put it/them (can't actually have
both at the same time) back on the map.  This has a side-effect of
saving thrown Mjollnir in bones if it kills hero when failing to be
caught upon return.  (I thought that that had been fixed ages ago?)
This commit is contained in:
PatR
2019-05-26 18:44:25 -07:00
parent b3689411dd
commit 6c0f92a264
2 changed files with 62 additions and 15 deletions

View File

@@ -1,4 +1,4 @@
$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.23 $ $NHDT-Date: 1558856435 2019/05/26 07:40:35 $
$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.24 $ $NHDT-Date: 1558921075 2019/05/27 01:37:55 $
This fixes36.3 file is here to capture information about updates in the 3.6.x
lineage following the release of 3.6.2 in May 2019. Please note, however,
@@ -26,6 +26,10 @@ on rare occasions, multiple mine's end luckstones were being marked as the
make sure the correct luckstone is the prize in mine's end
free dungeon overview's bones information at end of game
free current level's bones information at end of game
free ball and chain if Punished hero dies while descending stairs or dies or
quits while swallowed; put ball and chain in bones for the stairs case
if hero dies while a thrown or kicked object is in transit, put that object
on the map in case bones data gets saved
Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 end.c $NHDT-Date: 1557094801 2019/05/05 22:20:01 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.170 $ */
/* NetHack 3.6 end.c $NHDT-Date: 1558921075 2019/05/27 01:37:55 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.174 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -49,6 +49,7 @@ static void FDECL(done_hangup, (int));
STATIC_DCL void FDECL(disclose, (int, BOOLEAN_P));
STATIC_DCL void FDECL(get_valuables, (struct obj *));
STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *, int));
STATIC_DCL void NDECL(done_object_cleanup);
STATIC_DCL void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid));
STATIC_DCL void FDECL(really_done, (int)) NORETURN;
STATIC_DCL void FDECL(savelife, (int));
@@ -996,6 +997,55 @@ int what;
}
#endif
/* deal with some objects which may be in an abnormal state at end of game */
STATIC_OVL void
done_object_cleanup()
{
int ox, oy;
/* might have been killed while using a disposable item, so make sure
it's gone prior to inventory disclosure and creation of bones */
inven_inuse(TRUE);
/*
* Hero can die when throwing an object (by hitting an adjacent
* gas spore, for instance, or being hit by mis-returning Mjollnir),
* or while in transit (from falling down stairs). If that happens,
* some object(s) might be in limbo rather than on the map or in
* any inventory. Saving bones with an active light source in limbo
* would trigger an 'object not local' panic.
*
* We used to use dealloc_obj() on thrownobj and kickedobj but
* that keeps them out of bones and could leave uball in a confused
* state (gone but still attached). Place them on the map but
* bypass flooreffects(). That could lead to minor anomalies in
* bones, like undamaged paper at water or lava locations or piles
* not being knocked down holes, but it seems better to get this
* game over with than risk being tangled up in more and more details.
*/
ox = u.ux + u.dx, oy = u.uy + u.dy;
if (!isok(ox, oy) || !accessible(ox, oy))
ox = u.ux, oy = u.uy;
/* put thrown or kicked object on map (for bones); location might
be incorrect (perhaps killed by divine lightning when throwing at
a temple priest?) but this should be better than just vanishing
(fragile stuff should be taken care of before getting here) */
if (thrownobj && thrownobj->where == OBJ_FREE) {
place_object(thrownobj, ox, oy);
stackobj(thrownobj), thrownobj = 0;
}
if (kickedobj && kickedobj->where == OBJ_FREE) {
place_object(kickedobj, ox, oy);
stackobj(kickedobj), kickedobj = 0;
}
/* if Punished hero dies during level change or dies or quits while
swallowed, uball and uchain will be in limbo; put them on floor
so bones will have them and object list cleanup finds them */
if (uchain && uchain->where == OBJ_FREE) {
placebc();
}
return;
}
/* called twice; first to calculate total, then to list relevant items */
STATIC_OVL void
artifact_score(list, counting, endwin)
@@ -1167,15 +1217,10 @@ int how;
/* render vision subsystem inoperative */
iflags.vision_inited = 0;
/* might have been killed while using a disposable item, so make sure
it's gone prior to inventory disclosure and creation of bones data */
inven_inuse(TRUE);
/* maybe not on object lists; if an active light source, would cause
big trouble (`obj_is_local' panic) for savebones() -> savelev() */
if (thrownobj && thrownobj->where == OBJ_FREE)
dealloc_obj(thrownobj);
if (kickedobj && kickedobj->where == OBJ_FREE)
dealloc_obj(kickedobj);
/* maybe use up active invent item(s), place thrown/kicked missile,
deal with ball and chain possibly being temporarily off the map */
if (!program_state.panicking)
done_object_cleanup();
/* remember time of death here instead of having bones, rip, and
topten figure it out separately and possibly getting different
@@ -1302,10 +1347,8 @@ int how;
int mnum = u.umonnum;
if (!Upolyd) {
/* Base corpse on race when not poly'd since original
* u.umonnum is based on role, and all role monsters
* are human.
*/
/* Base corpse on race when not poly'd since original u.umonnum
is based on role, and all role monsters are human. */
mnum = (flags.female && urace.femalenum != NON_PM)
? urace.femalenum
: urace.malenum;