fix the "big abuse" reported a few days ago

It was possible to get a shopkeeper to carry the Amulet from the
bottom of the dungeon up to the location of his shop, thereby bypassing
the usual labor of lugging it up yourself.  [Drop the Amulet somewhere;
rob a shop so that the Kops are summoned and the shk comes after you;
when shk is next you, level teleport to the Amulet (probably two hops,
one to the Valley and another deeper into Gehennom); walk to the vicinity
of the Amulet; shk will eventually pick it up (shopkeepers like to pick
up magic items); now, pay him for the stolen goods--he'll be pacified
and migrate back to his shop, taking his inventory with him; lastly,
return to his shop and relieve him of his burder.]  This patch makes
shopkeepers drop the Amulet or invocation tools if/when they set set to
migrate to their normal location.

     Also fix another long standing risk that a monster that is sent
away (nurse when healing, Kops when you pacify a shopkeeper) might be
carrying the Amulet or one of the invocation tools and make the game
unwinnable.  I doubt that that's ever actually happened but I think it'd
be possible if a monster that likes magic items ever got polymorphed
into a Kop.  Such dismissed monsters will now drop the same stuff as
the shk above prior to leaving the game.
This commit is contained in:
nethack.rankin
2003-12-06 14:08:51 +00:00
parent 3b2b16ea9d
commit 2e28abcd5a
5 changed files with 73 additions and 25 deletions

View File

@@ -102,6 +102,8 @@ attempting to drop a subset of a stack of multiple cursed loadstones could
"miss" message was missing for thrown or kicked gold not caught by a monster
prevent recursive impossible() and panic() calls from leading to a stack overflow
tainted meat didn't invoke cannibalism
shopkeepers can't act as porters for the Amulet
dismissed monsters can't remove special items from play
Platform- and/or Interface-Specific Fixes

View File

@@ -1956,6 +1956,7 @@ E void FDECL(remove_worn_item, (struct obj *,BOOLEAN_P));
E int FDECL(steal, (struct monst *, char *));
E int FDECL(mpickobj, (struct monst *,struct obj *));
E void FDECL(stealamulet, (struct monst *));
E void FDECL(mdrop_special_objs, (struct monst *));
E void FDECL(relobj, (struct monst *,int,BOOLEAN_P));
#ifdef GOLDOBJ
E struct obj *FDECL(findgold, (struct obj *));

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)mon.c 3.4 2003/11/26 */
/* SCCS Id: @(#)mon.c 3.4 2003/12/04 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1583,13 +1583,18 @@ void
mongone(mdef)
register struct monst *mdef;
{
mdef->mhp = 0; /* can skip some inventory bookkeeping */
#ifdef STEED
/* Player is thrown from his steed when it disappears */
if (mdef == u.usteed)
dismount_steed(DISMOUNT_GENERIC);
#endif
discard_minvent(mdef); /* release monster's inventory */
/* drop special items like the Amulet so that a dismissed Kop or nurse
can't remove them from the game */
mdrop_special_objs(mdef);
/* release rest of monster's inventory--it is removed from game */
discard_minvent(mdef);
#ifndef GOLDOBJ
mdef->mgold = 0L;
#endif

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)shk.c 3.4 2003/08/18 */
/* SCCS Id: @(#)shk.c 3.4 2003/12/04 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -975,6 +975,9 @@ register boolean silentkops;
} else {
/* if sensed, does disappear regardless whether seen */
if (sensemon(shkp)) vanished = TRUE;
/* can't act as porter for the Amulet, even if shk
happens to be going farther down rather than up */
mdrop_special_objs(shkp);
/* arrive near shop's door */
migrate_to_level(shkp, ledger_no(&eshkp->shoplevel),
MIGR_APPROX_XY, &eshkp->shd);

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)steal.c 3.4 2003/11/14 */
/* SCCS Id: @(#)steal.c 3.4 2003/12/04 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -7,6 +7,7 @@
STATIC_PTR int NDECL(stealarm);
STATIC_DCL const char *FDECL(equipname, (struct obj *));
STATIC_DCL void FDECL(mdrop_obj, (struct monst *,struct obj *,BOOLEAN_P));
STATIC_OVL const char *
equipname(otmp)
@@ -508,6 +509,59 @@ struct monst *mtmp;
}
}
/* drop one object taken from a (possibly dead) monster's inventory */
STATIC_OVL void
mdrop_obj(mon, obj, verbosely)
struct monst *mon;
struct obj *obj;
boolean verbosely;
{
int omx = mon->mx, omy = mon->my;
if (obj->owornmask) {
/* perform worn item handling if the monster is still alive */
if (mon->mhp > 0) {
mon->misc_worn_check &= ~obj->owornmask;
update_mon_intrinsics(mon, obj, FALSE, TRUE);
/* obj_no_longer_held(obj); -- done by place_object */
if (obj->owornmask & W_WEP) setmnotwielded(mon, obj);
#ifdef STEED
/* don't charge for an owned saddle on dead steed */
} else if (mon->mtame && (obj->owornmask & W_SADDLE) &&
!obj->unpaid && costly_spot(omx, omy)) {
obj->no_charge = 1;
#endif
}
obj->owornmask = 0L;
}
if (verbosely && cansee(omx, omy))
pline("%s drops %s.", Monnam(mon), distant_name(obj, doname));
if (!flooreffects(obj, omx, omy, "fall")) {
place_object(obj, omx, omy);
stackobj(obj);
}
}
/* some monsters bypass the normal rules for moving between levels or
even leaving the game entirely; when that happens, prevent them from
taking the Amulet or invocation tools with them */
void
mdrop_special_objs(mon)
struct monst *mon;
{
struct obj *obj, *otmp;
for (obj = mon->minvent; obj; obj = otmp) {
otmp = obj->nobj;
/* the Amulet, invocation tools, and Rider corpses resist even when
artifacts and ordinary objects are given 0% resistance chance */
if (obj_resists(obj, 0, 0)) {
obj_extract_self(obj);
mdrop_obj(mon, obj, FALSE);
}
}
}
/* release the objects the creature is carrying */
void
relobj(mtmp,show,is_pet)
@@ -525,11 +579,12 @@ boolean is_pet; /* If true, pet should keep wielded/worn items */
item1 = item2 = TRUE;
if (!tunnels(mtmp->data) || !needspick(mtmp->data))
item1 = TRUE;
while ((otmp = mtmp->minvent) != 0) {
obj_extract_self(otmp);
/* special case: pick-axe and unicorn horn are non-worn */
/* items that we also want pets to keep 1 of */
/* (It is a coincidence that these can also be wielded. */
/* (It is a coincidence that these can also be wielded.) */
if (otmp->owornmask || otmp == wep ||
((!item1 && otmp->otyp == PICK_AXE) ||
(!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) {
@@ -542,28 +597,10 @@ boolean is_pet; /* If true, pet should keep wielded/worn items */
keepobj = otmp;
continue;
}
mtmp->misc_worn_check &= ~(otmp->owornmask);
#ifdef STEED
/* don't charge for an owned saddle on dead pet */
if (mtmp->mtame && mtmp->mhp == 0 &&
(otmp->owornmask & W_SADDLE) && !otmp->unpaid &&
costly_spot(mtmp->mx, mtmp->my))
otmp->no_charge = 1;
#endif
if (otmp->owornmask)
update_mon_intrinsics(mtmp, otmp, FALSE, TRUE);
/* obj_no_longer_held(otmp); -- done by place_object */
if (otmp->owornmask & W_WEP)
setmnotwielded(mtmp, otmp);
otmp->owornmask = 0L;
}
if (is_pet && cansee(omx, omy) && flags.verbose)
pline("%s drops %s.", Monnam(mtmp),
distant_name(otmp, doname));
if (flooreffects(otmp, omx, omy, "fall")) continue;
place_object(otmp, omx, omy);
stackobj(otmp);
mdrop_obj(mtmp, otmp, is_pet && flags.verbose);
}
/* put kept objects back */
while ((otmp = keepobj) != (struct obj *)0) {
keepobj = otmp->nobj;