]> granicus.if.org Git - nethack/commitdiff
container_impact_dmg
authornethack.rankin <nethack.rankin>
Sat, 5 May 2012 23:26:26 +0000 (23:26 +0000)
committernethack.rankin <nethack.rankin>
Sat, 5 May 2012 23:26:26 +0000 (23:26 +0000)
     A post-3.4.3 change made the contents of thrown (or dropped while
levitating) containers subject to breakage, but it had sequence issues.
When something was thrown from outside a shop (or from its doorway or
entry spot) and arrived inside, the shopkeeper was taking possession too
soon, charging the hero for any broken contents, and then going ballistic
(summoning kops and attacking) because the hero was outside the shop while
owing money.  We need to break contents before shk claims ownership, which
turned out to be trickier than it sounds since it has to occur after any
item-hits-floor message if such feedback is given.

    Also, clear the container's contents-known flag when contents break.
Conceivably it should stay set when there is only one item, since hearing
something break could only be that item, but this resets container->cknown
unconditionally if anything inside breaks.

include/extern.h
src/do.c
src/dokick.c
src/dothrow.c

index 0e7eb4420b0419fd56bd3a2c37e73e61650be8e2..e2b37fa18c0a939de76197bc20224e9f674c8aea 100644 (file)
@@ -349,6 +349,7 @@ E void FDECL(doaltarobj, (struct obj *));
 E boolean FDECL(canletgo, (struct obj *,const char *));
 E void FDECL(dropx, (struct obj *));
 E void FDECL(dropy, (struct obj *));
+E void FDECL(dropz, (struct obj *,BOOLEAN_P));
 E void FDECL(obj_no_longer_held, (struct obj *));
 E int NDECL(doddrop);
 E int NDECL(dodown);
@@ -486,7 +487,7 @@ E void FDECL(finish_meating,(struct monst *));
 /* ### dokick.c ### */
 
 E boolean FDECL(ghitm, (struct monst *,struct obj *));
-E void FDECL(container_impact_dmg, (struct obj *));
+E void FDECL(container_impact_dmg, (struct obj *,XCHAR_P,XCHAR_P));
 E int NDECL(dokick);
 E boolean FDECL(ship_object, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P));
 E void FDECL(obj_delivery, (BOOLEAN_P));
index 7818ec5798feda3946bb7176ea1135111646e235..643a0de1d92eabc3aa6a3745b652a848cf59aefb 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -511,8 +511,9 @@ register struct obj *obj;
        return(1);
 }
 
-/* Called in several places - may produce output */
-/* eg ship_object() and dropy() -> sellobj() both produce output */
+/* dropx - take dropped item out of inventory;
+   called in several places - may produce output
+   (eg ship_object() and dropy() -> sellobj() both produce output) */
 void
 dropx(obj)
 register struct obj *obj;
@@ -532,9 +533,19 @@ register struct obj *obj;
        dropy(obj);
 }
 
+/* dropy - put dropped object at its destination; called from lots of places */
 void
 dropy(obj)
-register struct obj *obj;
+struct obj *obj;
+{
+       dropz(obj, FALSE);
+}
+
+/* dropz - really put dropped object at its destination... */
+void
+dropz(obj, with_impact)
+struct obj *obj;
+boolean with_impact;
 {
        if (obj == uwep) setuwep((struct obj *)0);
        if (obj == uquiver) setuqwep((struct obj *)0);
@@ -580,6 +591,8 @@ register struct obj *obj;
            }
        } else  {
            place_object(obj, u.ux, u.uy);
+           if (with_impact)
+               container_impact_dmg(obj, u.ux, u.uy);
            if (obj == uball)
                drop_ball(u.ux,u.uy);
            else if (level.flags.has_shop)
index a8b82b8dbf7ed79774bb2654782e46e1a82a6b13..3dddd8b40d616d4100d95789a48a0a5a4ef87a16 100644 (file)
@@ -373,22 +373,24 @@ register struct obj *gold;
 /* container is kicked, dropped, thrown or otherwise impacted by player.
  * Assumes container is on floor.  Checks contents for possible damage. */
 void
-container_impact_dmg(obj)
+container_impact_dmg(obj, x, y)
 struct obj *obj;
+xchar x, y;    /* coordinates where object was before the impact, not after */
 {
        struct monst *shkp;
        struct obj *otmp, *otmp2;
        long loss = 0L;
-       boolean costly, insider;
-       xchar x = obj->ox, y = obj->oy;
+       boolean costly, insider, frominv;
 
        /* only consider normal containers */
-       if (!Is_container(obj) || Is_mbag(obj)) return;
+       if (!Is_container(obj) || !Has_contents(obj) || Is_mbag(obj)) return;
 
        costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) &&
                  costly_spot(x, y));
        insider = (*u.ushops && inside_shop(u.ux, u.uy) &&
                   *in_rooms(x, y, SHOPBASE) == *u.ushops);
+       /* if dropped or thrown, shop ownership flags are set on this obj */
+       frominv = (obj != kickedobj);
 
        for (otmp = obj->cobj; otmp; otmp = otmp2) {
            const char *result = (char *)0;
@@ -408,15 +410,19 @@ struct obj *obj;
                if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM)
                    change_luck(-1);
                You_hear("a muffled %s.", result);
-               if (costly)
+               if (costly) {
+                   if (frominv && !otmp->unpaid) otmp->no_charge = 1;
                    loss += stolen_value(otmp, x, y,
                                         (boolean)shkp->mpeaceful, TRUE);
-               if (otmp->quan > 1L)
+               }
+               if (otmp->quan > 1L) {
                    useup(otmp);
-               else {
+               else {
                    obj_extract_self(otmp);
                    obfree(otmp, (struct obj *) 0);
                }
+               /* contents of this container are no longer known */
+               obj->cknown = 0;
            }
        }
        if (costly && loss) {
@@ -559,7 +565,7 @@ xchar x, y;
                boolean otrp = kickedobj->otrapped;
 
                if (range < 2) pline("THUD!");
-               container_impact_dmg(kickedobj);
+               container_impact_dmg(kickedobj, x, y);
                if (kickedobj->olocked) {
                    if (!rn2(5) || (martial() && !rn2(2))) {
                        You("break open the lock!");
index 93cc7a49ccd2771e6d0608795542a9fb6c89b893..f1461df6edb2bf7538e8ea20d34f37f93ca822c5 100644 (file)
@@ -423,8 +423,7 @@ register struct obj *obj;
 
        if (hero_breaks(obj, u.ux, u.uy, TRUE)) return;
        if (ship_object(obj, u.ux, u.uy, FALSE)) return;
-       dropy(obj);
-       if (!u.uswallow) container_impact_dmg(obj);
+       dropz(obj, TRUE);
 }
 
 /*
@@ -1194,6 +1193,14 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
                }
                thrownobj = (struct obj*)0;
                place_object(obj, bhitpos.x, bhitpos.y);
+               /* container contents might break;
+                  do so before turning ownership of thrownobj over to shk
+                  (container_impact_dmg handles item already owned by shop) */
+               if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ))
+                   /* <x,y> is spot where you initiated throw, not bhitpos */
+                   container_impact_dmg(obj, u.ux, u.uy);
+               /* charge for items thrown out of shop;
+                  shk takes possession for items thrown into one */
                if ((*u.ushops || obj->unpaid) && obj != uball)
                    check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);
 
@@ -1204,8 +1211,6 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
                    newsym(bhitpos.x,bhitpos.y);
                if (obj_sheds_light(obj))
                    vision_full_recalc = 1;
-               if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ))
-                   container_impact_dmg(obj);
        }
 }