1/*	$NetBSD: hack.invent.c,v 1.17 2011/08/06 20:42:43 dholland Exp $	*/
2
3/*
4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5 * Amsterdam
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * - Neither the name of the Stichting Centrum voor Wiskunde en
20 * Informatica, nor the names of its contributors may be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37/*
38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39 * All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. The name of the author may not be used to endorse or promote products
50 *    derived from this software without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 */
63
64#include <sys/cdefs.h>
65#ifndef lint
66__RCSID("$NetBSD: hack.invent.c,v 1.17 2011/08/06 20:42:43 dholland Exp $");
67#endif				/* not lint */
68
69#include <assert.h>
70#include <stdlib.h>
71#include "hack.h"
72#include "extern.h"
73
74#ifndef NOWORM
75#include	"def.wseg.h"
76#endif	/* NOWORM */
77
78#define	NOINVSYM	'#'
79
80static int      lastinvnr = 51;	/* 0 ... 51 */
81
82static char *xprname(struct obj *, char);
83static void doinv(const char *);
84static int merged(struct obj *, struct obj *, int);
85
86static void
87assigninvlet(struct obj *otmp)
88{
89	boolean         inuse[52];
90	int             i;
91	struct obj     *obj;
92
93	for (i = 0; i < 52; i++)
94		inuse[i] = FALSE;
95	for (obj = invent; obj; obj = obj->nobj)
96		if (obj != otmp) {
97			i = obj->invlet;
98			if ('a' <= i && i <= 'z')
99				inuse[i - 'a'] = TRUE;
100			else if ('A' <= i && i <= 'Z')
101				inuse[i - 'A' + 26] = TRUE;
102			if (i == otmp->invlet)
103				otmp->invlet = 0;
104		}
105	if ((i = otmp->invlet) &&
106	    (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
107		return;
108	for (i = lastinvnr + 1; i != lastinvnr; i++) {
109		if (i == 52) {
110			i = -1;
111			continue;
112		}
113		if (!inuse[i])
114			break;
115	}
116	otmp->invlet = (inuse[i] ? NOINVSYM :
117			(i < 26) ? ('a' + i) : ('A' + i - 26));
118	lastinvnr = i;
119}
120
121struct obj     *
122addinv(struct obj *obj)
123{
124	struct obj     *otmp;
125
126	/* merge or attach to end of chain */
127	if (!invent) {
128		invent = obj;
129		otmp = 0;
130	} else
131		for (otmp = invent; /* otmp */ ; otmp = otmp->nobj) {
132			if (merged(otmp, obj, 0))
133				return (otmp);
134			if (!otmp->nobj) {
135				otmp->nobj = obj;
136				break;
137			}
138		}
139	obj->nobj = 0;
140
141	if (flags.invlet_constant) {
142		assigninvlet(obj);
143		/*
144		 * The ordering of the chain is nowhere significant
145		 * so in case you prefer some other order than the
146		 * historical one, change the code below.
147		 */
148		if (otmp) {	/* find proper place in chain */
149			otmp->nobj = 0;
150			if ((invent->invlet ^ 040) > (obj->invlet ^ 040)) {
151				obj->nobj = invent;
152				invent = obj;
153			} else
154				for (otmp = invent;; otmp = otmp->nobj) {
155					if (!otmp->nobj ||
156					    (otmp->nobj->invlet ^ 040) > (obj->invlet ^ 040)) {
157						obj->nobj = otmp->nobj;
158						otmp->nobj = obj;
159						break;
160					}
161				}
162		}
163	}
164	return (obj);
165}
166
167void
168useup(struct obj *obj)
169{
170	if (obj->quan > 1) {
171		obj->quan--;
172		obj->owt = weight(obj);
173	} else {
174		setnotworn(obj);
175		freeinv(obj);
176		obfree(obj, (struct obj *) 0);
177	}
178}
179
180void
181freeinv(struct obj *obj)
182{
183	struct obj     *otmp;
184
185	if (obj == invent)
186		invent = invent->nobj;
187	else {
188		for (otmp = invent; otmp->nobj != obj; otmp = otmp->nobj)
189			if (!otmp->nobj)
190				panic("freeinv");
191		otmp->nobj = obj->nobj;
192	}
193}
194
195/* destroy object in fobj chain (if unpaid, it remains on the bill) */
196void
197delobj(struct obj *obj)
198{
199	freeobj(obj);
200	unpobj(obj);
201	obfree(obj, (struct obj *) 0);
202}
203
204/* unlink obj from chain starting with fobj */
205void
206freeobj(struct obj *obj)
207{
208	struct obj     *otmp;
209
210	if (obj == fobj)
211		fobj = fobj->nobj;
212	else {
213		otmp = fobj;
214		while (otmp->nobj != obj) {
215			if (otmp->nobj == NULL)
216				panic("error in freeobj");
217			otmp = otmp->nobj;
218		}
219		otmp->nobj = obj->nobj;
220	}
221}
222
223/* Note: freegold throws away its argument! */
224void
225freegold(struct gold *gold)
226{
227	struct gold    *gtmp;
228
229	if (gold == fgold)
230		fgold = gold->ngold;
231	else {
232		gtmp = fgold;
233		while (gtmp->ngold != gold) {
234			if (gtmp->ngold == NULL)
235				panic("error in freegold");
236			gtmp = gtmp->ngold;
237		}
238		gtmp->ngold = gold->ngold;
239	}
240	free(gold);
241}
242
243void
244deltrap(struct trap *trap)
245{
246	struct trap    *ttmp;
247
248	if (trap == ftrap)
249		ftrap = ftrap->ntrap;
250	else {
251		for (ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap);
252		ttmp->ntrap = trap->ntrap;
253	}
254	free(trap);
255}
256
257struct wseg    *m_atseg;
258
259struct monst   *
260m_at(int x, int y)
261{
262	struct monst   *mtmp;
263#ifndef NOWORM
264	struct wseg    *wtmp;
265#endif	/* NOWORM */
266
267	m_atseg = 0;
268	for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
269		if (mtmp->mx == x && mtmp->my == y)
270			return (mtmp);
271#ifndef NOWORM
272		if (mtmp->wormno) {
273			for (wtmp = wsegs[mtmp->wormno]; wtmp; wtmp = wtmp->nseg)
274				if (wtmp->wx == x && wtmp->wy == y) {
275					m_atseg = wtmp;
276					return (mtmp);
277				}
278		}
279#endif	/* NOWORM */
280	}
281	return (0);
282}
283
284struct obj     *
285o_at(int x, int y)
286{
287	struct obj     *otmp;
288
289	for (otmp = fobj; otmp; otmp = otmp->nobj)
290		if (otmp->ox == x && otmp->oy == y)
291			return (otmp);
292	return (0);
293}
294
295struct obj     *
296sobj_at(int n, int x, int y)
297{
298	struct obj     *otmp;
299
300	for (otmp = fobj; otmp; otmp = otmp->nobj)
301		if (otmp->ox == x && otmp->oy == y && otmp->otyp == n)
302			return (otmp);
303	return (0);
304}
305
306int
307carried(struct obj *obj)
308{
309	struct obj     *otmp;
310	for (otmp = invent; otmp; otmp = otmp->nobj)
311		if (otmp == obj)
312			return (1);
313	return (0);
314}
315
316int
317carrying(int type)
318{
319	struct obj     *otmp;
320
321	for (otmp = invent; otmp; otmp = otmp->nobj)
322		if (otmp->otyp == type)
323			return (TRUE);
324	return (FALSE);
325}
326
327struct obj     *
328o_on(unsigned int id, struct obj *objchn)
329{
330	while (objchn) {
331		if (objchn->o_id == id)
332			return (objchn);
333		objchn = objchn->nobj;
334	}
335	return ((struct obj *) 0);
336}
337
338struct trap    *
339t_at(int x, int y)
340{
341	struct trap    *trap = ftrap;
342	while (trap) {
343		if (trap->tx == x && trap->ty == y)
344			return (trap);
345		trap = trap->ntrap;
346	}
347	return (0);
348}
349
350struct gold    *
351g_at(int x, int y)
352{
353	struct gold    *gold = fgold;
354	while (gold) {
355		if (gold->gx == x && gold->gy == y)
356			return (gold);
357		gold = gold->ngold;
358	}
359	return (0);
360}
361
362/* make dummy object structure containing gold - for temporary use only */
363static struct obj *
364mkgoldobj(long q)
365{
366	struct obj     *otmp;
367
368	otmp = newobj(0);
369	/* should set o_id etc. but otmp will be freed soon */
370	otmp->olet = '$';
371	u.ugold -= q;
372	OGOLD(otmp) = q;
373	flags.botl = 1;
374	return (otmp);
375}
376
377/*
378 * getobj returns:
379 *	struct obj *xxx:	object to do something with.
380 *	(struct obj *) 0	error return: no object.
381 *	&zeroobj		explicitly no object (as in w-).
382 */
383struct obj     *
384getobj(const char *let, const char *word)
385{
386	struct obj     *otmp;
387	char            ilet, ilet1, ilet2;
388	char            buf[BUFSZ];
389	char            lets[BUFSZ];
390	int             foo = 0, foo2;
391	char           *bp = buf;
392	xchar           allowcnt = 0;	/* 0, 1 or 2 */
393	boolean         allowgold = FALSE;
394	boolean         allowall = FALSE;
395	boolean         allownone = FALSE;
396	xchar           foox = 0;
397	long            cnt;
398
399	if (*let == '0')
400		let++, allowcnt = 1;
401	if (*let == '$')
402		let++, allowgold = TRUE;
403	if (*let == '#')
404		let++, allowall = TRUE;
405	if (*let == '-')
406		let++, allownone = TRUE;
407	if (allownone)
408		*bp++ = '-';
409	if (allowgold)
410		*bp++ = '$';
411	if (bp > buf && bp[-1] == '-')
412		*bp++ = ' ';
413
414	ilet = 'a';
415	for (otmp = invent; otmp; otmp = otmp->nobj) {
416		if (!*let || strchr(let, otmp->olet)) {
417			bp[foo++] = flags.invlet_constant ? otmp->invlet : ilet;
418
419			/* ugly check: remove inappropriate things */
420			if ((!strcmp(word, "take off") &&
421			     !(otmp->owornmask & (W_ARMOR - W_ARM2)))
422			    || (!strcmp(word, "wear") &&
423				(otmp->owornmask & (W_ARMOR | W_RING)))
424			    || (!strcmp(word, "wield") &&
425				(otmp->owornmask & W_WEP))) {
426				foo--;
427				foox++;
428			}
429		}
430		if (ilet == 'z')
431			ilet = 'A';
432		else
433			ilet++;
434	}
435	bp[foo] = 0;
436	if (foo == 0 && bp > buf && bp[-1] == ' ')
437		*--bp = 0;
438	(void) strcpy(lets, bp);/* necessary since we destroy buf */
439	if (foo > 5) {		/* compactify string */
440		foo = foo2 = 1;
441		ilet2 = bp[0];
442		ilet1 = bp[1];
443		while ((ilet = bp[++foo2] = bp[++foo]) != '\0') {
444			if (ilet == ilet1 + 1) {
445				if (ilet1 == ilet2 + 1)
446					bp[foo2 - 1] = ilet1 = '-';
447				else if (ilet2 == '-') {
448					bp[--foo2] = ++ilet1;
449					continue;
450				}
451			}
452			ilet2 = ilet1;
453			ilet1 = ilet;
454		}
455	}
456	if (!foo && !allowall && !allowgold && !allownone) {
457		pline("You don't have anything %sto %s.",
458		      foox ? "else " : "", word);
459		return (0);
460	}
461	for (;;) {
462		if (!buf[0])
463			pline("What do you want to %s [*]? ", word);
464		else
465			pline("What do you want to %s [%s or ?*]? ",
466			      word, buf);
467
468		cnt = 0;
469		ilet = readchar();
470		while (digit(ilet) && allowcnt) {
471			if (cnt < 100000000)
472				cnt = 10 * cnt + (ilet - '0');
473			else
474				cnt = 999999999;
475			allowcnt = 2;	/* signal presence of cnt */
476			ilet = readchar();
477		}
478		if (digit(ilet)) {
479			pline("No count allowed with this command.");
480			continue;
481		}
482		if (strchr(quitchars, ilet))
483			return ((struct obj *) 0);
484		if (ilet == '-') {
485			return (allownone ? &zeroobj : (struct obj *) 0);
486		}
487		if (ilet == '$') {
488			if (!allowgold) {
489				pline("You cannot %s gold.", word);
490				continue;
491			}
492			if (!(allowcnt == 2 && cnt < u.ugold))
493				cnt = u.ugold;
494			return (mkgoldobj(cnt));
495		}
496		if (ilet == '?') {
497			doinv(lets);
498			if (!(ilet = morc))
499				continue;
500			/* he typed a letter (not a space) to more() */
501		} else if (ilet == '*') {
502			doinv(NULL);
503			if (!(ilet = morc))
504				continue;
505			/* ... */
506		}
507		if (flags.invlet_constant) {
508			for (otmp = invent; otmp; otmp = otmp->nobj)
509				if (otmp->invlet == ilet)
510					break;
511		} else {
512			if (ilet >= 'A' && ilet <= 'Z')
513				ilet += 'z' - 'A' + 1;
514			ilet -= 'a';
515			for (otmp = invent; otmp && ilet;
516			     ilet--, otmp = otmp->nobj);
517		}
518		if (!otmp) {
519			pline("You don't have that object.");
520			continue;
521		}
522		if (cnt < 0 || otmp->quan < cnt) {
523			pline("You don't have that many! [You have %u]"
524			      ,otmp->quan);
525			continue;
526		}
527		break;
528	}
529	if (!allowall && let && !strchr(let, otmp->olet)) {
530		pline("That is a silly thing to %s.", word);
531		return (0);
532	}
533	if (allowcnt == 2) {	/* cnt given */
534		if (cnt == 0)
535			return (0);
536		if (cnt != otmp->quan) {
537			struct obj     *obj;
538			obj = splitobj(otmp, (int) cnt);
539			if (otmp == uwep)
540				setuwep(obj);
541		}
542	}
543	return (otmp);
544}
545
546static int
547ckunpaid(struct obj *otmp)
548{
549	return (otmp->unpaid);
550}
551
552/* interactive version of getobj - used for Drop and Identify */
553/* return the number of times fn was called successfully */
554int
555ggetobj(const char *word, int (*fn)(struct obj *), int max)
556{
557	char            buf[BUFSZ];
558	char           *ip;
559	char            sym;
560	unsigned        oletct = 0, iletct = 0;
561	boolean         allflag = FALSE;
562	char            olets[20], ilets[20];
563	int           (*ckfn)(struct obj *) =
564	    (int (*)(struct obj *)) 0;
565	xchar           allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0;	/* BAH */
566	if (!invent && !allowgold) {
567		pline("You have nothing to %s.", word);
568		return (0);
569	} else {
570		struct obj     *otmp = invent;
571		int             uflg = 0;
572
573		if (allowgold)
574			ilets[iletct++] = '$';
575		ilets[iletct] = 0;
576		while (otmp) {
577			if (!strchr(ilets, otmp->olet)) {
578				ilets[iletct++] = otmp->olet;
579				ilets[iletct] = 0;
580			}
581			if (otmp->unpaid)
582				uflg = 1;
583			otmp = otmp->nobj;
584		}
585		ilets[iletct++] = ' ';
586		if (uflg)
587			ilets[iletct++] = 'u';
588		if (invent)
589			ilets[iletct++] = 'a';
590		ilets[iletct] = 0;
591		assert(iletct < sizeof(ilets));
592	}
593	pline("What kinds of thing do you want to %s? [%s] ",
594	      word, ilets);
595	getlin(buf);
596	if (buf[0] == '\033') {
597		clrlin();
598		return (0);
599	}
600	ip = buf;
601	olets[0] = 0;
602	while ((sym = *ip++) != '\0') {
603		if (sym == ' ')
604			continue;
605		if (sym == '$') {
606			if (allowgold == 1)
607				(*fn) (mkgoldobj(u.ugold));
608			else if (!u.ugold)
609				pline("You have no gold.");
610			allowgold = 2;
611		} else if (sym == 'a' || sym == 'A')
612			allflag = TRUE;
613		else if (sym == 'u' || sym == 'U')
614			ckfn = ckunpaid;
615		else if (strchr("!%?[()=*/\"0", sym)) {
616			if (!strchr(olets, sym)) {
617				olets[oletct++] = sym;
618				olets[oletct] = 0;
619			}
620			assert(oletct < sizeof(olets));
621		} else
622			pline("You don't have any %c's.", sym);
623	}
624	if (allowgold == 2 && !oletct)
625		return (1);	/* he dropped gold (or at least tried to) */
626	else
627		return (askchain(invent, olets, allflag, fn, ckfn, max));
628}
629
630/*
631 * Walk through the chain starting at objchn and ask for all objects
632 * with olet in olets (if nonNULL) and satisfying ckfn (if nonNULL)
633 * whether the action in question (i.e., fn) has to be performed.
634 * If allflag then no questions are asked. Max gives the max nr of
635 * objects to be treated. Return the number of objects treated.
636 */
637int
638askchain(struct obj *objchn, char *olets, int allflag,
639	int (*fn)(struct obj *),
640	int (*ckfn)(struct obj *),
641	int max)
642{
643	struct obj     *otmp, *otmp2;
644	char            sym, ilet;
645	int             cnt = 0;
646	ilet = 'a' - 1;
647	for (otmp = objchn; otmp; otmp = otmp2) {
648		if (ilet == 'z')
649			ilet = 'A';
650		else
651			ilet++;
652		otmp2 = otmp->nobj;
653		if (olets && *olets && !strchr(olets, otmp->olet))
654			continue;
655		if (ckfn && !(*ckfn) (otmp))
656			continue;
657		if (!allflag) {
658			pline("%s", xprname(otmp, ilet));
659			addtopl(" [nyaq]? ");
660			sym = readchar();
661		} else
662			sym = 'y';
663
664		switch (sym) {
665		case 'a':
666			allflag = 1;
667			/* FALLTHROUGH */
668		case 'y':
669			cnt += (*fn) (otmp);
670			if (--max == 0)
671				goto ret;
672			break;
673		case 'n':
674		default:
675			break;
676		case 'q':
677			goto ret;
678		}
679	}
680	pline(cnt ? "That was all." : "No applicable objects.");
681ret:
682	return (cnt);
683}
684
685/* should of course only be called for things in invent */
686static char
687obj_to_let(struct obj *obj)
688{
689	struct obj     *otmp;
690	char            ilet;
691
692	if (flags.invlet_constant)
693		return (obj->invlet);
694	ilet = 'a';
695	for (otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
696		if (++ilet > 'z')
697			ilet = 'A';
698	return (otmp ? ilet : NOINVSYM);
699}
700
701void
702prinv(struct obj *obj)
703{
704	pline("%s", xprname(obj, obj_to_let(obj)));
705}
706
707static char *
708xprname(struct obj *obj, char let)
709{
710	static char     li[BUFSZ];
711
712	(void) snprintf(li, sizeof(li), "%c - %s.",
713		       flags.invlet_constant ? obj->invlet : let,
714		       doname(obj));
715	return (li);
716}
717
718int
719ddoinv(void)
720{
721	doinv(NULL);
722	return (0);
723}
724
725/* called with 0 or "": all objects in inventory */
726/* otherwise: all objects with (serial) letter in lets */
727static void
728doinv(const char *lets)
729{
730	struct obj     *otmp;
731	char            ilet;
732	unsigned        ct = 0;
733	char            any[BUFSZ];
734
735	morc = 0;		/* just to be sure */
736
737	if (!invent) {
738		pline("Not carrying anything.");
739		return;
740	}
741	cornline(0, NULL);
742	ilet = 'a';
743	for (otmp = invent; otmp; otmp = otmp->nobj) {
744		if (flags.invlet_constant)
745			ilet = otmp->invlet;
746		if (!lets || !*lets || strchr(lets, ilet)) {
747			cornline(1, xprname(otmp, ilet));
748			any[ct++] = ilet;
749		}
750		if (!flags.invlet_constant)
751			if (++ilet > 'z')
752				ilet = 'A';
753	}
754	any[ct] = 0;
755	assert(ct < sizeof(any));
756	cornline(2, any);
757}
758
759int
760dotypeinv(void)
761{				/* free after Robert Viduya */
762	/* Changed to one type only, so he doesnt have to type cr */
763	char            c, ilet;
764	char            stuff[BUFSZ];
765	unsigned        stct;
766	struct obj     *otmp;
767	boolean         billx = inshop() && doinvbill(0);
768	boolean         unpd = FALSE;
769
770	if (!invent && !u.ugold && !billx) {
771		pline("You aren't carrying anything.");
772		return (0);
773	}
774	stct = 0;
775	if (u.ugold)
776		stuff[stct++] = '$';
777	stuff[stct] = 0;
778	for (otmp = invent; otmp; otmp = otmp->nobj) {
779		if (!strchr(stuff, otmp->olet)) {
780			stuff[stct++] = otmp->olet;
781			stuff[stct] = 0;
782		}
783		if (otmp->unpaid)
784			unpd = TRUE;
785	}
786	if (unpd)
787		stuff[stct++] = 'u';
788	if (billx)
789		stuff[stct++] = 'x';
790	stuff[stct] = 0;
791	assert(stct < sizeof(stuff));
792
793	if (stct > 1) {
794		pline("What type of object [%s] do you want an inventory of? ",
795		      stuff);
796		c = readchar();
797		if (strchr(quitchars, c))
798			return (0);
799	} else
800		c = stuff[0];
801
802	if (c == '$')
803		return (doprgold());
804
805	if (c == 'x' || c == 'X') {
806		if (billx)
807			(void) doinvbill(1);
808		else
809			pline("No used-up objects on the shopping bill.");
810		return (0);
811	}
812	if ((c == 'u' || c == 'U') && !unpd) {
813		pline("You are not carrying any unpaid objects.");
814		return (0);
815	}
816	stct = 0;
817	ilet = 'a';
818	for (otmp = invent; otmp; otmp = otmp->nobj) {
819		if (flags.invlet_constant)
820			ilet = otmp->invlet;
821		if (c == otmp->olet || (c == 'u' && otmp->unpaid))
822			stuff[stct++] = ilet;
823		if (!flags.invlet_constant)
824			if (++ilet > 'z')
825				ilet = 'A';
826	}
827	stuff[stct] = '\0';
828	assert(stct < sizeof(stuff));
829
830	if (stct == 0)
831		pline("You have no such objects.");
832	else
833		doinv(stuff);
834
835	return (0);
836}
837
838/* look at what is here */
839int
840dolook(void)
841{
842	struct obj     *otmp = NULL, *otmp0 = NULL;
843	struct gold    *gold = NULL;
844	const char     *verb = Blind ? "feel" : "see";
845	int             ct = 0;
846
847	if (!u.uswallow) {
848		if (Blind) {
849			pline("You try to feel what is lying here on the floor.");
850			if (Levitation) {	/* ab@unido */
851				pline("You cannot reach the floor!");
852				return (1);
853			}
854		}
855		otmp0 = o_at(u.ux, u.uy);
856		gold = g_at(u.ux, u.uy);
857	}
858	if (u.uswallow || (!otmp0 && !gold)) {
859		pline("You %s no objects here.", verb);
860		return (!!Blind);
861	}
862	cornline(0, "Things that are here:");
863	for (otmp = otmp0; otmp; otmp = otmp->nobj) {
864		if (otmp->ox == u.ux && otmp->oy == u.uy) {
865			ct++;
866			cornline(1, doname(otmp));
867			if (Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) {
868				pline("Touching the dead cockatrice is a fatal mistake ...");
869				pline("You die ...");
870				killer = "dead cockatrice";
871				done("died");
872			}
873		}
874	}
875
876	if (gold) {
877		char            gbuf[30];
878
879		(void) snprintf(gbuf, sizeof(gbuf), "%ld gold piece%s",
880			       gold->amount, plur(gold->amount));
881		if (!ct++)
882			pline("You %s here %s.", verb, gbuf);
883		else
884			cornline(1, gbuf);
885	}
886	if (ct == 1 && !gold) {
887		pline("You %s here %s.", verb, doname(otmp0));
888		cornline(3, NULL);
889	}
890	if (ct > 1)
891		cornline(2, NULL);
892	return (!!Blind);
893}
894
895void
896stackobj(struct obj *obj)
897{
898	struct obj     *otmp = fobj;
899	for (otmp = fobj; otmp; otmp = otmp->nobj)
900		if (otmp != obj)
901			if (otmp->ox == obj->ox && otmp->oy == obj->oy &&
902			    merged(obj, otmp, 1))
903				return;
904}
905
906/* merge obj with otmp and delete obj if types agree */
907static int
908merged(struct obj *otmp, struct obj *obj, int lose)
909{
910	if (obj->otyp == otmp->otyp &&
911	    obj->unpaid == otmp->unpaid &&
912	    obj->spe == otmp->spe &&
913	    obj->dknown == otmp->dknown &&
914	    obj->cursed == otmp->cursed &&
915	    (strchr("%*?!", obj->olet) ||
916	     (obj->known == otmp->known &&
917	      (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) {
918		otmp->quan += obj->quan;
919		otmp->owt += obj->owt;
920		if (lose)
921			freeobj(obj);
922		obfree(obj, otmp);	/* free(obj), bill->otmp */
923		return (1);
924	} else
925		return (0);
926}
927
928static long goldcounted;
929/*
930 * Gold is no longer displayed; in fact, when you have a lot of money,
931 * it may take a while before you have counted it all.
932 * [Bug: d$ and pickup still tell you how much it was.]
933 */
934static int
935countgold(void)
936{
937	if ((goldcounted += 100 * (u.ulevel + 1)) >= u.ugold) {
938		long            eps = 0;
939		if (!rn2(2))
940			eps = rnd((int) (u.ugold / 100 + 1));
941		pline("You probably have about %ld gold pieces.",
942		      u.ugold + eps);
943		return (0);	/* done */
944	}
945	return (1);		/* continue */
946}
947
948int
949doprgold(void)
950{
951	if (!u.ugold)
952		pline("You do not carry any gold.");
953	else if (u.ugold <= 500)
954		pline("You are carrying %ld gold pieces.", u.ugold);
955	else {
956		pline("You sit down in order to count your gold pieces.");
957		goldcounted = 500;
958		occupation = countgold;
959		occtxt = "counting your gold";
960	}
961	return (1);
962}
963
964/* --- end of gold counting section --- */
965int
966doprwep(void)
967{
968	if (!uwep)
969		pline("You are empty handed.");
970	else
971		prinv(uwep);
972	return (0);
973}
974
975int
976doprarm(void)
977{
978	if (!uarm && !uarmg && !uarms && !uarmh)
979		pline("You are not wearing any armor.");
980	else {
981		char            lets[6];
982		int             ct = 0;
983
984		if (uarm)
985			lets[ct++] = obj_to_let(uarm);
986		if (uarm2)
987			lets[ct++] = obj_to_let(uarm2);
988		if (uarmh)
989			lets[ct++] = obj_to_let(uarmh);
990		if (uarms)
991			lets[ct++] = obj_to_let(uarms);
992		if (uarmg)
993			lets[ct++] = obj_to_let(uarmg);
994		lets[ct] = 0;
995		doinv(lets);
996	}
997	return (0);
998}
999
1000int
1001doprring(void)
1002{
1003	if (!uleft && !uright)
1004		pline("You are not wearing any rings.");
1005	else {
1006		char            lets[3];
1007		int             ct = 0;
1008
1009		if (uleft)
1010			lets[ct++] = obj_to_let(uleft);
1011		if (uright)
1012			lets[ct++] = obj_to_let(uright);
1013		lets[ct] = 0;
1014		doinv(lets);
1015	}
1016	return (0);
1017}
1018
1019int
1020digit(int c)
1021{
1022	return (c >= '0' && c <= '9');
1023}
1024