fix message sequencing when worn item is stolen

When a nymph or monkey successfully steals a worn item from hero,
first the item is unworn, side-effects of that take place (most
noticeably descending when losing levitation or flight) including
feedback about such side-effects, finally "<Mon> steals <item>" and
transfer from invent to thief's minvent.  If the side-effects were
fatal (such as drowning or burning up in lava), the player wouldn't
see any explanation for why that happened.

When a thief removes a worn item, give a message to that effect:
"<Mon> takes off your <item>."  That will usually be immediately
followed by "She stole <item>."  When the thief isn't a nymph or if
any messages were delivered after the "takes off" one, the monster
will be described by name:  "<Mon> stole <item>."
This commit is contained in:
PatR
2024-02-05 01:14:36 -08:00
parent 6b189fb0e1
commit 32e5c1df5c
2 changed files with 81 additions and 12 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 flag.h $NHDT-Date: 1698264779 2023/10/25 20:12:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.224 $ */
/* NetHack 3.7 flag.h $NHDT-Date: 1707122958 2024/02/05 08:49:18 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.236 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2006. */
/* NetHack may be freely redistributed. See license for details. */
@@ -475,6 +475,7 @@ enum plnmsg_types {
PLNMSG_BACK_ON_GROUND, /* leaving water */
PLNMSG_GROWL, /* growl() gave some message */
PLNMSG_HIDE_UNDER, /* hero saw a monster hide under something */
PLNMSG_MON_TAKES_OFF_ITEM, /* thief (nymph, monkey) taking worn item */
PLNMSG_enum /* 'none of the above' */
};

View File

@@ -1,16 +1,17 @@
/* NetHack 3.7 steal.c $NHDT-Date: 1702529046 2023/12/14 04:44:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.114 $ */
/* NetHack 3.7 steal.c $NHDT-Date: 1707122967 2024/02/05 08:49:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
static int stealarm(void);
static int unstolenarm(void);
static const char *equipname(struct obj *);
static int unstolenarm(void);
static int stealarm(void);
static void worn_item_removal(struct monst *, struct obj *);
static const char *
equipname(register struct obj* otmp)
equipname(struct obj *otmp)
{
return ((otmp == uarmu) ? shirt_simple_name(otmp)
: (otmp == uarmf) ? boots_simple_name(otmp)
@@ -172,6 +173,7 @@ unstolenarm(void)
return 0;
}
/* finish stealing an item of armor which takes multiple turns to take off */
static int
stealarm(void)
{
@@ -185,10 +187,17 @@ stealarm(void)
if (otmp->o_id == gs.stealoid) {
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (mtmp->m_id == gs.stealmid) {
if (DEADMONSTER(mtmp))
if (DEADMONSTER(mtmp)) {
impossible("stealarm(): dead monster stealing");
if (!dmgtype(mtmp->data, AD_SITM)) /* polymorphed */
goto botm;
goto botm; /* (could just use 'break' here) */
}
/* maybe the thief polymorphed into something without a
steal attack, or perhaps while stealing hero's suit
the thief took away other items causing hero to fall
into water or lava and then teleport to safety */
if (!dmgtype(mtmp->data, AD_SITM)
|| distu(mtmp->mx, mtmp->my) > 2)
goto botm; /* (could just use 'break' here) */
if (otmp->unpaid)
subfrombill(otmp, shop_keeper(*u.ushops));
freeinv(otmp);
@@ -303,10 +312,45 @@ remove_worn_item(
obj->in_use = oldinuse;
}
/* during theft of a worn item: remove_worn_item(), prefaced by a message */
static void
worn_item_removal(
struct monst *mon,
struct obj *obj)
{
char objbuf[BUFSZ], article[20], *p;
int strip_art;
Strcpy(objbuf, doname(obj));
/* massage the object description */
strip_art = !strncmp(objbuf, "the ", 4) ? 4
: !strncmp(objbuf, "an ", 3) ? 3
: !strncmp(objbuf, "a ", 2) ? 2
: 0;
if (strip_art) { /* convert "a/an/the <object>" to "your object" */
copynchars(article, objbuf, strip_art);
(void) strsubst(objbuf, article, "your ");
}
/* these ought to be guarded against matching user-supplied name */
(void) strsubst(objbuf, " (being worn)", "");
(void) strsubst(objbuf, " (alternate weapon; not wielded)", "");
/* convert "ring (on left hand)" to "ring (from left hand)" */
if ((p = strstri(objbuf, " (on "))
&& (!strncmp(p + 5, "left ", 5) || !strncmp(p + 5, "right ", 6)))
(void) strsubst(p + 2, "on", "from");
pline("%s takes off %s.", Monnam(mon), objbuf);
iflags.last_msg = PLNMSG_MON_TAKES_OFF_ITEM;
/* removal might trigger more messages (due to loss of Lev|Fly;
descending happens before the theft in progress finishes) */
remove_worn_item(obj, TRUE);
}
/* Returns 1 when something was stolen (or at least, when N should flee now),
* returns -1 if the monster died in the attempt.
* Avoid stealing the object 'stealoid'.
* Nymphs and monkeys won't steal coins.
* Nymphs and monkeys won't steal coins (so that their "steal item" attack
* doesn't become a superset of leprechaun's "steal gold" attack).
*/
int
steal(struct monst* mtmp, char* objnambuf)
@@ -362,6 +406,20 @@ steal(struct monst* mtmp, char* objnambuf)
if ((!uarm || otmp != uarmc) && otmp != uskin
&& otmp->oclass != COIN_CLASS)
tmp += (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) ? 5 : 1;
/*
* TODO?
* When inventory is empty and hero is Punished but not carrying
* uball (obviously, or invent wouldn't be empty), behave as if
* carrying uchain (at least when the thief is a nymph rather
* than a monkey)? Probably too complicated to bother with.
*
* (Simplest thing would be to move uchain to invent but that
* would be fraught with peril because every place that cares
* about uchain assumes that it is on the floor. Next simplest
* would be to move uball into invent and use that for otmp, but
* what about undesirable encumbrance feedback or being tethered
* to buried ball?)
*/
if (!tmp)
goto nothing_to_steal;
tmp = rn2(tmp);
@@ -459,7 +517,7 @@ steal(struct monst* mtmp, char* objnambuf)
case AMULET_CLASS:
case RING_CLASS:
case FOOD_CLASS: /* meat ring */
remove_worn_item(otmp, TRUE);
worn_item_removal(mtmp, otmp);
break;
case ARMOR_CLASS:
armordelay = objects[otmp->otyp].oc_delay;
@@ -471,7 +529,7 @@ steal(struct monst* mtmp, char* objnambuf)
hero can't be charmed into taking off his own armor */
if (armordelay >= 1 && !olddelay && rn2(10))
goto cant_take;
remove_worn_item(otmp, TRUE);
worn_item_removal(mtmp, otmp);
break;
} else {
int curssv = otmp->cursed;
@@ -516,7 +574,11 @@ steal(struct monst* mtmp, char* objnambuf)
otmp->oclass);
}
} else if (otmp->owornmask) { /* weapon or ball&chain */
remove_worn_item(otmp, TRUE);
struct obj *item = otmp;
if (otmp == uball && uchain != NULL)
item = uchain; /* yields a more accurate 'takes off' message */
worn_item_removal(mtmp, item);
}
/* do this before removing it from inventory */
@@ -531,6 +593,12 @@ steal(struct monst* mtmp, char* objnambuf)
subfrombill(otmp, shop_keeper(*u.ushops));
freeinv(otmp);
/* if attached ball was taken, uball and uchain are now Null */
/* if we just gave a message about removing a worn item and there have
been no intervening messages, shorten '<mon> stole <item>' message */
if (iflags.last_msg == PLNMSG_MON_TAKES_OFF_ITEM
&& mtmp->data->mlet == S_NYMPH)
++named;
urgent_pline("%s%s stole %s.", named ? "She" : Monnambuf,
(was_punished && !Punished) ? " removed your chain and" : "",
doname(otmp));