From 6e8da58cd04ba9562f462b6bbb819e7f75c00b93 Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Wed, 9 Mar 2016 14:20:58 +1100 Subject: [PATCH 1/3] Don't shop-donate non-empty bags dropped in sellobj_state SELL_DONTSELL For a shop to NOT charge for an object, two conditions apply: 1. The object's `no_charge` flag must be set. 2. That `no_charge` flag must be set regardless of whether or not the shop typically sells the object in question. There are two places in `sellobj()` which ignore the second condition, thus transferring object ownership from the player to the shop without the player's consent: 1. A container is dropped in a shop that typically sells such containers and `sellobj_state` is `SELL_DONTSELL`. 2. A zero-cost container holding nothing but gold is dropped in a shop that typically sells such containers. Neither occurs currently in NetHack: the latter because NetHack has no zero-cost containers, but the former is needed for an upcoming bug fix. This may be related to SC343-21: "Accounting is incorrect for containers dropped in a shop that does not sell them." Based on DynaHack commit 4e79b6a (Don't shop-donate non-empty bags dropped in sellobj_state SELL_DONTSELL) by me. --- src/shk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shk.c b/src/shk.c index 6cf62f2c0..0699d36e3 100644 --- a/src/shk.c +++ b/src/shk.c @@ -2885,7 +2885,7 @@ xchar x, y; if (container) { dropped_container(obj, shkp, FALSE); - if (!obj->unpaid && !saleitem) + if (!obj->unpaid) obj->no_charge = 1; if (unpaid) subfrombill(obj, shkp); @@ -2960,7 +2960,7 @@ xchar x, y; if (!isgold) { if (container) dropped_container(obj, shkp, FALSE); - if (!obj->unpaid && !saleitem) + if (!obj->unpaid) obj->no_charge = 1; subfrombill(obj, shkp); } From 8ccdea6d8dade3a792c3a90a12f0f8f6a63856a3 Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Wed, 9 Mar 2016 14:55:02 +1100 Subject: [PATCH 2/3] Credit/debit gold in containers even in sellobj_state SELL_DONTSELL There's no capacity for the shop logic to handle gold without also changing the credit/debit within it, so gold must always be handled in `sellobj()`, even when the state of it is set to `SELL_DONTSELL`. This is needed for an upcoming bug fix. Based on DynaHack commit b0784c5 (Credit/debit gold in containers even in sellobj_state SELL_DONTSELL) by me. --- src/shk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shk.c b/src/shk.c index 0699d36e3..5d3117563 100644 --- a/src/shk.c +++ b/src/shk.c @@ -2880,7 +2880,8 @@ xchar x, y; offer = ltmp + cltmp; /* get one case out of the way: nothing to sell, and no gold */ - if (!isgold && ((offer + gltmp) == 0L || sell_how == SELL_DONTSELL)) { + if (!(isgold || cgold) + && ((offer + gltmp) == 0L || sell_how == SELL_DONTSELL)) { boolean unpaid = is_unpaid(obj); if (container) { @@ -2956,7 +2957,7 @@ xchar x, y; currency(eshkp->credit)); } - if (!offer) { + if (!offer || sell_how == SELL_DONTSELL) { if (!isgold) { if (container) dropped_container(obj, shkp, FALSE); From 82386643a0dcf59c60869507a4e84a1690f1f1e1 Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Wed, 9 Mar 2016 15:18:15 +1100 Subject: [PATCH 3/3] Fix billing/credit when hero nests their containers on a shop floor This fixes a bug where the hero could accidentally donate the contents of their bag to a shopkeeper if they put it in another bag on the shop floor that also belonged to the hero. To reproduce: 1. Drop a sack on the floor, but don't sell it. 2. Get another sack and put in hero-owned objects. 3. Put the sack with objects into the sack on the shop floor. 4. Take out the sack with the objects from the sack on the shop floor. The shopkeeper will claim you owe them for the objects in the sack, and view the contents of the sack will show them as belonging to the shopkeeper. This fix is what those previous fixes for `SELL_DONTSELL` were for. Based on DynaHack commit f91ce0b (Fix billing/credit when hero nests their containers on a shop floor) by me. --- src/pickup.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pickup.c b/src/pickup.c index 38c7aeea7..a332e57f6 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -2025,15 +2025,14 @@ register struct obj *obj; (void) snuff_lit(obj); if (floor_container && costly_spot(u.ux, u.uy)) { - if (obj->oclass == COIN_CLASS) { - ; /* defer gold until after put-in message */ - } else if (current_container->no_charge && !obj->unpaid) { - /* don't sell when putting the item into your own container */ - obj->no_charge = 1; - } else { + /* defer gold until after put-in message */ + if (obj->oclass != COIN_CLASS) { /* sellobj() will take an unpaid item off the shop bill */ was_unpaid = obj->unpaid ? TRUE : FALSE; - sellobj_state(SELL_DELIBERATE); + /* don't sell when putting the item into your own container, + * but handle billing correctly */ + sellobj_state(current_container->no_charge + ? SELL_DONTSELL : SELL_DELIBERATE); sellobj(obj, u.ux, u.uy); sellobj_state(SELL_NORMAL); }