/*	SCCS Id: @(#)shk.c	3.1	93/01/12	*/
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "eshk.h"

/*#define DEBUG*/

#define PAY_SOME    2
#define PAY_BUY     1
#define PAY_CANT    0	/* too poor */
#define PAY_SKIP  (-1)
#define PAY_BROKE (-2)

#ifdef KOPS
STATIC_DCL void FDECL(makekops, (coord *));
STATIC_DCL void FDECL(call_kops, (struct monst *,BOOLEAN_P));
# ifdef OVLB
static void FDECL(kops_gone, (BOOLEAN_P));
# endif /* OVLB */
#endif /* KOPS */

#define IS_SHOP(x)	(rooms[x].rtype >= SHOPBASE)

extern const struct shclass shtypes[];	/* defined in shknam.c */

STATIC_VAR long int NEARDATA followmsg;	/* last time of follow message */

STATIC_DCL void FDECL(setpaid, (struct monst *));
STATIC_DCL long FDECL(addupbill, (struct monst *));
STATIC_DCL void FDECL(pacify_shk, (struct monst *));

#ifdef OVLB

static void FDECL(clear_unpaid,(struct obj *));
static struct bill_x *FDECL(onbill, (struct obj *, struct monst *, BOOLEAN_P));
static long FDECL(check_credit, (long, struct monst *));
static void FDECL(pay, (long, struct monst *));
static long FDECL(get_cost, (struct obj *, struct monst *));
static long FDECL(set_cost, (struct obj *, struct monst *));
static const char *FDECL(shk_embellish, (struct obj *, long));
static long FDECL(cost_per_charge, (struct obj *));
static long FDECL(cheapest_item, (struct monst *));
static int FDECL(dopayobj, (struct monst *, struct bill_x *,
			    struct obj *, int, BOOLEAN_P));
static long FDECL(stolen_container, (struct obj *, struct monst *, long,
				     BOOLEAN_P));
static long FDECL(getprice, (struct obj *));
static struct obj *FDECL(bp_to_obj, (struct bill_x *));
static boolean FDECL(inherits, (struct monst *, int, BOOLEAN_P));
static struct monst *FDECL(next_shkp, (struct monst *, BOOLEAN_P));
static boolean NDECL(angry_shk_exists);
static void FDECL(rile_shk, (struct monst *));
static void FDECL(remove_damage, (struct monst *, BOOLEAN_P));
static void FDECL(sub_one_frombill, (struct obj *, struct monst *));
static void FDECL(add_one_tobill, (struct obj *, BOOLEAN_P));
static void FDECL(dropped_container, (struct obj *));
static void FDECL(bill_box_content, (struct obj *, BOOLEAN_P, BOOLEAN_P,
				     struct monst *));

/*
	invariants: obj->unpaid iff onbill(obj) [unless bp->useup]
		obj->quan <= bp->bquan
 */

static struct monst *
next_shkp(shkp, withbill)
register struct monst *shkp;
register boolean withbill;
{
	for (; shkp; shkp = shkp->nmon)
	    if (shkp->isshk)
		if (ESHK(shkp)->billct || !withbill) break;

	if (shkp) {
	    if (NOTANGRY(shkp)) {
		if (ESHK(shkp)->surcharge) pacify_shk(shkp);
	    } else {
		if (!ESHK(shkp)->surcharge) rile_shk(shkp);
	    }
	}
	return(shkp);
}

char *
shkname(mtmp)				/* called in do_name.c */
register struct monst *mtmp;
{
	return(ESHK(mtmp)->shknam);
}

void
shkgone(mtmp)				/* called in mon.c */
register struct monst *mtmp;
{
	register struct eshk *eshk = ESHK(mtmp);

	if(on_level(&(eshk->shoplevel), &u.uz)) {
		remove_damage(mtmp, TRUE);
		rooms[eshk->shoproom - ROOMOFFSET].resident
						  = (struct monst *)0;
		if(!search_special(ANY_SHOP))
		    level.flags.has_shop = 0;
	}
	/* make sure bill is set only when the
	 * dead shk is the resident shk.	*/
	if(*u.ushops == eshk->shoproom) {
	    setpaid(mtmp);
	    /* dump core when referenced */
	    ESHK(mtmp)->bill_p = (struct bill_x *) -1000;
	    u.ushops[0] = '\0';
	}
}

void
set_residency(shkp, zero_out)
register struct monst *shkp;
register boolean zero_out;
{
	if (on_level(&(ESHK(shkp)->shoplevel), &u.uz))
	    rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident =
		(zero_out)? (struct monst *)0 : shkp;
}

void
replshk(mtmp,mtmp2)
register struct monst *mtmp, *mtmp2;
{
	if(inhishop(mtmp) && *u.ushops == ESHK(mtmp)->shoproom) {
		ESHK(mtmp2)->bill_p = &(ESHK(mtmp2)->bill[0]);
	}
}

/* do shopkeeper specific structure munging -dlc */
void
restshk(mtmp)
register struct monst *mtmp;
{
    if(ESHK(mtmp)->bill_p != (struct bill_x *) -1000)
	ESHK(mtmp)->bill_p = &(ESHK(mtmp)->bill[0]);
}

/* Clear the unpaid bit on all of the objects in the list. */
static void
clear_unpaid(list)
register struct obj *list;
{
    while (list) {
	if (Is_container(list)) clear_unpaid(list->cobj);
	list->unpaid = 0;
	list = list->nobj;
    }
}

STATIC_OVL void
setpaid(shkp)	/* either you paid or left the shop or the shopkeeper died */
register struct monst *shkp;
{
	register struct obj *obj;
	register struct monst *mtmp;

	clear_unpaid(invent);
	clear_unpaid(fobj);
	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
		clear_unpaid(mtmp->minvent);
	for(mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
		clear_unpaid(mtmp->minvent);

	while ((obj = billobjs) != 0) {
		billobjs = obj->nobj;
		dealloc_obj(obj);
	}
	if(shkp) {
		ESHK(shkp)->billct = 0;
		ESHK(shkp)->credit = 0L;
		ESHK(shkp)->debit = 0L;
		ESHK(shkp)->loan = 0L;
	}
}

STATIC_OVL long
addupbill(shkp)
register struct monst *shkp;
{
	register int ct = ESHK(shkp)->billct;
	register struct bill_x *bp = ESHK(shkp)->bill_p;
	register long total = 0L;

	while(ct--){
		total += bp->price * bp->bquan;
		bp++;
	}
	return(total);
}

#endif /* OVLB */
#ifdef OVL1

#ifdef KOPS
STATIC_OVL void
call_kops(shkp, nearshop)
register struct monst *shkp;
register boolean nearshop;
{
	/* Keystone Kops srt@ucla */
	register boolean nokops;

	if(!shkp) return;

	if (flags.soundok)
	    pline("An alarm sounds!");

	nokops = ((mons[PM_KEYSTONE_KOP].geno & (G_GENOD | G_EXTINCT)) &&
		  (mons[PM_KOP_SERGEANT].geno & (G_GENOD | G_EXTINCT)) &&
		  (mons[PM_KOP_LIEUTENANT].geno & (G_GENOD | G_EXTINCT)) &&
		  (mons[PM_KOP_KAPTAIN].geno & (G_GENOD | G_EXTINCT)));

	if(!angry_guards(!flags.soundok) && nokops) {
	    if(flags.verbose && flags.soundok)
		pline("But no one seems to respond to it.");
	    return;
	}

	if(nokops) return;

	{
	    coord mm;

	    if (nearshop) {
		/* Create swarm around you, if you merely "stepped out" */
		if (flags.verbose)
		    pline("The Keystone Kops appear!");
		mm.x = u.ux;
		mm.y = u.uy;
		makekops(&mm);
		return;
	    }
	    if (flags.verbose)
		 pline("The Keystone Kops are after you!");
	    /* Create swarm near down staircase (hinders return to level) */
	    mm.x = xdnstair;
	    mm.y = ydnstair;
	    makekops(&mm);
	    /* Create swarm near shopkeeper (hinders return to shop) */
	    mm.x = shkp->mx;
	    mm.y = shkp->my;
	    makekops(&mm);
	}
}
#endif	/* KOPS */

/* x,y is strictly inside shop */
char
inside_shop(x, y)
register xchar x, y;
{
	register char rno;

	rno = levl[x][y].roomno;
	if ((rno < ROOMOFFSET) || levl[x][y].edge || !IS_SHOP(rno-ROOMOFFSET))
	    return(NO_ROOM);
	else
	    return(rno);
}

void
u_left_shop(leavestring, newlev)
register char *leavestring;
register boolean newlev;
{
	register struct monst *shkp;
	register struct eshk *eshkp;
	register long total;

	/*
	 * IF player
	 * ((didn't leave outright) AND
	 *  ((he is now strictly-inside the shop) OR
	 *   (he wasn't strictly-inside last turn anyway)))
	 * THEN (there's nothing to do, so just return)
	 */
	if(!*leavestring &&
	   (!levl[u.ux][u.uy].edge || levl[u.ux0][u.uy0].edge))
	    return;

	shkp = shop_keeper(*u.ushops0);

	if(!shkp || !inhishop(shkp))
				/* shk died, teleported, changed levels... */
	    return;

	eshkp = ESHK(shkp);

	if(!eshkp->billct && !eshkp->debit)	/* bill is settled */
	    return;

	if(!*leavestring) {
	    /*
	     * Player just stepped onto shop-boundary (known from above logic).
	     * Try to intimidate him into paying his bill
	     */

	    verbalize(NOTANGRY(shkp) ?
		      "%s!  Please pay before leaving." :
		      "%s!  Don't you leave without paying!",
		      plname);
	    return;
	}
		/* by this point, we know an actual robbery has taken place */
	You("escaped the shop without paying!");
	total = (addupbill(shkp) + eshkp->debit);
	eshkp->robbed += total;
	eshkp->credit = 0L;
	eshkp->debit = 0L;
	setpaid(shkp);
	You("stole %ld zorkmid%s worth of merchandise.",
	    total, plur(total));
	if (pl_character[0] != 'R') /* stealing is unlawful */
	    adjalign(-sgn(u.ualign.type));

	hot_pursuit(shkp);
#ifdef KOPS
	call_kops(shkp, (!newlev && levl[u.ux0][u.uy0].edge));
#else
	(void) angry_guards(FALSE);
#endif
}

void
u_entered_shop(enterstring)
register char *enterstring;
{

	register int rt;
	register struct monst *shkp;
	register struct eshk *eshkp;
	static const char no_shk[] = "This shop appears to be deserted.";
	static char empty_shops[5];

	if(!*enterstring)
		return;

	if(!(shkp = shop_keeper(*enterstring))) {
	    if (!index(empty_shops, *enterstring) &&
		in_rooms(u.ux, u.uy, SHOPBASE) !=
				  in_rooms(u.ux0, u.uy0, SHOPBASE))
		pline(no_shk);
	    Strcpy(empty_shops, u.ushops);
	    u.ushops[0] = '\0';
	    return;
	}

	eshkp = ESHK(shkp);

	if (!inhishop(shkp)) {
	    /* dump core when referenced */
	    eshkp->bill_p = (struct bill_x *) -1000;
	    if (!index(empty_shops, *enterstring))
		pline(no_shk);
	    Strcpy(empty_shops, u.ushops);
	    u.ushops[0] = '\0';
	    return;
	}

	eshkp->bill_p = &(eshkp->bill[0]);

	if (!eshkp->visitct || strncmpi(eshkp->customer, plname, PL_NSIZ)) {
	    /* You seem to be new here */
	    eshkp->visitct = 0;
	    eshkp->following = 0;
	    (void) strncpy(eshkp->customer,plname,PL_NSIZ);
	    pacify_shk(shkp);
	}

	if (eshkp->following)
	    return;

	if (Invis) {
	    pline("%s senses your presence.", shkname(shkp));
	    verbalize("Invisible customers are not welcome!");
	    return;
	}

	rt = rooms[*enterstring - ROOMOFFSET].rtype;

	if (ANGRY(shkp)) {
	    verbalize("So, %s, you dare return to %s %s?!",
		      plname,
		      s_suffix(shkname(shkp)),
		      shtypes[rt - SHOPBASE].name);
	} else if (eshkp->robbed) {
	    verbalize("Beware, %s!  I am upset about missing stock!",
		      plname);
	} else {
	    verbalize("Hello, %s!  Welcome%s to %s %s!",
		      plname,
		      eshkp->visitct++ ? " again" : "",
		      s_suffix(shkname(shkp)),
		      shtypes[rt - SHOPBASE].name);
	}
	if(carrying(PICK_AXE) != (struct obj *)0 &&
				 /* can't do anything if teleported in */
				 !inside_shop(u.ux, u.uy)) {
	    verbalize(NOTANGRY(shkp) ?
		      "Will you please leave your pick-axe outside?" :
		      "Leave the pick-axe outside.");
	    (void) dochug(shkp);
	}
	return;
}

#endif /* OVL1 */
#ifdef OVLB

int
inhishop(mtmp)
register struct monst *mtmp;
{
	return(index(in_rooms(mtmp->mx, mtmp->my, SHOPBASE),
		     ESHK(mtmp)->shoproom) &&
		on_level(&(ESHK(mtmp)->shoplevel), &u.uz));
}

struct monst *
shop_keeper(rmno)
register char rmno;
{
	struct monst *shkp = rmno >= ROOMOFFSET ?
				rooms[rmno - ROOMOFFSET].resident : 0;

	if (shkp) {
	    if (NOTANGRY(shkp)) {
		if (ESHK(shkp)->surcharge) pacify_shk(shkp);
	    } else {
		if (!ESHK(shkp)->surcharge) rile_shk(shkp);
	    }
	}
	return shkp;
}

#ifdef SOUNDS
boolean
tended_shop(sroom)
register struct mkroom *sroom;
{
	register struct monst *mtmp = sroom->resident;

	if (!mtmp)
		return(FALSE);
	else
		return(inhishop(mtmp));
}
#endif	/* SOUNDS */

static struct bill_x *
onbill(obj, shkp, silent)
register struct obj *obj;
register struct monst *shkp;
register boolean silent;
{
	if (shkp) {
		register struct bill_x *bp = ESHK(shkp)->bill_p;
		register int ct = ESHK(shkp)->billct;

		while (--ct >= 0)
		    if (bp->bo_id == obj->o_id) {
			if (!obj->unpaid) pline("onbill: paid obj on bill?");
			return bp;
		    } else bp++;
	}
	if(obj->unpaid & !silent) pline("onbill: unpaid obj not on bill?");
	return (struct bill_x *)0;
}

/* Delete the contents of the given object. */
void
delete_contents(obj)
register struct obj *obj;
{
	register struct obj *curr, *next;

	for (curr = obj->cobj; curr; curr = next) {
		next = curr->nobj;
		obfree(curr, (struct obj *)0);
	}
	obj->cobj = (struct obj *) 0;
}

/* called with two args on merge */
void
obfree(obj, merge)
register struct obj *obj, *merge;
{
	register struct bill_x *bp;
	register struct bill_x *bpm;
	register struct monst *shkp;

	if(obj->oclass == FOOD_CLASS) food_disappears(obj);

	if (obj->cobj) delete_contents(obj);

	shkp = shop_keeper(*u.ushops);

	if ((bp = onbill(obj, shkp, FALSE)) != 0) {
		if(!merge){
			bp->useup = 1;
			obj->unpaid = 0;	/* only for doinvbill */
			obj->nobj = billobjs;
			billobjs = obj;
			return;
		}
		bpm = onbill(merge, shkp, FALSE);
		if(!bpm){
			/* this used to be a rename */
			impossible("obfree: not on bill??");
			return;
		} else {
			/* this was a merger */
			bpm->bquan += bp->bquan;
			ESHK(shkp)->billct--;
#ifdef DUMB
			{
			/* DRS/NS 2.2.6 messes up -- Peter Kendell */
				int indx = ESHK(shkp)->billct;
				*bp = ESHK(shkp)->bill_p[indx];
			}
#else
			*bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct];
#endif
		}
	}
	dealloc_obj(obj);
}

static long
check_credit(tmp, shkp)
long tmp;
register struct monst *shkp;
{
	long credit = ESHK(shkp)->credit;

	if(credit == 0L) return(tmp);
	if(credit >= tmp) {
		pline("The price is deducted from your credit.");
		ESHK(shkp)->credit -=tmp;
		tmp = 0L;
	} else {
		pline("The price is partially covered by your credit.");
		ESHK(shkp)->credit = 0L;
		tmp -= credit;
	}
	return(tmp);
}

static void
pay(tmp,shkp)
long tmp;
register struct monst *shkp;
{
	long robbed = ESHK(shkp)->robbed;
	long balance = ((tmp <= 0L) ? tmp : check_credit(tmp, shkp));

	u.ugold -= balance;
	shkp->mgold += balance;
	flags.botl = 1;
	if(robbed) {
		robbed -= tmp;
		if(robbed < 0) robbed = 0L;
		ESHK(shkp)->robbed = robbed;
	}
}

/* return shkp to home position */
void
home_shk(shkp, killkops)
register struct monst *shkp;
register boolean killkops;
{
	register xchar x = ESHK(shkp)->shk.x, y = ESHK(shkp)->shk.y;

	(void) mnearto(shkp, x, y, TRUE);
	level.flags.has_shop = 1;
	if (killkops) {
#ifdef KOPS
		kops_gone(TRUE);
#else
		You("feel vaguely apprehensive.");
#endif
		pacify_guards();
	}
}

static boolean
angry_shk_exists()
{
	register struct monst *shkp;

	for (shkp = next_shkp(fmon, FALSE);
		shkp; shkp = next_shkp(shkp->nmon, FALSE))
	    if (ANGRY(shkp)) return(TRUE);
	return(FALSE);
}

/* remove previously applied surcharge from all billed items */
STATIC_OVL void
pacify_shk(shkp)
register struct monst *shkp;
{
	NOTANGRY(shkp) = TRUE;	/* make peaceful */
	if (ESHK(shkp)->surcharge) {
		register struct bill_x *bp = ESHK(shkp)->bill_p;
		register int ct = ESHK(shkp)->billct;

		ESHK(shkp)->surcharge = FALSE;
		while (ct-- > 0) {
			register long reduction = (bp->price + 3L) / 4L;
			bp->price -= reduction;		/* undo 33% increase */
			bp++;
		}
	}
}

/* add aggravation surcharge to all billed items */
static void
rile_shk(shkp)
register struct monst *shkp;
{
	NOTANGRY(shkp) = FALSE;	/* make angry */
	if (!ESHK(shkp)->surcharge) {
		register struct bill_x *bp = ESHK(shkp)->bill_p;
		register int ct = ESHK(shkp)->billct;

		ESHK(shkp)->surcharge = TRUE;
		while (ct-- > 0) {
			register long surcharge = (bp->price + 2L) / 3L;
			bp->price += surcharge;
			bp++;
		}
	}
}

void
make_happy_shk(shkp, silentkops)
register struct monst *shkp;
register boolean silentkops;
{
	register boolean wasmad = ANGRY(shkp);

	pacify_shk(shkp);
	ESHK(shkp)->following = 0;
	ESHK(shkp)->robbed = 0L;
	if (pl_character[0] != 'R')
		adjalign(sgn(u.ualign.type));
	if(!inhishop(shkp)) {
		pline("Satisfied, %s suddenly disappears!", mon_nam(shkp));
		if(on_level(&(ESHK(shkp)->shoplevel), &u.uz))
			home_shk(shkp, FALSE);
		else
			migrate_to_level(shkp,
				 ledger_no(&(ESHK(shkp)->shoplevel)), 0);
	} else if(wasmad)
		pline("%s calms down.", Monnam(shkp));

	if(!angry_shk_exists()) {
#ifdef KOPS
		kops_gone(silentkops);
#endif
		pacify_guards();
	}
}

void
hot_pursuit(shkp)
register struct monst *shkp;
{
	if(!shkp->isshk) return;

	rile_shk(shkp);
	ESHK(shkp)->following = 1;
}

/* used when the shkp is teleported out of his shop,
 * or when the player is not on a costly_spot and he
 * damages something inside the shop.  these conditions
 * must be checked by the calling function.
 */
void
make_angry_shk(shkp, ox, oy)
register struct monst *shkp;
register xchar ox,oy;
{
	if(index(in_rooms(ox, oy, SHOPBASE), ESHK(shkp)->shoproom) &&
	    !ANGRY(shkp)) {
		ESHK(shkp)->robbed += (addupbill(shkp) +
				       ESHK(shkp)->debit + ESHK(shkp)->loan);
		ESHK(shkp)->robbed -= ESHK(shkp)->credit;
		if(ESHK(shkp)->robbed < 0L)
		    ESHK(shkp)->robbed = 0L;
		ESHK(shkp)->credit = 0L;
		setpaid(shkp);
	}
	if(!ANGRY(shkp)) pline("%s gets angry!", Monnam(shkp));
	else pline("%s is furious!", Monnam(shkp));
	hot_pursuit(shkp);
}

static const char no_money[] = "Moreover, you%s have no money.";

static long
cheapest_item(shkp)   /* delivers the cheapest item on the list */
register struct monst *shkp;
{
	register int ct = ESHK(shkp)->billct;
	register struct bill_x *bp = ESHK(shkp)->bill_p;
	register long gmin = (bp->price * bp->bquan);

	while(ct--){
		if(bp->price * bp->bquan < gmin)
			gmin = bp->price * bp->bquan;
		bp++;
	}
	return(gmin);
}

int
dopay()
{
	long ltmp;
	register struct monst *nxtm = (struct monst *)0;
	register struct monst *shkp, *resident = (struct monst *)0;
	register struct eshk *eshkp;
	int pass, tmp, sk = 0, seensk = 0;
	register boolean paid = FALSE, stashed_gold = (hidden_gold() > 0L);

	multi = 0;

	/* find how many shk's there are, how many are in */
	/* sight, and are you in a shop room with one.    */
	for (shkp = next_shkp(fmon, FALSE);
		shkp; shkp = next_shkp(shkp->nmon, FALSE)) {
	    sk++;
	    if (ANGRY(shkp) && distu(shkp->mx, shkp->my) <= 2) nxtm = shkp;
	    if (canseemon(shkp) || sensemon(shkp)) seensk++;
	    if (inhishop(shkp) && (*u.ushops == ESHK(shkp)->shoproom))
		resident = shkp;
	}

	if (nxtm) {			/* Player should always appease an */
	     shkp = nxtm;		/* irate shk standing next to them. */
	     goto proceed;
	}

	if ((!sk && (!Blind || Telepat)) || (!Blind && !seensk)) {
      pline("There appears to be no shopkeeper here to receive your payment.");
		return(0);
	}

	if(!seensk) {
		You("can't see...");
		return(0);
	}

	/* the usual case.  allow paying at a distance when */
	/* inside a tended shop.  should we change that?    */
	if(sk == 1 && resident) {
		shkp = resident;
		goto proceed;
	}

	if (seensk == 1) {
		for (shkp = next_shkp(fmon, FALSE);
			shkp; shkp = next_shkp(shkp->nmon, FALSE))
		    if (canseemon(shkp) || sensemon(shkp)) break;
		if (shkp != resident && distu(shkp->mx, shkp->my) > 2) {
		    pline("%s is not near enough to receive your payment.",
					     Monnam(shkp));
		    return(0);
		}
	} else {
		struct monst *mtmp;
		coord cc;
		int cx, cy;

		pline("Pay whom?");
		cc.x = u.ux;
		cc.y = u.uy;
		getpos(&cc, TRUE, "the creature you want to pay");
		cx = cc.x;
		cy = cc.y;
		if(cx == -10) return(0); /* player pressed esc */
		if(cx < 0) {
		     pline("Try again...");
		     return(0);
		}
		if(u.ux == cx && u.uy == cy) {
		     You("are generous to yourself.");
		     return(0);
		}
		mtmp = m_at(cx, cy);
		if(!mtmp) {
		     pline("There is no one there to receive your payment.");
		     return(0);
		}
		if(!mtmp->isshk) {
		     pline("%s is not interested in your payment.",
				    Monnam(mtmp));
		     return(0);
		}
		if (mtmp != resident && distu(mtmp->mx, mtmp->my) > 2) {
		     pline("%s is too far to receive your payment.",
				    Monnam(mtmp));
		     return(0);
		}
		shkp = mtmp;
	}

	if(!shkp) {
#ifdef DEBUG
		pline("dopay: null shkp.");
#endif
		return(0);
	}
proceed:
	eshkp = ESHK(shkp);

	ltmp = eshkp->robbed;
	if(shkp != resident && NOTANGRY(shkp)) {
		if(!ltmp)
		    You("do not owe %s anything.", mon_nam(shkp));
		else if(!u.ugold) {
		    You("%shave no money.", stashed_gold ? "seem to " : "");
		    if(stashed_gold)
			pline("But you have some gold stashed away.");
		} else {
		    const char *pronoun = shkp->female ? "she" : "he";
		    long ugold = u.ugold;

		    if(ugold > ltmp) {
			You("give %s the %ld gold piece%s %s asked for.",
			    mon_nam(shkp), ltmp, plur(ltmp), pronoun);
			pay(ltmp, shkp);
		    } else {
			You("give %s all your%s gold.", mon_nam(shkp),
					stashed_gold ? " openly kept" : "");
			pay(u.ugold, shkp);
			if (stashed_gold) pline("But you have hidden gold!");
		    }
		    if((ugold < ltmp/2L) || (ugold < ltmp && stashed_gold))
			pline("Unfortunately, %s doesn't look satisfied.",
			    pronoun);
		    else
			make_happy_shk(shkp, FALSE);
		}
		return(1);
	}

	/* ltmp is still eshkp->robbed here */
	if (!eshkp->billct && !eshkp->debit) {
		const char *pronoun = shkp->female ? "her" : "him";
		const char *possessive = shkp->female ? "her" : "his";

		if(!ltmp && NOTANGRY(shkp)) {
		    You("do not owe %s anything.", mon_nam(shkp));
		    if(!u.ugold) pline(no_money, stashed_gold ?
					   " seem to" : "");
		} else if(ltmp) {
		    pline("%s is after blood, not money!", Monnam(shkp));
		    if(u.ugold < ltmp/2L ||
				(u.ugold < ltmp && stashed_gold)) {
			if(!u.ugold) pline(no_money, stashed_gold ?
						       " seem to" : "");
			else pline("Besides, you don't have enough to interest %s.",
				pronoun);
			return(1);
		    }
		    pline("But since %s shop has been robbed recently,",
			possessive);
		    pline("you %scompensate %s for %s losses.",
			(u.ugold < ltmp) ? "partially " : "",
			mon_nam(shkp), possessive);
		    pay(u.ugold < ltmp ? u.ugold : ltmp, shkp);
		    make_happy_shk(shkp, FALSE);
		} else {
		    /* shopkeeper is angry, but has not been robbed --
		     * door broken, attacked, etc. */
		    pline("%s is after your hide, not your money!",
							 mon_nam(shkp));
		    if(u.ugold < 1000L) {
			if(!u.ugold) pline(no_money, stashed_gold ?
					     " seem to" : "");
			else pline("Besides, you don't have enough to interest %s.",
				    pronoun);
			return(1);
		    }
		    You("try to appease %s by giving %s 1000 gold pieces.",
			x_monnam(shkp, 1, "angry", 0), pronoun);
		    pay(1000L,shkp);
		    if (strncmp(eshkp->customer, plname, PL_NSIZ) || rn2(3))
			make_happy_shk(shkp, FALSE);
		    else
			pline("But %s is as angry as ever.", mon_nam(shkp));
		}
		return(1);
	}
	if(shkp != resident) {
		impossible("dopay: not to shopkeeper?");
		if(resident) setpaid(resident);
		return(0);
	}
	/* pay debt, if any, first */
	if(eshkp->debit) {
		long dtmp = eshkp->debit;
		long loan = eshkp->loan;
		char sbuf[BUFSZ];

		Sprintf(sbuf, "You owe %s %ld zorkmid%s ",
					   shkname(shkp), dtmp, plur(dtmp));
		if(loan) {
		    if(loan == dtmp)
			Strcat(sbuf, "you picked up in the store.");
		    else Strcat(sbuf,
			   "for gold picked up and the use of merchandise.");
		} else Strcat(sbuf, "for the use of merchandise.");
		pline(sbuf);
		if (u.ugold + eshkp->credit < dtmp) {
		    pline("But you don't%s have enough gold%s.",
			stashed_gold ? " seem to" : "",
			eshkp->credit ? " or credit" : "");
		    return(1);
		} else {
		    if (eshkp->credit >= dtmp) {
			eshkp->credit -= dtmp;
			eshkp->debit = 0L;
			eshkp->loan = 0L;
			Your("debt is covered by your credit.");
		    } else if (!eshkp->credit) {
			u.ugold -= dtmp;
			shkp->mgold += dtmp;
			eshkp->debit = 0L;
			eshkp->loan = 0L;
			You("pay that debt.");
			flags.botl = 1;
		    } else {
			dtmp -= eshkp->credit;
			eshkp->credit = 0L;
			u.ugold -= dtmp;
			shkp->mgold += dtmp;
			eshkp->debit = 0L;
			eshkp->loan = 0L;
			pline("That debt is partially offset by your credit.");
			You("pay the remainder.");
			flags.botl = 1;
		    }
		    paid = TRUE;
		}
	}
	/* now check items on bill */
	if (eshkp->billct) {
	    register boolean itemize;

	    if (!u.ugold && !eshkp->credit) {
		You("%shave no money or credit%s.",
				    stashed_gold ? "seem to " : "",
				    paid ? " left" : "");
		return(0);
	    }
	    if ((u.ugold + eshkp->credit) < cheapest_item(shkp)) {
		You("don't have enough money to buy%s the item%s you picked.",
		    eshkp->billct > 1 ? " any of" : "", plur(eshkp->billct));
		if(stashed_gold)
		    pline("Maybe you have some gold stashed away?");
		return(0);
	    }

	    /* this isn't quite right; it itemizes without asking if the
	     * single item on the bill is partly used up and partly unpaid */
	    itemize = (eshkp->billct > 1 ? yn("Itemized billing?") == 'y' : 1);

	    for (pass = 0; pass <= 1; pass++) {
		tmp = 0;
		while (tmp < eshkp->billct) {
		    register struct obj *otmp;
		    register struct bill_x *bp = &(eshkp->bill_p[tmp]);

		    /* find the object on one of the lists */
		    if (!(otmp = bp_to_obj(bp))) {
			impossible("Shopkeeper administration out of order.");
			setpaid(shkp);	/* be nice to the player */
			return 1;
		    }
		    if (pass == bp->useup && otmp->quan == bp->bquan) {
			/* pay for used-up items on first pass and others
			 * on second, so player will be stuck in the store
			 * less often; things which are partly used up
			 * are processed on both passes */
			tmp++;
		    } else {
			switch (dopayobj(shkp, bp, otmp, pass, itemize)) {
			  case PAY_CANT:
				return 1;	/*break*/
			  case PAY_BROKE:
				paid = TRUE;
				goto thanks;	/*break*/
			  case PAY_SKIP:
				tmp++;
				continue;	/*break*/
			  case PAY_SOME:
				paid = TRUE;
				if (itemize) bot();
				continue;	/*break*/
			  case PAY_BUY:
				paid = TRUE;
				break;
			}
			if (itemize) bot();
			*bp = eshkp->bill_p[--eshkp->billct];
		    }
		}
	    }
	}
thanks:
	if(!ANGRY(shkp) && paid)
	    verbalize("Thank you for shopping in %s %s!",
		s_suffix(shkname(shkp)),
		shtypes[rooms[eshkp->shoproom - ROOMOFFSET].rtype - SHOPBASE].name);
	return(1);
}

/* return 2 if used-up portion paid */
/*	  1 if paid successfully    */
/*	  0 if not enough money     */
/*	 -1 if skip this object     */
/*	 -2 if no money/credit left */
static int
dopayobj(shkp, bp, obj, which, itemize)
register struct monst *shkp;
register struct bill_x *bp;
register struct obj *obj;
int	which;		/* 0 => used-up item, 1 => other (unpaid or lost) */
boolean itemize;
{
	long ltmp, quan, save_quan;
	int buy;
	boolean stashed_gold = (hidden_gold() > 0L),
		consumed = (which == 0);

	if(!obj->unpaid && !bp->useup){
		impossible("Paid object on bill??");
		return PAY_BUY;
	}
	if(itemize && u.ugold + ESHK(shkp)->credit == 0L){
		You("%shave no money or credit left.",
			     stashed_gold ? "seem to " : "");
		return PAY_BROKE;
	}
	/* we may need to temporarily adjust the object, if part of the
	   original quantity has been used up but part remains unpaid  */
	save_quan = obj->quan;
	if (consumed) {
	    /* either completely used up (simple), or split needed */
	    quan = bp->bquan;
	    if (quan > obj->quan)	/* difference is amount used up */
		quan -= obj->quan;
	} else {
	    /* dealing with ordinary unpaid item */
	    quan = obj->quan;
	}
	obj->quan = quan;	/* to be used by doname() */
	obj->unpaid = 0;	/* ditto */
	ltmp = bp->price * quan;
	buy = PAY_BUY;		/* flag; if changed then return early */

	if (itemize) {
	    char qbuf[BUFSZ];
	    Sprintf(qbuf,"%s for %ld zorkmid%s.  Pay?", quan == 1L ?
		    Doname2(obj) : doname(obj), ltmp, plur(ltmp));
	    if (yn(qbuf) == 'n') {
		buy = PAY_SKIP;		/* don't want to buy */
	    } else if (quan < bp->bquan && !consumed) { /* partly used goods */
		obj->quan = bp->bquan - save_quan;	/* used up amount */
		verbalize("%s for the other %s before buying %s.",
			  ANGRY(shkp) ? "Pay" : "Please pay", xname(obj),
			  save_quan > 1L ? "these" : "this one");
		buy = PAY_SKIP;		/* shk won't sell */
	    }
	}
	if (buy == PAY_BUY && u.ugold + ESHK(shkp)->credit < ltmp) {
	    You("don't%s have gold%s enough to pay for %s.",
		stashed_gold ? " seem to" : "",
		(ESHK(shkp)->credit > 0L) ? " or credit" : "",
		doname(obj));
	    buy = itemize ? PAY_SKIP : PAY_CANT;
	}

	if (buy != PAY_BUY) {
	    /* restore unpaid object to original state */
	    obj->quan = save_quan;
	    obj->unpaid = 1;
	    return buy;
	}

	pay(ltmp, shkp);
	You("bought %s for %ld gold piece%s.",
		doname(obj), ltmp, plur(ltmp));
	obj->quan = save_quan;		/* restore original count */
	/* quan => amount just bought, save_quan => remaining unpaid count */
	if (consumed) {
	    if (quan != save_quan) {
		/* eliminate used-up portion; remainder is still unpaid */
		bp->bquan = obj->quan;
		obj->unpaid = 1;
		bp->useup = 0;
	    } else {	/* completely used-up, so get rid of it */
		register struct obj *otmp = billobjs;
		if(obj == billobjs)
			billobjs = obj->nobj;
		else {
			while(otmp && otmp->nobj != obj) otmp = otmp->nobj;
			if(otmp) otmp->nobj = obj->nobj;
			else impossible("Error in shopkeeper administration.");
		}
		dealloc_obj(obj);
	    }
	}
	return quan != save_quan ? PAY_SOME : PAY_BUY;
}

/* routine called after dying (or quitting) */
boolean
paybill(croaked)
register boolean croaked;
{
	register struct monst *mtmp, *mtmp2, *resident= (struct monst *)0;
	register boolean taken = FALSE;
	register int numsk = 0;

	/* give shopkeeper first crack */
	if ((mtmp = shop_keeper(*u.ushops)) && inhishop(mtmp)) {
	    numsk++;
	    resident = mtmp;
	    taken = inherits(resident, numsk, croaked);
	}
	for (mtmp = next_shkp(fmon, FALSE);
		mtmp; mtmp = next_shkp(mtmp2, FALSE)) {
	    mtmp2 = mtmp->nmon;
	    if (mtmp != resident) {
		/* for bones: we don't want a shopless shk around */
		if(!on_level(&(ESHK(mtmp)->shoplevel), &u.uz))
			mongone(mtmp);
		else {
		    numsk++;
		    taken |= inherits(mtmp, numsk, croaked);
		}
	    }
	}
	if(numsk == 0) return(FALSE);
	return(taken);
}

static boolean
inherits(shkp, numsk, croaked)
register struct monst *shkp;
register int numsk;
register boolean croaked;
{
	register long loss = 0L;
	register struct obj *otmp;
	register struct eshk *eshkp = ESHK(shkp);
	register xchar ox, oy;
	register boolean take = FALSE, taken = FALSE;
	register int roomno = *u.ushops;

	/* the simplifying principle is that first-come */
	/* already took everything you had.		*/
	if(numsk > 1) {
	    if(cansee(shkp->mx, shkp->my) && croaked)
		pline("%s %slooks at your corpse%s%s", Monnam(shkp),
		     (shkp->msleep || shkp->mfrozen) ?
				   "wakes up, " : "",
		     !rn2(2) ? (shkp->female ? ", shakes her head," :
				 ", shakes his head,") : "",
		     !inhishop(shkp) ? " and disappears. " : " and sighs.");
	    taken = (roomno == eshkp->shoproom);
	    goto skip;
	}

	/* get one case out of the way: you die in the shop, the */
	/* shopkeeper is peaceful, nothing stolen, nothing owed. */
	if(roomno == eshkp->shoproom && inhishop(shkp) &&
	    !IS_DOOR(levl[u.ux][u.uy].typ) && !eshkp->billct &&
	    !eshkp->robbed && !eshkp->debit &&
	     NOTANGRY(shkp) && !eshkp->following) {
		if (invent)
			pline("%s gratefully inherits all your possessions.",
				shkname(shkp));
		goto clear;
	}

	if (eshkp->billct || eshkp->debit || eshkp->robbed) {
		register long total = 0L;

		if(roomno == eshkp->shoproom && inhishop(shkp))
		    total = (addupbill(shkp) + eshkp->debit);
		loss = ((total >= eshkp->robbed) ? total : eshkp->robbed);
		take = TRUE;
	}

	if (eshkp->following || ANGRY(shkp) || take) {

		if(!invent && !u.ugold) goto skip;

		if((loss > u.ugold) || !loss) {
			pline("%s %s%stakes all your possessions.",
				shkname(shkp),
				(shkp->msleep || shkp->mfrozen) ?
				   "wakes up and " : "",
				(distu(shkp->mx, shkp->my) > 2) ?
				    "comes and " : "");
			taken = TRUE;
			shkp->mgold += u.ugold;
			u.ugold = 0L;
			/* in case bones: make it be for real... */
			if(!*u.ushops ||
			     IS_DOOR(levl[u.ux][u.uy].typ)) {
			    /* shk.x,shk.y is the position immediately in
			     * front of the door -- move in one more space
			     */
			    ox = eshkp->shk.x;
			    oy = eshkp->shk.y;
			    ox += sgn(ox - eshkp->shd.x);
			    oy += sgn(oy - eshkp->shd.y);
			} else {
			    ox = u.ux;
			    oy = u.uy;
			}

			if (invent) {
			    for(otmp = invent; otmp; otmp = otmp->nobj)
				place_object(otmp, ox, oy);

			    /* add to main object list at end so invent is
			       still good */
			    if (fobj) {
				otmp = fobj;
				while(otmp->nobj)
				    otmp = otmp->nobj;
				otmp->nobj = invent;
			    } else
				fobj = invent;
			}
		} else {
			u.ugold -= loss;
			shkp->mgold += loss;
			pline("%s %sand takes %ld zorkmid%s %sowed %s.",
			      Monnam(shkp),
			      (shkp->msleep || shkp->mfrozen) ?
					"wakes up " : "comes ",
			      loss, plur(loss),
			      strncmp(eshkp->customer,
					   plname, PL_NSIZ) ? "" : "you ",
			      shkp->female ? "her" : "him");
		}
skip:
		/* in case we create bones */
		if(!inhishop(shkp))
			home_shk(shkp, FALSE);
	}
clear:
	setpaid(shkp);
	return(taken);
}

/* find obj on one of the lists */
static struct obj *
bp_to_obj(bp)
register struct bill_x *bp;
{
	register struct obj *obj;
	register struct monst *mtmp;
	register unsigned int id = bp->bo_id;

	if(bp->useup)
		obj = o_on(id, billobjs);
	else if(!(obj = o_on(id, invent)) &&
		!(obj = o_on(id, fobj)) &&
		!(obj = o_on(id, migrating_objs))) {
		    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
			if ((obj = o_on(id, mtmp->minvent)) != 0)
			    return obj;
		    for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
			if ((obj = o_on(id, mtmp->minvent)) != 0)
			    return obj;
		}
	return(obj);
}

/* calculate the value that the shk will charge for [one of] an object */
static long
get_cost(obj, shkp)
register struct obj *obj;
register struct monst *shkp;	/* if angry, impose a surcharge */
{
	register long tmp = getprice(obj);

	if (!tmp) tmp = 5L;
	/* shopkeeper may notice if the player isn't very knowledgeable -
	   especially when gem prices are concerned */
	if (!objects[obj->otyp].oc_name_known)
		if (obj->oclass == GEM_CLASS) {
			/* all gems are priced high - real or not */
			if (objects[obj->otyp].oc_material == GLASS) {
				/* real gem's cost (worthless gems come
				   after jade but before luckstone) */
				tmp = (long)objects[
					obj->otyp - LUCKSTONE + JADE + 1].oc_cost;
			}
		} else if (!(obj->o_id % 4)) /* arbitrarily impose surcharge */
			tmp += tmp / 3L;
#ifdef TOURIST
	if((pl_character[0] == 'T' && u.ulevel < (MAXULEV/2))
	    || (uarmu && !uarm && !uarmc))	/* Hawaiian shirt visible */
		tmp += tmp / 3L;
#endif
	if (ACURR(A_CHA) > 18)		tmp /= 2L;
	else if (ACURR(A_CHA) > 17)	tmp -= tmp / 3L;
	else if (ACURR(A_CHA) > 15)	tmp -= tmp / 4L;
	else if (ACURR(A_CHA) < 6)	tmp *= 2L;
	else if (ACURR(A_CHA) < 8)	tmp += tmp / 2L;
	else if (ACURR(A_CHA) < 11)	tmp += tmp / 3L;
	if (tmp <= 0L) tmp = 1L;
	else if (obj->oartifact) tmp *= 4L;
	/* anger surcharge should match rile_shk's */
	if (shkp && ESHK(shkp)->surcharge) tmp += (tmp + 2L) / 3L;
	return tmp;
}

/* returns the price of a container's content.  the price
 * of the "top" container is added in the calling functions.
 * a different price quoted for selling as vs. buying.
 */
long
contained_cost(obj, shkp, price, usell)
register struct obj *obj;
register struct monst *shkp;
long price;
register boolean usell;
{
	register struct obj *otmp;

	/* the price of contained objects */
	for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {
	    register boolean goods = saleable(rooms[ESHK(shkp)->shoproom -
					   ROOMOFFSET].rtype-SHOPBASE, otmp);

	    if(otmp->otyp == GOLD_PIECE) continue;

	    /* the "top" container is evaluated by caller */
	    if(usell) {
		if(goods && !otmp->unpaid &&
			otmp->oclass != BALL_CLASS &&
			!(otmp->oclass == FOOD_CLASS && otmp->oeaten) &&
			!(Is_candle(otmp) && otmp->age <
				20L * (long)objects[otmp->otyp].oc_cost))
		    price += set_cost(otmp, shkp);
	    } else if(!otmp->no_charge) {
		    price += get_cost(otmp, shkp);
	    }

	    if(Is_container(otmp))
		    price += contained_cost(otmp, shkp, price, usell);
	}

	return(price);
}

long
contained_gold(obj)
register struct obj *obj;
{
	register struct obj *otmp;
	register long value = 0L;

	/* accumulate contained gold */
	for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
	    if (otmp->otyp == GOLD_PIECE)
		value += otmp->quan;
	    else if (Is_container(otmp))
		value += contained_gold(otmp);

	return(value);
}

static void
dropped_container(obj)
register struct obj *obj;
{
	register struct obj *otmp;

	/* the "top" container is treated in the calling fn */
	for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {

	    if(otmp->otyp == GOLD_PIECE) continue;

	    if(!otmp->unpaid)
		otmp->no_charge = 1;

	    if(Is_container(otmp))
		dropped_container(otmp);
	}
}

void
picked_container(obj)
register struct obj *obj;
{
	register struct obj *otmp;

	/* the "top" container is treated in the calling fn */
	for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {

	    if(otmp->otyp == GOLD_PIECE) continue;

	    if(otmp->no_charge)
		otmp->no_charge = 0;

	    if(Is_container(otmp))
		picked_container(otmp);
	}
}

/* calculate how much the shk will pay when buying [all of] an object */
static long
set_cost(obj, shkp)
register struct obj *obj;
register struct monst *shkp;
{
	long tmp = getprice(obj) * obj->quan;

#ifdef TOURIST
	if ((pl_character[0] == 'T' && u.ulevel < (MAXULEV/2))
	    || (uarmu && !uarm && !uarmc))	/* Hawaiian shirt visible */
		tmp /= 3L;
	else
#endif
		tmp /= 2L;
	/* shopkeeper may notice if the player isn't very knowledgeable -
	   especially when gem prices are concerned */
	if (!objects[obj->otyp].oc_name_known) {
		if (obj->oclass == GEM_CLASS) {
			/* different shop keepers give different prices */
			if (objects[obj->otyp].oc_material == GEMSTONE ||
			    objects[obj->otyp].oc_material == GLASS) {
				tmp = (obj->otyp % (6 - shkp->m_id % 3));
				tmp = (tmp + 3) * obj->quan;
			}
		} else if (tmp > 1L && !rn2(4))
			tmp -= tmp / 4L;
	}
	return tmp;
}

/* called from doinv(invent.c) for inventory of unpaid objects */
long
unpaid_cost(unp_obj)
register struct obj *unp_obj;	/* known to be unpaid */
{
	register struct bill_x *bp = (struct bill_x *)0;
	register struct monst *shkp;

	for(shkp = next_shkp(fmon, TRUE); shkp;
					shkp = next_shkp(shkp->nmon, TRUE))
	    if ((bp = onbill(unp_obj, shkp, TRUE)) != 0) break;

	/* onbill() gave no message if unexpected problem occurred */
	if(!bp) impossible("unpaid_cost: object wasn't on any bill!");

	return bp ? unp_obj->quan * bp->price : 0L;
}

static void
add_one_tobill(obj, dummy)
register struct obj *obj;
register boolean dummy;
{
	register struct monst *shkp;
	register struct bill_x *bp;
	register int bct;
	register char roomno = *u.ushops;

	if(!*u.ushops) return;

	if(!(shkp = shop_keeper(roomno))) return;

	if(!inhishop(shkp)) return;

	if(onbill(obj, shkp, FALSE) || /* perhaps thrown away earlier */
		    (obj->oclass == FOOD_CLASS && obj->oeaten))
		return;

	if(ESHK(shkp)->billct == BILLSZ) {
		You("got that for free!");
		return;
	}

	/* To recognize objects the shopkeeper is not interested in. -dgk
	 */
	if (obj->no_charge) {
		obj->no_charge = 0;
		return;
	}

	bct = ESHK(shkp)->billct;
	bp = &(ESHK(shkp)->bill_p[bct]);
	bp->bo_id = obj->o_id;
	bp->bquan = obj->quan;
	if(dummy) {		  /* a dummy object must be inserted into  */
	    bp->useup = 1;	  /* the billobjs chain here.  crucial for */
	    obj->nobj = billobjs; /* eating floorfood in shop.  see eat.c  */
	    billobjs = obj;
	} else	bp->useup = 0;
	bp->price = get_cost(obj, shkp);
	ESHK(shkp)->billct++;
	obj->unpaid = 1;
}

/* recursive billing of objects within containers. */
static void
bill_box_content(obj, ininv, dummy, shkp)
register struct obj *obj;
register boolean ininv, dummy;
register struct monst *shkp;
{
	register struct obj *otmp;

	for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {

		if(obj->otyp == GOLD_PIECE) continue;
		/* the "top" box is added in addtobill() */
		if(!otmp->no_charge)
		    add_one_tobill(otmp, dummy);
		if(Is_container(otmp))
		    bill_box_content(otmp, ininv, dummy, shkp);
	}

}

void
addtobill(obj, ininv, dummy, silent)
register struct obj *obj;
register boolean ininv, dummy, silent;
{
	register struct monst *shkp;
	register char roomno = *u.ushops;
	long ltmp = 0L, cltmp = 0L, gltmp = 0L;
	register boolean container = Is_container(obj);

	if(!*u.ushops) 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 == GOLD_CLASS) {
		costly_gold(obj->ox, obj->oy, obj->quan);
		return;
	}

	if(!obj->no_charge) ltmp = get_cost(obj, shkp);

	if (obj->no_charge && !container) {
		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);
		gltmp += contained_gold(obj);
	    }

	    if(ltmp) add_one_tobill(obj, dummy);
	    if(cltmp) bill_box_content(obj, ininv, dummy, shkp);
	    picked_container(obj); /* reset contained obj->no_charge */

	    ltmp += cltmp;

	    if(gltmp) {
		costly_gold(obj->ox, obj->oy, gltmp);
		if(!ltmp) return;
	    }

	    if(obj->no_charge)
		obj->no_charge = 0;

	} else /* i.e., !container */
	    add_one_tobill(obj, dummy);
speak:
	if(!shkp->msleep && !shkp->mfrozen && !silent) {
	    char buf[BUFSZ];

	    if(!ltmp) {
		pline("%s has no interest in %s.", Monnam(shkp),
					     the(xname(obj)));
		return;
	    }
	    Strcpy(buf, "\"For you, ");
	    if (ANGRY(shkp)) Strcat(buf, "scum ");
	    else {
		switch(rnd(4) + u.uevent.udemigod) {
		  case 1: Strcat(buf, "good");
			  break;
		  case 2: Strcat(buf, "honored");
			  break;
		  case 3: Strcat(buf, "most gracious");
			  break;
		  case 4: Strcat(buf, "esteemed");
			  break;
		  case 5: Strcat(buf, "most renowned and sacred");
			  break;
		}
#ifdef POLYSELF
		if(!is_human(uasmon)) Strcat(buf, " creature");
		else
#endif
		    Strcat(buf, (flags.female) ? " lady" : " sir");
	    }
	    /* after all, the shk is telling you what it is */
	    obj->dknown = 1;
	    exercise(A_WIS, TRUE);
	    if(ininv) {
		long quan = obj->quan;
		obj->quan = 1L; /* fool xname() into giving singular */
		pline("%s; only %ld %s %s.\"", buf, ltmp,
			(quan > 1L) ? "per" : "for this", xname(obj));
		obj->quan = quan;
	    } else
		pline("%s will cost you %ld zorkmid%s%s.",
			The(xname(obj)), ltmp, plur(ltmp),
			(obj->quan > 1L) ? " each" : "");
	} else if(!silent) {
	    if(ltmp) pline("The list price of %s is %ld zorkmid%s%s.",
				   the(xname(obj)), ltmp, plur(ltmp),
				   (obj->quan > 1L) ? " each" : "");
	    else pline("%s does not notice.", Monnam(shkp));
	}
}

void
splitbill(obj, otmp)
register struct obj *obj, *otmp;
{
	/* otmp has been split off from obj */
	register struct bill_x *bp;
	register long tmp;
	register struct monst *shkp = shop_keeper(*u.ushops);

	if(!shkp || !inhishop(shkp)) {
		impossible("splitbill: no resident shopkeeper??");
		return;
	}
	bp = onbill(obj, shkp, FALSE);
	if(!bp) {
		impossible("splitbill: not on bill?");
		return;
	}
	if(bp->bquan < otmp->quan) {
		impossible("Negative quantity on bill??");
	}
	if(bp->bquan == otmp->quan) {
		impossible("Zero quantity on bill??");
	}
	bp->bquan -= otmp->quan;

	if(ESHK(shkp)->billct == BILLSZ) otmp->unpaid = 0;
	else {
		tmp = bp->price;
		bp = &(ESHK(shkp)->bill_p[ESHK(shkp)->billct]);
		bp->bo_id = otmp->o_id;
		bp->bquan = otmp->quan;
		bp->useup = 0;
		bp->price = tmp;
		ESHK(shkp)->billct++;
	}
}

static void
sub_one_frombill(obj, shkp)
register struct obj *obj;
register struct monst *shkp;
{
	register struct bill_x *bp;

	if((bp = onbill(obj, shkp, FALSE)) != 0) {
		register struct obj *otmp;

		obj->unpaid = 0;
		if(bp->bquan > obj->quan){
			otmp = newobj(0);
			*otmp = *obj;
			bp->bo_id = otmp->o_id = flags.ident++;
			otmp->quan = (bp->bquan -= obj->quan);
			otmp->owt = 0;	/* superfluous */
			otmp->onamelth = 0;
			bp->useup = 1;
			otmp->nobj = billobjs;
			billobjs = otmp;
			return;
		}
		ESHK(shkp)->billct--;
#ifdef DUMB
		{
		/* DRS/NS 2.2.6 messes up -- Peter Kendell */
			int indx = ESHK(shkp)->billct;
			*bp = ESHK(shkp)->bill_p[indx];
		}
#else
		*bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct];
#endif
		return;
	} else if (obj->unpaid) {
		impossible("sub_one_frombill: unpaid object not on bill");
		obj->unpaid = 0;
	}
}

/* recursive check of unpaid objects within nested containers. */
void
subfrombill(obj, shkp)
register struct obj *obj;
register struct monst *shkp;
{
	register struct obj *otmp;

	sub_one_frombill(obj, shkp);

	if(Is_container(obj))
	    for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {
		if(otmp->otyp == GOLD_PIECE) continue;

		if(Is_container(otmp))
		    subfrombill(otmp, shkp);
		else
		    sub_one_frombill(otmp, shkp);
	    }
}

static long
stolen_container(obj, shkp, price, ininv)
register struct obj *obj;
register struct monst *shkp;
long price;
register boolean ininv;
{
	register struct obj *otmp;

	if(ininv && obj->unpaid)
	    price += get_cost(obj, shkp);
	else {
	    if(!obj->no_charge)
		price += get_cost(obj, shkp);
	    obj->no_charge = 0;
	}

	/* the price of contained objects, if any */
	for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {

	    if(otmp->otyp == GOLD_PIECE) continue;

	    if(!Is_container(otmp)) {
		if(ininv) {
		    if(otmp->unpaid)
			price += get_cost(otmp, shkp);
		} else {
		    if(!otmp->no_charge) {
			if(!(otmp->oclass == BALL_CLASS ||
			    (otmp->oclass == FOOD_CLASS && otmp->oeaten) ||
			    (Is_candle(otmp) && otmp->age <
				  20L * (long)objects[otmp->otyp].oc_cost))
			  ) price += get_cost(otmp, shkp);
		    }
		    otmp->no_charge = 0;
		}
	    } else
		price += stolen_container(otmp, shkp, price, ininv);
	}

	return(price);
}

long
stolen_value(obj, x, y, peaceful, silent)
register struct obj *obj;
register xchar x, y;
register boolean peaceful, silent;
{
	register long value = 0L, gvalue = 0L;
	register struct monst *shkp;
	register boolean goods;

	shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));

	if (!shkp || !inhishop(shkp))
	    return (0L);

	goods = saleable(rooms[ESHK(shkp)->shoproom -
				   ROOMOFFSET].rtype-SHOPBASE, obj);
	goods = (goods && !obj->no_charge);

	if(obj->otyp == GOLD_PIECE) {
	    gvalue += obj->quan;
	} else if(Is_container(obj)) {
	    register boolean ininv = !!count_unpaid(obj->cobj);

	    value += stolen_container(obj, shkp, value, ininv);
	    if(!ininv) gvalue += contained_gold(obj);
	} else if(goods) {
	    value += get_cost(obj, shkp);
	}

	if(gvalue + value == 0L) return(0L);

	value += gvalue;

	if(peaceful) {
	    value = check_credit(value, shkp);
	    ESHK(shkp)->debit += value;

	    if(!silent) {
		if(obj->otyp == GOLD_PIECE)
		    You("owe %s %ld zorkmids!", mon_nam(shkp), value);
		else You("owe %s %ld zorkmids for %s!",
			mon_nam(shkp),
			value,
			obj->quan > 1L ? "them" : "it");
	    }
	} else {
	    ESHK(shkp)->robbed += value;

	    if(!silent) {
		if(cansee(shkp->mx, shkp->my)) {
		    if(ESHK(shkp)->customer[0] == 0)
			(void) strncpy(ESHK(shkp)->customer,plname,PL_NSIZ);
		    Norep("%s booms: \"%s, you are a thief!\"",
				Monnam(shkp), plname);
		} else  Norep("You hear a scream, \"Thief!\"");
	    }
	    hot_pursuit(shkp);
	    (void) angry_guards(FALSE);
	}
	return(value);
}

/* auto-response flag for/from "sell foo?" 'a' => 'y', 'q' => 'n' */
static char sell_response = 'a';

void
sellobj_state(deliberate)	/* called from dodrop(do.c) and doddrop() */
register boolean deliberate;
{
	/* If we're deliberately dropping something, there's no automatic
	response to the shopkeeper's "want to sell" query; however, if we
	accidentally drop anything, the shk will buy it/them without asking.
	This retains the old pre-query risk that slippery fingers while in
	shops entailed:  you drop it, you've lost it.
	 */
	sell_response = deliberate ? '\0' : 'a';
}

void
sellobj(obj, x, y)
register struct obj *obj;
register xchar x, y;
{
	register struct monst *shkp;
	register struct eshk *eshkp;
	register long ltmp = 0L, cltmp = 0L, gltmp = 0L, offer;
	boolean saleitem, cgold = FALSE, container = Is_container(obj);
	boolean isgold = (obj->oclass == GOLD_CLASS);

	if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) ||
	   !inhishop(shkp)) return;
	if(!costly_spot(x, y))	return;
	if(!*u.ushops) return;

	saleitem = saleable(rooms[ESHK(shkp)->shoproom -
					ROOMOFFSET].rtype-SHOPBASE, obj);

	if(obj->unpaid && !container && !isgold) {
	    sub_one_frombill(obj, shkp);
	    return;
	}
	if(container) {
	    if(obj->cobj == (struct obj *)0) {
		if(obj->unpaid) {
		    sub_one_frombill(obj, shkp);
		    return;
		}
	    } else {
		/* find the price of content before subfrombill */
		cltmp += contained_cost(obj, shkp, cltmp, TRUE);
		/* find the value of contained gold */
		gltmp += contained_gold(obj);
		cgold = (gltmp > 0L);
	    }
	}

	if(!isgold && !obj->unpaid && saleitem)
	    ltmp = set_cost(obj, shkp);

	offer = ltmp + cltmp;

	if(!isgold && (offer + gltmp) == 0L) {
		register boolean unpaid = (obj->unpaid ||
				  (container && count_unpaid(obj->cobj)));

		if(container) {
		    if(obj->cobj != (struct obj *)0) {
			dropped_container(obj);
			if(obj->unpaid || count_unpaid(obj->cobj))
			    subfrombill(obj, shkp);
		    } else obj->no_charge = 1;
		} else obj->no_charge = 1;

		if(!unpaid)
		    pline("%s seems uninterested.", Monnam(shkp));
		return;
	}

	/* you dropped something of your own - probably want to sell it */
	if(shkp->msleep || !shkp->mcanmove) {
		if(container && obj->cobj != (struct obj *)0) {
		    dropped_container(obj);
		}
		if(!shkp->mcanmove) {
		    if(ANGRY(shkp) && !rn2(4))
			pline("%s utters a curse.", Monnam(shkp));
		    else pline("%s is indisposed.", Monnam(shkp));
		} else if(!rn2(3)) {
		    pline("%s snores indifferently.", Monnam(shkp));
		}
		subfrombill(obj, shkp);
		return;
	}

	eshkp = ESHK(shkp);

	if(isgold || cgold) {
		if(ANGRY(shkp)) {
		    if(!offer) {
			 pline("%s is not appeased.", Monnam(shkp));
			 if(cgold) subfrombill(obj, shkp);
			 return;
		    } else goto move_on;
		}

		if(!cgold) gltmp = obj->quan;

		if(eshkp->debit >= gltmp) {
		    if(eshkp->loan) { /* you carry shop's gold */
			 if(eshkp->loan >= gltmp)
			     eshkp->loan -= gltmp;
			 else eshkp->loan = 0L;
		    }
		    eshkp->debit -= gltmp;
		    Your("debt is %spaid off.",
				eshkp->debit ? "partially " : "");
		} else {
		    long delta = gltmp - eshkp->debit;

		    eshkp->credit += delta;
		    if(eshkp->debit) {
			eshkp->debit = 0L;
			eshkp->loan = 0L;
			Your("debt is paid off.");
		    }
		    pline("%ld zorkmid%s added to your credit.",
				delta, delta > 1L ? "s are" : " is");
		}
		if(offer) goto move_on;
		else {
		    if(container && obj->cobj != (struct obj *)0) {
			dropped_container(obj);
		    }
		    subfrombill(obj, shkp);
		    obj->no_charge = 1;
		    return;
		}
	}
move_on:
	if (ANGRY(shkp)) { /* they become shop-objects, no pay */
		pline("Thank you, scum!");
		subfrombill(obj, shkp);
		return;
	}

	if((!saleitem && !(container && cltmp > 0L))
	   || eshkp->billct == BILLSZ
	   || obj->oclass == BALL_CLASS || offer == 0L
	   || (obj->oclass == FOOD_CLASS && obj->oeaten)
	   || (Is_candle(obj) &&
		   obj->age < 20L * (long)objects[obj->otyp].oc_cost)) {
		pline("%s seems not interested%s.", Monnam(shkp),
					   cgold ? " in the rest" : "");
		if(container && obj->cobj != (struct obj *)0) {
		    dropped_container(obj);
		}
		obj->no_charge = 1;
		return;
	}

	if(eshkp->robbed) {  /* shkp is not angry? */
		if((eshkp->robbed -= offer < 0L))
			eshkp->robbed = 0L;
		verbalize(
  "Thank you for your contribution to restock this recently plundered shop.");
		subfrombill(obj, shkp);
		return;
	}

	if(!shkp->mgold) {
		long tmpcr = (ltmp + cltmp) * 2L;

		pline("%s cannot pay you at present.", Monnam(shkp));
		pline("Will you accept %ld zorkmids in credit for %s? ",
					 tmpcr, doname(obj));
		/* cannot use a yn function here */
		if (readchar() == 'y') {
		    You("have %ld zorkmids in %scredit.", tmpcr,
				ESHK(shkp)->credit > 0L ? "additional " : "");
		    ESHK(shkp)->credit += tmpcr;
		    subfrombill(obj, shkp);
		} else {
		    if(container && obj->cobj != (struct obj *)0) {
				dropped_container(obj);
		    }
		    subfrombill(obj, shkp);
		    obj->no_charge = 1;
		}
	} else {
		int qlen;
		char qbuf[BUFSZ];
		boolean short_funds = (offer > shkp->mgold);

		if (short_funds) offer = shkp->mgold;

		if (!sell_response) {
		    Sprintf(qbuf,
			    "%s offers%s %ld gold piece%s for%s your %s.",
			    Monnam(shkp), short_funds ? " only" : "",
			    offer, plur(offer),
			    (!ltmp && cltmp) ? " the contents of" : "",
			    xname(obj));
		    qlen = strlen(qbuf);
		    /*  Will the prompt fit on the topline? (or would
		     *	"--more--" force line wrap anyway?)  If so, combine
		     *	the message and prompt; otherwise, flush message
		     *	and prompt separately.
		     */
		    if (qlen > COLNO - 24 && qlen <= COLNO - 8)
			pline(qbuf),  qbuf[0] = '\0';
		    else  Strcat(qbuf, "  ");
		    Strcat(strcat(qbuf, "Sell "),
			    (obj->quan == 1L ? "it?" : "them?"));
		} else  qbuf[0] = '\0';		/* just to pacify lint */

		switch (sell_response ? sell_response : ynaq(qbuf)) {
		 case 'q':  sell_response = 'n';
		 case 'n':  if(container && obj->cobj != (struct obj *)0) {
				dropped_container(obj);
			    }
			    subfrombill(obj, shkp);
			    obj->no_charge = 1;
			    break;
		 case 'a':  sell_response = 'y';
		 case 'y':  subfrombill(obj, shkp);
			    pay(-offer, shkp);
			    You("sold %s for %ld gold piece%s.", doname(obj),
				offer, plur(offer));
			    break;
		 default:   impossible("invalid sell response");
		}
	}
}

int
doinvbill(mode)
int mode;		/* 0: deliver count 1: paged */
{
	register struct monst* shkp;
	register struct bill_x *bp, *end_bp;
	register struct obj *obj;
	long totused;
	char *buf_p;
	winid datawin;

	shkp = shop_keeper(*u.ushops);

	if(mode == 0) {
	    register int cnt = 0;

	    if(shkp && inhishop(shkp))
		for (bp = ESHK(shkp)->bill_p,
			end_bp = &(ESHK(shkp)->bill_p[ESHK(shkp)->billct]);
			bp < end_bp; bp++)
		    if(bp->useup ||
		       ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan))
			cnt++;
	    return(cnt);
	}

	if(!shkp || !inhishop(shkp)) {
		impossible("doinvbill: no shopkeeper?");
		return(0);
	}

	datawin = create_nhwindow(NHW_MENU);
	putstr(datawin, 0, "Unpaid articles already used up:");
	putstr(datawin, 0, "");

	totused = 0L;
	for (bp = ESHK(shkp)->bill_p,
		end_bp = &(ESHK(shkp)->bill_p[ESHK(shkp)->billct]);
		bp < end_bp; bp++) {
	    obj = bp_to_obj(bp);
	    if(!obj) {
		impossible("Bad shopkeeper administration.");
		goto quit;
	    }
	    if(bp->useup || bp->bquan > obj->quan) {
		register long oquan, uquan;
		long thisused;

		oquan = obj->quan;
		uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
		thisused = bp->price * uquan;
		totused += thisused;
		obj->quan = uquan;		/* cheat doname */
		buf_p = xprname(obj, ' ', FALSE, thisused);
		obj->quan = oquan;		/* restore value */
		putstr(datawin, 0, buf_p);
	    }
	}
	buf_p = xprname((struct obj *)0, '*', FALSE, totused);
	putstr(datawin, 0, "");
	putstr(datawin, 0, buf_p);
	display_nhwindow(datawin, FALSE);
    quit:
	destroy_nhwindow(datawin);
	return(0);
}

#define HUNGRY	2

static long
getprice(obj)
register struct obj *obj;
{
	register long tmp = (long) objects[obj->otyp].oc_cost;

	switch(obj->oclass) {
	case FOOD_CLASS:
		/* simpler hunger check, (2-4)*cost */
		if (u.uhs >= HUNGRY) tmp *= (long) u.uhs;
		if (obj->oeaten) tmp = 0L;
		break;
	case WAND_CLASS:
		if (obj->spe == -1) tmp = 0L;
		break;
	case POTION_CLASS:
		if (obj->otyp == POT_WATER && !obj->blessed && !obj->cursed)
			tmp = 0L;
		break;
	case ARMOR_CLASS:
	case WEAPON_CLASS:
		if (obj->spe > 0) tmp += 10L * (long) obj->spe;
		break;
	case CHAIN_CLASS:
		pline("Strange... carrying a chain?");
		break;
	case TOOL_CLASS:
		if (Is_candle(obj) &&
			obj->age < 20L * (long)objects[obj->otyp].oc_cost)
		    tmp /= 2L;
		break;
	}
	if (obj->oartifact) tmp *= 25L;
	return tmp;
}

int
shkcatch(obj, x, y)
register struct obj *obj;
register xchar x, y;
{
	register struct monst *shkp;

	if (!(shkp = shop_keeper(inside_shop(x, y))) ||
	    !inhishop(shkp)) return(0);

	if (shkp->mcanmove && !shkp->msleep &&
	    (*u.ushops != ESHK(shkp)->shoproom || !inside_shop(u.ux, u.uy)) &&
	    dist2(shkp->mx, shkp->my, x, y) < 3 &&
	    /* if it is the shk's pos, you hit and anger him */
	    (shkp->mx != x || shkp->my != y)) {
		if (mnearto(shkp, x, y, TRUE))
		    verbalize("Out of my way, scum!");
		pline("%s nimbly catches %s.", Monnam(shkp), the(xname(obj)));
		mpickobj(shkp, obj);
		subfrombill(obj, shkp);
		return(1);
	}
	return(0);
}

void
add_damage(x, y, cost)
register xchar x, y;
long cost;
{
	struct damage *tmp_dam;
	char *shops;

	if (IS_DOOR(levl[x][y].typ))
	    /* Don't schedule for repair unless it's a real shop entrance */
	    for (shops = in_rooms(x, y, SHOPBASE); *shops; shops++) {
		struct monst *mtmp = shop_keeper(*shops);

		if (!mtmp)
		    continue;
		if ((x != ESHK(mtmp)->shd.x) || (y != ESHK(mtmp)->shd.y))
		    return;
	    }
	tmp_dam = (struct damage *)alloc((unsigned)sizeof(struct damage));
	tmp_dam->when = monstermoves;
	tmp_dam->place.x = x;
	tmp_dam->place.y = y;
	tmp_dam->cost = cost;
	tmp_dam->typ = levl[x][y].typ;
	tmp_dam->next = level.damagelist;
	level.damagelist = tmp_dam;
	/* If player saw damage, display as a wall forever */
	if (cansee(x, y))
	    levl[x][y].seen = 1;
}

/*
 * Do something about damage. Either (!croaked) try to repair it, or
 * (croaked) just discard damage structs for non-shared locations, since
 * they'll never get repaired. Assume that shared locations will get
 * repaired eventually by the other shopkeeper(s). This might be an erroneous
 * assumption (they might all be dead too), but we have no reasonable way of
 * telling that.
 */
static
void
remove_damage(shkp, croaked)
register struct monst *shkp;
register boolean croaked;
{
	register struct damage *tmp_dam, *tmp2_dam;
	register boolean did_repair = FALSE, saw_door = FALSE;
	register boolean saw_floor = FALSE, stop_picking = FALSE;
	uchar saw_walls = 0;

	tmp_dam = level.damagelist;
	tmp2_dam = 0;
	while (tmp_dam) {
	    register xchar x = tmp_dam->place.x, y = tmp_dam->place.y;
	    char shops[5];
	    uchar disposition;

	    disposition = 0;
	    Strcpy(shops, in_rooms(x, y, SHOPBASE));
	    if (index(shops, ESHK(shkp)->shoproom)) {
		if (croaked)
		    disposition = (shops[1])? 0 : 1;
		else if (stop_picking)
		    disposition = repair_damage(shkp, tmp_dam);
		else {
		    /* Defer the stop_occupation() until after repair msgs */
		    if (closed_door(x, y))
			stop_picking = picking_at(x, y);
		    disposition = repair_damage(shkp, tmp_dam);
		    if (!disposition)
			stop_picking = FALSE;
		}
	    }

	    if (!disposition) {
		tmp2_dam = tmp_dam;
		tmp_dam = tmp_dam->next;
		continue;
	    }

	    if (disposition > 1) {
		did_repair = TRUE;
		if (cansee(x, y)) {
		    if (IS_WALL(levl[x][y].typ))
			saw_walls++;
		    else if (IS_DOOR(levl[x][y].typ))
			saw_door = TRUE;
		    else
			saw_floor = TRUE;
		}
	    }

	    tmp_dam = tmp_dam->next;
	    if (!tmp2_dam) {
		free((genericptr_t)level.damagelist);
		level.damagelist = tmp_dam;
	    } else {
		free((genericptr_t)tmp2_dam->next);
		tmp2_dam->next = tmp_dam;
	    }
	}
	if (!did_repair)
	    return;
	if (saw_walls) {
	    pline("Suddenly, %s section%s of wall close%s up!",
		  (saw_walls == 1) ? "a" : (saw_walls <= 3) ?
						  "some" : "several",
		  (saw_walls == 1) ? "" : "s", (saw_walls == 1) ? "s" : "");
	    if (saw_door)
		pline("The shop door reappears!");
	    if (saw_floor)
		pline("The floor is repaired!");
	} else {
	    if (saw_door)
		pline("Suddenly, the shop door reappears!");
	    else if (saw_floor)
		pline("Suddenly, the floor damage is gone!");
	    else if (inside_shop(u.ux, u.uy) == ESHK(shkp)->shoproom)
		You("feel more claustrophobic than before.");
	    else if (flags.soundok && !rn2(10))
		Norep("The dungeon acoustics noticeably change.");
	}
	if (stop_picking)
		stop_occupation();
}

/*
 * 0: repair postponed, 1: silent repair (no messages), 2: normal repair
 */
char
repair_damage(shkp, tmp_dam)
register struct monst *shkp;
register struct damage *tmp_dam;
{
	register xchar x, y, i;
	xchar litter[9];
	register struct monst *mtmp;
	register struct obj *otmp;
	register struct trap *ttmp;

	if ((monstermoves - tmp_dam->when) < REPAIR_DELAY)
	    return(0);
	if (ESHK(shkp)->following)
	    return(0);
	x = tmp_dam->place.x;
	y = tmp_dam->place.y;
	if (!IS_ROOM(tmp_dam->typ)) {
	    if ((x == u.ux) && (y == u.uy))
#ifdef POLYSELF
		if (!passes_walls(uasmon))
#endif
		    return(0);
	    if ((x == shkp->mx) && (y == shkp->my))
		return(0);
	    if ((mtmp = m_at(x, y)) && (!passes_walls(mtmp->data)))
		return(0);
	}
	if ((ttmp = t_at(x, y)) != 0)
	    deltrap(ttmp);
	if (IS_ROOM(tmp_dam->typ)) {
	    /* No messages if player already filled trapdoor */
	    if (!ttmp)
		return(1);
	    newsym(x, y);
	    return(2);
	}
	if (!ttmp && (tmp_dam->typ == levl[x][y].typ) &&
	    (!IS_DOOR(tmp_dam->typ) || (levl[x][y].doormask > D_BROKEN)))
	    /* No messages if player already replaced shop door */
	    return(1);
	levl[x][y].typ = tmp_dam->typ;
	(void) memset((genericptr_t)litter, 0, sizeof(litter));
	if ((otmp = level.objects[x][y]) != 0) {
	    /* Scatter objects haphazardly into the shop */
#define NEED_UPDATE 1
#define OPEN	    2
#define INSHOP	    4
#define horiz(i) ((i%3)-1)
#define vert(i)  ((i/3)-1)
	    for (i = 0; i < 9; i++) {
		if ((i == 4) || (!ZAP_POS(levl[x+horiz(i)][y+vert(i)].typ)))
		    continue;
		litter[i] = OPEN;
		if (inside_shop(x+horiz(i),
				y+vert(i)) == ESHK(shkp)->shoproom)
		    litter[i] |= INSHOP;
	    }
	    if (Punished && ((uchain->ox == x && uchain->oy == y) ||
					(uball->ox == x && uball->oy == y))) {
		/*
		 * Either the ball or chain is in the repair location.
		 *
		 * Take the easy way out and put ball&chain under hero.
		 */
		verbalize("Get your junk out of my wall!");
		unplacebc();	/* pick 'em up */
		placebc();	/* put 'em down */
	    }
	    while ((otmp = level.objects[x][y]) != 0)
		/* Don't mess w/ boulders -- just merge into wall */
		if ((otmp->otyp == BOULDER) || (otmp->otyp == ROCK)) {
		    freeobj(otmp);
		    obfree(otmp, (struct obj *)0);
		} else {
		    while (!(litter[i = rn2(9)] & INSHOP));
			remove_object(otmp);
			place_object(otmp, x+horiz(i), y+vert(i));
			litter[i] |= NEED_UPDATE;
		}
	}
	block_point(x, y);
	if(IS_DOOR(tmp_dam->typ)) {
	    levl[x][y].doormask = D_CLOSED; /* arbitrary */
	    newsym(x, y);
	} else {
	    levl[x][y].doormask = D_NODOOR;
	    if (IS_WALL(tmp_dam->typ) && cansee(x, y)) {
	    /* Player sees actual repair process, so they KNOW it's a wall */
		levl[x][y].seen = 1;
		newsym(x, y);
	    } else if (levl[x][y].seen) {
		/* Force a display update */
		levl[x][y].diggable |= W_REPAIRED;
	    }
	}
	for (i = 0; i < 9; i++)
	    if (litter[i] & NEED_UPDATE)
		newsym(x+horiz(i), y+vert(i));
	return(2);
#undef NEED_UPDATE
#undef OPEN
#undef INSHOP
#undef vert
#undef horiz
}

/*
 * shk_move: return 1: moved  0: didn't  -1: let m_move do it  -2: died
 */
int
shk_move(shkp)
register struct monst *shkp;
{
	register xchar gx,gy,omx,omy;
	register int udist;
	register schar appr;
	register struct eshk *eshkp = ESHK(shkp);
	int z;
	boolean uondoor = FALSE, satdoor, avoid = FALSE, badinv;

	omx = shkp->mx;
	omy = shkp->my;

	if (inhishop(shkp))
	    remove_damage(shkp, FALSE);

	if((udist = distu(omx,omy)) < 3 &&
	   (shkp->data != &mons[PM_GRID_BUG] || (omx==u.ux || omy==u.uy))) {
		if(ANGRY(shkp) ||
		   (Conflict && !resist(shkp, RING_CLASS, 0, 0))) {
			if(Displaced)
			  Your("displaced image doesn't fool %s!",
				mon_nam(shkp));
			(void) mattacku(shkp);
			return(0);
		}
		if(eshkp->following) {
			if(strncmp(eshkp->customer, plname, PL_NSIZ)) {
			    verbalize("Hello, %s!  I was looking for %s.",
				    plname, eshkp->customer);
				    eshkp->following = 0;
			    return(0);
			}
			if(moves > followmsg+4) {
			    verbalize("Hello, %s!  Didn't you forget to pay?",
				    plname);
			    followmsg = moves;
			    if (!rn2(4)) {
	    pline ("%s doesn't like customers who don't pay.", Monnam(shkp));
				rile_shk(shkp);
			    }
			}
			if(udist < 2)
			    return(0);
		}
	}

	appr = 1;
	gx = eshkp->shk.x;
	gy = eshkp->shk.y;
	satdoor = (gx == omx && gy == omy);
	if(eshkp->following || ((z = holetime()) >= 0 && z*z <= udist)){
		if(udist > 4)
		    return(-1);	/* leave it to m_move */
		gx = u.ux;
		gy = u.uy;
	} else if(ANGRY(shkp)) {
		/* Move towards the hero if the shopkeeper can see him. */
		if(shkp->mcansee && m_canseeu(shkp)) {
			gx = u.ux;
			gy = u.uy;
		}
		avoid = FALSE;
	} else {
#define	GDIST(x,y)	(dist2(x,y,gx,gy))
		if(Invis) {
		    avoid = FALSE;
		} else {
		    uondoor = (u.ux == eshkp->shd.x && u.uy == eshkp->shd.y);
		    if(uondoor) {
			badinv = (!!carrying(PICK_AXE));
			if(satdoor && badinv)
			    return(0);
			avoid = !badinv;
		    } else {
			avoid = (*u.ushops && distu(gx,gy) > 8);
			badinv = FALSE;
		    }

		    if(((!eshkp->robbed && !eshkp->billct && !eshkp->debit)
			|| avoid) && GDIST(omx,omy) < 3) {
			if (!badinv && !onlineu(omx,omy))
			    return(0);
			if(satdoor)
			    appr = gx = gy = 0;
		    }
		}
	}

	return(move_special(shkp,inhishop(shkp),
			    appr,uondoor,avoid,omx,omy,gx,gy));
}

/* for use in levl_follower (mondata.c) */
boolean
is_fshk(mtmp)
register struct monst *mtmp;
{
	return(mtmp->isshk && ESHK(mtmp)->following);
}

/* You are digging in the shop. */
void
shopdig(fall)
register int fall;
{
    register struct monst *shkp = shop_keeper(*u.ushops);

    if(!shkp) return;

    if(!inhishop(shkp)) {
	if (pl_character[0] == 'K') adjalign(-sgn(u.ualign.type));
	return;
    }

    if(!fall) {
	if(u.utraptype == TT_PIT)
	    verbalize("Be careful, %s, or you might fall through the floor.",
		flags.female ? "madam" : "sir");
	else
	    verbalize("%s, do not damage the floor here!",
			flags.female ? "Madam" : "Sir");
	if (pl_character[0] == 'K') adjalign(-sgn(u.ualign.type));
    } else if(!um_dist(shkp->mx, shkp->my, 5) &&
		!shkp->msleep && shkp->mcanmove &&
		(ESHK(shkp)->billct || ESHK(shkp)->debit)) {
	    register struct obj *obj, *obj2;

	    if (distu(shkp->mx, shkp->my) > 2) {
		mnexto(shkp);
		/* for some reason the shopkeeper can't come next to you */
		if (distu(shkp->mx, shkp->my) > 2) {
		    pline("%s curses you in anger and frustration!",
					shkname(shkp));
		    rile_shk(shkp);
		    return;
		} else pline("%s leaps, and grabs your backpack!",
					shkname(shkp));
	    } else pline("%s grabs your backpack!", shkname(shkp));

	    for(obj = invent; obj; obj = obj2) {
		obj2 = obj->nobj;
		if(obj->owornmask) continue;
#ifdef WALKIES
		if(obj->otyp == LEASH && obj->leashmon) continue;
#endif
		freeinv(obj);
		obj->nobj = shkp->minvent;
		shkp->minvent = obj;
		subfrombill(obj, shkp);
	    }
    }
}

#ifdef KOPS
STATIC_OVL void
makekops(mm)		/* returns the number of (all types of) Kops  made */
coord *mm;
{
	register int cnt = depth(&u.uz) + rnd(5);
	register int scnt = (cnt / 3) + 1;	/* at least one sarge */
	register int lcnt = (cnt / 6);		/* maybe a lieutenant */
	register int kcnt = (cnt / 9);		/* and maybe a kaptain */

	if (!(mons[PM_KEYSTONE_KOP].geno & G_EXTINCT)) {
	    while(cnt--) {
		if (enexto(mm, mm->x, mm->y, &mons[PM_KEYSTONE_KOP]))
			(void) makemon(&mons[PM_KEYSTONE_KOP], mm->x, mm->y);
	    }
	}
	if (!(mons[PM_KOP_SERGEANT].geno & G_EXTINCT)) {
	    while(scnt--) {
		if (enexto(mm, mm->x, mm->y, &mons[PM_KOP_SERGEANT]))
			(void) makemon(&mons[PM_KOP_SERGEANT], mm->x, mm->y);
	    }
	}
	if (!(mons[PM_KOP_LIEUTENANT].geno & G_EXTINCT)) {
	    while(lcnt--) {
		if (enexto(mm, mm->x, mm->y, &mons[PM_KOP_LIEUTENANT]))
		    (void) makemon(&mons[PM_KOP_LIEUTENANT], mm->x, mm->y);
	    }
	}
	if (!(mons[PM_KOP_KAPTAIN].geno & G_EXTINCT)) {
	    while(kcnt--) {
		if (enexto(mm, mm->x, mm->y, &mons[PM_KOP_KAPTAIN]))
		    (void) makemon(&mons[PM_KOP_KAPTAIN], mm->x, mm->y);
	    }
	}
}
#endif	/* KOPS */

void
pay_for_damage(dmgstr)
const char *dmgstr;
{
	register struct monst *shkp = (struct monst *)0;
	char shops_affected[5];
	register boolean uinshp = (*u.ushops != '\0');
	char qbuf[80];
	register xchar x, y;
	register boolean dugwall = !strcmp(dmgstr, "dig into");
	struct damage *tmp_dam, *appear_here = 0;
	/* any number >= (80*80)+(24*24) would do, actually */
	long cost_of_damage = 0L;
	unsigned int nearest_shk = 7000, nearest_damage = 7000;
	int picks = 0;

	for (tmp_dam = level.damagelist;
	     (tmp_dam && (tmp_dam->when == monstermoves));
	     tmp_dam = tmp_dam->next) {
	    char *shp;

	    if (!tmp_dam->cost)
		continue;
	    cost_of_damage += tmp_dam->cost;
	    Strcpy(shops_affected,
		   in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
	    for (shp = shops_affected; *shp; shp++) {
		struct monst *tmp_shk;
		unsigned int shk_distance;

		if (!(tmp_shk = shop_keeper(*shp)))
		    continue;
		if (tmp_shk == shkp) {
		    unsigned int damage_distance =
				   distu(tmp_dam->place.x, tmp_dam->place.y);

		    if (damage_distance < nearest_damage) {
			nearest_damage = damage_distance;
			appear_here = tmp_dam;
		    }
		    continue;
		}
		if (!inhishop(tmp_shk))
		    continue;
		shk_distance = distu(tmp_shk->mx, tmp_shk->my);
		if (shk_distance > nearest_shk)
		    continue;
		if ((shk_distance == nearest_shk) && picks) {
		    if (rn2(++picks))
			continue;
		} else
		    picks = 1;
		shkp = tmp_shk;
		nearest_shk = shk_distance;
		appear_here = tmp_dam;
		nearest_damage = distu(tmp_dam->place.x, tmp_dam->place.y);
	    }
	}

	if (!cost_of_damage || !shkp)
	    return;

	x = appear_here->place.x;
	y = appear_here->place.y;

	/* not the best introduction to the shk... */
	(void) strncpy(ESHK(shkp)->customer,plname,PL_NSIZ);

	/* if the shk is already on the war path, be sure it's all out */
	if(ANGRY(shkp) || ESHK(shkp)->following) {
		hot_pursuit(shkp);
		return;
	}

	/* if the shk is not in their shop.. */
	if(!*in_rooms(shkp->mx,shkp->my,SHOPBASE)) {
		if(!cansee(shkp->mx, shkp->my))
			return;
		goto getcad;
	}

	if(uinshp) {
		if(um_dist(shkp->mx, shkp->my, 1) &&
			!um_dist(shkp->mx, shkp->my, 3)) {
		    pline("%s leaps towards you!", shkname(shkp));
		    mnexto(shkp);
		}
		if(um_dist(shkp->mx, shkp->my, 1)) goto getcad;
	} else {
	    /*
	     * Make shkp show up at the door.  Effect:  If there is a monster
	     * in the doorway, have the hero hear the shopkeeper yell a bit,
	     * pause, then have the shopkeeper appear at the door, having
	     * yanked the hapless critter out of the way.
	     */
	    if (MON_AT(x, y)) {
		if(flags.soundok) {
		    You("hear an angry voice:");
		    verbalize("Out of my way, scum!");
		    wait_synch();
#if defined(UNIX) || defined(VMS)
# if defined(SYSV) || defined(ULTRIX) || defined(VMS)
		    (void)
# endif
			sleep(1);
#endif
		}
	    }
	    (void) mnearto(shkp, x, y, TRUE);
	}

	if((um_dist(x, y, 1) && !uinshp) ||
			(u.ugold + ESHK(shkp)->credit) < cost_of_damage
				|| !rn2(50)) {
		if(um_dist(x, y, 1) && !uinshp) {
		    pline("%s shouts:", shkname(shkp));
		    verbalize("Who dared %s my %s?", dmgstr,
					 dugwall ? "shop" : "door");
		} else {
getcad:
		    verbalize("How dare you %s my %s?", dmgstr,
					 dugwall ? "shop" : "door");
		}
		hot_pursuit(shkp);
		return;
	}

	if(Invis) Your("invisibility does not fool %s!", shkname(shkp));
	Sprintf(qbuf,"\"Cad!  You did %ld zorkmids worth of damage!\"  Pay? ",
		 cost_of_damage);
	if(yn(qbuf) != 'n') {
		cost_of_damage = check_credit(cost_of_damage, shkp);
		u.ugold -= cost_of_damage;
		shkp->mgold += cost_of_damage;
		flags.botl = 1;
		pline("Mollified, %s accepts your restitution.",
			shkname(shkp));
		/* move shk back to his home loc */
		home_shk(shkp, FALSE);
		pacify_shk(shkp);
	} else {
		verbalize("Oh, yes!  You'll pay!");
		hot_pursuit(shkp);
		adjalign(-sgn(u.ualign.type));
	}
}

/* called in dokick.c when we kick an object that might be in a store */
boolean
costly_spot(x, y)
register xchar x, y;
{
	register struct monst *shkp;

	if (!level.flags.has_shop) return FALSE;
	shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
	if(!shkp || !inhishop(shkp)) return(FALSE);

	return(inside_shop(x, y) &&
		!(x == ESHK(shkp)->shk.x &&
			y == ESHK(shkp)->shk.y));
}

/* called by dotalk(sounds.c) when #chatting; returns obj if location
   contains shop goods and shopkeeper is willing & able to speak */
struct obj *
shop_object(x, y)
register xchar x, y;
{
    register struct obj *otmp;
    register struct monst *shkp;

    if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) || !inhishop(shkp))
	return(struct obj *)0;

    for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
	if (otmp->otyp != GOLD_PIECE)
	    break;
    /* note: otmp might have ->no_charge set, but that's ok */
    return (otmp && costly_spot(x, y) && NOTANGRY(shkp)
	    && !shkp->msleep && !shkp->mfrozen)
		? otmp : (struct obj *)0;
}

/* give price quotes for all objects linked to this one (ie, on this spot) */
void
price_quote(first_obj)
register struct obj *first_obj;
{
    register struct obj *otmp;
    char buf[BUFSZ], price[40];
    long cost;
    int cnt = 0;
    winid tmpwin;

    tmpwin = create_nhwindow(NHW_MENU);
    putstr(tmpwin, 0, "Fine goods for sale:");
    putstr(tmpwin, 0, "");
    for (otmp = first_obj; otmp; otmp = otmp->nexthere) {
	if (otmp->otyp == GOLD_PIECE) {
	 /* if (otmp == first_obj)  first_obj = otmp->nexthere; */
	    continue;	/* don't quote a price on this */
	} else if (otmp->no_charge) {
	    Strcpy(price, "no charge");
	} else {
	    cost = get_cost(otmp, (struct monst *)0);
	    Sprintf(price, "%ld zorkmid%s%s", cost, plur(cost),
		    otmp->quan > 1L ? " each" : "");
	}
	Sprintf(buf, "%s, %s", doname(otmp), price);
	putstr(tmpwin, 0, buf),  cnt++;
    }
    if (cnt > 1) {
	display_nhwindow(tmpwin, TRUE);
    } else if (cnt == 1) {
	cost = get_cost(first_obj, (struct monst *)0);
	pline("%s, price %ld zorkmid%s%s%s", doname(first_obj),
		cost, plur(cost), first_obj->quan > 1L ? " each" : "",
		shk_embellish(first_obj, cost));
    }
    destroy_nhwindow(tmpwin);
}

static const char *
shk_embellish(itm, cost)
register struct obj *itm;
long cost;
{
    if (!rn2(3)) {
	register int o, choice = rn2(5);
	if (choice == 0) choice = (cost < 100L ? 1 : cost < 500L ? 2 : 3);
	switch (choice) {
	    case 4:
		if (cost < 10L) break; else o = itm->oclass;
		if (o == FOOD_CLASS) return ", gourmets' delight!";
		if (objects[itm->otyp].oc_name_known
		    ? objects[itm->otyp].oc_magic
		    : (o == AMULET_CLASS || o == RING_CLASS   ||
		       o == WAND_CLASS   || o == POTION_CLASS ||
		       o == SCROLL_CLASS || o == SPBOOK_CLASS))
		    return ", painstakingly developed!";
		return ", superb craftsmanship!";
	    case 3: return ", finest quality.";
	    case 2: return ", an excellent choice.";
	    case 1: return ", a real bargain.";
	   default: break;
	}
    } else if (itm->oartifact) {
	return ", one of a kind!";
    }
    return ".";
}

#ifdef KOPS
static void
kops_gone(silent)
register boolean silent;
{
	register int cnt = 0;
	register struct monst *mtmp, *mtmp2;

	/* turn off automatic resurrection of kops */
	allow_kops = FALSE;

	for(mtmp = fmon; mtmp; mtmp = mtmp2) {
		mtmp2 = mtmp->nmon;
		if(mtmp->data->mlet == S_KOP) {
			mongone(mtmp);
			cnt++;
		}
	}
	if(cnt && !silent)
		pline("The Kops (disappointed) disappear into thin air.");
	allow_kops = TRUE;
}
#endif	/* KOPS */

static long
cost_per_charge(otmp)
register struct obj *otmp;
{
	register long tmp = 0L;
	register struct monst *shkp = shop_keeper(*u.ushops);

	if(!shkp || !inhishop(shkp)) return(0L); /* insurance */
	tmp = get_cost(otmp, shkp);

	/* The idea is to make the exhaustive use of */
	/* an unpaid item more expensive than buying */
	/* it outright.				     */
	if(otmp->otyp == MAGIC_LAMP) {			 /* 1 */
		tmp += tmp / 3L;
	} else if(otmp->otyp == MAGIC_MARKER) {		 /* 70 - 100 */
		/* no way to determine in advance   */
		/* how many charges will be wasted. */
		/* so, arbitrarily, one half of the */
		/* price per use.		    */
		tmp /= 2L;
	} else if(otmp->otyp == BAG_OF_TRICKS ||	 /* 1 - 20 */
		  otmp->otyp == HORN_OF_PLENTY) {
		tmp /= 5L;
	} else if(otmp->otyp == CRYSTAL_BALL ||		 /* 1 - 5 */
		  otmp->otyp == OIL_LAMP ||		 /* 1 - 10 */
		  otmp->otyp == BRASS_LANTERN ||
		 (otmp->otyp >= MAGIC_FLUTE &&
		  otmp->otyp <= DRUM_OF_EARTHQUAKE) ||	 /* 5 - 9 */
		  otmp->oclass == WAND_CLASS) {		 /* 3 - 11 */
		    if (otmp->spe > 1) tmp /= 4L;
	} else if (otmp->oclass == SPBOOK_CLASS) {
		    tmp -= tmp / 5L;
	}
	return(tmp);
}

/* for using charges of unpaid objects */
void
check_unpaid(otmp)
register struct obj *otmp;
{
	if(!*u.ushops) return;

	if(otmp->oclass != SPBOOK_CLASS && otmp->spe <= 0) return;

	if(otmp->unpaid) {
		register long tmp = cost_per_charge(otmp);
		register struct monst *shkp = shop_keeper(*u.ushops);

		if(!shkp || !inhishop(shkp)) return;

		if(otmp->oclass == SPBOOK_CLASS && tmp > 0L)
		    pline("\"%sYou owe%s %ld zorkmids.\"",
			rn2(2) ? "This is no free library, cad!  " : "",
			ESHK(shkp)->debit > 0L ? " additional" : "", tmp);
		ESHK(shkp)->debit += tmp;
		exercise(A_WIS, TRUE);		/* you just got info */
	}
}

void
costly_gold(x, y, amount)
register xchar x, y;
register long amount;
{
	register long delta;
	register struct monst *shkp;
	register struct eshk *eshkp;

	if(!costly_spot(x, y)) return;
	/* shkp now guaranteed to exist by costly_spot() */
	shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));

	eshkp = ESHK(shkp);
	if(eshkp->credit >= amount) {
	    if(eshkp->credit > amount)
		Your("credit is reduced by %ld zorkmid%s.",
					amount, plur(amount));
	    else Your("credit is erased.");
	    eshkp->credit -= amount;
	} else {
	    delta = amount - eshkp->credit;
	    if(eshkp->credit)
		Your("credit is erased.");
	    if(eshkp->debit)
		Your("debt increases by %ld zorkmid%s.",
					delta, plur(delta));
	    else You("owe %s %ld zorkmid%s.",
				shkname(shkp), delta, plur(delta));
	    eshkp->debit += delta;
	    eshkp->loan += delta;
	    eshkp->credit = 0L;
	}
}

/* used in domove to block diagonal shop-exit */
/* x,y should always be a door */
boolean
block_door(x,y)
register xchar x, y;
{
	register int roomno = *in_rooms(x, y, SHOPBASE);
	register struct monst *shkp;

	if(roomno < 0 || !IS_SHOP(roomno)) return(FALSE);
	if(!IS_DOOR(levl[x][y].typ)) return(FALSE);
	if(roomno != *u.ushops) return(FALSE);

	if(!(shkp = shop_keeper((char)roomno)) || !inhishop(shkp))
		return(FALSE);

	if(shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y
	    /* Actually, the shk should be made to block _any_
	     * door, including a door the player digs, if the
	     * shk is within a 'jumping' distance.
	     */
	    && ESHK(shkp)->shd.x == x && ESHK(shkp)->shd.y == y
	    && shkp->mcanmove && !shkp->msleep
	    && (ESHK(shkp)->debit || ESHK(shkp)->billct ||
		ESHK(shkp)->robbed)) {
		pline("%s%s blocks your way!", shkname(shkp),
				Invis ? " senses your motion and" : "");
		return(TRUE);
	}
	return(FALSE);
}

/* used in domove to block diagonal shop-entry */
/* u.ux, u.uy should always be a door */
boolean
block_entry(x,y)
register xchar x, y;
{
	register xchar sx, sy;
	register int roomno;
	register struct monst *shkp;

	if(!(IS_DOOR(levl[u.ux][u.uy].typ) &&
		levl[u.ux][u.uy].doormask == D_BROKEN)) return(FALSE);

	roomno = *in_rooms(x, y, SHOPBASE);
	if(roomno < 0 || !IS_SHOP(roomno)) return(FALSE);
	if(!(shkp = shop_keeper((char)roomno)) || !inhishop(shkp))
		return(FALSE);

	if(ESHK(shkp)->shd.x != u.ux || ESHK(shkp)->shd.y != u.uy)
		return(FALSE);

	sx = ESHK(shkp)->shk.x;
	sy = ESHK(shkp)->shk.y;

	if(shkp->mx == sx && shkp->my == sy
		&& shkp->mcanmove && !shkp->msleep
		&& (x == sx-1 || x == sx+1 || y == sy-1 || y == sy+1)
		&& (Invis || carrying(PICK_AXE))
	  ) {
		pline("%s%s blocks your way!", shkname(shkp),
				Invis ? " senses your motion and" : "");
		return(TRUE);
	}
	return(FALSE);
}

#endif /* OVLB */

/*shk.c*/
