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.
This commit is contained in:
PatR
2020-11-25 14:33:14 -08:00
parent 6df9ebc1af
commit 2db51cf8bd
6 changed files with 81 additions and 47 deletions

View File

@@ -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);

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}

View File

@@ -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) {