From 2db51cf8bd5b0aa93f2375b61d0da51fcdbfc509 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 25 Nov 2020 14:33:14 -0800 Subject: [PATCH] fix #K3016 - kicking a bag of gold in a shop Kicking a container that had gold in it took the gold amount away from hero's credit or added to hero's debt, then didn't give a refund if the container and its gold landed within the shop. Throwing behaved likewise, just less verbosely. The problem is caused by addtobill() treating gold specially and then subfrombill() not being able to perform a reverse operation. Actually, it may be possible for subfrombill() to do that, but verifying all its uses is too much work. This moves the gold handling for drop+selling into its own routine and adds calls to that for the throwing and kicking refunds. The other calls to subfrombill() outside of shk.c appear to be ok as-is. (The calls inside that file are the ones that still need evaluation if the gold handling is to move to there.) bill_dummy_object() now uses the same o_id assignment for its dummy object as split_object() does for its new partial stack. I don't know whether the old code led to any price glitches. --- doc/fixes37.0 | 5 +++- include/extern.h | 7 ++--- src/dokick.c | 26 +++++++++++++----- src/dothrow.c | 11 +++++--- src/mkobj.c | 10 +++---- src/shk.c | 69 ++++++++++++++++++++++++++++-------------------- 6 files changed, 81 insertions(+), 47 deletions(-) diff --git a/doc/fixes37.0 b/doc/fixes37.0 index fbd016ffa..9e712a198 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.362 $ $NHDT-Date: 1606243387 2020/11/24 18:43:07 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.363 $ $NHDT-Date: 1606343573 2020/11/25 22:32:53 $ General Fixes and Modified Features ----------------------------------- @@ -308,6 +308,9 @@ wand/scroll of create monster or bag of tricks that makes a new monster which wizard mode (only way to get timed flying): if levitation and flying time out on same turn, player was told "You have stopped levitating and are now flying."; status line wasn't updated to remove stale Fly condition +throwing or kicking a shop container (that's light enough to move) made the + hero pay for any gold inside, then didn't refund that amount if the + container landed inside the shop Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 4ea7e7575..0147a619d 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1606008997 2020/11/22 01:36:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.880 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1606343573 2020/11/25 22:32:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.882 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2406,8 +2406,9 @@ E boolean FDECL(billable, (struct monst **, struct obj *, CHAR_P, BOOLEAN_P)); E void FDECL(addtobill, (struct obj *, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P)); E void FDECL(splitbill, (struct obj *, struct obj *)); E void FDECL(subfrombill, (struct obj *, struct monst *)); -E long FDECL(stolen_value, - (struct obj *, XCHAR_P, XCHAR_P, BOOLEAN_P, BOOLEAN_P)); +E long FDECL(stolen_value, (struct obj *, XCHAR_P, XCHAR_P, + BOOLEAN_P, BOOLEAN_P)); +E void FDECL(donate_gold, (long, struct monst *, BOOLEAN_P)); E void FDECL(sellobj_state, (int)); E void FDECL(sellobj, (struct obj *, XCHAR_P, XCHAR_P)); E int FDECL(doinvbill, (int)); diff --git a/src/dokick.c b/src/dokick.c index 65cb6f4e6..114a217b8 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dokick.c $NHDT-Date: 1606009001 2020/11/22 01:36:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.159 $ */ +/* NetHack 3.7 dokick.c $NHDT-Date: 1606343576 2020/11/25 22:32:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.160 $ */ /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -508,7 +508,8 @@ xchar x, y; } if (!uarmf && g.kickedobj->otyp == CORPSE - && touch_petrifies(&mons[g.kickedobj->corpsenm]) && !Stone_resistance) { + && touch_petrifies(&mons[g.kickedobj->corpsenm]) + && !Stone_resistance) { You("kick %s with your bare %s.", corpse_xname(g.kickedobj, (const char *) 0, CXN_PFX_THE), makeplural(body_part(FOOT))); @@ -705,12 +706,23 @@ xchar x, y; else (void) stolen_value(g.kickedobj, x, y, (boolean) shkp->mpeaceful, FALSE); + costly = FALSE; /* already billed */ } if (flooreffects(g.kickedobj, g.bhitpos.x, g.bhitpos.y, "fall")) return 1; - if (g.kickedobj->unpaid) - subfrombill(g.kickedobj, shkp); + if (costly) { + long gt = 0L; + + /* costly + landed outside shop handled above; must be inside shop */ + if (g.kickedobj->unpaid) + subfrombill(g.kickedobj, shkp); + + /* if billed for contained gold during kick, get a refund now */ + if (Has_contents(g.kickedobj) + && (gt = contained_gold(g.kickedobj, TRUE)) > 0L) + donate_gold(gt, shkp, FALSE); + } place_object(g.kickedobj, g.bhitpos.x, g.bhitpos.y); stackobj(g.kickedobj); newsym(g.kickedobj->ox, g.kickedobj->oy); @@ -1723,7 +1735,8 @@ unsigned long deliverflags; continue; if (otmp->migr_species != NON_PM - && (mtmp->data->mflags2 & DELIVER_PM) == (unsigned) otmp->migr_species) { + && ((mtmp->data->mflags2 & DELIVER_PM) + == (unsigned) otmp->migr_species)) { obj_extract_self(otmp); otmp->owornmask = 0L; otmp->ox = otmp->oy = 0; @@ -1777,7 +1790,8 @@ long num; if (nodrop) Sprintf(eos(xbuf), "."); else - Sprintf(eos(xbuf), " and %s %s.", otense(otmp, "fall"), g.gate_str); + Sprintf(eos(xbuf), " and %s %s.", + otense(otmp, "fall"), g.gate_str); pline("%s%s", obuf, xbuf); } else if (!nodrop) pline("%s %s %s.", obuf, otense(otmp, "fall"), g.gate_str); diff --git a/src/dothrow.c b/src/dothrow.c index 6719dc9d8..f3015bfd1 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dothrow.c $NHDT-Date: 1596498161 2020/08/03 23:42:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.188 $ */ +/* NetHack 3.7 dothrow.c $NHDT-Date: 1606343578 2020/11/25 22:32:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.190 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -935,10 +935,15 @@ boolean broken; /* ushops0: in case we threw while levitating and recoiled out of shop (most likely to the shk's spot in front of door) */ if (*oshops == *u.ushops || *oshops == *u.ushops0) { - if (is_unpaid(obj)) + if (is_unpaid(obj)) { + long gt = Has_contents(obj) ? contained_gold(obj, TRUE) : 0L; + subfrombill(obj, shkp); - else if (x != shkp->mx || y != shkp->my) + if (gt > 0L) + donate_gold(gt, shkp, TRUE); + } else if (x != shkp->mx || y != shkp->my) { sellobj(obj, x, y); + } } } } diff --git a/src/mkobj.c b/src/mkobj.c index 71ef55a88..4c1b810bb 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkobj.c $NHDT-Date: 1596498183 2020/08/03 23:43:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.186 $ */ +/* NetHack 3.7 mkobj.c $NHDT-Date: 1606343579 2020/11/25 22:32:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.191 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -633,9 +633,7 @@ register struct obj *otmp; *dummy = *otmp; dummy->oextra = (struct oextra *) 0; dummy->where = OBJ_FREE; - dummy->o_id = g.context.ident++; - if (!dummy->o_id) - dummy->o_id = g.context.ident++; /* ident overflowed */ + dummy->o_id = nextoid(otmp, dummy); dummy->timed = 0; copy_oextra(dummy, otmp); if (has_omid(dummy)) @@ -647,8 +645,8 @@ register struct obj *otmp; if (cost) alter_cost(dummy, -cost); /* no_charge is only valid for some locations */ - otmp->no_charge = - (otmp->where == OBJ_FLOOR || otmp->where == OBJ_CONTAINED) ? 1 : 0; + otmp->no_charge = (otmp->where == OBJ_FLOOR + || otmp->where == OBJ_CONTAINED) ? 1 : 0; otmp->unpaid = 0; return; } diff --git a/src/shk.c b/src/shk.c index 5ad0fb07a..64afd3207 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 shk.c $NHDT-Date: 1606009003 2020/11/22 01:36:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.191 $ */ +/* NetHack 3.7 shk.c $NHDT-Date: 1606343581 2020/11/25 22:33:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.192 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3003,6 +3003,44 @@ boolean peaceful, silent; return value; } +/* opposite of costly_gold(); hero has dropped gold in a shop; + called from sellobj(); ought to be called from subfrombill() too */ +void +donate_gold(gltmp, shkp, selling) +long gltmp; +struct monst *shkp; +boolean selling; /* True: dropped in shop; False: kicked and landed in shop */ +{ + struct eshk *eshkp = ESHK(shkp); + + if (eshkp->debit >= gltmp) { + if (eshkp->loan) { /* you carry shop's gold */ + if (eshkp->loan > gltmp) + eshkp->loan -= gltmp; + else + eshkp->loan = 0L; + } + eshkp->debit -= gltmp; + Your("debt is %spaid off.", eshkp->debit ? "partially " : ""); + } else { + long delta = gltmp - eshkp->debit; + + eshkp->credit += delta; + if (eshkp->debit) { + eshkp->debit = 0L; + eshkp->loan = 0L; + Your("debt is paid off."); + } + if (eshkp->credit == delta) + You("have %sestablished %ld %s credit.", + !selling ? "re-" : "", delta, currency(delta)); + else + pline("%ld %s added%s to your credit; total is now %ld %s.", + delta, currency(delta), !selling ? " back" : "", + eshkp->credit, currency(eshkp->credit)); + } +} + void sellobj_state(deliberate) int deliberate; @@ -3088,7 +3126,7 @@ xchar x, y; return; } - if (eshkp->robbed) { /* shkp is not angry? */ + if (eshkp->robbed) { /* bones; shop robbed by previous customer */ if (isgold) offer = obj->quan; else if (cgold) @@ -3106,32 +3144,7 @@ xchar x, y; if (!cgold) gltmp = obj->quan; - if (eshkp->debit >= gltmp) { - if (eshkp->loan) { /* you carry shop's gold */ - if (eshkp->loan >= gltmp) - eshkp->loan -= gltmp; - else - eshkp->loan = 0L; - } - eshkp->debit -= gltmp; - Your("debt is %spaid off.", eshkp->debit ? "partially " : ""); - } else { - long delta = gltmp - eshkp->debit; - - eshkp->credit += delta; - if (eshkp->debit) { - eshkp->debit = 0L; - eshkp->loan = 0L; - Your("debt is paid off."); - } - if (eshkp->credit == delta) - You("have established %ld %s credit.", delta, - currency(delta)); - else - pline("%ld %s added to your credit; total is now %ld %s.", - delta, currency(delta), eshkp->credit, - currency(eshkp->credit)); - } + donate_gold(gltmp, shkp, TRUE); if (!offer || g.sell_how == SELL_DONTSELL) { if (!isgold) {