From 9d8f6fdc8ec182028be15abce3ea827aa6fef5fd Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Tue, 31 Oct 2006 07:03:37 +0000 Subject: [PATCH] shop fixes Try to fix multiple container-in-shop bugs; it seemed as if every time I tried to investigate one I stumbled into another. billable() (post-3.4.3 code, but already present in branch as well as trunk) didn't handle containers properly. It sometimes gave the wrong answer and it didn't clear the no_charge flag of contained items as the old code in addtobill() it replaced did. stolen_value() didn't use the actual bill price for an item already on the shop bill; it generated a new price which might be different if the object was unID'd (3.4.3 had this one). Throwing didn't handle a container owned by the hero which contained items owned by the shopkeeper. I'm still not quite sure what's going on there, but throwing the container out of the shop didn't give any feedback but did add to shop charges which don't show up in ``I x'' (but do get revealed by ``$'' or ``I $''). (This was intertwined with some of the other stuff and I don't know how 3.4.3 behaved in this regard. Now there's some shop feedback for the throw and the contents show up for ``I x''.) An unpaid container which contained another nonempty unpaid container showed a different price when picked up and in inventory display [k - a bag (unpaid, NNN zorkmids)] compared to what was on the bill and showed by ``I u'' because the first two included double billing of the nested container (just it, not its own contents). [There where a few recursive calls of the form x += func(x) which should have been either x = func(x) or x += func(0).] Purchasing used the right amount, I think. I'm not sure whether 3.4.3 had this particular bug or whether fixes for some of its other container bugs inadvertently caused this one. This patch also changes stolen_container() to work like the other recursive shop routines: it just handles the contents, leaving the outer container itself to be handled by its caller. It seemed inconsistent, and changing it simplified the fix for using bill price on stolen unpaid items. --- src/shk.c | 80 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/src/shk.c b/src/shk.c index e85af72bc..3ce43cdd8 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1856,18 +1856,18 @@ register struct monst *shkp; /* if angry, impose a surcharge */ */ long contained_cost(obj, shkp, price, usell, unpaid_only) -register struct obj *obj; -register struct monst *shkp; +struct obj *obj; +struct monst *shkp; long price; -register boolean usell; -register boolean unpaid_only; +boolean usell; +boolean unpaid_only; { register struct obj *otmp; - /* the price of contained objects */ + /* price of contained objects; "top" container handled by caller */ for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { if (otmp->oclass == COIN_CLASS) continue; - /* the "top" container is evaluated by caller */ + if (usell) { if (saleable(shkp, otmp) && !otmp->unpaid && otmp->oclass != BALL_CLASS && @@ -1875,13 +1875,12 @@ register boolean unpaid_only; !(Is_candle(otmp) && otmp->age < 20L * (long)objects[otmp->otyp].oc_cost)) price += set_cost(otmp, shkp); - } else if (!otmp->no_charge && - (!unpaid_only || (unpaid_only && otmp->unpaid))) { - price += get_cost(otmp, shkp) * otmp->quan; + } else if (!otmp->no_charge && (otmp->unpaid || !unpaid_only)) { + price += get_cost(otmp, shkp) * otmp->quan; } if (Has_contents(otmp)) - price += contained_cost(otmp, shkp, price, usell, unpaid_only); + price = contained_cost(otmp, shkp, price, usell, unpaid_only); } return(price); @@ -2178,10 +2177,12 @@ boolean reset_nocharge; if (obj->no_charge) { if (!Has_contents(obj) || (contained_gold(obj) == 0L && - contained_cost(obj, shkp, 0L, FALSE, TRUE) == 0L)) + contained_cost(obj, shkp, 0L, FALSE, !reset_nocharge) == 0L)) shkp = 0; /* not billable */ - if (reset_nocharge && !shkp && obj->oclass != COIN_CLASS) + if (reset_nocharge && !shkp && obj->oclass != COIN_CLASS) { obj->no_charge = 0; + if (Has_contents(obj)) picked_container(obj); /* clear no_charge */ + } } return shkp ? TRUE : FALSE; } @@ -2402,30 +2403,31 @@ long price; boolean ininv; { struct obj *otmp; + struct bill_x *bp; + long billamt; - if (ininv ? obj->unpaid : !obj->no_charge) - price += get_cost(obj, shkp); /* container itself (quan 1) */ - else - obj->no_charge = 0; - - /* the price of contained objects, if any */ + /* the price of contained objects; caller handles top container */ for(otmp = obj->cobj; otmp; otmp = otmp->nobj) { if (otmp->oclass == COIN_CLASS) continue; + billamt = 0L; if (!billable(&shkp, otmp, ESHK(shkp)->shoproom, TRUE)) { /* billable() returns false for objects already on bill */ - if (!onbill(obj, shkp, FALSE)) continue; + if ((bp = onbill(otmp, shkp, FALSE)) == 0) continue; /* this assumes that we're being called by stolen_value() (or by a recursive call to self on behalf of it) where the cost of this object is about to be added to shop debt in place of having it remain on the current bill */ - subfrombill(obj, shkp); /* avoid double billing */ + billamt = bp->bquan * bp->price; + sub_one_frombill(otmp, shkp); /* avoid double billing */ } - if (!Has_contents(otmp)) { - if (otmp->unpaid || !ininv) - price += otmp->quan * get_cost(otmp, shkp); - } else - price += stolen_container(otmp, shkp, price, ininv); + if (billamt) + price += billamt; + else if (ininv ? otmp->unpaid : !otmp->no_charge) + price += otmp->quan * get_cost(otmp, shkp); + + if (Has_contents(otmp)) + price = stolen_container(otmp, shkp, price, ininv); } return(price); @@ -2437,27 +2439,35 @@ struct obj *obj; xchar x, y; boolean peaceful, silent; { - long value = 0L, gvalue = 0L; + long value = 0L, gvalue = 0L, billamt = 0L; char roomno = *in_rooms(x, y, SHOPBASE); + struct bill_x *bp; struct monst *shkp = 0; if (!billable(&shkp, obj, roomno, FALSE)) { /* things already on the bill yield a not-billable result, so we need to check bill before deciding that shk doesn't care */ - if (!onbill(obj, shkp, FALSE)) return 0L; + if ((bp = onbill(obj, shkp, FALSE)) == 0) return 0L; /* shk does care; take obj off bill to avoid double billing */ - subfrombill(obj, shkp); + billamt = bp->bquan * bp->price; + sub_one_frombill(obj, shkp); } if(obj->oclass == COIN_CLASS) { gvalue += obj->quan; - } else if (Has_contents(obj)) { - boolean ininv = !!count_unpaid(obj->cobj); + } else { + if (billamt) + value += billamt; + else if (!obj->no_charge) + value += obj->quan * get_cost(obj, shkp); - value += stolen_container(obj, shkp, value, ininv); - if(!ininv) gvalue += contained_gold(obj); - } else if (!obj->no_charge) { - value += obj->quan * get_cost(obj, shkp); + if (Has_contents(obj)) { + boolean ininv = (obj->where == OBJ_INVENT || + obj->where == OBJ_FREE); + + value += stolen_container(obj, shkp, 0L, ininv); + if(!ininv) gvalue += contained_gold(obj); + } } if(gvalue + value == 0L) return(0L); @@ -2557,7 +2567,7 @@ xchar x, y; } if(container) { /* find the price of content before subfrombill */ - cltmp += contained_cost(obj, shkp, cltmp, TRUE, FALSE); + cltmp = contained_cost(obj, shkp, cltmp, TRUE, FALSE); /* find the value of contained gold */ gltmp += contained_gold(obj); cgold = (gltmp > 0L); -- 2.40.0