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

#define OTHER_SERVICES

#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 NEARDATA long int 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 *, struct monst *,
				      BOOLEAN_P));
static void FDECL(bill_box_content, (struct obj *, BOOLEAN_P, BOOLEAN_P,
				     struct monst *));
static void FDECL(shk_names_obj, (struct obj *));

#ifdef OTHER_SERVICES
#define SHK_NOMATCH     0       /* Shk !know this class of object       */
#define SHK_MATCH       1       /* Shk is expert                        */
#define SHK_GENERAL     2       /* Shk runs a general store             */
#define NOBOUND         (-1)    /* No lower/upper limit to charge       */
static void NDECL(shk_other_services);
static void FDECL(shk_identify, (char *, struct monst *));
static void FDECL(shk_uncurse, (char *, struct monst *));
static void FDECL(shk_appraisal, (char *, struct monst *));
static void FDECL(shk_weapon_works, (char *, struct monst *));
static void FDECL(shk_armor_works, (char *, struct monst *));
static void FDECL(shk_charge, (char *, struct monst *));
static boolean FDECL(shk_obj_match, (struct obj *, struct monst *));
static int FDECL(shk_class_match, (long class, struct monst *shkp));
static boolean FDECL(shk_offer_price, (char *, int, struct monst *));
static void FDECL(shk_smooth_charge, (int *, int, int));
#endif


/*
#ifdef BLACKMARKET
static void FDECL(attack_pet, (register struct monst *));
#endif /* BLACKMARKET */

/*
	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;
{
	rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = 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, ghostly)
register struct monst *mtmp;
boolean ghostly;
{
    if(u.uz.dlevel) {
	if(ESHK(mtmp)->bill_p != (struct bill_x *) -1000)
	    ESHK(mtmp)->bill_p = &(ESHK(mtmp)->bill[0]);
	/* shoplevel can change as dungeons move around */
	/* savebones guarantees that non-homed shk's will be gone */
	if (ghostly)
	    assign_level(&(ESHK(mtmp)->shoplevel), &u.uz);
    }
}

/* Clear the unpaid bit on all of the objects in the list. */
static void
clear_unpaid(list)
register struct obj *list;
{
    while (list) {
	if (Has_contents(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);
	clear_unpaid(level.buriedobjlist);
	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;
	char kopname[20];

	Strcpy(kopname, "Keystone Kops");

	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 defined(BLACKMARKET) && defined(ARMY)
	if (Is_blackmarket(&u.uz)) {
	  nokops = ((mons[PM_SOLDIER].geno & (G_GENOD | G_EXTINCT)) &&
		    (mons[PM_SERGEANT].geno & (G_GENOD | G_EXTINCT)) &&
		    (mons[PM_LIEUTENANT].geno & (G_GENOD | G_EXTINCT)) &&
		    (mons[PM_CAPTAIN].geno & (G_GENOD | G_EXTINCT)));

	  Strcpy(kopname, "guards");
	}
#endif /* defined(BLACKMARKET) && defined(ARMY) */

	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) 
#ifdef BLACKMARKET
	      if (!Is_blackmarket(&u.uz)) 
#endif /* BLACKMARKET */
		{
		/* Create swarm around you, if you merely "stepped out" */
		if (flags.verbose)
		    pline("The %s appear!", kopname);
		mm.x = u.ux;
		mm.y = u.uy;
		makekops(&mm);
		return;
	    }
	    if (flags.verbose)
		 pline("The %s are after you!", kopname);
	    /* Create swarm near down staircase (hinders return to level) */
#ifdef BLACKMARKET
	    if (Is_blackmarket(&u.uz)) {
	      struct trap *trap = ftrap;
	      while (trap) {
		if (trap->ttyp == MAGIC_PORTAL) {
		  mm.x = trap->tx;
		  mm.y = trap->ty;
		}
		trap = trap->ntrap;
	      }
	    } else {
	      mm.x = xdnstair;
	      mm.y = ydnstair;
	    }
#else /* BLACKMARKET */
	    mm.x = xdnstair;
	    mm.y = ydnstair;
#endif /* BLACKMARKET */
	    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 && shkp->mcanmove && !shkp->msleep) {
	    /*
	     * 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;
	}
	total = (addupbill(shkp) + eshkp->debit);
	if (eshkp->credit >= total) {
	    Your("credit of %ld zorkmid%s is used to cover your shopping bill.",
		 eshkp->credit, plur(eshkp->credit));
	    total = 0L;         /* credit gets cleared by setpaid() */
	} else {
	    You("escaped the shop without paying!");
	    total -= eshkp->credit;
	}
	setpaid(shkp);
	if (!total) return;

	/* by this point, we know an actual robbery has taken place */
	eshkp->robbed += total;
	You("stole %ld zorkmid%s worth of merchandise.",
	    total, plur(total));
	if (pl_character[0] != 'R') { /* stealing is unlawful */
	    adjalign(-sgn(u.ualign.type));
	    You("feel like an evil rogue.");
	}

	hot_pursuit(shkp);

#ifdef BLACKMARKET
	if (Is_blackmarket(&u.uz)) {
	  blkmar_guards(shkp);
	}
#endif /* BLACKMARKET */

#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 (shkp->msleep || !shkp->mcanmove || eshkp->following) /* no dialog */
	    return;

	if (Invis) {
	    pline("%s senses your presence.", shkname(shkp));
#ifdef BLACKMARKET
	    if (!Is_blackmarket(&u.uz)) {
	      verbalize("Invisible customers are not welcome!");
	      return;
	    }
#else /* BLACKMARKET */
	    verbalize("Invisible customers are not welcome!");
	    return;
#endif /* BLACKMARKET */
	}

#ifdef BLACKMARKET
	    if (Is_blackmarket(&u.uz) &&
		u.umonnum>0 && mons[u.umonnum].mlet != S_HUMAN) {
	      verbalize("Non-human customers are not welcome!");
	      return;
	    }
#endif /* BLACKMARKET */

	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) {
	    pline("%s mutters imprecations against shoplifters.", shkname(shkp));
	} 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;
}

/*
   Decide whether two unpaid items are mergable; caller is responsible for
   making sure they're unpaid and the same type of object; we check the price
   quoted by the shopkeeper and also that they both belong to the same shk.
 */
boolean same_price(obj1, obj2)
struct obj *obj1, *obj2;
{
	register struct monst *shkp1, *shkp2;
	register struct bill_x *bp1 = 0, *bp2 = 0;
	register boolean are_mergable = FALSE;

	/* look up the first object by finding shk whose bill it's on */
	for (shkp1 = next_shkp(fmon, TRUE); shkp1;
		shkp1 = next_shkp(shkp1, TRUE))
	    if ((bp1 = onbill(obj1, shkp1, TRUE)) != 0) break;
	/* second object is probably owned by same shk; if not, look harder */
	if (shkp1 && (bp2 = onbill(obj2, shkp1, TRUE)) != 0) {
	    shkp2 = shkp1;
	} else {
	    for (shkp2 = next_shkp(fmon, TRUE); shkp2;
		    shkp2 = next_shkp(shkp2, TRUE))
		if ((bp2 = onbill(obj2, shkp2, TRUE)) != 0) break;
	}

	if (!bp1 || !bp2) impossible("same_price: object wasn't on any bill!");
	else are_mergable = (shkp1 == shkp2 && bp1->price == bp2->price);
	return are_mergable;
}

#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((boolean)(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));
		You("feel your guilt vanish.");
	}
	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:

	if (shkp->msleep || !shkp->mcanmove) {
		pline("%s %s.", Monnam(shkp),
		      rn2(2) ? "seems to be napping" : "doesn't respond");
		return 0;
	}
	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 = him[shkp->female];
		const char *possessive = his[shkp->female];

		if(!ltmp && NOTANGRY(shkp)) {
		    You("do not owe %s anything.", mon_nam(shkp));
		    if(!u.ugold) pline(no_money, stashed_gold ?
					   " seem to" : "");

#ifdef OTHER_SERVICES
		    else
			shk_other_services();
#endif                
		
		} 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) {
		    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)) != 0) {
			/* if completely used up, object quantity is stale;
			   restoring it to its original value here avoids
			   making the partly-used-up code more complicated */
			if (bp->useup) otmp->quan = bp->bquan;
		    } else {
			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);
#ifdef OTHER_SERVICES
	shk_other_services();
#endif
}

/* 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_p, which, itemize)
register struct monst *shkp;
register struct bill_x *bp;
struct obj **obj_p;
int     which;          /* 0 => used-up item, 1 => other (unpaid or lost) */
boolean itemize;
{
	register struct obj *obj = *obj_p;
	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);
	shk_names_obj(obj);     /* identify some non-magic objects */
	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 != bp->bquan) {
		/* eliminate used-up portion; remainder is still unpaid */
		bp->bquan = obj->quan;
		obj->unpaid = 1;
		bp->useup = 0;
		buy = PAY_SOME;
	    } else {    /* completely used-up, so get rid of it */
		if (obj == billobjs) {
		    billobjs = obj->nobj;
		} else {
		    register struct obj *otmp = billobjs;

		    while (otmp && otmp->nobj != obj) otmp = otmp->nobj;
		    if (otmp) otmp->nobj = obj->nobj;
		    else impossible("Error in shopkeeper administration.");
		}
	     /* assert( obj == *obj_p ); */
		dealloc_obj(obj);
		*obj_p = 0;     /* destroy pointer to freed object */
	    }
	}
	return 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->mcanmove) ?
				   "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->mcanmove) ?
				   "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->mcanmove) ?
					"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, level.buriedobjlist)) &&
		!(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;

	/* character classes who are discriminated against... */
	/* barbarians are gullible... */
	if (pl_character[0] == 'B') tmp *= 3L;
	/* rogues are untrustworthy... */
	if (pl_character[0] == 'R') tmp *= 2L;
	/* samurais are from out of town... */
	if (pl_character[0] == 'S') tmp *= 2L;

	/* anger surcharge should match rile_shk's */
	if (shkp && ESHK(shkp)->surcharge) tmp += (tmp + 2L) / 3L;

#ifdef BLACKMARKET
	if (Is_blackmarket(&u.uz)) {
	  if (obj->oclass==RING_CLASS    || obj->oclass==AMULET_CLASS   ||
	      obj->oclass==POTION_CLASS  || obj->oclass==SCROLL_CLASS   ||
	      obj->oclass==SPBOOK_CLASS  || obj->oclass==WAND_CLASS     ||
	      obj->otyp==LUCKSTONE       || obj->otyp==LOADSTONE        || 
	      obj->otyp==STONE_OF_HEALTH || obj->otyp==STONE_OF_ROTTING || 
	      objects[obj->otyp].oc_magic) {
	    tmp *= 50;
	  } else {
	    tmp *= 25;
	  }
	}
#endif /* BLACKMARKET */

	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->oclass == GOLD_CLASS) 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 (Has_contents(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->oclass == GOLD_CLASS)
		value += otmp->quan;
	    else if (Has_contents(otmp))
		value += contained_gold(otmp);

	return(value);
}

static void
dropped_container(obj, shkp, sale)
register struct obj *obj;
register struct monst *shkp;
register boolean sale;
{
	register struct obj *otmp;
	register boolean saleitem;

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

	    if(otmp->oclass == GOLD_CLASS) continue;

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

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

	    if (Has_contents(otmp))
		dropped_container(otmp, shkp, sale);
	}
}

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->oclass == GOLD_CLASS) continue;

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

	    if (Has_contents(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(otmp->oclass == GOLD_CLASS) continue;
		/* the "top" box is added in addtobill() */
		if(!otmp->no_charge)
		    add_one_tobill(otmp, dummy);
		if (Has_contents(otmp))
		    bill_box_content(otmp, ininv, dummy, shkp);
	}

}

static void
shk_names_obj(obj)
register struct obj *obj;
/* shopkeeper tells you what an object is */
{
	obj->dknown = TRUE;
	/* use real name for ordinary weapons/armor, and spell-less
	 * scrolls/books (that is, blank and mail).
	 */
	if (!objects[obj->otyp].oc_magic &&
	    (obj->oclass == WEAPON_CLASS || obj->oclass == ARMOR_CLASS ||
	     obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS))
	    makeknown(obj->otyp);
}

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 = Has_contents(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->mcanmove && !shkp->msleep && !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 {
		static const char *honored[5] = {
		  "good", "honored", "most gracious", "esteemed",
		  "most renowned and sacred"
		};
		Strcat(buf, honored[rn2(4) + u.uevent.udemigod]);
#ifdef POLYSELF
		if(!is_human(uasmon)) Strcat(buf, " creature");
		else
#endif
		    Strcat(buf, (flags.female) ? " lady" : " sir");
	    }
	    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 (Has_contents(obj))
	    for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {
		if(otmp->oclass == GOLD_CLASS) continue;

		if (Has_contents(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->oclass == GOLD_CLASS) continue;

	    if (!Has_contents(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->oclass == GOLD_CLASS) {
	    gvalue += obj->quan;
	} else if (Has_contents(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->oclass == GOLD_CLASS)
		    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 = Has_contents(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) {
		/* 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;

	/* get one case out of the way: nothing to sell, and no gold */
	if(!isgold && (offer + gltmp) == 0L) {
		register boolean unpaid = (obj->unpaid ||
				  (container && count_unpaid(obj->cobj)));

		if(container) {
			dropped_container(obj, shkp, FALSE);
			if(!obj->unpaid && !saleitem)
			    obj->no_charge = 1;
			if(obj->unpaid || count_unpaid(obj->cobj))
			    subfrombill(obj, shkp);
		} 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)
		    dropped_container(obj, shkp, TRUE);
		if (!obj->unpaid)
		    obj->no_charge = 1;
		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 (ANGRY(shkp)) { /* they become shop-objects, no pay */
		pline("Thank you, scum!");
		subfrombill(obj, shkp);
		return;
	}

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

	if(isgold || cgold) {
		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(!isgold) {
			if (container)
			    dropped_container(obj, shkp, FALSE);
			if (!obj->unpaid && !saleitem) obj->no_charge = 1;
			subfrombill(obj, shkp);
		    }
		    return;
		}
	}
move_on:
	if((!saleitem && !(container && cltmp > 0L))
	   || eshkp->billct == BILLSZ
	   || obj->oclass == BALL_CLASS
	   || obj->oclass == CHAIN_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)
		    dropped_container(obj, shkp, FALSE);
		obj->no_charge = 1;
		return;
	}

	if(!shkp->mgold) {
		char c, qbuf[BUFSZ];
		long tmpcr = (ltmp + cltmp)/* * 2L*/;

		if (sell_response != 'n') {
		    pline("%s cannot pay you at present.", Monnam(shkp));
		    Sprintf(qbuf,
			    "Will you accept %ld zorkmids in credit for %s? ",
			    tmpcr, doname(obj));
		    /* won't accept 'a' response here */
		    c = ynq(qbuf);
		} else          /* previously specified "quit" */
		    c = 'n';

		if (c == 'y') {
		    You("have %ld zorkmids in %scredit.", tmpcr,
			ESHK(shkp)->credit > 0L ? "additional " : "");
		    ESHK(shkp)->credit += tmpcr;
		    subfrombill(obj, shkp);
		} else {
		    if (c == 'q') sell_response = 'n';
		    if (container)
			dropped_container(obj, shkp, FALSE);
		    if (!obj->unpaid) obj->no_charge = 1;
		    subfrombill(obj, shkp);
		}
	} 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)
				dropped_container(obj, shkp, FALSE);
			    if (!obj->unpaid) obj->no_charge = 1;
			    subfrombill(obj, shkp);
			    break;
		 case 'a':  sell_response = 'y';
		 case 'y':  if (container)
				dropped_container(obj, shkp, TRUE);
			    if (!obj->unpaid && !saleitem) obj->no_charge = 1;
			    subfrombill(obj, shkp);
			    pay(-offer, shkp);
			    shk_names_obj(obj);     /* identify some non-magic objects */
			    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 */
{
#ifdef  __SASC
	void sasc_bug(struct obj *, unsigned);
#endif
	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, thisused;
		unsigned save_unpaid;

		save_unpaid = obj->unpaid;
		oquan = obj->quan;
		uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
		thisused = bp->price * uquan;
		totused += thisused;
		obj->quan = uquan;              /* cheat doname */
		obj->unpaid = 0;                /* ditto */
		buf_p = xprname(obj, ' ', FALSE, thisused);
		obj->quan = oquan;              /* restore value */
#ifdef __SASC
				/* SAS/C 6.2 can't cope for some reason */
		sasc_bug(obj,save_unpaid);
#else
		obj->unpaid = save_unpaid;
#endif
		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 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;
}

/* shk catches thrown pick-axe */
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 (shkp->msleep || !shkp->mcanmove || 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 && !u.uswallow &&
				((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))
#ifdef BLACKMARKET
	    if ((Is_blackmarket(&u.uz) && u.umonnum>0 &&
		 mons[u.umonnum].mlet != S_HUMAN) ||
		(!Is_blackmarket(&u.uz) && Invis))
#else /* BLACKMARKET */
		if(Invis) 
#endif /* BLACKMARKET */
		{
		    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((boolean)(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));
	You("feel like a common thief.");
	}
	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') { 
	You("feel like a common thief.");
	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
/* modified by M. Campostrini (campo@sunthpi3.difi.unipi.it) */
/* to allow for multiple choices of kops */
STATIC_OVL void
makekops(mm)            /* returns the number of (all types of) Kops  made */
coord *mm;
{
	int kop_cnt[5];
	int kop_pm[5];
	int ik, cnt;

	kop_pm[0] = PM_KEYSTONE_KOP;
	kop_pm[1] = PM_KOP_SERGEANT;
	kop_pm[2] = PM_KOP_LIEUTENANT;
	kop_pm[3] = PM_KOP_KAPTAIN;
	kop_pm[4] = 0;

	cnt = abs(depth(&u.uz)) + rnd(5);

#if defined(BLACKMARKET) && defined(ARMY)
	if (Is_blackmarket(&u.uz)) {
	  kop_pm[0] = PM_SOLDIER;
	  kop_pm[1] = PM_SERGEANT;
	  kop_pm[2] = PM_LIEUTENANT;
	  kop_pm[3] = PM_CAPTAIN;
	  kop_pm[4] = 0;

	  cnt = 7 + rnd(10);
	}
#endif /* defined(BLACKMARKET) && defined(ARMY) */

	kop_cnt[0] = cnt;
	kop_cnt[1] = (cnt / 3) + 1;   /* at least one sarge */
	kop_cnt[2] = (cnt / 6);       /* maybe a lieutenant */
	kop_cnt[3] = (cnt / 9);       /* and maybe a kaptain */

	for (ik=0; kop_pm[ik]; ik++) {
	  if (!(mons[kop_pm[ik]].geno & G_EXTINCT)) {
	    while(kop_cnt[ik]--) {
	      if (enexto(mm, mm->x, mm->y, &mons[kop_pm[ik]]))
		(void) makemon(&mons[kop_pm[ik]], 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((boolean)(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->mcanmove && !shkp->msleep)
		? 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 || otmp == uball || otmp == uchain) {
	    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) {
	if (first_obj->no_charge || first_obj == uball || first_obj == uchain){
	    pline("%s!", buf);  /* buf still contains the string */
	} else {
	    /* print cost in slightly different format, so can't reuse buf */
	    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 SOUNDS
void
shk_chat(shkp)
register struct monst *shkp;
{
	register struct eshk *eshk = ESHK(shkp);

	if (ANGRY(shkp))
		pline("%s mentions how much %s dislikes %s customers.",
			shkname(shkp), he[shkp->female],
			eshk->robbed ? "non-paying" : "rude");
	else if (eshk->following)
		if (strncmp(eshk->customer, plname, PL_NSIZ)) {
		    verbalize("Hello %s!  I was looking for %s.",
			    plname, eshk->customer);
		    eshk->following = 0;
		} else {
		    verbalize("Hello %s!  Didn't you forget to pay?", plname);
		}
	else if (eshk->billct) {
		register long total = addupbill(shkp) + eshk->debit;
		pline("%s says that your bill comes to %ld zorkmid%s.",
		      shkname(shkp), total, plur(total));
	} else if (eshk->debit)
		pline("%s reminds you that you owe %s %ld zorkmid%s.",
		      shkname(shkp), him[shkp->female],
		      eshk->debit, plur(eshk->debit));
	else if (eshk->credit)
		pline("%s encourages you to use your %ld zorkmid%s of credit.",
		      shkname(shkp), eshk->credit, plur(eshk->credit));
	else if (eshk->robbed)
		pline("%s complains about a recent robbery.", shkname(shkp));
	else if (shkp->mgold < 50)
		pline("%s complains that business is bad.", shkname(shkp));
	else if (shkp->mgold > 4000)
		pline("%s says that business is good.", shkname(shkp));
	else
		pline("%s talks about the problem of shoplifters.", shkname(shkp));
}
#endif  /* SOUNDS */

#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 || 
	   otmp->otyp == ORB_OF_DESTRUCTION) {           /* 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 == MEDICAL_KIT ||
		  otmp->otyp == HORN_OF_PLENTY) {
		tmp /= 5L;
	} else if(otmp->otyp == CRYSTAL_BALL ||          /* 1 - 5 */
		  otmp->otyp == ORB_OF_ENCHANTMENT ||
		  otmp->otyp == ORB_OF_CHARGING ||
		  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;
	} else if (otmp->otyp == CAN_OF_GREASE)
		    tmp /= 10L;
	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 */

#ifdef __SASC
void
sasc_bug(struct obj *op, unsigned x){
	op->unpaid=x;
}
#endif

#ifdef OTHER_SERVICES
static NEARDATA const char identify_types[] = { ALL_CLASSES, 0 };
static NEARDATA const char weapon_types[] = { WEAPON_CLASS, 0 };
static NEARDATA const char armor_types[] = { ARMOR_CLASS, 0 };

/*
** FUNCTION shk_other_services
**
** Called when you don't owe any money.  Called after all checks have been
** made (in shop, not angry shopkeeper, etc.)
*/
static void
shk_other_services()
{
	char c;
	char *slang;                    /* What shk calls you           */
	struct monst *shkp;             /* The shopkeeper               */
	char svc_buffer[132];           /* "Services available..."      */
	char svc_chars[10];             /* What services are offered    */
	int i;                          /* svc_chars index              */

	/* Init your name */
#ifdef POLYSELF
	if( !is_human(uasmon))
		slang="ugly";
	else
#endif
		slang = (flags.female) ? "lady" : "buddy";

	/* Init the shopkeeper */
	shkp = shop_keeper(/* roomno= */*u.ushops);

	/*
	** Figure out what services he offers
	**
	** i = identify
	** a = appraise weapon's worth
	** u = uncurse
	** w = weapon-works
	** r = armor-works
	** c = charge wands
	*/
	i = 0;
	strcpy(svc_buffer, "Services available: ");

	/* All shops can identify (some better than others) */
	svc_chars[i++] = 'i';
	strcat(svc_buffer, "[i]dentify,");

	/* All shops can uncurse */
	svc_chars[i++] = 'u';
	strcat(svc_buffer, " [u]ncurse,");

	/* Weapon appraisals.  Weapon & general stores can do this. */
	if (wizard || shk_class_match(WEAPON_CLASS, shkp))
	{
		svc_chars[i++] = 'a';
		strcat(svc_buffer, " [a]ppraise,");
	}

	/* Weapon-works!  Only a weapon store. */
	if (wizard || shk_class_match(WEAPON_CLASS, shkp) == SHK_MATCH)
	{
		svc_chars[i++] = 'w';
		strcat(svc_buffer, " [w]eapon-works,");
	}

	/* Armor-works */
	if (wizard || shk_class_match(ARMOR_CLASS, shkp) == SHK_MATCH)
	{
		svc_chars[i++] = 'r';
		strcat(svc_buffer, " a[r]mor-works,");
	}

	/* Charging: / ( = */
	if (wizard ||
		(shk_class_match(WAND_CLASS, shkp) == SHK_MATCH) ||
		(shk_class_match(TOOL_CLASS, shkp) == SHK_MATCH) ||
		(shk_class_match(RING_CLASS, shkp) == SHK_MATCH))
	{
		svc_chars[i++] = 'c';
		strcat(svc_buffer, " [c]harge,");
	}

	/* Cap off end of allowable inputs with a 0 */
	svc_chars[i] = '\0';
	/* Overwrite trailing comma with 0 */
	svc_buffer[strlen(svc_buffer)-1] = '\0';
	
	/* Here we go */
	c=yn_function(  /* What to print */     svc_buffer,
			/* Allowable inputs */  svc_chars,
			/* Default response */  '\0');

	switch (c)
	{
	    case 'i':
		shk_identify(slang, shkp);
		break;

	    case 'u':
		shk_uncurse(slang, shkp);
		break;

	    case 'a':
		shk_appraisal(slang, shkp);
		break;

	    case 'w':
		shk_weapon_works(slang, shkp);
		break;

	    case 'r':
		shk_armor_works(slang, shkp);
		break;

	    case 'c':
		shk_charge(slang, shkp);

	    default:
		break;
	}
}


/*
** FUNCTION shk_identify
**
** Pay the shopkeeper to identify an item.
*/
static NEARDATA const char ident_chars[] = "bp";

static void
shk_identify(slang, shkp)
	char *slang;
	struct monst *shkp;
{
	register struct obj *obj;       /* The object to identify       */
	int charge, mult;          /* Cost to identify             */
	char sbuf[BUFSZ];
	boolean guesswork;              /* Will shkp be guessing?       */
	boolean ripoff;                 /* Shkp ripping you off?        */
	char ident_type;

	/* Pick object */
	if ( !(obj = getobj(identify_types, "have identified"))) return;

	/* Will shk be guessing? */
	if (guesswork = !shk_obj_match(obj, shkp))
	{
		pline("I don't handle that sort of item, but I could try...");
	}

	/* Here we go */
/*	ident_type = yn_function("[B]asic service or [P]remier",
		ident_chars, '\0');
	if (ident_type == '\0') return; */
	ident_type = 'b';

	/*
	** Shopkeeper is ripping you off if:
	** Basic service and object already known.
	** Premier service, object known, + know blessed/cursed and
	**      rustproof, etc.
	*/
	if (obj->known && objects[obj->otyp].oc_name_known)
	{
		if (ident_type=='b') ripoff=TRUE;
		if (ident_type=='p' && obj->bknown && obj->rknown) ripoff=TRUE;
	}
	else
	{
		ripoff = FALSE;
	}

	/* Compute the charge */
	
	if (ripoff)
	{
		/* Object already identified: Try and cheat the customer. */
		pline("%s chuckles greedily...", mon_nam(shkp));
		mult = 1;
	
	/* basic */        
	} else if (ident_type=='b') mult = 1;

	/* premier */
	else mult = 1.5;
	
	switch (obj->oclass) {        
		case AMULET_CLASS:      charge = 375 * mult;
					break;
		case WEAPON_CLASS:      charge = 75 * mult;
					break;
		case ARMOR_CLASS:       charge = 100 * mult;
					break;
		case FOOD_CLASS:        charge = 25 * mult;   
					break;
		case SCROLL_CLASS:      charge = 150 * mult;   
					break;
		case SPBOOK_CLASS:      charge = 250 * mult;   
					break;
		case POTION_CLASS:      charge = 150 * mult;   
					break;
		case RING_CLASS:        charge = 300 * mult;   
					break;
		case WAND_CLASS:        charge = 200 * mult;   
					break;
		case TOOL_CLASS:        charge = 50 * mult;   
					break;
		case GEM_CLASS:         charge = 500 * mult;
					break;
		default:                charge = 75 * mult;
					break;
		}
		
	/* Artifacts cost more to deal with */
	if (obj->oartifact) charge *= 1.5;
	
	/* Smooth out the charge a bit (lower bound only) */
	shk_smooth_charge(&charge, 25, 750 );
	
	/* Go ahead? */
	if (shk_offer_price(slang, charge, shkp) == FALSE) return;

	/* Shopkeeper deviousness */
	if (Hallucination)
	{
		pline("You hear %s tell you it's a pot of flowers.",
			mon_nam(shkp));
		return;
	}
	else if (Confusion)
	{
		pline("%s tells you but you forget.", mon_nam(shkp));
		return;
	}

	/* Is shopkeeper guessing? */
	if (guesswork)
	{
		/*
		** Identify successful if rn2() < #.  
		*/
		if (rn2(4) == 0)
		{
			pline("Success!");
			/* Rest of msg will come from identify(); */
		}
		else
		{
			pline("Sorry.  I guess it's not your lucky day.");
			return;
		}
	}

	/* Premier service */
	if (ident_type=='p') obj->dknown=obj->bknown=obj->rknown=TRUE;

	/* Identify the object */
	identify(obj);
}


/*
** FUNCTION shk_uncurse
**
** Uncurse an item for the customer
*/
static void
shk_uncurse(slang, shkp)
	char *slang;
	struct monst *shkp;
{
	struct obj *obj;                /* The object picked            */
	int charge;                     /* How much to uncurse          */

	/* Pick object */
	if ( !(obj = getobj(identify_types, "uncurse"))) return;

	/* Charge is same as cost */
	charge = get_cost(obj, shop_keeper(/* roomno= */*u.ushops));
		
	/* Artifacts cost more to deal with */
	if (obj->oartifact) charge *= 1.5;

	/* Smooth out the charge a bit */
	shk_smooth_charge(&charge, 50, 250);

	/* Go ahead? */
	if (shk_offer_price(slang, charge, shkp) == FALSE) return;

	/* Shopkeeper responses */
	if ( ! (obj->known && objects[obj->otyp].oc_name_known) )
	{
		/* Not identified! */
		pline("%s snickers and says \"See, nice and uncursed!\"",
			mon_nam(shkp));
		obj->bknown = FALSE;
	}
	else if (Confusion)
	{
		/* Curse the item! */
		You("accidentally ask for the item to be cursed");
		obj->cursed = TRUE;
		obj->blessed = FALSE;
	}
	else if (Hallucination)
	{
		/*
		** Let's have some fun:  If you're hallucinating,
		** then there's a chance for the object to be blessed!
		*/
		if (rn2(4) == 0)
		{
		    pline("Distracted by your blood-shot %s, the shopkeeper",
			makeplural(body_part(EYE)));
		    pline("accidentally blesses the item!");
			obj->cursed = FALSE;
			obj->blessed = TRUE;
		}
		else
		{
			You("can't see straight and point to the wrong item");
		}
	}
	else
	{
		verbalize("All done - safe to handle, now!");
		obj->cursed = FALSE;
	}
}


/*
** FUNCTION shk_appraisal
**
** Appraise a weapon or armor
*/
static const char basic_damage[] =
	"Basic damage against small foes %s, against large foes %s.";

static void
shk_appraisal(slang, shkp)
	char *slang;
	struct monst *shkp;
{
	struct obj *obj;                /* The object picked            */
	int charge;                     /* How much for appraisal       */
	boolean guesswork;              /* Shopkeeper unsure?           */
	char sbuf[BUFSZ];
	char ascii_wsdam[5];            /* Ascii form of damage         */
	char ascii_wldam[5];

	/* Pick object */
	if ( !(obj = getobj(weapon_types, "appraise"))) return;

	charge = get_cost(obj, shop_keeper(/* roomno= */*u.ushops)) / 3;

	/* Smooth out the charge a bit */
	shk_smooth_charge(&charge, 5, 50);

	/* If not identified, complain. */
	if ( ! (obj->known && objects[obj->otyp].oc_name_known) )
	{
		verbalize("This weapon needs to be identified first!");
		return;
	}
	else if (shk_class_match(WEAPON_CLASS, shkp) == SHK_MATCH)
	{
		verbalize("Ok, %s, let's see what we have here.", slang);
		guesswork = FALSE;
	}
	else
	{
		verbalize("Mind you, I'm not an expert in this field.");
		guesswork = TRUE;
	}

	/* Go ahead? */
	if (shk_offer_price(slang, charge, shkp) == FALSE) return;

	/* Shopkeeper deviousness */
	if (Confusion)
	{
		pline("The numbers get all mixed up in your head.");
		return;
	}
	else if (Hallucination)
	{
		You("hear %s say it'll \"knock 'em dead\"",
			mon_nam(shkp));
		return;
	}

	/* Convert damage to ascii */
	Sprintf(ascii_wsdam, "%d", objects[obj->otyp].oc_wsdam);
	Sprintf(ascii_wldam, "%d", objects[obj->otyp].oc_wldam);

	/* Will shopkeeper be unsure? */
	if (guesswork)
	{
		switch (rn2(10))
		{
		    case 1:
			/* Shkp's an idiot */
			pline("Sorry, %s, but I'm not certain.", slang);
			break;

		    case 2:
			/* Not sure about large foes */
			pline(basic_damage, ascii_wsdam, "?");
			break;

		    case 3:
			/* Not sure about small foes */
			pline(basic_damage, "?", ascii_wldam);
			break;

		    default:
			pline(basic_damage, ascii_wsdam, ascii_wldam);
			break;
			
		}
	}
	else
	{
		pline(basic_damage, ascii_wsdam, ascii_wldam);
	}
}


/*
** FUNCTION shk_weapon_works
**
** Perform ops on weapon for customer
*/
static const char we_offer[] = "We offer the finest service available!";
static void
shk_weapon_works(slang, shkp)
	char *slang;
	struct monst *shkp;
{
	struct obj *obj;
	char c;
	int charge;
	char *yn_string;
	char *yn_allows;

	/* Pick weapon */
	if ( !(obj = getobj(weapon_types, "improve"))) return;

	/* Can object be poisoned? */
	if (obj->otyp <= BEC_DE_CORBIN)
	{
		yn_string = "[r]ust/fireproof, [e]nchant, or [p]oison";
		yn_allows = "rep";
	}
	else
	{
		yn_string = "[r]ust/fireproof, or [e]nchant";
		yn_allows = "re";
	}

	/* Here we go */
	c=yn_function(yn_string, yn_allows, '\0');

	verbalize(we_offer);

	switch(c)
	{
	    case 'r':
		verbalize("This'll leave your %s untouchable!", xname(obj));
		
		/* Costs more the more rusty it is (oeroded 0-3) */
		charge = 500 * (obj->oeroded+1);
		if (obj->oeroded > 2)
			pline("This thing's in pretty sad condition, %s",
				slang);
		
		/* Artifacts cost more to deal with */
		if (obj->oartifact) charge *= 1.5;

		/* Smooth out the charge a bit */
		shk_smooth_charge(&charge, 200, 1500);

		if (shk_offer_price(slang, charge, shkp) == FALSE) return;

		/* Have some fun, but for this $$$ it better work. */
		if (Confusion)
			You("fall over in appreciation");
		else if (Hallucination)
			Your(" - tin roof, un-rusted!");

		obj->oeroded = 0;
		obj->rknown = TRUE;
		obj->oerodeproof = TRUE;
		break;

	    case 'e':
		verbalize("Guaranteed not to harm your weapon, or your money back!");

		/*
		** The higher the enchantment, the more costly!
		** Gets to the point where you need to rob fort ludios
		** in order to get it to +5!!
		*/
		charge = (obj->spe+1) * (obj->spe+1) * 625;

		if (obj->spe < 0) charge = 100;

		/* Artifacts cost more to deal with */
		if (obj->oartifact) charge *= 2;

		/* Smooth out the charge a bit (lower bound only) */
		shk_smooth_charge(&charge, 50, NOBOUND);

		if (shk_offer_price(slang, charge, shkp) == FALSE) return;
		if (obj->spe+1 > 5) { 
			pline("I can't enchant this any higher!");
			charge = 0;
			break;
			}
		/* Have some fun! */
		if (Confusion)
			Your("%s vibrates unexpectedly!", xname(obj));
		else if (Hallucination)
			Your("%s evaporates into thin air!", xname(obj));

		/* No vibrating and no evaporating */
		
		if(uwep->otyp == WORM_TOOTH) {
			uwep->otyp = CRYSKNIFE;
			Your("weapon seems sharper now.");
			uwep->cursed = 0;
			break;
		}

		obj->spe++;
		break;

	    case 'p':
		verbalize("Just imagine what a poisoned %s can do!",
			xname(obj));

		charge = 10 * obj->quan;

		if (shk_offer_price(slang, charge, shkp) == FALSE) return;

		obj->opoisoned = TRUE;
		break;

	    default:
		return;
	}
}


/*
** FUNCTION shk_armor_works
**
** Perform ops on armor for customer
*/
static void
shk_armor_works(slang, shkp)
	char *slang;
	struct monst *shkp;
{
	struct obj *obj;
	char c;
	int charge;

	/* Pick weapon */
	if ( !(obj = getobj(armor_types, "improve"))) return;

	/* Here we go */
	c=yn_function("[r]ust/fireproof, or [e]nchant", "re", '\0');

	verbalize(we_offer);

	switch(c)
	{
	    case 'r':
		if (!flags.female
#ifdef POLYSELF
		&& is_human(uasmon)
#endif
		)
		     verbalize("They'll call you the man of stainless steel!");

		/* Costs more the more rusty it is (oeroded 0-3) */
		charge = 300 * (obj->oeroded+1);
		if (obj->oeroded > 2) pline("Yikes!  This thing's a mess!");

		/* Artifacts cost more to deal with */
		if (obj->oartifact) charge *= 1.5;
		
		/* Smooth out the charge a bit */
		shk_smooth_charge(&charge, 100, 1000);

		if (shk_offer_price(slang, charge, shkp) == FALSE) return;

		/* Have some fun, but for this $$$ it better work. */
		if (Confusion)
			You("forget how to put your %s back on!", xname(obj));
		else if (Hallucination)
			You("mistake your %s for a pot and...", xname(obj));

		obj->oeroded = 0;
		obj->rknown = TRUE;
		obj->oerodeproof = TRUE;
		break;

	    case 'e':
		verbalize("Nobody will ever hit on you again.");
 
		/* Higher enchantment levels cost more. */
		charge = (obj->spe+1) * (obj->spe+1) * 500;
						
		if (obj->spe < 0) charge = 100;                

		/* Artifacts cost more to deal with */
		if (obj->oartifact) charge *= 2;
		
		/* Smooth out the charge a bit */
		shk_smooth_charge(&charge, 50, NOBOUND);

		if (shk_offer_price(slang, charge, shkp) == FALSE) return;
		if (obj->spe+1 > 5) { 
			pline("I can't enchant this any higher!");
			charge = 0;
			break;
			}
		 /* Have some fun! */
		if (Hallucination) Your("%s looks dented.", xname(obj));
		
		if (obj->otyp >= GRAY_DRAGON_SCALES &&
					obj->otyp <= YELLOW_DRAGON_SCALES) {
			/* dragon scales get turned into dragon scale mail */
			Your("%s merges and hardens!", xname(obj));
			setworn((struct obj *)0, W_ARM);
			/* assumes same order */
			obj->otyp = GRAY_DRAGON_SCALE_MAIL +
						obj->otyp - GRAY_DRAGON_SCALES;
			obj->cursed = 0;
			obj->known = 1;
			setworn(obj, W_ARM);
			break;
		}

		obj->spe++;
		adj_abon(obj, 1);
		break;

	    default:
		return;
	}
}


/*
** FUNCTION shk_charge
**
** Charge something (for a price!)
*/
static NEARDATA const char wand_types[] = { WAND_CLASS, 0 };
static NEARDATA const char tool_types[] = { TOOL_CLASS, 0 };
static NEARDATA const char ring_types[] = { RING_CLASS, 0 };

static void
shk_charge(slang, shkp)
	char *slang;
	struct monst *shkp;
{
	struct obj *obj;        /* The object picked            */
	struct obj *tobj;       /* Temp obj                     */
	char type;              /* Basic/premier service        */
	int charge;             /* How much to charge customer  */
	char invlet;            /* Inventory letter             */

	/* What type of shop are we? */
	if (shk_class_match(WAND_CLASS, shkp) == SHK_MATCH)
		obj = getobj(wand_types, "charge");
	else if (shk_class_match(TOOL_CLASS, shkp) == SHK_MATCH)
		obj = getobj(tool_types, "charge");
	else if (shk_class_match(RING_CLASS, shkp) == SHK_MATCH)
		obj = getobj(ring_types, "charge");
	if (!obj) return;

	/*
	** Wand shops can offer special service!
	** Extra charges (for a lot of extra money!)
	*/
	if (obj->oclass==WAND_CLASS)
	{
		/* What type of service? */
		type = yn_function("[B]asic service or [P]remier",
			ident_chars, '\0');
		if (!type) return;
	}
	else
	{
		type = 'b';
	}

	/* Compute charge */
	if (type == 'b')
		charge = 300;
	else
		charge = 1000;

	/* Wands of wishing should be hard to get recharged */
	if (obj->oclass==WAND_CLASS && obj->otyp==WAN_WISHING)
		charge *= 3;
	else /* Smooth out the charge a bit */
		shk_smooth_charge(&charge, 100, 1000);

	/* Go for it? */
	if (shk_offer_price(slang, charge, shkp) == FALSE) return;

	/* Shopkeeper deviousness */
	if (Confusion || Hallucination)
	{
		pline("%s says it's charged and pushes you toward the door",
			mon_nam(shkp));
		return;
	}

	/* Do it */
	invlet = obj->invlet;
	recharge(obj, (type=='b') ? 0 : 1);

	/*
	** Did the object blow up?  We need to check this in a way
	** that has nothing to do with dereferencing the obj pointer.
	** We saved the inventory letter of this item; now cycle
	** through all objects and see if there is an object
	** with that letter.
	*/
	for(obj=0, tobj=invent; tobj; tobj=tobj->nobj)
		if(tobj->invlet == invlet)
		{
			obj = tobj;
			break;
		}
	if (!obj)
	{
		verbalize("Oops!  Sorry about that...");
		return;
	}

	/* Wands get special treatment */
	if (obj->oclass==WAND_CLASS)
	{
		/* Wand of wishing? */
		if (obj->otyp==WAN_WISHING)
		{
			/* Premier gives you ONE more charge */
			if (type == 'p') obj->spe++;

			/* Fun */
		verbalize("Since you'll have everything you always wanted,");
			verbalize("...How about loaning me some money?");
			shkp->mgold += u.ugold;
			u.ugold = 0;
			bot();
		}
		else
		{
			/*
			** Basic: recharge() will have given 1 charge.
			** Premier: recharge() will have given 5-10, say.
			** Add a few more still.
			*/
			if (obj->spe < 20) obj->spe += 1;
		 else   if (obj->spe < 16) obj->spe += rn1(5,5);
		}
	}
}


/*
** FUNCTION shk_obj_match
**
** Does object "obj" match the type of shop?
*/
static boolean
shk_obj_match(obj, shkp)
	struct obj *obj;
	struct monst *shkp;
{
	if /* object matches type of shop? */
		( saleable(rooms[ESHK(shkp)->shoproom -
			ROOMOFFSET].rtype-SHOPBASE, obj) )
	{
		return(TRUE);
	}
	else
	{
		return(FALSE);
	}
}


/*
** FUNCTION shk_class_match
**
** Return TRUE if a object class matches the shop type.
** I.e. shk_class_match(WEAPON_CLASS, shkp)
**
** Return:      SHK_MATCH, SHK_NOMATCH, SHK_GENERAL
*/
static int
shk_class_match(class, shkp)
	long class;
	struct monst *shkp;
{
	/* There must be a better way.  Need nh data structures 101. */
	const struct shclass *shp = &shtypes[rooms[ESHK(shkp)->shoproom-ROOMOFFSET].rtype-SHOPBASE];

	/* RANDOM_CLASS is a general store */
	if (shp->symb == RANDOM_CLASS)
	{
		return(SHK_GENERAL);
	}
	else if (shp->symb == class)
	{
		return(SHK_MATCH);
	}
	else
	{
		return(SHK_NOMATCH);
	}
}


/*
** FUNCTION shk_offer_price
**
** Tell customer how much it'll cost, ask if he wants to pay,
** and deduct from $$ if agreable.
*/
static boolean
shk_offer_price(slang, charge, shkp)
	char *slang;
	int charge;
	struct monst *shkp;
{
	char sbuf[BUFSZ];

	/* Ask y/n if player wants to pay */
	Sprintf(sbuf, "It'll cost you %ld zorkmid%s.  Interested?",
		charge, plur(charge));

	if ( yn(sbuf) != 'y' )
	{
		verbalize("It's your call, %s.", slang);
		return(FALSE);
	}

	/* Player _wants_ to pay, but can he? */
	if (charge > u.ugold)
	{
		verbalize("Cash on the spot, %s, and you ain't got the dough!",
			slang);
		return(FALSE);
	}

	/* Charge the customer */
	u.ugold -= charge;
	shkp->mgold += charge;
	bot();

	return(TRUE);
}


/*
** FUNCTION shk_smooth_charge
**
** Smooth out the lower/upper bounds on the price to get something
** done.  Make sure that it (1) varies depending on charisma and
** (2) is constant.
*/
static void
shk_smooth_charge(pcharge, lower, upper)
	int *pcharge;
	int lower;
	int upper;
{
	int charisma;
	int bonus;

	     if(ACURR(A_CHA) > 21) *pcharge *= 1.1;
	else if(ACURR(A_CHA) > 18) *pcharge *= 1.2;
	else if(ACURR(A_CHA) > 15) *pcharge *= 1.3;
	else if(ACURR(A_CHA) > 12) *pcharge *= 1.4;
	else if(ACURR(A_CHA) > 10) *pcharge *= 1.5;
	else if(ACURR(A_CHA) > 8)  *pcharge *= 1.6;
	else if(ACURR(A_CHA) > 7)  *pcharge *= 1.7;
	else if(ACURR(A_CHA) > 6)  *pcharge *= 1.8;
	else if(ACURR(A_CHA) > 5)  *pcharge *= 1.9;
	else if(ACURR(A_CHA) > 4)  *pcharge *= 2.0;
	else *pcharge *= 2.1;

#ifdef BLACKMARKET
	if (Is_blackmarket(&u.uz)) *pcharge *= 3;
#endif
	/* Skip upper stuff? */
	if (upper == NOBOUND) goto check_lower;

	/* This should give us something like a charisma of 5 to 25. */
	charisma = ABASE(A_CHA) + ABON(A_CHA) + ATEMP(A_CHA);        

	/* Now: 0 to 10 = 0.  11 and up = 1 to whatever. */
	if (charisma <= 10)
		charisma = 0;
	else
		charisma -= 10;

	/* Charismatic players get smaller upper bounds */
	bonus=((upper/50)*charisma);

	/* Adjust upper.  Upper > lower! */
	upper -= bonus;
	upper = (upper>=lower) ? upper : lower;

	/* Ok, do the min/max stuff */
	if (*pcharge > upper) *pcharge=upper;
check_lower:
	if (*pcharge < lower) *pcharge=lower;
}

#endif /* OTHER_SERVICES */



#ifdef BLACKMARKET
void 
blkmar_guards(shkp)
register struct monst *shkp;
{
  register struct monst *mt;

/* wake up assistants */
  pline("%s calls for his assistants!", Monnam(shkp));
  for (mt = fmon; mt; mt = mt->nmon) {
/* non-tame named monsters are presumably black marketeer's assistants */
    if (!mt->mtame && NAME(mt) && *NAME(mt) &&
	inside_shop(mt->mx, mt->my)) {
      wakeup(mt);
    }
  }
}
#endif /* BLACKMARKET */

/*shk.c*/

