ftp.nice.ch/pub/next/games/strategic/NetHack.s.tar.gz#/NetHackSource/src/shk.c

This is shk.c in view mode; [Download] [Up]

/*	SCCS Id: @(#)shk.c	3.0	89/11/27
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#define MONATTK_H	/* comment line for pre-compiled headers */
/* block some unused #defines to avoid overloading some cpp's */
#include "hack.h"

#include "eshk.h"

#ifdef KOPS
static int FDECL(makekops, (coord *));
static void NDECL(kops_gone);
#endif /* KOPS */

#define	NOTANGRY(mon)	mon->mpeaceful
#define	ANGRY(mon)	!NOTANGRY(mon)

/* Descriptor of current shopkeeper. Note that the bill need not be
   per-shopkeeper, since it is valid only when in a shop. */
VSTATIC struct monst *shopkeeper;
VSTATIC struct bill_x *bill;
VSTATIC int shlevel;		/* level of this shopkeeper */
/* struct obj *billobjs;	/* objects on bill with bp->useup */
				/* only accessed here and by save & restore */
VSTATIC long int total; 	/* filled by addupbill() */
VSTATIC long int followmsg;	/* last time of follow message */

static void setpaid(), FDECL(findshk, (int));
static int FDECL(dopayobj, (struct bill_x *)), FDECL(getprice, (struct obj *));
static struct obj *FDECL(bp_to_obj, (struct bill_x *));

#ifdef OVLB

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

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

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

	if(eshk->shoplevel == dlevel)
		rooms[eshk->shoproom].rtype = OROOM;
	if(mtmp == shopkeeper) {
		setpaid();
		shopkeeper = 0;
		bill = (struct bill_x *) -1000;	/* dump core when referenced */
	}
}

void
replshk(mtmp,mtmp2)
register struct monst *mtmp, *mtmp2;
{
	if(mtmp == shopkeeper) {
		shopkeeper = mtmp2;
		bill = &(ESHK(shopkeeper)->bill[0]);
	}
}

static void
setpaid(){	/* caller has checked that shopkeeper exists */
		/* either we paid or left the shop or he just died */
	register struct obj *obj;
	register struct monst *mtmp;
	for(obj = invent; obj; obj = obj->nobj)
		obj->unpaid = 0;
	for(obj = fobj; obj; obj = obj->nobj)
		obj->unpaid = 0;
	for(obj = fcobj; obj; obj = obj->nobj)
		obj->unpaid = 0;
	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
		for(obj = mtmp->minvent; obj; obj = obj->nobj)
			obj->unpaid = 0;
	for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
		for(obj = mtmp->minvent; obj; obj = obj->nobj)
			obj->unpaid = 0;
	while(obj = billobjs){
		billobjs = obj->nobj;
		free((genericptr_t) obj);
	}
	if(shopkeeper) {
		ESHK(shopkeeper)->billct = 0;
		ESHK(shopkeeper)->credit = 0L;
		ESHK(shopkeeper)->debit = 0L;
	}
}

static void
addupbill(){	/* delivers result in total */
		/* caller has checked that shopkeeper exists */
	register int ct = ESHK(shopkeeper)->billct;
	register struct bill_x *bp = bill;
	total = 0;
	while(ct--){
		total += bp->price * bp->bquan;
		bp++;
	}
}

#endif /* OVLB */
#ifdef OVL2

int
inshop() {
	register int roomno = inroom(u.ux,u.uy);

	/* Did we just leave a shop? */
	if(u.uinshop &&
	    (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) {

	/* This is part of the bugfix for shopkeepers not having their
	 * bill paid.  As reported by ab@unido -dgk
	 * I made this standard due to the KOPS code below. -mrs
	 */
		if(shopkeeper) {
		    if(ESHK(shopkeeper)->billct || ESHK(shopkeeper)->debit) {
			if(inroom(shopkeeper->mx, shopkeeper->my)
			    == u.uinshop - 1)	/* ab@unido */
			    You("escaped the shop without paying!");
			addupbill();
			total += ESHK(shopkeeper)->debit;
			You("stole %ld zorkmid%s worth of merchandise.",
				total, plur(total));
			ESHK(shopkeeper)->robbed += total;
			ESHK(shopkeeper)->credit = 0L;
			ESHK(shopkeeper)->debit = 0L;
			if (pl_character[0] != 'R') /* stealing is unlawful */
				adjalign(-sgn(u.ualigntyp));
			setpaid();
			if((rooms[ESHK(shopkeeper)->shoproom].rtype == SHOPBASE)
			    == (rn2(3) == 0))
			    ESHK(shopkeeper)->following = 1;
#ifdef KOPS
		    {   /* Keystone Kops srt@ucla */
			coord mm;

			if (flags.soundok)
			    pline("An alarm sounds throughout the dungeon!");
			if(flags.verbose) {
			    if((mons[PM_KEYSTONE_KOP].geno & G_GENOD) &&
 			       (mons[PM_KOP_SERGEANT].geno & G_GENOD) &&
 			       (mons[PM_KOP_LIEUTENANT].geno & G_GENOD) &&
			       (mons[PM_KOP_KAPTAIN].geno & G_GENOD)) {
				if (flags.soundok)
				    pline("But no one seems to respond to it.");
			    } else
				pline("The Keystone Kops are after you!");
			}
			/* Create a swarm near the staircase */
			mm.x = xdnstair;
			mm.y = ydnstair;
			(void) makekops(&mm);
			/* Create a swarm near the shopkeeper */
			mm.x = shopkeeper->mx;
			mm.y = shopkeeper->my;
			(void) makekops(&mm);
		    }
#endif
		    }
		    shopkeeper = 0;
		    shlevel = 0;
		}
		u.uinshop = 0;
	}

	/* Did we just enter a zoo of some kind? */
	/* This counts everything except shops and vaults
	   -- vault.c insists that a vault remain a VAULT */
	if(roomno >= 0) {
		register int rt = rooms[roomno].rtype;
		register struct monst *mtmp;

		switch (rt) {
		case ZOO:
		    pline("Welcome to David's treasure zoo!");
		    break;
		case SWAMP:
		    pline("It looks rather muddy down here.");
		    break;
#ifdef THRONES
		case COURT:
		    You("enter an opulent throne room!");
		    break;
#endif
		case MORGUE:
		    if(midnight())
			pline("Run away!  Run away!");
		    else
			You("have an uncanny feeling...");
		    break;
		case BEEHIVE:
		    You("enter a giant beehive!");
		    break;
#ifdef ARMY
		case BARRACKS:
		    if(!((mons[PM_SOLDIER].geno & G_GENOD) &&
		         (mons[PM_SERGEANT].geno & G_GENOD) &&
		         (mons[PM_LIEUTENANT].geno & G_GENOD) &&
		         (mons[PM_CAPTAIN].geno & G_GENOD)))
		    	You("enter a military barracks!");
		    else You("enter an abandoned barracks.");
		    break;
#endif
#ifdef ORACLE
		case DELPHI:
		    if(!(mons[PM_ORACLE].geno & G_GENOD))
		        pline("\"Hello, %s, welcome to Delphi!\"", plname);
		    break;
#endif
		default:
		    rt = 0;
		}

		if(rt != 0) {
		    rooms[roomno].rtype = OROOM;
		    if(rt==COURT || rt==SWAMP || rt==MORGUE || rt==ZOO)
			for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
			    /* was if(rt != ZOO || !rn2(3)) -- why should ZOO
			       be different from COURT or MORGUE? */
			    if(!Stealth && !rn2(3))
				mtmp->msleep = 0;
		}
	}
#if defined(ALTARS) && defined(THEOLOGY)
	if(roomno >= 0 && rooms[roomno].rtype == TEMPLE) {
	    intemple();
	}
#endif
	/* Did we just enter a shop? */
	if(roomno >= 0 && rooms[roomno].rtype >= SHOPBASE) {
	    register int rt = rooms[roomno].rtype;

	    if(shlevel != dlevel || !shopkeeper
				 || ESHK(shopkeeper)->shoproom != roomno)
		findshk(roomno);
	    if(!shopkeeper) {
		rooms[roomno].rtype = OROOM;
		u.uinshop = 0;
	    } else if(!u.uinshop){
		if(!ESHK(shopkeeper)->visitct ||
		   strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)) {
		    /* He seems to be new here */
		    ESHK(shopkeeper)->visitct = 0;
		    ESHK(shopkeeper)->following = 0;
		    (void) strncpy(ESHK(shopkeeper)->customer,plname,PL_NSIZ);
		    NOTANGRY(shopkeeper) = 1;
		}
		if(!ESHK(shopkeeper)->following && inhishop(shopkeeper)) {
		    if(Invis) {
			pline("%s senses your presence.", shkname(shopkeeper));
			verbalize("Invisible customers are not welcome!");
		    }
		    else
		    if(ANGRY(shopkeeper))
			pline("\"So, %s, you dare return to %s's %s?!\"",
			    plname,
			    shkname(shopkeeper),
			    shtypes[rt - SHOPBASE].name);
		    else
		    if(ESHK(shopkeeper)->robbed)
			pline("\"Beware, %s!  I am upset about missing stock!\"",
			    plname);
		    else
			pline("\"Hello, %s!  Welcome%s to %s's %s!\"",
			    plname,
			    ESHK(shopkeeper)->visitct++ ? " again" : "",
			    shkname(shopkeeper),
			    shtypes[rt - SHOPBASE].name);
		    if(carrying(PICK_AXE) != (struct obj *)0 && !Invis) {
			verbalize(NOTANGRY(shopkeeper) ?
			   "Will you please leave your pick-axe outside?" :
			   "Leave the pick-axe outside.");
			if(dochug(shopkeeper)) {
			    u.uinshop = 0;	/* he died moving */
			    return(0);
			}
		    }
		}
		u.uinshop = (unsigned int)(roomno + 1);
	    }
	}
	return (int)u.uinshop;
}

#endif /* OVL2 */
#ifdef OVLB

int
inhishop(mtmp)
register struct monst *mtmp;
{
	return((ESHK(mtmp)->shoproom == inroom(mtmp->mx, mtmp->my) &&
		ESHK(mtmp)->shoplevel == dlevel));
}

boolean
tended_shop(sroom)
struct mkroom *sroom;
{
	register struct monst *mtmp;

	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
	    if(mtmp->isshk && &rooms[ESHK(mtmp)->shoproom] == sroom
		&& inhishop(mtmp)) return(TRUE);
	return(FALSE);
}

static void
findshk(roomno)
register int roomno;
{
	register struct monst *mtmp;

	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
	    if(mtmp->isshk && ESHK(mtmp)->shoproom == roomno
			   && ESHK(mtmp)->shoplevel == dlevel) {
		shopkeeper = mtmp;
		bill = &(ESHK(shopkeeper)->bill[0]);
		shlevel = dlevel;
		if(ANGRY(shopkeeper) &&
		   strncmp(ESHK(shopkeeper)->customer,plname,PL_NSIZ))
			NOTANGRY(shopkeeper) = 1;
		/* billobjs = 0; -- this is wrong if we save in a shop */
		/* (and it is harmless to have too many things in billobjs) */
		return;
	}
	shopkeeper = 0;
	shlevel = 0;
	bill = (struct bill_x *) -1000;	/* dump core when referenced */
}

static struct bill_x *
onbill(obj)
register struct obj *obj;
{
	register struct bill_x *bp;
	if(!shopkeeper) return (struct bill_x *)0;
	for(bp = bill; bp < &bill[ESHK(shopkeeper)->billct]; bp++)
		if(bp->bo_id == obj->o_id) {
			if(!obj->unpaid) pline("onbill: paid obj on bill?");
			return(bp);
		}
	if(obj->unpaid) pline("onbill: unpaid obj not on bill?");
	return (struct bill_x *)0;
}

/* called with two args on merge */
void
obfree(obj, merge)
register struct obj *obj, *merge;
{
	register struct bill_x *bp = onbill(obj);
	register struct bill_x *bpm;
	if(bp) {
		if(!merge){
			bp->useup = 1;
			obj->unpaid = 0;	/* only for doinvbill */
			obj->nobj = billobjs;
			billobjs = obj;
			return;
		}
		bpm = onbill(merge);
		if(!bpm){
			/* this used to be a rename */
			impossible("obfree: not on bill??");
			return;
		} else {
			/* this was a merger */
			bpm->bquan += bp->bquan;
			ESHK(shopkeeper)->billct--;
			*bp = bill[ESHK(shopkeeper)->billct];
		}
	}
	free((genericptr_t) 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)
register struct monst *shkp;
{
	register xchar x = ESHK(shkp)->shk.x, y = ESHK(shkp)->shk.y;
	if(MON_AT(x, y))
		mnearto(m_at(x,y), x, y, FALSE);
	remove_monster(shkp->mx, shkp->my);
	place_monster(shkp, x, y);
	unpmon(shkp);
}

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

	NOTANGRY(shkp) = 1;
	ESHK(shkp)->following = 0;
	ESHK(shkp)->robbed = 0L;
	if (pl_character[0] != 'R')
		adjalign(sgn(u.ualigntyp));
	if(!inhishop(shkp)) {
		pline("Satisfied, %s suddenly disappears!", mon_nam(shkp));
		if(ESHK(shkp)->shoplevel == dlevel)
			home_shk(shkp);
		else
			fall_down(shkp, ESHK(shkp)->shoplevel);
	} else if(wasmad)
		pline("%s calms down.", Monnam(shkp));
#ifdef KOPS
	kops_gone();
#endif
}

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

int
dopay()
{
	long ltmp;
	register struct bill_x *bp;
	register struct monst *shkp;
	int pass, tmp;

	multi = 0;
	(void) inshop();
	for(shkp = fmon; shkp; shkp = shkp->nmon)
		if(shkp->isshk && dist(shkp->mx,shkp->my) < 3)
			break;
	if(!shkp && u.uinshop && inhishop(shopkeeper))
		shkp = shopkeeper;

	if(!shkp) {
		pline("There is nobody here to receive your payment.");
		return(0);
	}
	ltmp = ESHK(shkp)->robbed;
	if(shkp != shopkeeper && NOTANGRY(shkp)) {
		if(!ltmp)
		    You("do not owe %s anything.", mon_nam(shkp));
		else if(!u.ugold)
		    You("have no money.");
		else {
		    long ugold = u.ugold;

		    if(ugold > ltmp) {
			You("give %s the %ld gold piece%s %s asked for.",
			    mon_nam(shkp), ltmp, plur(ltmp),
			    ESHK(shkp)->ismale ? "he" : "she");
			pay(ltmp, shkp);
		    } else {
			You("give %s all your gold.", mon_nam(shkp));
			pay(u.ugold, shkp);
		    }
		    if(ugold < ltmp/2L)
			pline("Unfortunately, %s doesn't look satisfied.",
			    ESHK(shkp)->ismale ? "he" : "she");
		    else
			make_happy_shk(shkp);
		}
		return(1);
	}

	/* ltmp is still ESHK(shkp)->robbed here */
	if(!ESHK(shkp)->billct && !ESHK(shkp)->debit) {
		if(!ltmp && NOTANGRY(shkp)) {
		    You("do not owe %s anything.", mon_nam(shkp));
		    if(!u.ugold) pline(no_money);
		} else if(ltmp) {
		    pline("%s is after blood, not money!", mon_nam(shkp));
		    if(u.ugold < ltmp/2L) {
			if(!u.ugold) pline(no_money);
			else pline("Besides, you don't have enough to interest %s.",
				ESHK(shkp)->ismale ? "him" : "her");
			return(1);
		    }
		    pline("But since %s shop has been robbed recently,",
			ESHK(shkp)->ismale ? "his" : "her");
		    pline("you %scompensate %s for %s losses.",
			(u.ugold < ltmp) ? "partially " : "",
			mon_nam(shkp),
			ESHK(shkp)->ismale ? "his" : "her");
		    pay(u.ugold < ltmp ? u.ugold : ltmp, shkp);
		    make_happy_shk(shkp);
		} 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);
			else
		pline("Besides, you don't have enough to interest %s.",
				ESHK(shkp)->ismale ? "him" : "her");
			return(1);
		    }
		    You("try to appease %s by giving %s 1000 gold pieces.",
				a_monnam(shkp, "angry"),
				ESHK(shkp)->ismale ? "him" : "her");
		    pay(1000L,shkp);
		    if(strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)
		    		|| rn2(3))
			make_happy_shk(shkp);
		    else
			pline("But %s is as angry as ever.", Monnam(shkp));
		}
		return(1);
	}
	if(shkp != shopkeeper) {
		impossible("dopay: not to shopkeeper?");
		if(shopkeeper) setpaid();
		return(0);
	}
	/* pay debt, if any, first */
	if(ESHK(shopkeeper)->debit) {
	        You("owe %s %ld zorkmid%s for the use of merchandise.",
			shkname(shopkeeper), ESHK(shopkeeper)->debit,
		        plur(ESHK(shopkeeper)->debit));
	        if(u.ugold + ESHK(shopkeeper)->credit < 
					ESHK(shopkeeper)->debit) {
		    pline("But you don't have enough gold%s.",
			ESHK(shopkeeper)->credit ? " or credit" : "");
		    return(1);
	        } else {
		    long dtmp = ESHK(shopkeeper)->debit;

		    if(ESHK(shopkeeper)->credit >= dtmp) {
			ESHK(shopkeeper)->credit -= dtmp;
			ESHK(shopkeeper)->debit = 0L;
	                Your("debt is covered by your credit.");
		    } else if(!ESHK(shopkeeper)->credit) {
			u.ugold -= dtmp;
			shopkeeper->mgold += dtmp;
			ESHK(shopkeeper)->debit = 0L;
			You("pay that debt.");
			flags.botl = 1;
		    } else {
			dtmp -= ESHK(shopkeeper)->credit;
			ESHK(shopkeeper)->credit = 0L;
			u.ugold -= dtmp;
			shopkeeper->mgold += dtmp;
			ESHK(shopkeeper)->debit = 0L;
			pline("That debt is partially offset by your credit.");
			You("pay the remainder.");
			flags.botl = 1;
		    }
		}
	}
	for(pass = 0; pass <= 1; pass++) {
		tmp = 0;
		while(tmp < ESHK(shopkeeper)->billct) {
		    bp = &bill[tmp];
		    if(!pass && !bp->useup) {
			tmp++;
			continue;
		    }
		    if(!dopayobj(bp)) return(1);
#ifdef MSDOS
		    *bp = bill[--ESHK(shopkeeper)->billct];
#else
		    bill[tmp] = bill[--ESHK(shopkeeper)->billct];
#endif /* MSDOS /**/
		}
	}
	if(!ANGRY(shopkeeper))
	    pline("\"Thank you for shopping in %s's %s!\"",
		shkname(shopkeeper),
		shtypes[rooms[ESHK(shopkeeper)->shoproom].rtype - SHOPBASE].name);
	return(1);
}

/* return 1 if paid successfully */
/*        0 if not enough money */
/*       -1 if object could not be found (but was paid) */
static int
dopayobj(bp)
register struct bill_x *bp;
{
	register struct obj *obj;
	long ltmp;

	/* find the object on one of the lists */
	obj = bp_to_obj(bp);

	if(!obj) {
		impossible("Shopkeeper administration out of order.");
		setpaid();	/* be nice to the player */
		return(0);
	}

	if(!obj->unpaid && !bp->useup){
		impossible("Paid object on bill??");
		return(1);
	}
	obj->unpaid = 0;
	ltmp = bp->price * bp->bquan;
	if(ANGRY(shopkeeper)) ltmp += ltmp/3L;
	if(u.ugold + ESHK(shopkeeper)->credit < ltmp){
		You("don't have gold%s enough to pay for %s.",
			(ESHK(shopkeeper)->credit > 0L) ? " or credit" : "",
			doname(obj));
		obj->unpaid = 1;
		return(0);
	}
	pay(ltmp, shopkeeper);
	You("bought %s for %ld gold piece%s.",
		doname(obj), ltmp, plur(ltmp));
	if(bp->useup) {
		register struct obj *otmp = billobjs;
		if(obj == billobjs)
			billobjs = obj->nobj;
		else {
			while(otmp && otmp->nobj != obj) otmp = otmp->nobj;
			if(otmp) otmp->nobj = obj->nobj;
			else pline("Error in shopkeeper administration.");
		}
		free((genericptr_t) obj);
	}
	return(1);
}

/* routine called after dying (or quitting) with nonempty bill or upset shk */
boolean
paybill(){
	register struct monst *mtmp;
	register long loss = 0L;
	register struct obj *otmp;
	register xchar ox, oy;
	register boolean take = FALSE;
	register boolean taken = FALSE;

	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
	    if(mtmp->isshk) {
		/* for bones: we don't want a shopless shk around */
		if(ESHK(mtmp)->shoplevel != dlevel) mongone(mtmp);
		else shopkeeper = mtmp;
	    }

	if(!shopkeeper) return(FALSE);

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

	if(ESHK(shopkeeper)->billct || ESHK(shopkeeper)->debit ||
			ESHK(shopkeeper)->robbed) {
		addupbill();
		total += ESHK(shopkeeper)->debit;
		loss = ((total >= ESHK(shopkeeper)->robbed) ? total :
				ESHK(shopkeeper)->robbed);
		take = TRUE;
	}

	if(ESHK(shopkeeper)->following || ANGRY(shopkeeper) || take) {
		if((loss > u.ugold) || !loss) {
			pline("%s comes and takes all your possessions.",
					Monnam(shopkeeper));
			taken = TRUE;
			shopkeeper->mgold += u.ugold;
			u.ugold = 0L;
			/* in case bones: make it be for real... */
			if(!in_shop(u.ux, u.uy) || 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 = ESHK(shopkeeper)->shk.x;
			    oy = ESHK(shopkeeper)->shk.y;
			    ox += sgn(ox - ESHK(shopkeeper)->shd.x);
			    oy += sgn(oy - ESHK(shopkeeper)->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;
			shopkeeper->mgold += loss;
			pline("%s comes and takes %ld zorkmid%s %sowed %s.",
			      Monnam(shopkeeper),
			      loss,
			      plur(loss),
			      strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ) ? "" : "you ",
			      ESHK(shopkeeper)->ismale ? "him" : "her");
		}

		/* in case we create bones */
		if(!inhishop(shopkeeper))
			home_shk(shopkeeper);
	}
clear:
	setpaid();
	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, fcobj))) {
		    for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
			if(obj = o_on(id, mtmp->minvent))
			    break;
		    for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
			if(obj = o_on(id, mtmp->minvent))
			    break;
		}
	return(obj);
}

static long
get_cost(obj)
register struct obj *obj;
{
	register long tmp;

	tmp = (long) getprice(obj);
	if (!tmp) tmp = 5L;
	if (ANGRY(shopkeeper) || 
		(pl_character[0] == 'T' && u.ulevel < (MAXULEV/2))
#ifdef SHIRT
	    || (uarmu && !uarm) /* wearing just a Hawaiian shirt */
#endif
	   )
		tmp += tmp/3L;
	if (ACURR(A_CHA) > 18)		tmp /= 2L;
	else if (ACURR(A_CHA) > 17)	tmp = (tmp * 2L)/3L;
	else if (ACURR(A_CHA) > 15)	tmp = (tmp * 3L)/4L;
	else if (ACURR(A_CHA) < 6)	tmp *= 2L;
	else if (ACURR(A_CHA) < 8)	tmp = (tmp * 3L)/2L;
	else if (ACURR(A_CHA) < 11)	tmp = (tmp * 4L)/3L;
	if (!tmp) return 1;
	return(tmp);
}


/* called in hack.c when we pickup an object */
void
addtobill(obj, ininv)
register struct obj *obj;
register boolean ininv;
{
	register struct bill_x *bp;
	char	buf[40];
	if(!shopkeeper || !inhishop(shopkeeper)) return;

	if(!costly_spot(obj->ox,obj->oy) ||	/* either pickup or kick */
		onbill(obj) /* perhaps we threw it away earlier */
	      ) return;
	if(ESHK(shopkeeper)->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;
	}
	bp = &bill[ESHK(shopkeeper)->billct];
	bp->bo_id = obj->o_id;
	bp->bquan = obj->quan;
	bp->useup = 0;
	bp->price = get_cost(obj);
	Strcpy(buf, "\"For you, ");
	if (ANGRY(shopkeeper)) Strcat(buf, "scum ");
	else {
	    switch(rnd(4)
#ifdef HARD
		   + u.udemigod
#endif
				) {
		case 1:	Strcat(buf, "good");
			break;
		case 2:	Strcat(buf, "honored");
			break;
		case 3:	Strcat(buf, "most gracious");
			break;
		case 4:	Strcat(buf, "esteemed");
			break;
		case 5: if (u.ualigntyp == U_CHAOTIC) Strcat(buf, "un");
			Strcat(buf, "holy");
			break;
	    }
#ifdef POLYSELF
	    if(!is_human(uasmon)) Strcat(buf, " creature");
	    else
#endif
		Strcat(buf, (flags.female) ? " lady" : " sir");
	}
	obj->dknown = 1; /* after all, the shk is telling you what it is */
	if(ininv) {
		obj->quan = 1; /* fool xname() into giving singular */
		pline("%s; only %d %s %s.\"", buf, bp->price,
			(bp->bquan > 1) ? "per" : "for this", xname(obj));
		obj->quan = bp->bquan;
	} else pline("The %s will cost you %d zorkmid%s%s.",
			xname(obj), bp->price, plur((long)bp->price),
			(bp->bquan > 1) ? " each" : "");
	ESHK(shopkeeper)->billct++;
	obj->unpaid = 1;
}

void
splitbill(obj, otmp)
register struct obj *obj, *otmp;
{
	/* otmp has been split off from obj */
	register struct bill_x *bp;
	register int tmp;
	bp = onbill(obj);
	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(shopkeeper)->billct == BILLSZ) otmp->unpaid = 0;
	else {
		tmp = bp->price;
		bp = &bill[ESHK(shopkeeper)->billct];
		bp->bo_id = otmp->o_id;
		bp->bquan = otmp->quan;
		bp->useup = 0;
		bp->price = tmp;
		ESHK(shopkeeper)->billct++;
	}
}

static void
subfrombill(obj)
register struct obj *obj;
{
	register struct bill_x *bp;

	if((bp = onbill(obj)) != 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(shopkeeper)->billct--;
		*bp = bill[ESHK(shopkeeper)->billct];
		return;
	} else if (obj->unpaid) {
		impossible("subfrombill: unpaid object not on bill");
		obj->unpaid = 0;
	}
}

void
sellobj(obj)
register struct obj *obj;
{
	long ltmp;

	if(!costly_spot(u.ux,u.uy))
		return;
	if(obj->unpaid) {
		subfrombill(obj);
		return;
	}
	/* you dropped something of your own - probably want to sell it */
	if(shopkeeper->msleep || !shopkeeper->mcanmove || !inhishop(shopkeeper))
		return;
	ltmp = (long) getprice(obj) * (long) obj->quan;
	if (ANGRY(shopkeeper) || (pl_character[0] == 'T' && u.ulevel < (MAXULEV/2))
#ifdef SHIRT
	    || (uarmu && !uarm) /* wearing just a Hawaiian shirt */
#endif
	   ) {
		ltmp /= 3L;
		NOTANGRY(shopkeeper) = 1;
	} else	ltmp /= 2L;
	if(ESHK(shopkeeper)->billct == BILLSZ
	   || !saleable(rooms[ESHK(shopkeeper)->shoproom].rtype-SHOPBASE, obj)
	   || obj->olet == BALL_SYM || ltmp == 0L
	   || (obj->olet == FOOD_SYM && obj->oeaten)) {
		pline("%s seems not interested.", Monnam(shopkeeper));
		obj->no_charge = 1;
		return;
	}
	if(ESHK(shopkeeper)->robbed) {
		if((ESHK(shopkeeper)->robbed -= ltmp) < 0L)
			ESHK(shopkeeper)->robbed = 0L;
verbalize("Thank you for your contribution to restock this recently plundered shop.");
		return;
	}
	if(ltmp > shopkeeper->mgold)
		ltmp = shopkeeper->mgold;
	pay(-ltmp, shopkeeper);
	if(!ltmp) {
		pline("%s gladly accepts %s but cannot pay you at present.",
			Monnam(shopkeeper), doname(obj));
			obj->no_charge = 1;
	} else
	You("sold %s for %ld gold piece%s.", doname(obj), ltmp,
		plur(ltmp));
}

int
doinvbill(mode)
int mode;		/* 0: deliver count 1: paged */
{
	register struct bill_x *bp;
	register struct obj *obj;
#ifdef __GNULINT__
	long totused, thisused = 0L;
/* possibly a bug in the GCC; clearly thisused is always set before use */
#else
	long totused, thisused;
#endif
	char buf[BUFSZ];

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

	    if(shopkeeper)
		for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++)
		    if(bp->useup ||
		      ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan))
			cnt++;
	    return(cnt);
	}

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

	set_pager(0);
	if(page_line("Unpaid articles already used up:") || page_line(""))
	    goto quit;

	totused = 0L;
	for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) {
	    obj = bp_to_obj(bp);
	    if(!obj) {
		impossible("Bad shopkeeper administration.");
		goto quit;
	    }
	    if(bp->useup || bp->bquan > obj->quan) {
		register int cnt, oquan, uquan;

		oquan = obj->quan;
		uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
		thisused = bp->price * uquan;
		totused += thisused;
		obj->quan = uquan;		/* cheat doname */
		Sprintf(buf, "x -  %s", doname(obj));
		obj->quan = oquan;		/* restore value */
		for(cnt = 0; buf[cnt]; cnt++);
		while(cnt < 50)
			buf[cnt++] = ' ';
		Sprintf(&buf[cnt], " %5ld zorkmid%s", thisused, plur(thisused));
		if(page_line(buf))
			goto quit;
	    }
	}
	Sprintf(buf, "Total:%50ld zorkmid%s", totused, plur(totused));
	if(page_line("") || page_line(buf))
		goto quit;
	set_pager(1);
	return(0);
quit:
	set_pager(2);
	return(0);
}

#define HUNGRY	2
static int
getprice(obj)
register struct obj *obj;
{
	register int tmp = objects[obj->otyp].oc_cost;

	switch(obj->olet) {
	case AMULET_SYM:
		if(obj->otyp == AMULET_OF_YENDOR) {
			/* don't let the player get rich selling fakes */
			tmp = (obj->spe < 0 ? 0 : 3500);
		}
		break;
	case FOOD_SYM:
		/* simpler hunger check, (2-4)*cost */
		if (u.uhs >= HUNGRY) tmp *= u.uhs;
		if (obj->oeaten) tmp = eaten_stat(tmp, obj); /* partly eaten */
		break;
	case WAND_SYM:
		if (obj->spe == -1) tmp = 0;
		break;
	case POTION_SYM:
		if (obj->otyp == POT_WATER && !obj->blessed && !obj->cursed)
			tmp = 0;
		break;
	case ARMOR_SYM:
	case WEAPON_SYM:
		if (obj->spe > 0) tmp += 10 * obj->spe;
		break;
	case CHAIN_SYM:
		pline("Strange... carrying a chain?");
		break;
	}
	return(tmp);
}

int
shkcatch(obj)
register struct obj *obj;
{
	register struct monst *shkp = shopkeeper;

	if(obj->otyp != PICK_AXE) return(0);
	if(u.uinshop && shkp && shkp->mcanmove && !shkp->msleep &&
	    inroom(u.ux+u.dx, u.uy+u.dy) + 1 == u.uinshop &&
	    shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y &&
	    u.ux == ESHK(shkp)->shd.x && u.uy == ESHK(shkp)->shd.y) {
		pline("%s nimbly catches the %s.", Monnam(shkp), xname(obj));
		obj->nobj = shkp->minvent;
		shkp->minvent = obj;
		subfrombill(obj);
		return(1);
	}
	return(0);
}

/*
 * shk_move: return 1: he moved  0: he 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;
	int z;
	schar shkroom;
	boolean uondoor = FALSE, satdoor, avoid = FALSE, badinv;

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

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

	shkroom = inroom(omx,omy);
	appr = 1;
	gx = ESHK(shkp)->shk.x;
	gy = ESHK(shkp)->shk.y;
	satdoor = (gx == omx && gy == omy);
	if(ESHK(shkp)->following || ((z = holetime()) >= 0 && z*z <= udist)){
		gx = u.ux;
		gy = u.uy;
		if(shkroom < 0 || shkroom != inroom(u.ux,u.uy))
		    if(udist > 4)
			return(-1);	/* leave it to m_move */
	} else if(ANGRY(shkp)) {
		long saveBlind = Blinded;
		struct obj *saveUblindf = ublindf;
		Blinded = 0;
		ublindf = (struct obj *)0;
		if(shkp->mcansee && !Invis && cansee(omx,omy)) {
			gx = u.ux;
			gy = u.uy;
		}
		Blinded = saveBlind;
		ublindf = saveUblindf;
		avoid = FALSE;
	} else {
#define	GDIST(x,y)	(dist2(x,y,gx,gy))
		if(Invis)
		    avoid = FALSE;
		else {
		    uondoor = (u.ux == ESHK(shkp)->shd.x &&
				u.uy == ESHK(shkp)->shd.y);
		    if(uondoor) {
			if((ESHK(shkp)->billct || ESHK(shkp)->debit) 
					&& inhishop(shkp))
			    pline(NOTANGRY(shkp) ?
				"\"Hello, %s!  Will you please pay before leaving?\"" :
				"\"Hey, %s!  Don't leave without paying!\"",
				plname);
			badinv = (!!carrying(PICK_AXE));
			if(satdoor && badinv)
			    return(0);
			avoid = !badinv;
		    } else {
			avoid = (u.uinshop && dist(gx,gy) > 8);
			badinv = FALSE;
		    }

		    if(((!ESHK(shkp)->robbed && !ESHK(shkp)->billct &&
				!ESHK(shkp)->debit) || avoid)
			&& GDIST(omx,omy) < 3) {
			if(!badinv && !online(omx,omy))
			    return(0);
			if(satdoor)
			    appr = gx = gy = 0;
		    }
		}
	}
	
	return(move_special(shkp,shkroom,appr,uondoor,avoid,omx,omy,gx,gy));
}

#endif /* OVLB */
#ifdef OVL0

int
online(x,y)		/*	New version to speed things up.
			 *	Compiler dependant, may not always work.
			 */
register xchar x, y;
{
	return((x-=u.ux) == 0 || (y-=u.uy) == 0 || x == y || (x+=y) == 0);
}

/*			Original version, just in case...
 *online(x,y) {
 *	return(x==u.ux || y==u.uy || (x-u.ux)*(x-u.ux) == (y-u.uy)*(y-u.uy));
 *}
 */

#endif /* OVL0 */
#ifdef OVLB

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

/* He is digging in the shop. */
void
shopdig(fall)
register int fall;
{
    if(!shopkeeper) return;
    if(!inhishop(shopkeeper)) {
	if (pl_character[0] == 'K') adjalign(-sgn(u.ualigntyp));
	return;
    }

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

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

	    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 = shopkeeper->minvent;
		shopkeeper->minvent = obj;
		subfrombill(obj);
	    }
    }
}

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

	while(cnt--) {
	    enexto(mm, mm->x, mm->y, &mons[PM_KEYSTONE_KOP]);
	    (void) makemon(&mons[PM_KEYSTONE_KOP], mm->x, mm->y);
	}
	while(scnt--) {
	    enexto(mm, mm->x, mm->y, &mons[PM_KOP_SERGEANT]);
	    (void) makemon(&mons[PM_KOP_SERGEANT], mm->x, mm->y);
	}
	while(lcnt--) {
	    enexto(mm, mm->x, mm->y, &mons[PM_KOP_LIEUTENANT]);
	    (void) makemon(&mons[PM_KOP_LIEUTENANT], mm->x, mm->y);
	}
	while(kcnt--) {
	    enexto(mm, mm->x, mm->y, &mons[PM_KOP_KAPTAIN]);
	    (void) makemon(&mons[PM_KOP_KAPTAIN], mm->x, mm->y);
	}
	return(cnt + scnt + lcnt + kcnt);
}
#endif

#endif /* OVLB */
#ifdef OVL1

boolean
in_shop(x,y)
register int x, y;
{
	register int roomno = inroom(x, y);

	if (roomno < 0) return(FALSE);
	return (IS_SHOP(rooms[roomno]));
}

#endif /* OVL1 */
#ifdef OVLB

void
pay_for_door(x,y,dmgstr)
int x, y;
const char *dmgstr;
{
	struct monst *mtmp;
	int roomno = inroom(x, y);
	long damage;
	boolean uinshp = in_shop(u.ux, u.uy);

	/* make sure this function is not used in the wrong place */
	if(!(IS_DOOR(levl[x][y].typ) && in_shop(x, y))) return;

	findshk(roomno);

	if(!shopkeeper) return;

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

	/* if he is already on the war path, be sure it's all out */
	if(ANGRY(shopkeeper) || ESHK(shopkeeper)->following) {
		NOTANGRY(shopkeeper) = 0;
		ESHK(shopkeeper)->following = 1;
		return;
	}

	/* if he's not in his shop.. */
	if(!in_shop(shopkeeper->mx ,shopkeeper->my)) {
		if(!cansee(shopkeeper->mx, shopkeeper->my)) return;
		goto gethim;
	}

	if(uinshp) {
		if(um_dist(shopkeeper->mx, shopkeeper->my, 1) &&
		       !um_dist(shopkeeper->mx, shopkeeper->my, 3)) { 
		    pline("%s leaps towards you!", shkname(shopkeeper));
		    mnexto(shopkeeper);
		}
		if(um_dist(shopkeeper->mx, shopkeeper->my, 1)) goto gethim;
	} else {
	    /* if a !shopkeeper shows up at the door, move him */
	    if(MON_AT(x, y) && (mtmp = m_at(x, y)) != shopkeeper) {
		if(flags.soundok) {
		    You("hear an angry voice:");
		    verbalize("Out of my way, scum!");
		    (void) fflush(stdout);
#if defined(SYSV) || defined(ULTRIX) || defined(VMS)
		    (void)
#endif
#if defined(UNIX) || defined(VMS)
			sleep(1);
#endif
		}
		mnearto(mtmp, x, y, FALSE);
	    }

	    /* make shk show up at the door */
	    remove_monster(shopkeeper->mx, shopkeeper->my);
	    place_monster(shopkeeper, x, y);
	    pmon(shopkeeper);
	}

	if(!strcmp(dmgstr, "destroy")) damage = 400L;
	else damage = (long)(ACURR(A_STR) > 18) ? 400 : 20 * ACURR(A_STR);

	if((um_dist(x, y, 1) && !uinshp) || 
			(u.ugold + ESHK(shopkeeper)->credit) < damage 
				|| !rn2(50)) {
		if(um_dist(x, y, 1) && !uinshp) {
		    pline("%s shouts:", shkname(shopkeeper));
		    pline("\"Who dared %s my door?\"", dmgstr);
		} 
		else
gethim:
		    pline("\"How dare you %s my door?\"", dmgstr);

		NOTANGRY(shopkeeper) = 0;
		ESHK(shopkeeper)->following = 1;
		return;
	}

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

/* called in dokick.c when we kick an object in a store */
boolean
costly_spot(x, y)
register int x, y;
{
	register struct monst *shkp = shopkeeper;
	
	if(!shkp) return(FALSE);

	return(in_shop(x, y) && levl[x][y].typ != DOOR &&
		!(x == ESHK(shkp)->shk.x && y == ESHK(shkp)->shk.y));
}

#ifdef KOPS
static void
kops_gone()
{
	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) pline("The Kops (disappointed) disappear into thin air.");
	allow_kops = TRUE;
}
#endif

static long
cost_per_charge(otmp)
register struct obj *otmp;
{
	register long tmp = get_cost(otmp);

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

/* for using charges of unpaid objects */
void
check_unpaid(otmp)
register struct obj *otmp;
{
	if(!in_shop(u.ux, u.uy)) return;
	
	if(otmp->spe <= 0) return;

	if(otmp->unpaid) {
		ESHK(shopkeeper)->debit += cost_per_charge(otmp);
	}
}

boolean
block_door(x,y)  	/* used in domove to block diagonal shop-exit */
register int x, y;
{
	register int roomno = inroom(x, y);

	if(!in_shop(u.ux, u.uy)) return(FALSE);

	if(!IS_DOOR(levl[x][y].typ)) return(FALSE);

	if(roomno != inroom(u.ux,u.uy)) return(FALSE);

	findshk(roomno);

	if(inhishop(shopkeeper)
	    && shopkeeper->mx == ESHK(shopkeeper)->shk.x
	    && shopkeeper->my == ESHK(shopkeeper)->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(shopkeeper)->shd.x == x && ESHK(shopkeeper)->shd.y == y
	    && shopkeeper->mcanmove && !shopkeeper->msleep
	    && (ESHK(shopkeeper)->debit || ESHK(shopkeeper)->billct ||
		ESHK(shopkeeper)->robbed)) {
		pline("%s%s blocks your way!", shkname(shopkeeper),
				Invis ? " senses your motion and" : "");
		return(TRUE);
	}
	return(FALSE);
}

boolean
block_entry(x,y)  	/* used in domove to block diagonal shop-entry */
register int x, y;
{
	register int sx, sy, roomno = inroom(x, y);

	if(roomno != inroom(u.ux,u.uy)) return(FALSE);

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

	findshk(roomno);
	if(!inhishop(shopkeeper)) return(FALSE);

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

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

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

#endif /* OVLB */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.