]> granicus.if.org Git - nethack/commitdiff
Fix dangling chain bug.
authorBart House <bart@barthouse.com>
Mon, 24 Jun 2019 04:39:22 +0000 (21:39 -0700)
committerBart House <bart@barthouse.com>
Mon, 24 Jun 2019 04:39:22 +0000 (21:39 -0700)
If a punished player picks up the iron ball, gets engulfed and
saves, then the saved game will have missed saving the dangling
chain since it was not on the floor or in the inventory.  Upon
restoring the saved game, the game will be in a bad state since
the ball will be worn but the chain will be missing.

include/hack.h
src/restore.c
src/save.c

index 0b8a780a4a592f8cd01b785526916aecf459ddfb..5ba3bb9a0227bd0a62c78089374d030078724fcf 100644 (file)
@@ -436,6 +436,7 @@ enum bodypart_types {
 #define POTION_OCCUPANT_CHANCE(n) (13 + 2 * (n))
 #define WAND_BACKFIRE_CHANCE 100
 #define BALL_IN_MON (u.uswallow && uball && uball->where == OBJ_FREE)
+#define CHAIN_IN_MON (u.uswallow && uchain && uchain->where == OBJ_FREE)
 #define NODIAG(monnum) ((monnum) == PM_GRID_BUG)
 
 /* Flags to control menus */
index 4f19ed87853befcd5dee6a5f5f85072bf029dc68..010ef75c80a1efa8dbe7af3e4fe3262acb37ba7b 100644 (file)
@@ -540,7 +540,8 @@ unsigned int *stuckid, *steedid;
     struct sysflag newgamesysflags;
 #endif
     struct context_info newgamecontext; /* all 0, but has some pointers */
-    struct obj *otmp, *tmp_bc;
+    struct obj *otmp;
+    struct obj *bc_obj;
     char timebuf[15];
     unsigned long uid;
     boolean defer_perm_invent;
@@ -643,17 +644,15 @@ unsigned int *stuckid, *steedid;
     restore_timers(fd, RANGE_GLOBAL, FALSE, 0L);
     restore_light_sources(fd);
     invent = restobjchn(fd, FALSE, FALSE);
-    /* tmp_bc only gets set here if the ball & chain were orphaned
-       because you were swallowed; otherwise they will be on the floor
-       or in your inventory */
-    tmp_bc = restobjchn(fd, FALSE, FALSE);
-    if (tmp_bc) {
-        for (otmp = tmp_bc; otmp; otmp = otmp->nobj) {
-            if (otmp->owornmask)
-                setworn(otmp, otmp->owornmask);
-        }
-        if (!uball || !uchain)
-            impossible("restgamestate: lost ball & chain");
+
+    /* restore dangling (not on floor or in inventory) ball and/or chain */
+    bc_obj = restobjchn(fd, FALSE, FALSE);
+    while(bc_obj) {
+        struct obj * nobj = bc_obj->nobj;
+        if (bc_obj->owornmask)
+            setworn(bc_obj, bc_obj->owornmask);
+        bc_obj->nobj = (struct obj *)0;
+        bc_obj = nobj;
     }
 
     migrating_objs = restobjchn(fd, FALSE, FALSE);
@@ -671,6 +670,10 @@ unsigned int *stuckid, *steedid;
     for (otmp = invent; otmp; otmp = otmp->nobj)
         if (otmp->owornmask)
             setworn(otmp, otmp->owornmask);
+
+    if ((uball && !uchain) || (uchain && !uball))
+        impossible("restgamestate: lost ball & chain");
+
     /* reset weapon so that player will get a reminder about "bashing"
        during next fight when bare-handed or wielding an unconventional
        item; for pick-axe, we aren't able to distinguish between having
index 8bca5964a596e224c870d72af1e895307c39e702..e0af4b8d2f61a9561356e67b5d662323a8857c89 100644 (file)
@@ -280,6 +280,7 @@ savegamestate(fd, mode)
 register int fd, mode;
 {
     unsigned long uid;
+    struct obj * bc_objs = (struct obj *)0;
 
 #ifdef MFLOPPY
     count_only = (mode & COUNT_SAVE);
@@ -307,14 +308,18 @@ register int fd, mode;
     save_light_sources(fd, mode, RANGE_GLOBAL);
 
     saveobjchn(fd, invent, mode);
+
+    /* save ball and chain if they are currently dangling free (i.e. not on
+       floor or in inventory) */
+    if (CHAIN_IN_MON) {
+        uchain->nobj = bc_objs;
+        bc_objs = uchain;
+    }
     if (BALL_IN_MON) {
-        /* prevent loss of ball & chain when swallowed */
-        uball->nobj = uchain;
-        uchain->nobj = (struct obj *) 0;
-        saveobjchn(fd, uball, mode);
-    } else {
-        saveobjchn(fd, (struct obj *) 0, mode);
+        uball->nobj = bc_objs;
+        bc_objs = uball;
     }
+    saveobjchn(fd, bc_objs, mode);
 
     saveobjchn(fd, migrating_objs, mode);
     savemonchn(fd, migrating_mons, mode);