]> granicus.if.org Git - nethack/commitdiff
theft detection
authornethack.rankin <nethack.rankin>
Sun, 6 Mar 2005 06:58:46 +0000 (06:58 +0000)
committernethack.rankin <nethack.rankin>
Sun, 6 Mar 2005 06:58:46 +0000 (06:58 +0000)
     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
src/shk.c

index f19d1c83669dae309bbf84670618f6a0b81597b7..bb2d3e1277d217711e32e4d44a4cdada776c0cb5 100644 (file)
@@ -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
index 1846c43219246bbc8e3bf8f3cef66c6a866ebdb0..060675c131fdf2d598406f8070583eeae89dbe2d 100644 (file)
--- 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;
        }
 }
 
-void
-addtobill(obj, ininv, dummy, silent)
-register struct obj *obj;
-register boolean ininv, dummy, silent;
+/* 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;
 {
-       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 = *shkpp;
 
-       if(!*u.ushops) return;
-
-       if(!(shkp = shop_keeper(roomno))) return;
+    /* 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;
+}
 
-       if(!inhishop(shkp)) return;
+void
+addtobill(obj, ininv, dummy, silent)
+struct obj *obj;
+boolean ininv, dummy, silent;
+{
+       struct monst *shkp = 0;
+       long ltmp, cltmp, gltmp;
+       boolean container;
 
-       if(/* perhaps we threw it away earlier */
-                onbill(obj, shkp, FALSE) ||
-                (obj->oclass == FOOD_CLASS && obj->oeaten)
-             ) return;
+       if (!billable(&shkp, obj, *u.ushops, TRUE))
+           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);