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

#include "hack.h"
#include "artifact.h"

#define NOINVSYM        '#'
#define CONTAINED_SYM   '>'     /* designator for inside a container */

#ifdef OVL1
static void NDECL(reorder_invent);
static boolean FDECL(mergable,(struct obj *,struct obj *));
static int FDECL(merged,(struct obj *,struct obj *,int));
#endif /* OVL1 */
STATIC_DCL void FDECL(assigninvlet,(struct obj *));
STATIC_DCL void FDECL(unlinkinv,(struct obj*));
STATIC_DCL void FDECL(compactify,(char *));
STATIC_PTR int FDECL(ckunpaid,(struct obj *));
#ifdef OVLB
static struct obj *FDECL(find_unpaid,(struct obj *,struct obj **));
static boolean NDECL(wearing_armor);
static boolean FDECL(is_worn,(struct obj *));
static boolean FDECL(is_fully_identified,(struct obj *));
#endif /* OVLB */
STATIC_DCL char FDECL(obj_to_let,(struct obj *));

#ifdef OVLB

static int lastinvnr = 51;      /* 0 ... 51 (never saved&restored) */

#ifdef WIZARD
/* wizards can wish for venom, which will become an invisible inventory
 * item without this.  putting it in inv_order would mean venom would
 * suddenly become a choice for all the inventory-class commands, which
 * would probably cause mass confusion.  the test for inventory venom
 * is only WIZARD and not wizard because the wizard can leave venom lying
 * around on a bones level for normal players to find.
 */
static char venom_inv[] = { VENOM_CLASS, 0 };   /* (constant) */
#endif

STATIC_OVL void
assigninvlet(otmp)
register struct obj *otmp;
{
	boolean inuse[52];
	register int i;
	register struct obj *obj;

	for(i = 0; i < 52; i++) inuse[i] = FALSE;
	for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) {
		i = obj->invlet;
		if('a' <= i && i <= 'z') inuse[i - 'a'] = TRUE; else
		if('A' <= i && i <= 'Z') inuse[i - 'A' + 26] = TRUE;
		if(i == otmp->invlet) otmp->invlet = 0;
	}
	if((i = otmp->invlet) &&
	    (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
		return;
	for(i = lastinvnr+1; i != lastinvnr; i++) {
		if(i == 52) { i = -1; continue; }
		if(!inuse[i]) break;
	}
	otmp->invlet = (inuse[i] ? NOINVSYM :
			(i < 26) ? ('a'+i) : ('A'+i-26));
	lastinvnr = i;
}

#endif /* OVLB */
#ifdef OVL1

/* note: assumes ASCII; toggling a bit puts lowercase in front of uppercase */
#define inv_rank(o) ((o)->invlet ^ 040)

/* sort the inventory; used by addinv() and doorganize() */
static void
reorder_invent()
{
	struct obj *otmp, *prev, *next;
	boolean need_more_sorting;

	do {
	    /*
	     * We expect at most one item to be out of order, so this
	     * isn't nearly as inefficient as it may first appear.
	     */
	    need_more_sorting = FALSE;
	    for (otmp = invent, prev = 0; otmp; ) {
		next = otmp->nobj;
		if (next && inv_rank(next) < inv_rank(otmp)) {
		    need_more_sorting = TRUE;
		    if (prev) prev->nobj = next;
		    else      invent = next;
		    otmp->nobj = next->nobj;
		    next->nobj = otmp;
		    prev = next;
		} else {
		    prev = otmp;
		    otmp = next;
		}
	    }
	} while (need_more_sorting);
}

#undef inv_rank

/* merge obj with otmp and delete obj if types agree */
STATIC_OVL int
merged(otmp, obj, lose)
register struct obj *otmp, *obj;
register int lose;
{
	if(mergable(otmp, obj)) {
		/* Approximate age: we do it this way because if we were to
		 * do it "accurately" (merge only when ages are identical)
		 * we'd wind up never merging any corpses.
		 * otmp->age = otmp->age*(1-proportion) + obj->age*proportion;
		 */
		otmp->age = ((otmp->age*otmp->quan) + (obj->age*obj->quan))
			/ (otmp->quan + obj->quan);
		otmp->quan += obj->quan;
		otmp->owt += obj->owt;
		if(!otmp->onamelth && obj->onamelth)
			otmp = oname(otmp, ONAME(obj), 1);
		if(lose) freeobj(obj);
		obfree(obj,otmp);       /* free(obj), bill->otmp */
		return(1);
	} else  return(0);
}

struct obj *
addinv(obj)
register struct obj *obj;
{
	register struct obj *otmp, *prev;

	
	if (obj->otyp == GOLD_PIECE) {
		u.ugold += obj->quan;
		flags.botl = 1;
		return obj;
	} else if (obj->otyp == AMULET_OF_YENDOR) {
		if (u.uhave.amulet) impossible ("already have amulet?");
		u.uhave.amulet = 1;
	} else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
		if (u.uhave.menorah) impossible ("already have candelabrum?");
		u.uhave.menorah = 1;
	} else if (obj->otyp == BELL_OF_OPENING) {
		if (u.uhave.bell) impossible ("already have silver bell?");
		u.uhave.bell = 1;
	} else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
		if (u.uhave.book) impossible ("already have the book?");
		u.uhave.book = 1;
#ifdef MULDGN
	} else if (is_quest_artifact(obj)) {
		if (u.uhave.questart) impossible ("already have the artifact?");
		u.uhave.questart = 1;
		artitouch();
		set_artifact_intrinsic(obj, 1, W_ART);
#endif
	} else if(obj->oartifact) {
		set_artifact_intrinsic(obj, 1, W_ART);
	
	}
	
	/* merge if possible; find end of chain in the process */
	for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj)
	    if (merged(otmp, obj, 0)) {
		obj = otmp;
		goto added;
	    }
	/* didn't merge, so insert into chain */
	if (flags.invlet_constant || !prev) {
	    if (flags.invlet_constant) assigninvlet(obj);
	    obj->nobj = invent;         /* insert at beginning */
	    invent = obj;
	    if (flags.invlet_constant) reorder_invent();
	     
	} else {
	    prev->nobj = obj;           /* insert at end */
	    obj->nobj = 0;
	}

added:
	
	if (obj->otyp == LUCKSTONE
	    || (obj->oartifact && spec_ability(obj, SPFX_LUCK))) {
		/* new luckstone must be in inventory by this point
		 * for correct calculation */
		if (stone_luck(TRUE) >= 0) u.moreluck = LUCKADD;
		else u.moreluck = -LUCKADD;
	} else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP)
		check_lamps();
	update_inventory();
	return(obj);
}

#endif /* OVL1 */
#ifdef OVLB

/* Add an item to the inventory unless we're fumbling, and give a message.
 * If there aren't any free inventory slots, we'll drop it instead.
 * If both success and failure messages are NULL, then we're just doing the
 * fumbling/slot-limit checking for a silent grab.
 * Note: will drop the whole bunch if the object merges first.
 */
struct obj *
hold_another_object(obj, drop_fmt, drop_arg, hold_msg)
struct obj *obj;
const char *drop_fmt, *drop_arg, *hold_msg;
{
	long oquan = obj->quan;
	if (!Blind) obj->dknown = 1;    /* maximize mergibility */
	if (Fumbling) {
		if (drop_fmt) pline(drop_fmt, drop_arg);
		dropy(obj);
	} else {
		obj = addinv(obj);
		if (inv_cnt() > 52
		    || ((obj->otyp != LOADSTONE || !obj->cursed)
			&& near_capacity() >= OVERLOADED)) {
			if (drop_fmt) pline(drop_fmt, drop_arg);
			dropx(obj);
		} else {
			if (hold_msg || drop_fmt) prinv(hold_msg, obj, oquan);
		}
	}
	return obj;
}

void
useup(obj)
register struct obj *obj;
{
	/*  Note:  This works correctly for containers because they */
	/*         (containers) don't merge.                        */
	if(obj->quan > 1L){
#ifndef NO_SIGNAL
		obj->in_use = FALSE;    /* no longer in use */
#endif
		obj->quan--;
		obj->owt = weight(obj);
	} else {
		setnotworn(obj);
		freeinv(obj);
		obfree(obj, (struct obj *) 0);  /* deletes contents also */
	}
}

#endif /* OVLB */
#ifdef OVL3

/* used by freeinv and doorganize to do list manipulation */
STATIC_OVL
void
unlinkinv(obj)
register struct obj *obj;
{
	register struct obj *otmp;

	if(obj == invent)
		invent = invent->nobj;
	else {
		for(otmp = invent; otmp->nobj != obj; otmp = otmp->nobj)
			if(!otmp->nobj) panic("unlinkinv");
		otmp->nobj = obj->nobj;
	}
	obj->nobj = 0;
}

void
freeinv(obj)
register struct obj *obj;
{
	unlinkinv(obj);

	if (obj->otyp == GOLD_PIECE) {
		u.ugold -= obj->quan;
		flags.botl = 1;
		return;
	} else if (obj->otyp == AMULET_OF_YENDOR) {
		if (!u.uhave.amulet) impossible ("don't have amulet?");
		u.uhave.amulet = 0;
	} else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
		if (!u.uhave.menorah) impossible ("don't have candelabrum?");
		u.uhave.menorah = 0;
	} else if (obj->otyp == BELL_OF_OPENING) {
		if (!u.uhave.bell) impossible ("don't have silver bell?");
		u.uhave.bell = 0;
	} else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
		if (!u.uhave.book) impossible ("don't have the book?");
		u.uhave.book = 0;
#ifdef MULDGN
	} else if (is_quest_artifact(obj)) {
		if(!u.uhave.questart) impossible ("don't have the artifact?");
		u.uhave.questart = 0;
		set_artifact_intrinsic(obj, 0, W_ART);
#endif
	} else if (obj->oartifact) {
		set_artifact_intrinsic(obj, 0, W_ART);
	} else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP
		   || obj->otyp == BRASS_LANTERN) {
		if (obj->lamplit) {
			obj->lamplit = 0;
			if (!Blind) pline("%s goes out!", The(xname(obj)));
		}
		check_lamps();
	} else if (obj->otyp == LOADSTONE) {
		curse(obj);
	} else if (obj->otyp == LUCKSTONE
		   || (obj->oartifact && spec_ability(obj, SPFX_LUCK))) {
		int luckbon = stone_luck(TRUE);
		if (!luckbon && !carrying(LUCKSTONE)) u.moreluck = 0;
		else if (luckbon >= 0) u.moreluck = LUCKADD;
		else u.moreluck = -LUCKADD;
		flags.botl = 1;
	}
	update_inventory();
}

void
delallobj(x, y)
int x, y;
{
	struct obj *otmp, *otmp2;

	for (otmp = level.objects[x][y]; otmp; otmp = otmp2) {
		if (otmp == uball)
			unpunish();
		/* after unpunish(), or might get deallocated chain */
		otmp2 = otmp->nexthere;
		if (otmp == uchain)
			continue;
		delobj(otmp);
	}
}

/* move objects from fobj/nexthere lists to buriedobjlist, keeping position
 * information */
void
bury_objs(x, y)
int x, y;
{
	struct obj *otmp, *otmp2;

	for (otmp = level.objects[x][y]; otmp; otmp = otmp2) {
		if (otmp == uball)
			unpunish();
		/* after unpunish(), or might get deallocated chain */
		otmp2 = otmp->nexthere;
		if (otmp == uchain)
			continue;
#ifdef WALKIES
		if (otmp->otyp == LEASH && otmp->leashmon != 0)
			o_unleash(otmp);
#endif
		if (otmp->lamplit) {
			otmp->lamplit = 0;
			check_lamps();
		}
		freeobj(otmp);
		if (otmp->otyp == ROCK) {
			/* melts into burying material */
			obfree(otmp, (struct obj *)0);
			continue;
		}
		otmp->nexthere = (struct obj *)0;
		otmp->nobj = level.buriedobjlist;
		level.buriedobjlist = otmp;
	}
	/* don't expect any engravings here, but just in case */
	del_engr_at(x, y);
	newsym(x, y);
}

/* delete buried object */
void
delburiedobj(obj)
struct obj *obj;
{
	struct obj *otmp;
	if (obj == level.buriedobjlist)
		level.buriedobjlist = obj->nobj;
	else {
		for (otmp = level.buriedobjlist; otmp; otmp = otmp->nobj)
			if (otmp->nobj == obj) {
				otmp->nobj = obj->nobj;
				break;
			}
		if (!otmp) panic("error in delburiedobj");
	}
	obfree(obj, (struct obj *) 0);  /* frees contents also */
}

/* move objects from buriedobjlist to fobj/nexthere lists */
void
unearth_objs(x, y)
int x, y;
{
	struct obj *otmp, *otmp2, *prevobj;

	remove_cadavers(&level.buriedobjlist);

	prevobj = (struct obj *) 0;
	for (otmp = level.buriedobjlist; otmp; otmp = otmp2) {
		otmp2 = otmp->nobj;
		if (otmp->ox == x && otmp->oy == y) {
			if (prevobj)
				prevobj->nobj = otmp2;
			else
				level.buriedobjlist = otmp2;
			if (is_organic(otmp) && rn2(2)) {
				/* rotted away */
				obfree(otmp, (struct obj *) 0);
			} else {
				otmp->nobj = fobj;
				fobj = otmp;
				place_object(otmp, x, y);
				stackobj(otmp);
			}
		} else
			prevobj = otmp;
	}
	del_engr_at(x, y);
	newsym(x, y);
}

#endif /* OVL3 */
#ifdef OVL2

/* destroy object in fobj chain (if unpaid, it remains on the bill) */
void
delobj(obj)
register struct obj *obj;
{
#ifdef WALKIES
	if(obj->otyp == LEASH && obj->leashmon != 0) o_unleash(obj);
#endif
	if (obj->otyp == AMULET_OF_YENDOR ||
			obj->otyp == CANDELABRUM_OF_INVOCATION ||
			obj->otyp == BELL_OF_OPENING ||
			obj->otyp == SPE_BOOK_OF_THE_DEAD) {
		/* player might be doing something stupid, but we
		 * can't guarantee that.  assume special artifacts
		 * are indestructible via drawbridges, and exploding
		 * chests, and golem creation, and ...
		 */
		return;
	}
	freeobj(obj);
	newsym(obj->ox,obj->oy);
	obfree(obj, (struct obj *) 0);  /* frees contents also */
}

/* unlink obj from chain starting with fobj */
void
freeobj(obj)
register struct obj *obj;
{
	register struct obj *otmp;

	if (obj == fobj)
	    fobj = fobj->nobj;
	else {
	    for(otmp = fobj; otmp; otmp = otmp->nobj)
		if (otmp->nobj == obj) {
		    otmp->nobj = obj->nobj;
		    break;
		}
	    if (!otmp) panic("error in freeobj");
	}
	remove_object(obj);
}

#endif /* OVL2 */
#ifdef OVL0

struct obj *
sobj_at(n,x,y)
register int n, x, y;
{
	register struct obj *otmp;

	for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
		if(otmp->otyp == n)
		    return(otmp);
	return((struct obj *)0);
}

#endif /* OVL0 */
#ifdef OVLB

int
carried(obj)
register struct obj *obj;
{
	register struct obj *otmp;

	for(otmp = invent; otmp; otmp = otmp->nobj)
		if(otmp == obj) return(1);
	return(0);
}

struct obj *
carrying(type)
register int type;
{
	register struct obj *otmp;

	for(otmp = invent; otmp; otmp = otmp->nobj)
		if(otmp->otyp == type)
			return(otmp);
	return((struct obj *) 0);
}

boolean
have_lizard()
{
	register struct obj *otmp;

	for(otmp = invent; otmp; otmp = otmp->nobj)
		if(otmp->otyp == CORPSE && otmp->corpsenm == PM_LIZARD)
			return(TRUE);
	return(FALSE);
}

struct obj *
o_on(id, objchn)
unsigned int id;
register struct obj *objchn;
{
	struct obj *temp;

	while(objchn) {
		if(objchn->o_id == id) return(objchn);
		if (Has_contents(objchn) && (temp = o_on(id,objchn->cobj)))
			return temp;
		objchn = objchn->nobj;
	}
	return((struct obj *) 0);
}

boolean
obj_here(obj, x, y)
register struct obj *obj;
int x, y;
{
	register struct obj *otmp;

	for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
		if(obj == otmp) return(TRUE);
	return(FALSE);
}

#endif /* OVLB */
#ifdef OVL2

struct obj *
g_at(x,y)
register int x, y;
{
	register struct obj *obj = level.objects[x][y];
	while(obj) {
	    if (obj->otyp == GOLD_PIECE) return obj;
	    obj = obj->nexthere;
	}
	return((struct obj *)0);
}

#endif /* OVL2 */
#ifdef OVLB

/* Make a gold object from the hero's gold. */
struct obj *
mkgoldobj(q)
register long q;
{
	register struct obj *otmp;

	otmp = mksobj(GOLD_PIECE, FALSE, FALSE);
	u.ugold -= q;
	otmp->quan = q;
	otmp->owt = weight(otmp);
	flags.botl = 1;
	return(otmp);
}

#endif /* OVLB */
#ifdef OVL1

STATIC_OVL void
compactify(buf)
register char *buf;
/* compact a string of inventory letters by dashing runs of letters */
{
	register int i1 = 1, i2 = 1;
	register char ilet, ilet1, ilet2;

	ilet2 = buf[0];
	ilet1 = buf[1];
	buf[++i2] = buf[++i1];
	ilet = buf[i1];
	while(ilet) {
		if(ilet == ilet1+1) {
			if(ilet1 == ilet2+1)
				buf[i2 - 1] = ilet1 = '-';
			else if(ilet2 == '-') {
				buf[i2 - 1] = ++ilet1;
				buf[i2] = buf[++i1];
				ilet = buf[i1];
				continue;
			}
		}
		ilet2 = ilet1;
		ilet1 = ilet;
		buf[++i2] = buf[++i1];
		ilet = buf[i1];
	}
}

/*
 * getobj returns:
 *      struct obj *xxx:        object to do something with.
 *      (struct obj *) 0        error return: no object.
 *      &zeroobj                explicitly no object (as in w-).
 */
struct obj *
getobj(let,word)
register const char *let,*word;
{
	register struct obj *otmp;
	register char ilet;
	char buf[BUFSZ], qbuf[QBUFSZ];
	char lets[BUFSZ];
	register int foo = 0;
	register char *bp = buf;
	xchar allowcnt = 0;     /* 0, 1 or 2 */
	boolean allowgold = FALSE, usegold = FALSE;
		/* Two possibilities: they can't use gold because it's illegal,
		 * or they can't use gold because they don't have any.
		 */
	boolean allowall = FALSE;
	boolean allownone = FALSE;
	xchar foox = 0;
	long cnt;
	boolean prezero = FALSE;

	if(*let == ALLOW_COUNT) let++, allowcnt = 1;
	if(*let == GOLD_CLASS) let++,
		usegold = TRUE, allowgold = (u.ugold ? TRUE : FALSE);
#ifdef POLYSELF
	/* Equivalent of an "ugly check" for gold */
	if (usegold && !strcmp(word, "eat") && !metallivorous(uasmon))
		usegold = allowgold = FALSE;
#endif
	if(*let == ALL_CLASSES) let++, allowall = TRUE;
	if(*let == ALLOW_NONE) let++, allownone = TRUE;
	/* "ugly check" for reading fortune cookies, part 1 */
	if(allowall && !strcmp(word, "read")) allowall = FALSE;

	if(allownone) *bp++ = '-';
	if(allowgold) *bp++ = def_oc_syms[GOLD_CLASS];
	if(bp > buf && bp[-1] == '-') *bp++ = ' ';

	ilet = 'a';
	for(otmp = invent; otmp; otmp = otmp->nobj){
	    if(!*let || index(let, otmp->oclass)) {
		bp[foo++] = flags.invlet_constant ? otmp->invlet : ilet;

		/* ugly check: remove inappropriate things */
		if((!strcmp(word, "take off") &&
		    (!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))
		     || (otmp==uarm && uarmc)
#ifdef TOURIST
		     || (otmp==uarmu && (uarm || uarmc))
#endif
		    ))
		|| (!strcmp(word, "wear") &&
		     (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
							/* already worn */
		|| (!strcmp(word, "wield") &&
		    (otmp->owornmask & W_WEP))
		    ) {
			foo--;
			foox++;
		}

		/* Second ugly check; unlike the first it won't trigger an
		 * "else" in "you don't have anything else to ___".
		 */
		else if ((!strcmp(word, "wear") &&
		    (otmp->oclass == TOOL_CLASS &&
		     otmp->otyp != BLINDFOLD && otmp->otyp != TOWEL))
		|| (!strcmp(word, "wield") &&
		    (otmp->oclass == TOOL_CLASS &&
		     otmp->otyp != PICK_AXE && otmp->otyp != UNICORN_HORN))
#ifdef POLYSELF
		|| (!strcmp(word, "eat") && !is_edible(otmp))
#endif
		|| (!strcmp(word, "sacrifice") &&
		    (otmp->otyp != CORPSE &&
		     otmp->otyp != AMULET_OF_YENDOR &&
		     otmp->otyp != ART_EYE_OF_VECNA &&
		     otmp->otyp != ART_HAND_OF_VECNA &&
		     otmp->otyp != FAKE_AMULET_OF_YENDOR))
		|| (!strcmp(word, "write with") &&
		    (otmp->oclass == TOOL_CLASS &&
		     otmp->otyp != MAGIC_MARKER && otmp->otyp != TOWEL))
		|| (!strcmp(word, "tin") &&
		    (otmp->otyp != CORPSE || !tinnable(otmp)))
		|| (!strcmp(word, "rub") &&
		    (otmp->oclass == TOOL_CLASS &&
		     otmp->otyp != OIL_LAMP && otmp->otyp != MAGIC_LAMP &&
		     otmp->otyp != BRASS_LANTERN))
		    )
			foo--;
	    } else {

		/* "ugly check" for reading fortune cookies, part 2 */
		if ((!strcmp(word, "read") && otmp->otyp == FORTUNE_COOKIE))
			allowall = TRUE;
	    }

	    if(ilet == 'z') ilet = 'A'; else ilet++;
	}
	bp[foo] = 0;
	if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0;
	Strcpy(lets, bp);       /* necessary since we destroy buf */
	if(foo > 5)                     /* compactify string */
		compactify(bp);

	if(!foo && !allowall && !allowgold && !allownone) {
		You("don't have anything %sto %s.",
			foox ? "else " : "", word);
		return((struct obj *)0);
	}
	for(;;) {
		cnt = 0;
		if (allowcnt == 2) allowcnt = 1;  /* abort previous count */
		if(!buf[0]) {
			Sprintf(qbuf, "What do you want to %s? [*]", word);
		} else {
			Sprintf(qbuf, "What do you want to %s? [%s or ?*]",
				word, buf);
		}
#ifdef REDO
		if (in_doagain)
		    ilet = readchar();
		else
#endif
		    ilet = yn_function(qbuf, NULL, '\0');
		if(ilet == '0') prezero = TRUE;
		while(digit(ilet) && allowcnt) {
#ifdef REDO
			if (ilet != '?' && ilet != '*') savech(ilet);
#endif
			cnt = 10*cnt + (ilet - '0');
			allowcnt = 2;   /* signal presence of cnt */
			ilet = readchar();
		}
		if(digit(ilet)) {
			pline("No count allowed with this command.");
			continue;
		}
		if(index(quitchars,ilet)) {
		    if(flags.verbose)
			pline("Never mind.");
		    return((struct obj *)0);
		}
		if(ilet == '-') {
			return(allownone ? &zeroobj : (struct obj *) 0);
		}
		if(ilet == def_oc_syms[GOLD_CLASS]) {
			if(!usegold){
				You("cannot %s gold.", word);
				return(struct obj *)0;
			} else if (!allowgold) {
				You("are not carrying any gold.");
				return(struct obj *)0;
			}
			if(cnt == 0 && prezero) return((struct obj *)0);
			/* Historic note: early Nethack had a bug which was
			 * first reported for Larn, where trying to drop 2^32-n
			 * gold pieces was allowed, and did interesting things
			 * to your money supply.  The LRS is the tax bureau
			 * from Larn.
			 */
			if(cnt < 0) {
	pline("The LRS would be very interested to know you have that much.");
				return(struct obj *)0;
			}

			if(!(allowcnt == 2 && cnt < u.ugold))
				cnt = u.ugold;
			return(mkgoldobj(cnt));
		}
		if(allowcnt == 2 && !strcmp(word,"throw")) {
			/* permit counts for throwing gold, but don't accept
			 * counts for other things since the throw code will
			 * split off a single item anyway */
			allowcnt = 1;
			if(cnt == 0 && prezero) return((struct obj *)0);
			if(cnt > 1) {
			    You("can only throw one item at a time.");
			    continue;
			}
		}
		if(ilet == '?' || ilet == '*') {
		    ilet = display_inventory(ilet == '?' ? lets : NULL, FALSE);
		    if(!ilet) continue;
		    if(ilet == '\033') {
			if(flags.verbose)
			    pline("Never mind.");
			return((struct obj *)0);
		    }
		    /* they typed a letter (not a space) at the prompt */
		}
#ifdef REDO
		savech(ilet);
#endif
		if(flags.invlet_constant) {
			for(otmp = invent; otmp; otmp = otmp->nobj)
				if(otmp->invlet == ilet) break;
		} else {
			if(ilet >= 'A' && ilet <= 'Z') ilet += 'z' - 'A' + 1;
			ilet -= 'a';
			for(otmp = invent; otmp && ilet;
					ilet--, otmp = otmp->nobj) ;
		}
		if(!otmp) {
			You("don't have that object.");
#ifdef REDO
			if (in_doagain) return((struct obj *) 0);
#endif
			continue;
		} else if (cnt < 0 || otmp->quan < cnt) {
			You("don't have that many!  You have only %ld.",
			    otmp->quan);
#ifdef REDO
			if (in_doagain) return((struct obj *) 0);
#endif
			continue;
		}
		break;
	}
	if(!allowall && let && !index(let,otmp->oclass)) {
		pline(silly_thing_to, word);
		return((struct obj *)0);
	}
	if(allowcnt == 2) {     /* cnt given */
		if(cnt == 0) return (struct obj *)0;
		if(cnt != otmp->quan) {
			register struct obj *obj = splitobj(otmp, cnt);
		/* Very ugly kludge necessary to prevent someone from trying
		 * to drop one of several loadstones and having the loadstone
		 * now be separate.
		 */
			if (!strcmp(word, "drop") &&
			    obj->otyp == LOADSTONE && obj->cursed)
				otmp->corpsenm = obj->invlet;
			if(otmp == uwep) setuwep(obj);
		}
	}
	return(otmp);
}

#endif /* OVL1 */
#ifdef OVLB

STATIC_PTR int
ckunpaid(otmp)
register struct obj *otmp;
{
	return((int)(otmp->unpaid));
}

static boolean
wearing_armor() {
	return((boolean)(uarm || uarmc || uarmf || uarmg || uarmh || uarms
#ifdef TOURIST
		|| uarmu
#endif
		));
}

static boolean
is_worn(otmp)
register struct obj *otmp;
{
    return((boolean)(!!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL | W_WEP))));
}

static boolean
is_fully_identified(otmp)
register struct obj *otmp;
{
    return((boolean)(otmp->known && otmp->dknown && otmp->bknown && otmp->rknown
	   && objects[otmp->otyp].oc_name_known));
}

static NEARDATA const char removeables[] =
	{ ARMOR_CLASS, WEAPON_CLASS, RING_CLASS, AMULET_CLASS, TOOL_CLASS, 0 };

/* interactive version of getobj - used for Drop, Identify and */
/* Takeoff (A). Return the number of times fn was called successfully */
int
ggetobj(word, fn, mx)
register const char *word;
register int FDECL((*fn),(OBJ_P)), mx;
{
	char buf[BUFSZ], qbuf[QBUFSZ];
	register char *ip;
	register char sym;
	register int oletct = 0, iletct = 0;
	register boolean allflag = FALSE;
	char olets[20], ilets[20];
	int FDECL((*ckfn),(OBJ_P)) = (int (*)()) 0;
	xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */
	register boolean takeoff = !strcmp(word, "take off");
	register boolean ident = !strcmp(word, "identify");
	struct obj *obj;
	int unpaid, oc_of_sym;

	if(takeoff && !wearing_armor() && !uwep && !uamul &&
			!uleft && !uright && !ublindf) {
		You("are not wearing anything.");
		return(0);
	}
	if(!invent && !allowgold){
		You("have nothing to %s.", word);
		return(0);
	}

	if (allowgold) ilets[iletct++] = def_oc_syms[GOLD_CLASS];
	ilets[iletct] = '\0';   /* terminate for index() */
	unpaid = 0;
	for (obj = invent; obj; obj = obj->nobj) {
		sym = (char) def_oc_syms[(int) obj->oclass];
		if (!index(ilets, sym) && (!takeoff || is_worn(obj))
		    && (!ident || !is_fully_identified(obj))) {
			ilets[iletct++] = sym;
			/* necessary because of index() being used above */
			ilets[iletct] = '\0';
		}

		if (obj->unpaid) unpaid = 1;
	}

	if (ident && !iletct) {
	    You("have already identified all your possessions.");
	    return -1;          /* special case for seffects(read.c) */
	} else if (!takeoff && (unpaid || invent)) {
	    ilets[iletct++] = ' ';
	    if (unpaid) ilets[iletct++] = 'u';
	    if (invent) ilets[iletct++] = 'a';
	} else if (takeoff && invent) {
	    ilets[iletct++] = ' ';
	}
	ilets[iletct++] = 'i';
	ilets[iletct] = '\0';

	do {
	    Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]",
		    word, ilets);
	    getlin(qbuf, buf);
	    if (buf[0] == '\033') return(0);
	    if (index(buf, 'i')) {
		(void) display_inventory(NULL, FALSE);
	    } else
		break;
	} while (1);

	ip = buf;
	olets[0] = 0;
	while ((sym = *ip++) != 0) {
		if(sym == ' ') continue;
		oc_of_sym = def_char_to_objclass(sym);
		if(takeoff && !(uwep && oc_of_sym == uwep->oclass)
		   && (oc_of_sym != MAXOCLASSES)) {
		    if(!index(removeables,oc_of_sym)) {
			pline("Not applicable.");
			return(0);
		    } else if(oc_of_sym == ARMOR_CLASS && !wearing_armor()) {
			You("are not wearing any armor.");
			return(0);
		    } else if(oc_of_sym == WEAPON_CLASS && !uwep) {
			You("are not wielding anything.");
			return(0);
		    } else if(oc_of_sym == RING_CLASS && !uright && !uleft) {
			You("are not wearing rings.");
			return(0);
		    } else if(oc_of_sym == AMULET_CLASS && !uamul) {
			You("are not wearing an amulet.");
			return(0);
		    } else if(oc_of_sym == TOOL_CLASS && !ublindf) {
			You("are not wearing a blindfold.");
			return(0);
		    }
		}
		if(oc_of_sym == GOLD_CLASS) {
			if(allowgold == 1)
				(*fn)(mkgoldobj(u.ugold));
			else if(!u.ugold)
				You("have no gold.");
			allowgold = 2;
		} else if(sym == 'a' || sym == 'A')
		    allflag = TRUE;
		else if(sym == 'u' || sym == 'U')
		    ckfn = ckunpaid;
		else if (oc_of_sym == MAXOCLASSES)
			You("don't have any %c's.", sym);
		else if (oc_of_sym != VENOM_CLASS) {/* venom doesn't show up */
			if (!index(olets, oc_of_sym)) {
				olets[oletct++] = oc_of_sym;
				olets[oletct] = 0;
			}
		}
	}
	if(allowgold == 2 && !oletct)
		return 1;       /* you dropped gold (or at least tried to) */
	else
		return askchain((struct obj **)&invent, olets, allflag,
				fn, ckfn, mx, word);
}

/*
 * Walk through the chain starting at objchn and ask for all objects
 * with olet in olets (if nonNULL) and satisfying ckfn (if nonNULL)
 * whether the action in question (i.e., fn) has to be performed.
 * If allflag then no questions are asked. Max gives the max nr of
 * objects to be treated. Return the number of objects treated.
 */
int
askchain(objchn, olets, allflag, fn, ckfn, mx, word)
struct obj **objchn;
register int allflag, mx;
register const char *olets, *word;      /* olets is an Obj Class char array */
register int FDECL((*fn),(OBJ_P)), FDECL((*ckfn),(OBJ_P));
{
	register struct obj *otmp, *otmp2;
	register char sym, ilet;
	register int cnt = 0, dud = 0, tmp;
	boolean takeoff, nodot, ident, ininv;
	char qbuf[QBUFSZ];

	takeoff = !strcmp(word, "take off");
	ident = !strcmp(word, "identify");
	nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") ||
		 ident || takeoff);
	ininv = (*objchn == invent);
	/* Changed so the askchain is interrogated in the order specified.
	 * For example, if a person specifies =/ then first all rings will be
	 * asked about followed by all wands -dgk
	 */
nextclass:
	ilet = 'a'-1;
	if (*objchn && (*objchn)->otyp == GOLD_PIECE) ilet--;   /* extra iteration */
	for (otmp = *objchn; otmp; otmp = otmp2) {
		if(ilet == 'z') ilet = 'A'; else ilet++;
		otmp2 = otmp->nobj;
		if (olets && *olets && otmp->oclass != *olets) continue;
		if (takeoff && !is_worn(otmp)) continue;
		if (ident && is_fully_identified(otmp)) continue;
		if (ckfn && !(*ckfn)(otmp)) continue;
		if (!allflag) {
			Strcpy(qbuf, ininv ?
				xprname(otmp, ilet, !nodot, 0L) : doname(otmp));
			Strcat(qbuf, "?");
			sym = (takeoff || ident || otmp->quan < 2L) ?
				nyaq(qbuf) : nyNaq(qbuf);
		}
		else    sym = 'y';

		if (sym == '#') {
		 /* Number was entered; split the object unless it corresponds
		    to 'none' or 'all'.  2 special cases: cursed loadstones and
		    welded weapons (eg, multiple daggers) will remain as merged
		    unit; done to avoid splitting an object that won't be
		    droppable (even if we're picking up rather than dropping).
		  */
		    if (!yn_number)
			sym = 'n';
		    else {
			sym = 'y';
			if (yn_number < otmp->quan && !welded(otmp) &&
			    (!otmp->cursed || otmp->otyp != LOADSTONE)) {
			    struct obj *otmpx = splitobj(otmp, yn_number);
			    if (!otmpx || otmpx->nobj != otmp2)
				impossible("bad object split in askchain");
			}
		    }
		}
		switch(sym){
		case 'a':
			allflag = 1;
		case 'y':
			tmp = (*fn)(otmp);
			if(tmp < 0) goto ret;
			cnt += tmp;
			if(--mx == 0) goto ret;
		case 'n':
			if(nodot) dud++;
		default:
			break;
		case 'q':
			/* special case for seffects() */
			if (ident) cnt = -1;
			goto ret;
		}
	}
	if (olets && *olets && *++olets)
		goto nextclass;
	if(!takeoff && (dud || cnt)) pline("That was all.");
	else if(!dud && !cnt) pline("No applicable objects.");
ret:
	return(cnt);
}

#endif /* OVLB */
#ifdef OVL2

STATIC_OVL char
obj_to_let(obj) /* should of course only be called for things in invent */
register struct obj *obj;
{
	register struct obj *otmp;
	register char ilet;

	if (obj->otyp == GOLD_PIECE)
		return GOLD_SYM;
	else if (flags.invlet_constant)
		return obj->invlet;
	ilet = 'a';
	for(otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
		if(++ilet > 'z') ilet = 'A';
	return((char)(otmp ? ilet : NOINVSYM));
}

/*
 * Print the indicated quantity of the given object.  If quan == 0L then use
 * the current quantity.
 */
void
prinv(prefix, obj, quan)
const char *prefix;
register struct obj *obj;
long quan;
{
#ifdef GCC_WARN
	long savequan = 0;
#else
	long savequan;
#endif
	if ( !prefix ) prefix = "";
	if (quan) {
		savequan = obj->quan;
		obj->quan = quan;
	}
	pline("%s%s%s",
	      prefix, *prefix ? " " : "",
	      xprname(obj, obj_to_let(obj), TRUE, 0L));
	if (quan) obj->quan = savequan;
}

#endif /* OVL2 */
#ifdef OVL1

char *
xprname(obj,let,dot,cost)
register struct obj *obj;
register char let;
register boolean dot;   /* append period; (dot && cost => Iu) */
register long cost;     /* cost (for inventory of unpaid or expended items) */
{
#ifdef LINT     /* handle static char li[BUFSZ]; */
	char li[BUFSZ];
#else
	static char li[BUFSZ];
#endif
	boolean use_invlet = flags.invlet_constant && let != CONTAINED_SYM;
    /*
     * If let is:
     *  *  Then obj == NULL and we are printing a total amount.
     *  >  Then the object is contained and doesn't have an inventory letter.
     */
    if (cost != 0 || let == '*') {
	/* if dot is true, we're doing Iu, otherwise Ix */
	Sprintf(li, "%c - %-45s %6ld zorkmid%s",
		(dot && use_invlet ? obj->invlet : let),
		(let != '*' ? doname(obj) : "Total:"), cost, plur(cost));
    } else if (obj->otyp == GOLD_PIECE) {
	Sprintf(li, "%ld gold piece%s%s", obj->quan, plur(obj->quan),
		(dot ? "." : ""));
    } else {
	/* ordinary inventory display or pickup message */
	Sprintf(li, "%c - %s",
		(use_invlet ? obj->invlet : let),
		doname(obj));
	if(dot) Strcat(li,".");
    }
    return li;
}

#endif /* OVL1 */
#ifdef OVLB

int
ddoinv()
{
	(void) display_inventory(NULL, FALSE);
	return 0;
}

/*
 *  find_unpaid()
 *
 *  Scan the given list of objects.  If last_found is NULL, return the first
 *  unpaid object found.  If last_found is not NULL, then skip over unpaid
 *  objects until last_found is reached, then set last_found to NULL so the
 *  next unpaid object is returned.  This routine recursively follows
 *  containers.
 */
static struct obj *
find_unpaid(list, last_found)
    struct obj *list, **last_found;
{
    struct obj *obj;

    while (list) {
	if (list->unpaid) {
	    if (*last_found) {
		/* still looking for previous unpaid object */
		if (list == *last_found)
		    *last_found = (struct obj *) 0;
	    } else
		return (*last_found = list);
	}
	if (Has_contents(list)) {
	    if ((obj = find_unpaid(list->cobj, last_found)) != 0)
		return obj;
	}
	list = list->nobj;
    }
    return (struct obj *) 0;
}

/*
 *  If lets == NULL or "", list all objects in the inventory.  Otherwise,
 *  list all objects with object classes that match the order in lets.
 *  The last letter could possibly be a '>' which means list unpaid contained
 *  objects.
 *  Returns the letter identifier of a selected item, or 0 (nothing was
 *  selected), or '\033' (the menu was cancelled).
 */
char
display_inventory(lets,show_cost)
register const char *lets;
boolean show_cost;
{
	register struct obj *otmp;
	struct obj *z_obj;
	register char ilet;
	char *invlet = flags.inv_order;
	int classcount;
#if defined(LINT) || defined(GCC_WARN)
	int save_unpaid = 0;
#else
	int save_unpaid;
#endif
	long cost, totcost;
	boolean do_containers = FALSE;

	if(!invent){
		pline("Not carrying anything.");
		return 0;
	}
	if (lets != NULL) {
	    int ct = strlen(lets); /* count number of inventory slots to show */
	    /* If we've got unpaid items in containers, count all unpaid
	       objects.  At least one won't be in any inventory slot. */
	    do_containers = (ct && lets[ct-1] == CONTAINED_SYM);
	    if (do_containers && ct == 1) ct = count_unpaid(invent);
	    /* if only one item of interest, use pline instead of menus */
	    if (ct == 1) {
		if (do_containers) {    /* single non-inventory object */
		    z_obj = (struct obj *) 0;
		    if ((otmp = find_unpaid(invent, &z_obj)) != 0)
			pline("%s",
			      xprname(otmp, CONTAINED_SYM, TRUE,
				     (show_cost ? unpaid_cost(otmp) : 0L)));
		    else
			impossible(
		    "display_inventory: no singular unpaid contained objects");
		} else {
		    for(otmp = invent; otmp; otmp = otmp->nobj) {
			if (otmp->invlet == lets[0]) {
			    pline("%s",
				  xprname(otmp, lets[0], TRUE,
					 (show_cost ? unpaid_cost(otmp) : 0L)));
			    break;
			}
		    }
		}
		return 0;
	    }
	}

	start_menu(WIN_INVEN);
	cost = totcost = 0;
nextclass:
	classcount = 0;
	ilet = 'a';
	for(otmp = invent; otmp; otmp = otmp->nobj) {
		if(flags.invlet_constant) ilet = otmp->invlet;
		if(!lets || !*lets || index(lets, ilet)) {
			if (!flags.sortpack || otmp->oclass == *invlet) {
			    if (flags.sortpack && !classcount) {
				add_menu(WIN_INVEN, 0, ATR_INVERSE,
					 let_to_name(*invlet, show_cost));
				classcount++;
			    }
			    if (show_cost) {
				totcost += cost = unpaid_cost(otmp);
				/* suppress "(unpaid)" suffix */
				save_unpaid = otmp->unpaid;
				otmp->unpaid = 0;
			    }
			    add_menu(WIN_INVEN, ilet, 0,
				     xprname(otmp, ilet, TRUE, cost));
			    if (show_cost)  otmp->unpaid = save_unpaid;
			}
		}
		if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A';
	}
	if (flags.sortpack) {
		if (*++invlet) goto nextclass;
#ifdef WIZARD
		if (--invlet != venom_inv) {
			invlet = venom_inv;
			goto nextclass;
		}
#endif
	}

	/* right now, we only do this for unpaid items, so always do cost */
	if (do_containers) {
	    if (flags.sortpack) add_menu(WIN_INVEN, 0, ATR_INVERSE,
					 let_to_name(CONTAINED_SYM, 1));
	    /*
	     *  Search through the container objects in the inventory for
	     *  unpaid items.  Note that we check each container, not
	     *  the invent list.  This is because the invent list could
	     *  have unpaid items that have been already listed.
	     */
	    for (otmp = invent; otmp; otmp = otmp->nobj) {
		if (Has_contents(otmp)) {
		    z_obj = (struct obj *) 0;   /* haven't found any */
		    while (find_unpaid(otmp->cobj, (struct obj **)&z_obj)) {
			totcost += cost = unpaid_cost(z_obj);
			save_unpaid = z_obj->unpaid;
			z_obj->unpaid = 0;    /* suppress "(unpaid)" suffix */
			add_menu(WIN_INVEN, 0, 0,
				 xprname(z_obj, CONTAINED_SYM, TRUE, cost));
			z_obj->unpaid = save_unpaid;
		    }
		}
	    }
	}

	if (show_cost) {
	    /* give 'Totals' line */
	    add_menu(WIN_INVEN, 0, 0, "");
	    add_menu(WIN_INVEN, 0, 0,
		     xprname((struct obj *)0, '*', FALSE, totcost));
	}
	end_menu(WIN_INVEN, '\033', "\033 ", NULL);
	return select_menu(WIN_INVEN);
}

/*
 *  Returns the number of unpaid items within the given list.  This includes
 *  contained objects.
 */
int
count_unpaid(list)
    struct obj *list;
{
    int count = 0;

    while (list) {
	if (list->unpaid) count++;
	if (Has_contents(list))
	    count += count_unpaid(list->cobj);
	list = list->nobj;
    }
    return count;
}

int
dotypeinv()                             /* free after Robert Viduya */
/* Changed to one type only, so you don't have to type return */
{
	char c, ilet;
	char stuff[BUFSZ];
	register int stct;
	register struct obj *otmp;
	boolean billx = *u.ushops && doinvbill(0);
	boolean do_unpd = FALSE;
	int unpd, class;

	if (!invent && !u.ugold && !billx) {
	    You("aren't carrying anything.");
	    return 0;
	}

	Strcpy(stuff, "What type of object do you want an inventory of? [");
	/* collect a list of classes of objects carried, for use as a prompt */
	stct = collect_obj_classes(eos(stuff), invent, FALSE, (u.ugold != 0));
	unpd = count_unpaid(invent);
	if(unpd) Strcat(stuff, "u");
	if(billx) Strcat(stuff, "x");
	Strcat(stuff, "]");

	if(stct > 1) {
	  c = yn_function(stuff, NULL, '\0');
#ifdef REDO
	    savech(c);
#endif
	    if(c == '\0') {
		clear_nhwindow(WIN_MESSAGE);
		return 0;
	    }
	} else
	    c = stuff[0];

	if(c == 'x' || c == 'X') {
	    if(billx)
		(void) doinvbill(1);
	    else
		pline("No used-up objects on the shopping bill.");
	    return(0);
	}

	if (c == 'u' || c == 'U') {
	    if (!unpd) {
		You("are not carrying any unpaid objects.");
		return(0);
	    }
	    do_unpd = TRUE;
	}

	class = def_char_to_objclass(c);        /* change to object class */

	if(class == GOLD_CLASS)
	    return(doprgold());

	/* collect all items which match the selected objclass */
	stct = 0;
	ilet = 'a';
	for (otmp = invent; otmp; otmp = otmp->nobj) {
	    if(flags.invlet_constant) ilet = otmp->invlet;
	    if (class == otmp->oclass || (do_unpd && otmp->unpaid)) {
		stuff[stct++] = ilet;
		--unpd; /* decrement unpaid count */
	    }
	    if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A';
	}
	/* unpd is now the number of unpaid contained objects */
	if (do_unpd && unpd)
	    stuff[stct++] = CONTAINED_SYM;      /* list contained unpaid items */

	stuff[stct] = '\0';
	if(stct == 0)
		You("have no such objects.");
	else
		(void) display_inventory(stuff, do_unpd);

	return(0);
}

/* look at what is here */
int
dolook()
{
	register struct obj *otmp, *otmp0;
	struct trap *trap;
	const char *verb = Blind ? "feel" : "see";
	const char *dfeature = (char*) 0;
	char fbuf[BUFSZ], fbuf2[BUFSZ];
	boolean no_article = FALSE;
	winid tmpwin;

	if(u.uswallow) {
		You("%s no objects here.", verb);
		return(!!Blind);
	}
	read_engr_at(u.ux, u.uy); /* Eric Backus */
	if ((trap = t_at(u.ux,u.uy)) && trap->tseen)
		pline("There is a%s here.", traps[trap->ttyp]);

	otmp0 = level.objects[u.ux][u.uy];

	if(IS_DOOR(levl[u.ux][u.uy].typ))  {
		switch(levl[u.ux][u.uy].doormask) {
		    case D_NODOOR:
			dfeature = "doorway"; break;
		    case D_ISOPEN:
			dfeature = "open door"; break;
		    case D_BROKEN:
			dfeature = "broken door"; break;
		    default:
			dfeature = "closed door";
		}
	} else if(IS_FOUNTAIN(levl[u.ux][u.uy].typ))
		/* added by GAN 10/30/86 */
		dfeature = "fountain";
	else if(IS_THRONE(levl[u.ux][u.uy].typ))
		dfeature = "opulent throne";
	else if(is_lava(u.ux,u.uy))
		dfeature = "molten lava",  no_article = TRUE;
	else if(is_ice(u.ux,u.uy))
		dfeature = "ice",  no_article = TRUE;
	else if(is_pool(u.ux,u.uy) && !Underwater)
		dfeature = "pool of water";
#ifdef SINKS
	else if(IS_SINK(levl[u.ux][u.uy].typ))
		dfeature = "kitchen sink";
	else if(IS_TOILET(levl[u.ux][u.uy].typ))
		dfeature = "bathroom toilet";
#endif
	else if(IS_GRAVE(levl[u.ux][u.uy].typ))
		dfeature = "grave";
	else if(IS_ALTAR(levl[u.ux][u.uy].typ))  {
		Sprintf(fbuf2, "altar to %s (%s)",
			a_gname(),
			align_str(Amask2align(levl[u.ux][u.uy].altarmask
							    & ~AM_SHRINE)));
		dfeature = fbuf2;
	} else if(u.ux == xupstair && u.uy == yupstair)
		dfeature = "stairway up";
	else if(u.ux == xdnstair && u.uy == ydnstair)
		dfeature = "stairway down";
	else if(u.ux == sstairs.sx && u.uy == sstairs.sy) {
		if (sstairs.up)
			dfeature = "stairway up";
		else
			dfeature = "stairway down";
	} else if(u.ux == xupladder && u.uy == yupladder)
		dfeature = "ladder up";
	else if(u.ux == xdnladder && u.uy == ydnladder)
		dfeature = "ladder down";
	else if (levl[u.ux][u.uy].typ == DRAWBRIDGE_DOWN)
		dfeature = "lowered drawbridge";

	if (Blind) {
		You("try to feel what is %s.",
		    Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) ?
			"floating here" :
			"lying here on the floor");
		if (Levitation &&
		    !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) {
			pline("But you can't reach it!");
			return(0);
		}
	}

	if (dfeature)
		Sprintf(fbuf, "There is %s%s here.",
			no_article ? "" :
				index(vowels,dfeature[0]) ? "an " : "a ",
			dfeature);

	if(!otmp0 || (is_pool(u.ux,u.uy) && !Underwater)) {
		if(dfeature) pline(fbuf);
		if(Blind || !dfeature)
			You("%s no objects here.", verb);
		return(!!Blind);
	}
	/* we know there is something here */

	if (!otmp0->nexthere) {
	    /* only one object */
	    if (dfeature) pline(fbuf);
#ifdef INVISIBLE_OBJECTS
	if (otmp0->oinvis && !See_invisible) {
	   verb = "feel";
	   otmp0->dknown = 1;
	}
#endif
	    You("%s here %s.", verb, doname(otmp0));
	} else {
	    display_nhwindow(WIN_MESSAGE, FALSE);
	    tmpwin = create_nhwindow(NHW_MENU);
	    if(dfeature) {
		putstr(tmpwin, 0, fbuf);
		putstr(tmpwin, 0, "");
	    }
	    putstr(tmpwin, 0, "Things that are here:");
	    for(otmp = otmp0; otmp; otmp = otmp->nexthere) {
		putstr(tmpwin, 0, doname(otmp));

		if(Blind  && !uarmg &&
#ifdef POLYSELF
			    !resists_ston(uasmon) &&
#endif
			    (otmp->otyp == CORPSE &&
					otmp->corpsenm == PM_COCKATRICE)) {
#if defined(POLYSELF)
		    if(poly_when_stoned(uasmon)) {
			You("touched the cockatrice corpse with your bare hands.");
			(void) polymon(PM_STONE_GOLEM);
		    } else
#endif
		    {
			pline("Touching the cockatrice corpse is a fatal mistake...");
			You("turn to stone...");
			killer_format = KILLED_BY_AN;
			killer = "cockatrice corpse";
			done(STONING);
		    }
		}
		if(Blind  && !uarmg &&
#ifdef POLYSELF
			    !resists_ston(uasmon) &&
#endif
			    (otmp->otyp == CORPSE &&
					otmp->corpsenm == PM_CHICKATRICE)) {
#if defined(POLYSELF)
		    if(poly_when_stoned(uasmon)) {
			You("touched the chickatrice corpse with your bare hands.");
			(void) polymon(PM_STONE_GOLEM);
		    } else
#endif
		    {
			pline("Touching the chickatrice corpse is a fatal mistake...");
			You("turn to stone...");
			killer_format = KILLED_BY_AN;
			killer = "chickatrice corpse";
			done(STONING);
		    }
		}
		if(Blind  && !uarmg &&
#ifdef POLYSELF
			    !resists_ston(uasmon) &&
#endif
			    (otmp->otyp == CORPSE &&
					otmp->corpsenm == PM_BASILISK)) {
#if defined(POLYSELF)
		    if(poly_when_stoned(uasmon)) {
			You("touched the basilisk corpse with your bare hands.");
			(void) polymon(PM_STONE_GOLEM);
		    } else
#endif
		    {
			pline("Touching the basilisk corpse is a fatal mistake...");
			You("turn to stone...");
			killer_format = KILLED_BY_AN;
			killer = "basilisk corpse";
			done(STONING);
		    }
		}
		if(Blind  && !uarmg &&
#ifdef POLYSELF
			    !resists_ston(uasmon) &&
#endif
			    (otmp->otyp == CORPSE &&
					otmp->corpsenm == PM_ASPHYNX)) {
#if defined(POLYSELF)
		    if(poly_when_stoned(uasmon)) {
			You("touched the asphynx corpse with your bare hands.");
			(void) polymon(PM_STONE_GOLEM);
		    } else
#endif
		    {
			pline("Touching the asphynx corpse is a fatal mistake...");
			You("turn to stone...");
			killer_format = KILLED_BY_AN;
			killer = "asphynx corpse";
			done(STONING);
		    }
		}
	    }
	    display_nhwindow(tmpwin, TRUE);
	    destroy_nhwindow(tmpwin);
	}
	return(!!Blind);
}

#endif /* OVLB */
#ifdef OVL1

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

	for(otmp = level.objects[obj->ox][obj->oy]; otmp; otmp = otmp->nexthere)
		if(otmp != obj && merged(obj,otmp,1))
			break;
	return;
}

static boolean
mergable(otmp, obj)     /* returns TRUE if obj  & otmp can be merged */
	register struct obj *otmp, *obj;
{
	if (obj->otyp != otmp->otyp || obj->unpaid != otmp->unpaid ||
	    obj->spe != otmp->spe || obj->dknown != otmp->dknown ||
	    (obj->bknown != otmp->bknown && pl_character[0] != 'P') ||
	    obj->cursed != otmp->cursed || obj->blessed != otmp->blessed ||
	    obj->no_charge != otmp->no_charge ||
	    obj->obroken != otmp->obroken ||
	    obj->otrapped != otmp->otrapped ||
	    obj->lamplit != otmp->lamplit ||
	    obj->oeroded != otmp->oeroded)
	    return(FALSE);

	if ((obj->oclass==WEAPON_CLASS || obj->oclass==ARMOR_CLASS) &&
	    (obj->oerodeproof!=otmp->oerodeproof || obj->rknown!=otmp->rknown))
	    return FALSE;

	if (obj->oclass == FOOD_CLASS && (obj->oeaten != otmp->oeaten ||
					  obj->orotten != otmp->orotten))
	    return(FALSE);

	if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) {
		if((obj->corpsenm != otmp->corpsenm) ||
			(ONAME(obj) && strcmp(ONAME(obj), ONAME(otmp))))
				return FALSE;
	}

	/* allow candle merging only if their ages are close */
	/* see burn_lamps() for a reference for the magic "25" */
	if (Is_candle(obj) && obj->age/25 != otmp->age/25)
	    return(FALSE);

	/* don't merge surcharged item with base-cost item */
	if (obj->unpaid && !same_price(obj, otmp))
	    return FALSE;

/* if they have names, make sure they're the same */
	if ( (obj->onamelth != otmp->onamelth &&
		((obj->onamelth && otmp->onamelth) || obj->otyp == CORPSE)
	     ) ||
	    (obj->onamelth &&
		    strncmp(ONAME(obj), ONAME(otmp), (int)obj->onamelth)))
		return FALSE;

	if(obj->oartifact != otmp->oartifact) return FALSE;

	if(obj->known == otmp->known ||
		!objects[otmp->otyp].oc_uses_known) {
		return((boolean)(objects[obj->otyp].oc_merge));
	} else return(FALSE);
}

#endif /* OVL1 */
#ifdef OVLB

int
doprgold(){
	if(!u.ugold)
		You("do not carry any gold.");
	else
		You("are carrying %ld gold piece%s.", u.ugold, plur(u.ugold));
	return 0;
}

int
doprwep()
{
	if(!uwep) You("are empty %s.", body_part(HANDED));
	else prinv(NULL, uwep, 0L);
	return 0;
}

int
doprarm(){
	if(!wearing_armor())
		You("are not wearing any armor.");
	else {
#ifdef TOURIST
		char lets[8];
#else
		char lets[7];
#endif
		register int ct = 0;

#ifdef TOURIST
		if(uarmu) lets[ct++] = obj_to_let(uarmu);
#endif
		if(uarm) lets[ct++] = obj_to_let(uarm);
		if(uarmc) lets[ct++] = obj_to_let(uarmc);
		if(uarmh) lets[ct++] = obj_to_let(uarmh);
		if(uarms) lets[ct++] = obj_to_let(uarms);
		if(uarmg) lets[ct++] = obj_to_let(uarmg);
		if(uarmf) lets[ct++] = obj_to_let(uarmf);
		lets[ct] = 0;
		(void) display_inventory(lets, FALSE);
	}
	return 0;
}

int
doprring(){
	if(!uleft && !uright)
		You("are not wearing any rings.");
	else {
		char lets[3];
		register int ct = 0;

		if(uleft) lets[ct++] = obj_to_let(uleft);
		if(uright) lets[ct++] = obj_to_let(uright);
		lets[ct] = 0;
		(void) display_inventory(lets, FALSE);
	}
	return 0;
}

int
dopramulet(){
	if (!uamul)
		You("are not wearing an amulet.");
	else
		prinv(NULL, uamul, 0L);
	return 0;
}

int
doprtool(){
	register struct obj *otmp;
	register int ct=0;
	char lets[3]; /* Maximum: pick-axe, blindfold */

	for(otmp = invent; otmp; otmp = otmp->nobj) {
		if (((otmp->owornmask & W_TOOL) && otmp->oclass==TOOL_CLASS)
		   || (otmp==uwep &&
		   (otmp->otyp==PICK_AXE || otmp->otyp==TIN_OPENER)))
			lets[ct++] = obj_to_let(otmp);
	}
	lets[ct] = 0;
	if (!ct) You("are not using any tools.");
	else (void) display_inventory(lets, FALSE);
	return 0;
}

/*
 * uses up an object that's on the floor, charging for it as necessary
 */
void
useupf(obj)
register struct obj *obj;
{
	register struct obj *otmp;

	/* burn_floor_paper() keeps an object pointer that it tries to
	 * useupf() multiple times, so obj must survive if plural */
	if(obj->quan > 1L)
		otmp = splitobj(obj, obj->quan - 1L);
	else
		otmp = obj;
	if(costly_spot(otmp->ox, otmp->oy)) {
	    if(index(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0)))
		addtobill(otmp, FALSE, FALSE, FALSE);
	    else (void)stolen_value(otmp, otmp->ox, otmp->oy, FALSE, FALSE);
	}
	delobj(otmp);
}

#endif /* OVLB */
#ifdef OVL1

extern const char obj_symbols[];        /* o_init.c */
/*
 * Conversion from a symbol to a string for printing object classes.
 * This must match the array obj_symbols[].
 */
static NEARDATA const char *names[] = {
	"Illegal objects", "Amulets", "Coins", "Comestibles", "Weapons",
	"Tools", "Iron balls", "Chains", "Boulders/Statues", "Armor",
	"Potions", "Scrolls", "Wands", "Spellbooks", "Rings", "Gems"
};

static NEARDATA const char oth_symbols[] = {
#ifdef WIZARD
	VENOM_CLASS,
#endif
	CONTAINED_SYM,
	'\0'
};

static NEARDATA const char *oth_names[] = {
#ifdef WIZARD
	"Venoms",
#endif
	"Bagged/Boxed items"
};

char *
let_to_name(let,unpaid)
char let;
boolean unpaid;
{
	const char *class_name;
	const char *pos = index(obj_symbols, let);
	int len;
	static NEARDATA char *buf = NULL;
	static NEARDATA unsigned bufsiz = 0;

	if (pos)
	    class_name = names[pos - obj_symbols];
	else if ((pos = index(oth_symbols, let)) != 0)
	    class_name = oth_names[pos - oth_symbols];
	else
	    class_name = names[0];

	len = strlen(class_name)
	     + (unpaid ? sizeof("unpaid_") : 1);    /* count terminating NUL */
	if (len > bufsiz) {
	    if (buf)  free((genericptr_t)buf),  buf = NULL;
	    bufsiz = len + 10; /* add slop to avoid incremental reallocations */
	    buf = (char *) alloc(bufsiz);
	}
	if (unpaid)
	    Strcat(strcpy(buf, "Unpaid "), class_name);
	else
	    Strcpy(buf, class_name);
	return (buf);
}

#endif /* OVL1 */
#ifdef OVLB 

void
reassign()
{
	register int i;
	register struct obj *obj;

	for(obj = invent, i = 0; obj; obj = obj->nobj, i++)
		obj->invlet = (i < 26) ? ('a'+i) : ('A'+i-26);
	lastinvnr = i;
}

#endif /* OVLB */
#ifdef OVL1
int
doorganize()    /* inventory organizer by Del Lamb */
{
	register struct obj *obj, *otmp;
	register int ix, cur;
	register char let;
	char alphabet[52+1], buf[52+1];
	char qbuf[QBUFSZ];
	char allow_all[2];
	const char *adj_type;

	/* check to see if the inventory is of the fixed kind */
	if (!flags.invlet_constant ) {
		pline("Sorry, only fixed inventories can be adjusted.");
		return(0);
	}
#if 0
	/* check to see if the inventory has any gaps left in it */
	if (inv_cnt() >= 52) {
		pline("Sorry, no available letters for adjustment.");
		return(0);
	}
#endif
	/* get a pointer to the object the user wants to organize */
	allow_all[0] = ALL_CLASSES; allow_all[1] = '\0';
	if (!(obj = getobj(allow_all,"adjust"))) return(0);

	/* initialize the list with all upper and lower case letters */
	for (let = 'a', ix = 0;  let <= 'z';) alphabet[ix++] = let++;
	for (let = 'A', ix = 26; let <= 'Z';) alphabet[ix++] = let++;
	alphabet[52] = 0;

	/* blank out all the letters currently in use in the inventory */
	/* except those that will be merged with the selected object   */
	for (otmp = invent; otmp; otmp = otmp->nobj)
		if (otmp != obj && !mergable(otmp,obj))
			if (otmp->invlet <= 'Z')
				alphabet[(otmp->invlet) - 'A' + 26] = ' ';
			else    alphabet[(otmp->invlet) - 'a']      = ' ';

	/* compact the list by removing all the blanks */
	for (ix = cur = 0; ix <= 52; ix++)
		if (alphabet[ix] != ' ') buf[cur++] = alphabet[ix];

	/* and by dashing runs of letters */
	if(cur > 5) compactify(buf);

	/* get new letter to use as inventory letter */
	for (;;) {
		Sprintf(qbuf, "Adjust letter to what [%s]?",buf);
		let = yn_function(qbuf, NULL, '\0');
		if(index(quitchars,let)) {
			pline("Never mind.");
			return(0);
		}
		if (let == '@' || !letter(let))
			pline("Select an inventory slot letter.");
		else
			break;
	}

	/* change the inventory and print the resulting item */
	adj_type = "Moving:";

	/*
	 * don't use freeinv/addinv to avoid double-touching artifacts,
	 * dousing lamps, losing luck, cursing loadstone, etc.
	 */
	unlinkinv(obj);

	for (otmp = invent; otmp;)
		if (merged(otmp,obj,0)) {
			adj_type = "Merging:";
			obj = otmp;
			otmp = otmp->nobj;
			unlinkinv(obj);
		} else {
			if (otmp->invlet == let) {
				adj_type = "Swapping:";
				otmp->invlet = obj->invlet;
			}
			otmp = otmp->nobj;
		}

	/* inline addinv (assuming flags.invlet_constant and !merged) */
	obj->invlet = let;
	obj->nobj = invent; /* insert at beginning */
	invent = obj;
	reorder_invent();

	prinv(adj_type, obj, 0L);
	update_inventory();
	return(0);
}

#endif /* OVL1 */

/*invent.c*/
