no_charge items sanity - shop theft

More unpaid/no_charge sanity checking.  If a shop contained any
no_charge objects and was robbed, they would be left no_charge and
trigger sanity check warnings (no_charge in "untended shop") once
the shopkeeper got past any Kops in the way and exited the shop.
Earlier testing didn't wait around long enough for that exit to
happen.

Clear no_charge as soon as the robbery is detected.
This commit is contained in:
PatR
2022-12-04 08:59:22 -08:00
parent be9e1f31af
commit a7b714ec5c
+51 -16
View File
@@ -178,7 +178,7 @@ next_shkp(register struct monst *shkp, boolean withbill)
/* called in mon.c */ /* called in mon.c */
void void
shkgone(struct monst* mtmp) shkgone(struct monst *mtmp)
{ {
struct eshk *eshk = ESHK(mtmp); struct eshk *eshk = ESHK(mtmp);
struct mkroom *sroom = &gr.rooms[eshk->shoproom - ROOMOFFSET]; struct mkroom *sroom = &gr.rooms[eshk->shoproom - ROOMOFFSET];
@@ -215,7 +215,7 @@ shkgone(struct monst* mtmp)
} }
void void
set_residency(register struct monst* shkp, register boolean zero_out) set_residency(struct monst *shkp, boolean zero_out)
{ {
if (on_level(&(ESHK(shkp)->shoplevel), &u.uz)) if (on_level(&(ESHK(shkp)->shoplevel), &u.uz))
gr.rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident = gr.rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident =
@@ -223,7 +223,7 @@ set_residency(register struct monst* shkp, register boolean zero_out)
} }
void void
replshk(register struct monst* mtmp, register struct monst* mtmp2) replshk(struct monst *mtmp, struct monst *mtmp2)
{ {
gr.rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = mtmp2; gr.rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = mtmp2;
if (inhishop(mtmp) && *u.ushops == ESHK(mtmp)->shoproom) { if (inhishop(mtmp) && *u.ushops == ESHK(mtmp)->shoproom) {
@@ -233,7 +233,7 @@ replshk(register struct monst* mtmp, register struct monst* mtmp2)
/* do shopkeeper specific structure munging -dlc */ /* do shopkeeper specific structure munging -dlc */
void void
restshk(struct monst* shkp, boolean ghostly) restshk(struct monst *shkp, boolean ghostly)
{ {
if (u.uz.dlevel) { if (u.uz.dlevel) {
struct eshk *eshkp = ESHK(shkp); struct eshk *eshkp = ESHK(shkp);
@@ -250,19 +250,54 @@ restshk(struct monst* shkp, boolean ghostly)
} }
} }
/* Clear the unpaid bit on a single object and its contents. */ /* Clear the unpaid and no_charge bits on a single object and its contents. */
static void static void
clear_unpaid_obj(struct monst* shkp, struct obj* otmp) clear_unpaid_obj(struct monst *shkp, struct obj *otmp)
{ {
if (Has_contents(otmp)) if (Has_contents(otmp))
clear_unpaid(shkp, otmp->cobj); clear_unpaid(shkp, otmp->cobj);
if (onbill(otmp, shkp, TRUE)) if (onbill(otmp, shkp, TRUE))
otmp->unpaid = 0; otmp->unpaid = 0;
if (otmp->no_charge) {
struct monst *rm_shkp;
int rno;
coordxy x, y;
/*
* Clear no_charge if
* not located somewhere that we expect no_charge (which is
* floor [of shop] or inside container [on shop floor])
* or can't find object's map coordinates (should never happen
* for floor or contained; conceivable if on shop bill somehow
* but would have failed the floor-or-contained test since
* containers get emptied before going onto bill)
* or fails location sanity check (should always be good when
* location successfully found)
* or not inside any room
* or the room isn't a shop
* or the shop has no shopkeeper (deserted)
* or shopkeeper is the current one (to avoid clearing no_charge
* for items located in some rival's shop).
*
* no_charge items in a shop which is only temporarily deserted
* become owned by the shop now and will be for-sale once the shk
* returns.
*/
if ((otmp->where != OBJ_FLOOR && otmp->where != OBJ_CONTAINED)
|| !get_obj_location(otmp, &x, &y, OBJ_CONTAINED | OBJ_BURIED)
|| !isok(x, y)
|| (rno = levl[x][y].roomno) < ROOMOFFSET
|| !IS_SHOP(rno - ROOMOFFSET)
|| (rm_shkp = gr.rooms[rno - ROOMOFFSET].resident) == 0
|| rm_shkp == shkp)
otmp->no_charge = 0;
}
} }
/* Clear the unpaid bit on all of the objects in the list. */ /* Clear the unpaid bit on all of the objects in the list. */
static void static void
clear_unpaid(struct monst* shkp, struct obj* list) clear_unpaid(struct monst *shkp, struct obj *list)
{ {
while (list) { while (list) {
clear_unpaid_obj(shkp, list); clear_unpaid_obj(shkp, list);
@@ -272,7 +307,7 @@ clear_unpaid(struct monst* shkp, struct obj* list)
/* either you paid or left the shop or the shopkeeper died */ /* either you paid or left the shop or the shopkeeper died */
void void
setpaid(register struct monst* shkp) setpaid(register struct monst *shkp)
{ {
register struct obj *obj; register struct obj *obj;
register struct monst *mtmp; register struct monst *mtmp;
@@ -302,11 +337,11 @@ setpaid(register struct monst* shkp)
} }
static long static long
addupbill(register struct monst* shkp) addupbill(struct monst *shkp)
{ {
register int ct = ESHK(shkp)->billct; int ct = ESHK(shkp)->billct;
register struct bill_x *bp = ESHK(shkp)->bill_p; struct bill_x *bp = ESHK(shkp)->bill_p;
register long total = 0L; long total = 0L;
while (ct--) { while (ct--) {
total += bp->price * bp->bquan; total += bp->price * bp->bquan;
@@ -316,10 +351,10 @@ addupbill(register struct monst* shkp)
} }
static void static void
call_kops(register struct monst* shkp, register boolean nearshop) call_kops(struct monst *shkp, boolean nearshop)
{ {
/* Keystone Kops srt@ucla */ /* Keystone Kops srt@ucla */
register boolean nokops; boolean nokops;
if (!shkp) if (!shkp)
return; return;
@@ -373,9 +408,9 @@ call_kops(register struct monst* shkp, register boolean nearshop)
/* x,y is strictly inside shop */ /* x,y is strictly inside shop */
char char
inside_shop(register coordxy x, register coordxy y) inside_shop(coordxy x, coordxy y)
{ {
register char rno; char rno;
rno = levl[x][y].roomno; rno = levl[x][y].roomno;
if ((rno < ROOMOFFSET) || levl[x][y].edge || !IS_SHOP(rno - ROOMOFFSET)) if ((rno < ROOMOFFSET) || levl[x][y].edge || !IS_SHOP(rno - ROOMOFFSET))