]> granicus.if.org Git - nethack/commitdiff
fix the "big abuse" reported a few days ago
authornethack.rankin <nethack.rankin>
Sat, 6 Dec 2003 14:08:51 +0000 (14:08 +0000)
committernethack.rankin <nethack.rankin>
Sat, 6 Dec 2003 14:08:51 +0000 (14:08 +0000)
     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.

doc/fixes34.3
include/extern.h
src/mon.c
src/shk.c
src/steal.c

index 58a78771cf6fcf5d7b36960a56de620fbdab7dfa..9f3487e934fd0ae0fc8db582f8b69b2f3a12c458 100644 (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
index 14fc2d176fe09fa0ff3248ec0600648177cdd074..aaa9f52e6dc23709692a7b155ac384b88b705f01 100644 (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 *));
index f679c11e25ef6dac119f2a9e670960bf943736ec..1cd3835ec42b925f5bff3f977e0e534509ff6799 100644 (file)
--- 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
index e0fc11f1179c270f5447576419e5b6339f0cc182..4673e057e53e14c3a4816a5c6f85f054496617e9 100644 (file)
--- 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);
index 61f0b93ca429a03fe7c0d2be6a35909ea510d72e..e6b1bc1df3141b111d8818a4ad0ce04ec89f96a0 100644 (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;