]> granicus.if.org Git - nethack/commitdiff
more fix for #H5056 - achievement tracking
authorPatR <rankin@nethack.org>
Tue, 24 Oct 2017 07:37:21 +0000 (00:37 -0700)
committerPatR <rankin@nethack.org>
Tue, 24 Oct 2017 07:37:21 +0000 (00:37 -0700)
The followup message about the fix for #5056 was trapped by the spam
filter so didn't reach us for a while.

xlogfile has an extra field to track various achievements made during
the game it logs, two of which are fully exploring the gnomish mines
and fully exploring sokoban.  Those are accomplished by finding the
special 'prize' item on the final level of their branch:  luckstone
for mines and bag of holding or amulet of reflecition for sokoban.
3.6.0 had a bug where any item of the target type found anywhere in
the dungeon resulted in achieving the relevant goal.  A post-3.6.1 fix
for that required that the item be found on the end level of the branch
and attempted to require that it an item explicitly placed there by the
special level loader, but the latter aspect had a bug which meant that
random items of the appropriate type placed on final level would count
as the prize.  Chance of extra luckstones on mines' end is fairly high,
so potential for false completion of the achievement was also high.

The second complaint was that since the achievement was only recorded
if the special prize item was found on final level, then if a monster
took it to another level then the achievement became impossible.  (Not
true, the player could take it back, drop it, and pick it up again, but
that is admittedly a pretty silly hoop to jump through.)  On the other
hand, if a monster removed the item before the hero found it, then a
case could be made that the hero hadn't really fully explored the
level.  However, this fix records the achievement no matter where the
hero picks up the item.  The final level must be entered--otherwise no
monster could possibly acquire and transport the item--but it isn't
guaranteed to have been fully explored.  Big deal....

The prize could also be acquired in bones data.  Before the second
portion of this fix, that wouldn't have mattered.  But now it does, so
clear the prize indicator when saving bones unless it happens to be the
same level where that item is created (impossible for sokoban, where no
bones are left; not sure offhand about mines' end).  The former prize
stone or bag or amulet becomes an ordinary one of its type.

This can all be done in a much cleaner fashion once we give up on the
current save file compatability.  Putting obj->o_id values into new
context.mines_prize and context.soko_prize, plus a hack to mkobj() to
not reuse those two values if the o_id counter ever wraps back to 0,
would cover most of the details.  Adding an achievement tracking flag
to lev_comp's object handling for use by the special level loader
would cover most of the rest.

doc/fixes36.1
include/flag.h
include/obj.h
src/bones.c
src/invent.c
src/options.c
src/sp_lev.c

index ff470d323e4eacb7ea407bc3c6a83a927619e509..7be6ef2e3b77c3f60703d3cb1919a70759095d74 100644 (file)
@@ -521,6 +521,7 @@ hero poly'd into vampire could drain monster down to 0 HP without killing it,
 fix mention_walls reporting secret doors as solid stone
 jumping over water unintentionally moved hero through that water, causing
        drowning if not able to water walk or fly
+try again to fix achievement recording bug with mines and sokoban prizes
 
 
 Platform- and/or Interface-Specific Fixes
index 5b205e5b7035e50a376526d21f7f2b86d33fcb5a..3bccbaa18cfca518b1299171e2a1cf013cdfaead 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 flag.h  $NHDT-Date: 1505214875 2017/09/12 11:14:35 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.123 $ */
+/* NetHack 3.6 flag.h  $NHDT-Date: 1508827590 2017/10/24 06:46:30 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -349,25 +349,24 @@ struct instance_flags {
     int wc_fontsiz_menu;    /* font size for the menu window       */
     int wc_fontsiz_text;    /* font size for text windows          */
     int wc_scroll_amount;   /* scroll this amount at scroll_margin */
-    int wc_scroll_margin;   /* scroll map when this far from
-                                    the edge */
+    int wc_scroll_margin;   /* scroll map when this far from the edge */
     int wc_map_mode;        /* specify map viewing options, mostly
-                                    for backward compatibility */
+                             * for backward compatibility */
     int wc_player_selection;    /* method of choosing character */
     boolean wc_splash_screen;   /* display an opening splash screen or not */
     boolean wc_popup_dialog;    /* put queries in pop up dialogs instead of
-                                        in the message window */
+                                 * in the message window */
     boolean wc_eight_bit_input; /* allow eight bit input               */
     boolean wc_mouse_support;   /* allow mouse support */
     boolean wc2_fullscreen;     /* run fullscreen */
     boolean wc2_softkeyboard;   /* use software keyboard */
     boolean wc2_wraptext;       /* wrap text */
     boolean wc2_selectsaved;    /* display a menu of user's saved games */
-    boolean wc2_darkgray; /* try to use dark-gray color for black glyphs */
-    boolean wc2_hitpointbar;    /* show graphical bar representing hit points */
-    boolean cmdassist;    /* provide detailed assistance for some commands */
-    boolean clicklook;    /* allow right-clicking for look */
-    boolean obsolete; /* obsolete options can point at this, it isn't used */
+    boolean wc2_darkgray;    /* try to use dark-gray color for black glyphs */
+    boolean wc2_hitpointbar;  /* show graphical bar representing hit points */
+    boolean cmdassist;     /* provide detailed assistance for some commands */
+    boolean clicklook;          /* allow right-clicking for look */
+    boolean obsolete;  /* obsolete options can point at this, it isn't used */
     struct autopickup_exception *autopickup_exceptions[2];
 #define AP_LEAVE 0
 #define AP_GRAB 1
@@ -381,6 +380,11 @@ struct instance_flags {
     Bitfield(save_uswallow, 1);
     Bitfield(save_uinwater, 1);
     Bitfield(save_uburied, 1);
+    /* item types used to acomplish "special achievements"; find the target
+       object and you'll be flagged as having achieved something... */
+    short mines_prize_type;     /* luckstone */
+    short soko_prize_type1;     /* bag of holding or    */
+    short soko_prize_type2;     /* amulet of reflection */
 };
 
 /*
index 75b35779d355c5597eb9496f0c543ee5d8bc5149..f87caaa02434d3033050347b789f3f02e851793a 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 obj.h   $NHDT-Date: 1456618994 2016/02/28 00:23:14 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.53 $ */
+/* NetHack 3.6 obj.h   $NHDT-Date: 1508827590 2017/10/24 06:46:30 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -343,13 +343,19 @@ struct obj {
          && !undiscovered_artifact(ART_EYES_OF_THE_OVERWORLD)))
 #define pair_of(o) ((o)->otyp == LENSES || is_gloves(o) || is_boots(o))
 
+/* 'PRIZE' values override obj->corpsenm so prizes mustn't be object types
+   which use that field for monster type (or other overloaded purpose) */
+#define MINES_PRIZE 1
+#define SOKO_PRIZE1 2
+#define SOKO_PRIZE2 3
 #define is_mines_prize(o) \
-    ((o)->otyp == LUCKSTONE && Is_mineend_level(&u.uz))
-#define is_soko_prize(o)                \
-    (((o)->otyp == AMULET_OF_REFLECTION \
-      || (o)->otyp == BAG_OF_HOLDING)   \
-     && Is_sokoend_level(&u.uz))
-
+    ((o)->otyp == iflags.mines_prize_type                \
+     && (o)->record_achieve_special == MINES_PRIZE)
+#define is_soko_prize(o) \
+    (((o)->otyp == iflags.soko_prize_type1               \
+      && (o)->record_achieve_special == SOKO_PRIZE1)     \
+     || ((o)->otyp == iflags.soko_prize_type2            \
+         && (o)->record_achieve_special == SOKO_PRIZE2))
 
 /* Flags for get_obj_location(). */
 #define CONTAINED_TOO 0x1
index 4d3f6713826b48f634bee5600f8c62520bac8b5c..66efd480b2f74a18b0472e710f41eae4c81ea1a8 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 bones.c $NHDT-Date: 1503309019 2017/08/21 09:50:19 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.70 $ */
+/* NetHack 3.6 bones.c $NHDT-Date: 1508827591 2017/10/24 06:46:31 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.71 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -124,7 +124,7 @@ boolean restore;
                     otmp->spe = 1;
 #endif
             } else if (otmp->otyp == EGG) {
-                otmp->spe = 0;
+                otmp->spe = 0; /* not "laid by you" in next game */
             } else if (otmp->otyp == TIN) {
                 /* make tins of unique monster's meat be empty */
                 if (otmp->corpsenm >= LOW_PM
@@ -133,24 +133,30 @@ boolean restore;
             } else if (otmp->otyp == CORPSE || otmp->otyp == STATUE) {
                 int mnum = otmp->corpsenm;
 
-                /* Discard incarnation details of unique
-                   monsters (by passing null instead of otmp
-                   for object), shopkeepers (by passing false
-                   for revival flag), temple priests, and
-                   vault guards in order to prevent corpse
-                   revival or statue reanimation. */
+                /* Discard incarnation details of unique monsters
+                   (by passing null instead of otmp for object),
+                   shopkeepers (by passing false for revival flag),
+                   temple priests, and vault guards in order to
+                   prevent corpse revival or statue reanimation. */
                 if (has_omonst(otmp)
                     && cant_revive(&mnum, FALSE, (struct obj *) 0)) {
                     free_omonst(otmp);
-                    /* mnum is now either human_zombie or
-                       doppelganger; for corpses of uniques,
-                       we need to force the transformation
-                       now rather than wait until a revival
-                       attempt, otherwise eating this corpse
+                    /* mnum is now either human_zombie or doppelganger;
+                       for corpses of uniques, we need to force the
+                       transformation now rather than wait until a
+                       revival attempt, otherwise eating this corpse
                        would behave as if it remains unique */
                     if (mnum == PM_DOPPELGANGER && otmp->otyp == CORPSE)
                         set_corpsenm(otmp, mnum);
                 }
+            } else if ((otmp->otyp == iflags.mines_prize_type
+                        && !Is_mineend_level(&u.uz))
+                       || ((otmp->otyp == iflags.soko_prize_type1
+                            || otmp->otyp == iflags.soko_prize_type2)
+                           && !Is_sokoend_level(&u.uz))) {
+                /* "special prize" in this game becomes ordinary object
+                   if loaded into another game */
+                otmp->record_achieve_special = NON_PM;
             } else if (otmp->otyp == AMULET_OF_YENDOR) {
                 /* no longer the real Amulet */
                 otmp->otyp = FAKE_AMULET_OF_YENDOR;
@@ -183,8 +189,8 @@ sanitize_name(namebuf)
 char *namebuf;
 {
     int c;
-    boolean strip_8th_bit =
-        !strcmp(windowprocs.name, "tty") && !iflags.wc_eight_bit_input;
+    boolean strip_8th_bit = (!strcmp(windowprocs.name, "tty")
+                             && !iflags.wc_eight_bit_input);
 
     /* it's tempting to skip this for single-user platforms, since
        only the current player could have left these bones--except
index 0c61b3deea3acffc18b91b39b2b2e9d6ade0a12c..429b5a9d56acbd29a92a3021a43a24105690d1ae 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 invent.c        $NHDT-Date: 1498078873 2017/06/21 21:01:13 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.214 $ */
+/* NetHack 3.6 invent.c        $NHDT-Date: 1508827592 2017/10/24 06:46:32 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.220 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -504,12 +504,21 @@ struct obj *obj;
         }
         set_artifact_intrinsic(obj, 1, W_ART);
     }
-    if (is_mines_prize(obj) && obj->record_achieve_special) {
+
+    /* "special achievements" aren't discoverable during play, they
+       end up being recorded in XLOGFILE at end of game, nowhere else;
+       record_achieve_special overloads corpsenm which is ordinarily
+       initialized to NON_PM (-1) rather than to 0; any special prize
+       must never be a corpse, egg, tin, figurine, or statue because
+       their use of obj->corpsenm for monster type would conflict,
+       nor be a leash (corpsenm overloaded for m_id of leashed
+       monster) or a novel (corpsenm overloaded for novel index) */
+    if (is_mines_prize(obj)) {
         u.uachieve.mines_luckstone = 1;
-        obj->record_achieve_special = 0;
-    } else if (is_soko_prize(obj) && obj->record_achieve_special) {
+        obj->record_achieve_special = NON_PM;
+    } else if (is_soko_prize(obj)) {
         u.uachieve.finish_sokoban = 1;
-        obj->record_achieve_special = 0;
+        obj->record_achieve_special = NON_PM;
     }
 }
 
index b281ba6954b729c3acb5ce60920ee1c912c2cc7b..d378d22007338d19df8a2dffa87b2f319715fbb3 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 options.c       $NHDT-Date: 1507846854 2017/10/12 22:20:54 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.315 $ */
+/* NetHack 3.6 options.c       $NHDT-Date: 1508827592 2017/10/24 06:46:32 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.316 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -724,6 +724,12 @@ initoptions_init()
 
     iflags.travelcc.x = iflags.travelcc.y = -1;
 
+    /* for "special achievement" tracking (see obj.h,
+       create_object(sp_lev.c), addinv_core1(invent.c) */
+    iflags.mines_prize_type = LUCKSTONE;
+    iflags.soko_prize_type1 = BAG_OF_HOLDING;
+    iflags.soko_prize_type2 = AMULET_OF_REFLECTION;
+
     /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
     (void) memcpy((genericptr_t) flags.inv_order,
                   (genericptr_t) def_inv_order, sizeof flags.inv_order);
index 448907f633c03846b6badb271e291bab593ffb33..78dec8ebe24dfb8bdab70d9560470a06b5cb702f 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 sp_lev.c        $NHDT-Date: 1449269920 2015/12/04 22:58:40 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.77 $ */
+/* NetHack 3.6 sp_lev.c        $NHDT-Date: 1508827593 2017/10/24 06:46:33 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.89 $ */
 /*      Copyright (c) 1989 by Jean-Christophe Collet */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -1881,6 +1881,7 @@ struct mkroom *croom;
             }
         } else {
             struct obj *cobj = container_obj[container_idx - 1];
+
             remove_object(otmp);
             if (cobj) {
                 (void) add_to_container(cobj, otmp);
@@ -1941,12 +1942,32 @@ struct mkroom *croom;
         }
     }
 
-    /* Nasty hack here: try to determine if this is the Mines or Sokoban
-     * "prize" and then set record_achieve_special (maps to corpsenm)
-     * for the object.  That field will later be checked to find out if
-     * the player obtained the prize. */
-    if (is_mines_prize(otmp) || is_soko_prize(otmp)) {
-        otmp->record_achieve_special = 1;
+    if (o->id != -1) {
+        static const char prize_warning[] = "multiple prizes on %s level";
+        static int mcount = 0, scount = 0;
+
+        /* if this is a specific item of the right type and it is being
+           created on the right level, flag it as the designated item
+           used to detect a special achievement (to whit, reaching and
+           exploring the target level, although the exploration part
+           might be short-circuited if a monster brings object to hero) */
+        if (Is_mineend_level(&u.uz)) {
+            if (otmp->otyp == iflags.mines_prize_type) {
+                otmp->record_achieve_special = MINES_PRIZE;
+                if (++mcount > 1)
+                    impossible(prize_warning, "mines end");
+            }
+        } else if (Is_sokoend_level(&u.uz)) {
+            if (otmp->otyp == iflags.soko_prize_type1) {
+                otmp->record_achieve_special = SOKO_PRIZE1;
+                if (++scount > 1)
+                    impossible(prize_warning, "sokoban end");
+            } else if (otmp->otyp == iflags.soko_prize_type2) {
+                otmp->record_achieve_special = SOKO_PRIZE2;
+                if (++scount > 1)
+                    impossible(prize_warning, "sokoban end");
+            }
+        }
     }
 
     stackobj(otmp);