more fix for #H5056 - achievement tracking
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.
This commit is contained in:
36
src/bones.c
36
src/bones.c
@@ -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
|
||||
|
||||
19
src/invent.c
19
src/invent.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
35
src/sp_lev.c
35
src/sp_lev.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user