fix #H6338 - naming mimicked potion

Player tried to #name a potion on the floor and got prompted to call a
stream of fluid (sink feedback) instead of a potion.  A mimic posing
as an object is represented by a partially initialized object when
examining its map location.  #name for floor object uses the same data
as look_at.

obj->fromsink overloads obj->corpsenm which is set to NON_PM (-1) even
when creating a non-init'd object.  'fromsink' was only being forced to
0 when creating an init'd object (unlike leash which has its overload
of corpsenm set properly regardless of caller's request to init).  So
docall() treated a mimicked potion as a sink stream.

The fix is straightforward but has pointed out another bug which is
harder to fix.  Examining a floor object next to you sets that obj's
dknown flag as if you had seen it up close (a new feature in 3.6.0).
But a mimicked item is discarded as soon as it's been looked at, so
looking again from a non-adjacent spot will give different feedback
since the previously set dknown will be unset when replaced by a new
fake object.  So you can use '/' and ';' to recognize mimics without
provoking them into motion.  Best fix:  mimicking an object should use
a fully initialized one which is tracked via monst->mextra, but that
will break save file compatibility.  Possible hack:  change monst->
mappearance into a mask which uses N bits for object type (instead of
full 'int') and one of the other bits to track obj->dknown.  Examining
an adjacent object probably ought to set bknown for priests, so bknown
and blessed/uncursed/cursed would need to be tracked too.
This commit is contained in:
PatR
2017-10-28 01:18:25 -07:00
parent bf0223fd9f
commit 25c27cd4cf
3 changed files with 28 additions and 20 deletions

View File

@@ -464,6 +464,8 @@ improve #adjust command's handling of the '$' and '#' inventory slots
prevent #adjust from allowing anything to be moved into the special '-' slot
sometimes rings dropped into sinks can be found in the pipes
doors in special levels were always generated in vertical orientation
assigning a type name to a potion on the floor which is actually a mimic could
prompt "Call a stream of <potion-type> fluid:" (bogus 'fromsink')
Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository

View File

@@ -223,6 +223,7 @@ boolean init, artif;
return otmp;
}
/* mkobj(): select a type of item from a class, use mksobj() to create it */
struct obj *
mkobj(oclass, artif)
char oclass;
@@ -277,7 +278,7 @@ struct obj *box;
n = 0;
break;
}
/*else FALLTHRU*/
/*else FALLTHRU*/
case BAG_OF_HOLDING:
n = 1;
break;
@@ -712,6 +713,7 @@ static const char dknowns[] = { WAND_CLASS, RING_CLASS, POTION_CLASS,
SCROLL_CLASS, GEM_CLASS, SPBOOK_CLASS,
WEAPON_CLASS, TOOL_CLASS, 0 };
/* mksobj(): create a specific type of object */
struct obj *
mksobj(otyp, init, artif)
int otyp;
@@ -743,7 +745,7 @@ boolean artif;
otmp->cknown = 0;
otmp->corpsenm = NON_PM;
if (init)
if (init) {
switch (let) {
case WEAPON_CLASS:
otmp->quan = is_multigen(otmp) ? (long) rn1(6, 6) : 1L;
@@ -773,9 +775,8 @@ boolean artif;
&& (--tryct > 0));
if (tryct == 0) {
/* perhaps rndmonnum() only wants to make G_NOCORPSE
monsters on
this level; let's create an adventurer's corpse
instead, then */
monsters on this level; create an adventurer's
corpse instead, then */
otmp->corpsenm = PM_HUMAN;
}
/* timer set below */
@@ -889,14 +890,13 @@ boolean artif;
case BAG_OF_TRICKS:
otmp->spe = rnd(20);
break;
case FIGURINE: {
int tryct2 = 0;
case FIGURINE:
tryct = 0;
do
otmp->corpsenm = rndmonnum();
while (is_human(&mons[otmp->corpsenm]) && tryct2++ < 30);
while (is_human(&mons[otmp->corpsenm]) && tryct++ < 30);
blessorcurse(otmp, 4);
break;
}
case BELL_OF_OPENING:
otmp->spe = 3;
break;
@@ -918,15 +918,12 @@ boolean artif;
curse(otmp);
} else
blessorcurse(otmp, 10);
break;
case VENOM_CLASS:
case CHAIN_CLASS:
case BALL_CLASS:
break;
case POTION_CLASS:
otmp->fromsink = 0;
if (otmp->otyp == POT_OIL)
otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */
/* fall through */
case POTION_CLASS: /* note: potions get some additional init below */
case SCROLL_CLASS:
#ifdef MAIL
if (otmp->otyp != SCR_MAIL)
@@ -1012,27 +1009,36 @@ boolean artif;
objects[otmp->otyp].oc_class);
return (struct obj *) 0;
}
}
/* some things must get done (corpsenm, timers) even if init = 0 */
switch (otmp->otyp) {
switch ((otmp->oclass == POTION_CLASS && otmp->otyp != POT_OIL)
? POT_WATER
: otmp->otyp) {
case CORPSE:
if (otmp->corpsenm == NON_PM) {
otmp->corpsenm = undead_to_corpse(rndmonnum());
if (mvitals[otmp->corpsenm].mvflags & (G_NOCORPSE | G_GONE))
otmp->corpsenm = urole.malenum;
}
/*FALLTHRU*/
/*FALLTHRU*/
case STATUE:
case FIGURINE:
if (otmp->corpsenm == NON_PM)
otmp->corpsenm = rndmonnum();
/*FALLTHRU*/
/*FALLTHRU*/
case EGG:
/* case TIN: */
/* case TIN: */
set_corpsenm(otmp, otmp->corpsenm);
break;
case POT_OIL:
otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */
/*FALLTHRU*/
case POT_WATER: /* POTION_CLASS */
otmp->fromsink = 0; /* overloads corpsenm, which was set to NON_PM */
break;
case LEASH:
otmp->leashmon = 0;
otmp->leashmon = 0; /* overloads corpsenm, which was set to NON_PM */
break;
case SPE_NOVEL:
otmp->novelidx = -1; /* "none of the above"; will be changed */

View File

@@ -195,7 +195,7 @@ struct obj **obj_p;
glyph among floor and buried objects; when !Blind, any buried
object's glyph will have been replaced by whatever is present
on the surface as soon as we moved next to its spot */
&& otmp->where == OBJ_FLOOR /* not buried */
&& (fakeobj || otmp->where == OBJ_FLOOR) /* not buried */
/* terrain mode views what's already known, doesn't learn new stuff */
&& !iflags.terrainmode) /* so don't set dknown when in terrain mode */
otmp->dknown = 1; /* if a pile, clearly see the top item only */