From: nethack.rankin Date: Sat, 6 Dec 2003 14:08:51 +0000 (+0000) Subject: fix the "big abuse" reported a few days ago X-Git-Tag: MOVE2GIT~1529 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2e28abcd5aca6f13bc1d3b6e9ec956d824d8ddfd;p=nethack 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. --- diff --git a/doc/fixes34.3 b/doc/fixes34.3 index 58a78771c..9f3487e93 100644 --- a/doc/fixes34.3 +++ b/doc/fixes34.3 @@ -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 diff --git a/include/extern.h b/include/extern.h index 14fc2d176..aaa9f52e6 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/src/mon.c b/src/mon.c index f679c11e2..1cd3835ec 100644 --- a/src/mon.c +++ b/src/mon.c @@ -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 diff --git a/src/shk.c b/src/shk.c index e0fc11f11..4673e057e 100644 --- a/src/shk.c +++ b/src/shk.c @@ -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); diff --git a/src/steal.c b/src/steal.c index 61f0b93ca..e6b1bc1df 100644 --- a/src/steal.c +++ b/src/steal.c @@ -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;