From 5d25ed46aaadb6ed49179cddd01f7d7e9e1f068c Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Sun, 6 Mar 2005 06:58:46 +0000 Subject: [PATCH] theft detection Shop items stolen or destroyed without being in inventory were handled inconsistently compared to simply picking up unpaid items because different criteria got used to decide whether the shk cares about something. Last December a hack to deal with this for container contents was introduced but that left the problem for ordinary items. This patch attempts to address it by using a common check for theft and for pickup's add-to-bill. It hasn't had nearly enough testing and I won't be very surprised if one or more new obscure shop bugs have now come into being, but perhaps they'll at least be consistent bugs as far as shop billing is concerned.... --- doc/fixes34.4 | 1 + src/shk.c | 150 ++++++++++++++++++++++++++------------------------ 2 files changed, 79 insertions(+), 72 deletions(-) diff --git a/doc/fixes34.4 b/doc/fixes34.4 index f19d1c836..bb2d3e127 100644 --- a/doc/fixes34.4 +++ b/doc/fixes34.4 @@ -67,6 +67,7 @@ various helmet messages changed to distinguish between "helm" and "hat" helmets don't protect against cockatrice eggs thrown straight up breaking container contents in a shop didn't always charge for them some types of shop theft of a stack of items only charged for a single one +some thefts weren't charged at all even though shopkeeper noticed wizard mode: WIZKIT wish for own quest artifact triggered crash at startup avoid "your steed is still eating" message when going through a magic portal cannot drink from fountain, sink or surrounding water while swallowed diff --git a/src/shk.c b/src/shk.c index 1846c4321..060675c13 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)shk.c 3.5 2004/12/21 */ +/* SCCS Id: @(#)shk.c 3.5 2005/03/05 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -58,6 +58,8 @@ STATIC_DCL void FDECL(rouse_shk, (struct monst *,BOOLEAN_P)); STATIC_DCL void FDECL(remove_damage, (struct monst *, BOOLEAN_P)); STATIC_DCL void FDECL(sub_one_frombill, (struct obj *, struct monst *)); STATIC_DCL void FDECL(add_one_tobill, (struct obj *, BOOLEAN_P)); +STATIC_DCL boolean FDECL(billable, (struct monst **,struct obj *, + CHAR_P,BOOLEAN_P)); STATIC_DCL void FDECL(dropped_container, (struct obj *, struct monst *, BOOLEAN_P)); STATIC_DCL void FDECL(add_to_billobjs, (struct obj *)); @@ -2144,58 +2146,74 @@ const char *arg; } } +/* decide whether a shopkeeper thinks an item belongs to her */ +STATIC_OVL boolean +billable(shkpp, obj, roomno, reset_nocharge) +struct monst **shkpp; /* in: non-null if shk has been validated; out: shk */ +struct obj *obj; +char roomno; +boolean reset_nocharge; +{ + struct monst *shkp = *shkpp; + + /* if caller hasn't supplied a shopkeeper, look one up now */ + if (!shkp) { + if (!roomno) return FALSE; + shkp = shop_keeper(roomno); + if (!shkp || !inhishop(shkp)) return FALSE; + *shkpp = shkp; + } + /* perhaps we threw it away earlier */ + if (onbill(obj, shkp, FALSE) || + (obj->oclass == FOOD_CLASS && obj->oeaten)) + return FALSE; + /* outer container might be marked no_charge but still have contents + which should be charged for; clear no_charge when picking things up */ + if (obj->no_charge) { + if (!Has_contents(obj) || + (contained_gold(obj) == 0L && + contained_cost(obj, shkp, 0L, FALSE, TRUE) == 0L)) + shkp = 0; /* not billable */ + if (reset_nocharge && !shkp && obj->oclass != COIN_CLASS) + obj->no_charge = 0; + } + return shkp ? TRUE : FALSE; +} + void addtobill(obj, ininv, dummy, silent) -register struct obj *obj; -register boolean ininv, dummy, silent; +struct obj *obj; +boolean ininv, dummy, silent; { - register struct monst *shkp; - register char roomno = *u.ushops; - long ltmp = 0L, cltmp = 0L, gltmp = 0L; - register boolean container = Has_contents(obj); + struct monst *shkp = 0; + long ltmp, cltmp, gltmp; + boolean container; - if(!*u.ushops) return; + if (!billable(&shkp, obj, *u.ushops, TRUE)) + return; - if(!(shkp = shop_keeper(roomno))) return; - - if(!inhishop(shkp)) return; - - if(/* perhaps we threw it away earlier */ - onbill(obj, shkp, FALSE) || - (obj->oclass == FOOD_CLASS && obj->oeaten) - ) return; - - if(ESHK(shkp)->billct == BILLSZ) { - You("got that for free!"); - return; + if (obj->oclass == COIN_CLASS) { + costly_gold(obj->ox, obj->oy, obj->quan); + return; + } else if (ESHK(shkp)->billct == BILLSZ) { + You("got that for free!"); + return; } - if(obj->oclass == COIN_CLASS) { - costly_gold(obj->ox, obj->oy, obj->quan); - return; - } + ltmp = cltmp = gltmp = 0L; + container = Has_contents(obj); if(!obj->no_charge) ltmp = get_cost(obj, shkp); if (obj->no_charge && !container) { - obj->no_charge = 0; - return; + obj->no_charge = 0; + return; } if(container) { - if(obj->cobj == (struct obj *)0) { - if(obj->no_charge) { - obj->no_charge = 0; - return; - } else { - add_one_tobill(obj, dummy); - goto speak; - } - } else { - cltmp += contained_cost(obj, shkp, cltmp, FALSE, FALSE); - gltmp += contained_gold(obj); - } + cltmp = contained_cost(obj, shkp, cltmp, FALSE, FALSE); + gltmp = contained_gold(obj); if(ltmp) add_one_tobill(obj, dummy); if(cltmp) bill_box_content(obj, ininv, dummy, shkp); @@ -2213,6 +2231,7 @@ register boolean ininv, dummy, silent; } else /* i.e., !container */ add_one_tobill(obj, dummy); + speak: if (shkp->mcanmove && !shkp->msleeping && !silent) { char buf[BUFSZ]; @@ -2355,37 +2374,26 @@ register struct monst *shkp; STATIC_OVL long stolen_container(obj, shkp, price, ininv) -register struct obj *obj; -register struct monst *shkp; +struct obj *obj; +struct monst *shkp; long price; -register boolean ininv; +boolean ininv; { - register struct obj *otmp; + struct obj *otmp; - if(ininv && obj->unpaid) - price += get_cost(obj, shkp); - else { - if(!obj->no_charge) - price += get_cost(obj, shkp); + 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 */ for(otmp = obj->cobj; otmp; otmp = otmp->nobj) { - - if(otmp->oclass == COIN_CLASS) continue; + if (otmp->oclass == COIN_CLASS) continue; + if (!billable(&shkp, otmp, ESHK(shkp)->shoproom, TRUE)) continue; if (!Has_contents(otmp)) { - if(ininv) { - if(otmp->unpaid) - price += otmp->quan * get_cost(otmp, shkp); - } else { - if(!otmp->no_charge) { - if(otmp->oclass != FOOD_CLASS || !otmp->oeaten) - price += otmp->quan * get_cost(otmp, shkp); - } - otmp->no_charge = 0; - } + if (otmp->unpaid || !ininv) + price += otmp->quan * get_cost(otmp, shkp); } else price += stolen_container(otmp, shkp, price, ininv); } @@ -2395,28 +2403,26 @@ register boolean ininv; long stolen_value(obj, x, y, peaceful, silent) -register struct obj *obj; -register xchar x, y; -register boolean peaceful, silent; +struct obj *obj; +xchar x, y; +boolean peaceful, silent; { - register long value = 0L, gvalue = 0L; - register struct monst *shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); + long value = 0L, gvalue = 0L; + char roomno = *in_rooms(x, y, SHOPBASE); + struct monst *shkp = 0; - if (!shkp || !inhishop(shkp)) - return (0L); + if (!billable(&shkp, obj, roomno, FALSE)) + return 0L; if(obj->oclass == COIN_CLASS) { gvalue += obj->quan; } else if (Has_contents(obj)) { - register boolean ininv = !!count_unpaid(obj->cobj); + boolean ininv = !!count_unpaid(obj->cobj); value += stolen_container(obj, shkp, value, ininv); if(!ininv) gvalue += contained_gold(obj); } else if (!obj->no_charge) { - /* treat items inside containers as "saleable" */ - if ((saleable(shkp, obj) || obj->where == OBJ_CONTAINED) && - (obj->oclass != FOOD_CLASS || !obj->oeaten)) - value += obj->quan * get_cost(obj, shkp); + value += obj->quan * get_cost(obj, shkp); } if(gvalue + value == 0L) return(0L);