fix issue #1046 - unseen monster reading scroll

Reported by copperwater "on behalf of Maud":  when hero hears an
unseen monster read a not-yet-discovered scroll of create monster
and doesn't see any new monster appear, the player is given a chance
to call the scroll something.  When hearing an unseen monster read
a not-yet-discovered scroll of teleportation, the label is revealed
but no such 'call it' opportunity is offered, so the player could
deduce which of the two scroll types it was for either case.

Provide an opportunity to supply a 'called' name for scrolls of
teleportation when heard being read, same as with create monster.
Surprisingly complicated to achieve.

Eliminated a couple of goto's in the process.

Fixes #1046
This commit is contained in:
PatR
2023-06-04 16:22:38 -07:00
parent 22d79e245d
commit 7e4be52a6c
2 changed files with 58 additions and 36 deletions

View File

@@ -1199,6 +1199,10 @@ applying a cream pie (always) or lump of royal jelly (sometimes) would use up
the object and then access its memory after that had been freed
keep track of hero's pending movement points across save and restore
give feedback if hero sees a monster become hidden under an object or water
hero might hear unseen monster read scroll of create monster or scroll of
teleportation; when it was create monster, player was given a chance
to call it something but not when it was teleportation, allowing the
player to deduce which type of scroll it actually was
Fixes to 3.7.0-x General Problems Exposed Via git Repository

View File

@@ -21,6 +21,7 @@ static void mreadmsg(struct monst *, struct obj *);
static void mquaffmsg(struct monst *, struct obj *);
static boolean m_use_healing(struct monst *);
static boolean m_sees_sleepy_soldier(struct monst *);
static void m_tele(struct monst *, boolean, boolean, int);
static boolean linedup_chk_corpse(coordxy, coordxy);
static void m_use_undead_turning(struct monst *, struct obj *);
static boolean hero_behind_chokepoint(struct monst *);
@@ -364,6 +365,29 @@ m_sees_sleepy_soldier(struct monst *mtmp)
return FALSE;
}
static void
m_tele(
struct monst *mtmp, /* monst that might be teleported */
boolean vismon, /* can see it */
boolean oseen, /* have seen the object that triggered this */
int how) /* type of that object */
{
if (tele_restrict(mtmp)) { /* mysterious force... */
if (vismon && how) /* mentions 'teleport' */
makeknown(how);
/* monster learns that teleportation isn't useful here */
if (noteleport_level(mtmp))
mon_learns_traps(mtmp, TELEP_TRAP);
} else if ((mon_has_amulet(mtmp) || On_W_tower_level(&u.uz)) && !rn2(3)) {
if (vismon)
pline("%s seems disoriented for a moment.", Monnam(mtmp));
} else {
if (oseen && how)
makeknown(how);
(void) rloc(mtmp, RLOC_MSG);
}
}
/* Select a defensive item/action for a monster. Returns TRUE iff one is
found. */
boolean
@@ -683,7 +707,7 @@ find_defensive(struct monst *mtmp, boolean tryescape)
int
use_defensive(struct monst *mtmp)
{
int i, fleetim, how = 0;
int i, fleetim;
struct obj *otmp = gm.m.defensive;
boolean vis, vismon, oseen;
const char *Mnam;
@@ -734,24 +758,7 @@ use_defensive(struct monst *mtmp)
return 2;
m_flee(mtmp);
mzapwand(mtmp, otmp, TRUE);
how = WAN_TELEPORTATION;
mon_tele:
if (tele_restrict(mtmp)) { /* mysterious force... */
if (vismon && how) /* mentions 'teleport' */
makeknown(how);
/* monster learns that teleportation isn't useful here */
if (noteleport_level(mtmp))
mon_learns_traps(mtmp, TELEP_TRAP);
return 2;
}
if ((mon_has_amulet(mtmp) || On_W_tower_level(&u.uz)) && !rn2(3)) {
if (vismon)
pline("%s seems disoriented for a moment.", Monnam(mtmp));
return 2;
}
if (oseen && how)
makeknown(how);
(void) rloc(mtmp, RLOC_MSG);
m_tele(mtmp, vismon, oseen, WAN_TELEPORTATION);
return 2;
case MUSE_WAN_TELEPORTATION:
gz.zap_oseen = oseen;
@@ -769,32 +776,42 @@ use_defensive(struct monst *mtmp)
if (mtmp->isshk || mtmp->isgd || mtmp->ispriest)
return 2;
m_flee(mtmp);
mreadmsg(mtmp, otmp);
m_useup(mtmp, otmp); /* otmp might be free'ed */
how = SCR_TELEPORTATION;
/* we want to be able to access otmp after the teleport but it
might get destroyed if still in mtmp's inventory (maybe mtmp
lands in lava or on a fire trap) so take it out in advance */
if (otmp->quan > 1L)
otmp = splitobj(otmp, 1L);
extract_from_minvent(mtmp, otmp, FALSE, FALSE);
/* 'last_msg' will be changed to PLNMSG_UNKNOWN if any messages
are issued by mreadmsg(), 'if (vismon) pline()', or m_tele() */
iflags.last_msg = PLNMSG_enum;
mreadmsg(mtmp, otmp); /* sets otmp->dknown if !Blind or !Deaf */
if (obj_is_cursed || mtmp->mconf) {
int nlev;
d_level flev;
nlev = random_teleport_level();
if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) {
if (vismon)
pline("%s seems very disoriented for a moment.",
Monnam(mtmp));
return 2;
}
nlev = random_teleport_level();
if (nlev == depth(&u.uz)) {
} else if (nlev == depth(&u.uz)) {
if (vismon)
pline("%s shudders for a moment.", Monnam(mtmp));
return 2;
} else {
get_level(&flev, nlev);
migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM,
(coord *) 0);
}
get_level(&flev, nlev);
migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM,
(coord *) 0);
if (oseen)
makeknown(SCR_TELEPORTATION);
} else
goto mon_tele;
} else {
m_tele(mtmp, vismon, oseen, SCR_TELEPORTATION);
}
/* m_tele() handles makeknown(); trycall() will be a no-op when
otmp->otyp is already discovered */
if (otmp->dknown && iflags.last_msg != PLNMSG_enum)
trycall(otmp);
/* already removed from mtmp->minvent so not 'm_useup(mtmp, otmp)' */
obfree(otmp, (struct obj *) 0);
return 2;
}
case MUSE_WAN_DIGGING: {
@@ -1047,8 +1064,9 @@ use_defensive(struct monst *mtmp)
if (mtmp->wormno)
worm_move(mtmp);
newsym(gt.trapx, gt.trapy);
goto mon_tele;
/* 0: 'no object' rather than STRANGE_OBJECT; FALSE: obj not seen */
m_tele(mtmp, vismon, FALSE, 0);
return 2;
case MUSE_POT_HEALING:
mquaffmsg(mtmp, otmp);
i = d(6 + 2 * bcsign(otmp), 4);