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....
This commit is contained in:
nethack.rankin
2005-03-06 06:58:46 +00:00
parent 3d3088554c
commit 5d25ed46aa
2 changed files with 79 additions and 72 deletions

View 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

150
src/shk.c
View File

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