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 prevent #adjust from allowing anything to be moved into the special '-' slot
sometimes rings dropped into sinks can be found in the pipes sometimes rings dropped into sinks can be found in the pipes
doors in special levels were always generated in vertical orientation 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 Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository

View File

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