From f8b0e766d85289ef0c66be9e50f93c5258929c2f Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Wed, 9 Mar 2016 00:26:57 +1100 Subject: [PATCH 1/2] Fix & instead of && in onbill() It's obviously supposed to be the latter and not the former. Interesting note: This same bug was found and fixed in NitroHack commit 4973ce4 (static checker day: fixes for scan-build and PVS warnings). --- src/shk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shk.c b/src/shk.c index 6cf62f2c0..addf8aac7 100644 --- a/src/shk.c +++ b/src/shk.c @@ -859,7 +859,7 @@ boolean silent; } else bp++; } - if (obj->unpaid & !silent) + if (obj->unpaid && !silent) pline("onbill: unpaid obj not on bill?"); return (struct bill_x *) 0; } From e1d0faa5849c79741c29b962a51d356574ab3c06 Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Wed, 9 Mar 2016 00:48:52 +1100 Subject: [PATCH 2/2] Fix paid object on bill when angering another shopkeeper To test: 1. Get a level layout with two shops facing each other, e.g. minetn-4. 2. Sell a fragile object to one of the shops. 3. Dig a pit in the other shop's door space so its shopkeeper stays out of the way. 4. Pick up an object in that other shop so it appears on your bill. 5. Zap a wand of striking at the first shop to break the fragile object. 6. 'p'ay for the object picked up. Expected result: Object gets the standard prompt to pay for it. Actual result: "Paid object on bill??" followed by "Program in disorder perhaps you'd better #quit." followed by the object being given to the player for free. The cause? This comment going all the way back to 2002: > /* FIXME: object handling should be limited to > items which are on this particular shk's bill */ Originally reported by PaRaD0xx in FreeNode's #NetHack IRC channel whilst playing NAO343. Based on DynaHack commit d995ed1 (Fix paid object on bill when angering another shkp) by me. --- src/shk.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/shk.c b/src/shk.c index addf8aac7..4d683f500 100644 --- a/src/shk.c +++ b/src/shk.c @@ -38,7 +38,8 @@ STATIC_DCL struct monst *FDECL(next_shkp, (struct monst *, BOOLEAN_P)); STATIC_DCL long FDECL(shop_debt, (struct eshk *)); STATIC_DCL char *FDECL(shk_owns, (char *, struct obj *)); STATIC_DCL char *FDECL(mon_owns, (char *, struct obj *)); -STATIC_DCL void FDECL(clear_unpaid, (struct obj *)); +STATIC_DCL void FDECL(clear_unpaid_obj, (struct monst *, struct obj *)); +STATIC_DCL void FDECL(clear_unpaid, (struct monst *, struct obj *)); STATIC_DCL long FDECL(check_credit, (long, struct monst *)); STATIC_DCL void FDECL(pay, (long, struct monst *)); STATIC_DCL long FDECL(get_cost, (struct obj *, struct monst *)); @@ -260,15 +261,26 @@ boolean ghostly; } } +/* Clear the unpaid bit on a single object and its contents. */ +STATIC_OVL void +clear_unpaid_obj(shkp, otmp) +struct monst *shkp; +struct obj *otmp; +{ + if (Has_contents(otmp)) + clear_unpaid(shkp, otmp->cobj); + if (onbill(otmp, shkp, TRUE)) + otmp->unpaid = 0; +} + /* Clear the unpaid bit on all of the objects in the list. */ STATIC_OVL void -clear_unpaid(list) -register struct obj *list; +clear_unpaid(shkp, list) +struct monst *shkp; +struct obj *list; { while (list) { - if (Has_contents(list)) - clear_unpaid(list->cobj); - list->unpaid = 0; + clear_unpaid_obj(shkp, list); list = list->nobj; } } @@ -281,20 +293,17 @@ register struct monst *shkp; register struct obj *obj; register struct monst *mtmp; - /* FIXME: object handling should be limited to - items which are on this particular shk's bill */ - - clear_unpaid(invent); - clear_unpaid(fobj); - clear_unpaid(level.buriedobjlist); + clear_unpaid(shkp, invent); + clear_unpaid(shkp, fobj); + clear_unpaid(shkp, level.buriedobjlist); if (thrownobj) - thrownobj->unpaid = 0; + clear_unpaid_obj(shkp, thrownobj); if (kickedobj) - kickedobj->unpaid = 0; + clear_unpaid_obj(shkp, kickedobj); for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - clear_unpaid(mtmp->minvent); + clear_unpaid(shkp, mtmp->minvent); for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) - clear_unpaid(mtmp->minvent); + clear_unpaid(shkp, mtmp->minvent); while ((obj = billobjs) != 0) { obj_extract_self(obj);