xref: /netbsd/games/hack/hack.invent.c (revision 6550d01e)
1 /*	$NetBSD: hack.invent.c,v 1.14 2009/08/12 07:28:40 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.14 2009/08/12 07:28:40 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 
80 static int      lastinvnr = 51;	/* 0 ... 51 */
81 
82 static char *xprname(struct obj *, char);
83 static void doinv(char *);
84 static int merged(struct obj *, struct obj *, int);
85 
86 static void
87 assigninvlet(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 
121 struct obj     *
122 addinv(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 
167 void
168 useup(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 
180 void
181 freeinv(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) */
196 void
197 delobj(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 */
205 void
206 freeobj(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! */
224 void
225 freegold(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((char *) gold);
241 }
242 
243 void
244 deltrap(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((char *) trap);
255 }
256 
257 struct wseg    *m_atseg;
258 
259 struct monst   *
260 m_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 
284 struct obj     *
285 o_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 
295 struct obj     *
296 sobj_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 
306 int
307 carried(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 
316 int
317 carrying(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 
327 struct obj     *
328 o_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 
338 struct trap    *
339 t_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 
350 struct gold    *
351 g_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 */
363 static struct obj *
364 mkgoldobj(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  */
383 struct obj     *
384 getobj(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((char *) 0);
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 
546 static int
547 ckunpaid(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 */
554 int
555 ggetobj(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  */
637 int
638 askchain(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(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 		case 'y':
668 			cnt += (*fn) (otmp);
669 			if (--max == 0)
670 				goto ret;
671 		case 'n':
672 		default:
673 			break;
674 		case 'q':
675 			goto ret;
676 		}
677 	}
678 	pline(cnt ? "That was all." : "No applicable objects.");
679 ret:
680 	return (cnt);
681 }
682 
683 /* should of course only be called for things in invent */
684 static char
685 obj_to_let(struct obj *obj)
686 {
687 	struct obj     *otmp;
688 	char            ilet;
689 
690 	if (flags.invlet_constant)
691 		return (obj->invlet);
692 	ilet = 'a';
693 	for (otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
694 		if (++ilet > 'z')
695 			ilet = 'A';
696 	return (otmp ? ilet : NOINVSYM);
697 }
698 
699 void
700 prinv(struct obj *obj)
701 {
702 	pline(xprname(obj, obj_to_let(obj)));
703 }
704 
705 static char *
706 xprname(struct obj *obj, char let)
707 {
708 	static char     li[BUFSZ];
709 
710 	(void) snprintf(li, sizeof(li), "%c - %s.",
711 		       flags.invlet_constant ? obj->invlet : let,
712 		       doname(obj));
713 	return (li);
714 }
715 
716 int
717 ddoinv(void)
718 {
719 	doinv((char *) 0);
720 	return (0);
721 }
722 
723 /* called with 0 or "": all objects in inventory */
724 /* otherwise: all objects with (serial) letter in lets */
725 static void
726 doinv(char *lets)
727 {
728 	struct obj     *otmp;
729 	char            ilet;
730 	unsigned        ct = 0;
731 	char            any[BUFSZ];
732 
733 	morc = 0;		/* just to be sure */
734 
735 	if (!invent) {
736 		pline("Not carrying anything.");
737 		return;
738 	}
739 	cornline(0, (char *) 0);
740 	ilet = 'a';
741 	for (otmp = invent; otmp; otmp = otmp->nobj) {
742 		if (flags.invlet_constant)
743 			ilet = otmp->invlet;
744 		if (!lets || !*lets || strchr(lets, ilet)) {
745 			cornline(1, xprname(otmp, ilet));
746 			any[ct++] = ilet;
747 		}
748 		if (!flags.invlet_constant)
749 			if (++ilet > 'z')
750 				ilet = 'A';
751 	}
752 	any[ct] = 0;
753 	assert(ct < sizeof(any));
754 	cornline(2, any);
755 }
756 
757 int
758 dotypeinv(void)
759 {				/* free after Robert Viduya */
760 	/* Changed to one type only, so he doesnt have to type cr */
761 	char            c, ilet;
762 	char            stuff[BUFSZ];
763 	unsigned        stct;
764 	struct obj     *otmp;
765 	boolean         billx = inshop() && doinvbill(0);
766 	boolean         unpd = FALSE;
767 
768 	if (!invent && !u.ugold && !billx) {
769 		pline("You aren't carrying anything.");
770 		return (0);
771 	}
772 	stct = 0;
773 	if (u.ugold)
774 		stuff[stct++] = '$';
775 	stuff[stct] = 0;
776 	for (otmp = invent; otmp; otmp = otmp->nobj) {
777 		if (!strchr(stuff, otmp->olet)) {
778 			stuff[stct++] = otmp->olet;
779 			stuff[stct] = 0;
780 		}
781 		if (otmp->unpaid)
782 			unpd = TRUE;
783 	}
784 	if (unpd)
785 		stuff[stct++] = 'u';
786 	if (billx)
787 		stuff[stct++] = 'x';
788 	stuff[stct] = 0;
789 	assert(stct < sizeof(stuff));
790 
791 	if (stct > 1) {
792 		pline("What type of object [%s] do you want an inventory of? ",
793 		      stuff);
794 		c = readchar();
795 		if (strchr(quitchars, c))
796 			return (0);
797 	} else
798 		c = stuff[0];
799 
800 	if (c == '$')
801 		return (doprgold());
802 
803 	if (c == 'x' || c == 'X') {
804 		if (billx)
805 			(void) doinvbill(1);
806 		else
807 			pline("No used-up objects on the shopping bill.");
808 		return (0);
809 	}
810 	if ((c == 'u' || c == 'U') && !unpd) {
811 		pline("You are not carrying any unpaid objects.");
812 		return (0);
813 	}
814 	stct = 0;
815 	ilet = 'a';
816 	for (otmp = invent; otmp; otmp = otmp->nobj) {
817 		if (flags.invlet_constant)
818 			ilet = otmp->invlet;
819 		if (c == otmp->olet || (c == 'u' && otmp->unpaid))
820 			stuff[stct++] = ilet;
821 		if (!flags.invlet_constant)
822 			if (++ilet > 'z')
823 				ilet = 'A';
824 	}
825 	stuff[stct] = '\0';
826 	assert(stct < sizeof(stuff));
827 
828 	if (stct == 0)
829 		pline("You have no such objects.");
830 	else
831 		doinv(stuff);
832 
833 	return (0);
834 }
835 
836 /* look at what is here */
837 int
838 dolook(void)
839 {
840 	struct obj     *otmp = NULL, *otmp0 = NULL;
841 	struct gold    *gold = NULL;
842 	const char     *verb = Blind ? "feel" : "see";
843 	int             ct = 0;
844 
845 	if (!u.uswallow) {
846 		if (Blind) {
847 			pline("You try to feel what is lying here on the floor.");
848 			if (Levitation) {	/* ab@unido */
849 				pline("You cannot reach the floor!");
850 				return (1);
851 			}
852 		}
853 		otmp0 = o_at(u.ux, u.uy);
854 		gold = g_at(u.ux, u.uy);
855 	}
856 	if (u.uswallow || (!otmp0 && !gold)) {
857 		pline("You %s no objects here.", verb);
858 		return (!!Blind);
859 	}
860 	cornline(0, "Things that are here:");
861 	for (otmp = otmp0; otmp; otmp = otmp->nobj) {
862 		if (otmp->ox == u.ux && otmp->oy == u.uy) {
863 			ct++;
864 			cornline(1, doname(otmp));
865 			if (Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) {
866 				pline("Touching the dead cockatrice is a fatal mistake ...");
867 				pline("You die ...");
868 				killer = "dead cockatrice";
869 				done("died");
870 			}
871 		}
872 	}
873 
874 	if (gold) {
875 		char            gbuf[30];
876 
877 		(void) snprintf(gbuf, sizeof(gbuf), "%ld gold piece%s",
878 			       gold->amount, plur(gold->amount));
879 		if (!ct++)
880 			pline("You %s here %s.", verb, gbuf);
881 		else
882 			cornline(1, gbuf);
883 	}
884 	if (ct == 1 && !gold) {
885 		pline("You %s here %s.", verb, doname(otmp0));
886 		cornline(3, (char *) 0);
887 	}
888 	if (ct > 1)
889 		cornline(2, (char *) 0);
890 	return (!!Blind);
891 }
892 
893 void
894 stackobj(struct obj *obj)
895 {
896 	struct obj     *otmp = fobj;
897 	for (otmp = fobj; otmp; otmp = otmp->nobj)
898 		if (otmp != obj)
899 			if (otmp->ox == obj->ox && otmp->oy == obj->oy &&
900 			    merged(obj, otmp, 1))
901 				return;
902 }
903 
904 /* merge obj with otmp and delete obj if types agree */
905 static int
906 merged(struct obj *otmp, struct obj *obj, int lose)
907 {
908 	if (obj->otyp == otmp->otyp &&
909 	    obj->unpaid == otmp->unpaid &&
910 	    obj->spe == otmp->spe &&
911 	    obj->dknown == otmp->dknown &&
912 	    obj->cursed == otmp->cursed &&
913 	    (strchr("%*?!", obj->olet) ||
914 	     (obj->known == otmp->known &&
915 	      (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) {
916 		otmp->quan += obj->quan;
917 		otmp->owt += obj->owt;
918 		if (lose)
919 			freeobj(obj);
920 		obfree(obj, otmp);	/* free(obj), bill->otmp */
921 		return (1);
922 	} else
923 		return (0);
924 }
925 
926 static long goldcounted;
927 /*
928  * Gold is no longer displayed; in fact, when you have a lot of money,
929  * it may take a while before you have counted it all.
930  * [Bug: d$ and pickup still tell you how much it was.]
931  */
932 static int
933 countgold(void)
934 {
935 	if ((goldcounted += 100 * (u.ulevel + 1)) >= u.ugold) {
936 		long            eps = 0;
937 		if (!rn2(2))
938 			eps = rnd((int) (u.ugold / 100 + 1));
939 		pline("You probably have about %ld gold pieces.",
940 		      u.ugold + eps);
941 		return (0);	/* done */
942 	}
943 	return (1);		/* continue */
944 }
945 
946 int
947 doprgold(void)
948 {
949 	if (!u.ugold)
950 		pline("You do not carry any gold.");
951 	else if (u.ugold <= 500)
952 		pline("You are carrying %ld gold pieces.", u.ugold);
953 	else {
954 		pline("You sit down in order to count your gold pieces.");
955 		goldcounted = 500;
956 		occupation = countgold;
957 		occtxt = "counting your gold";
958 	}
959 	return (1);
960 }
961 
962 /* --- end of gold counting section --- */
963 int
964 doprwep(void)
965 {
966 	if (!uwep)
967 		pline("You are empty handed.");
968 	else
969 		prinv(uwep);
970 	return (0);
971 }
972 
973 int
974 doprarm(void)
975 {
976 	if (!uarm && !uarmg && !uarms && !uarmh)
977 		pline("You are not wearing any armor.");
978 	else {
979 		char            lets[6];
980 		int             ct = 0;
981 
982 		if (uarm)
983 			lets[ct++] = obj_to_let(uarm);
984 		if (uarm2)
985 			lets[ct++] = obj_to_let(uarm2);
986 		if (uarmh)
987 			lets[ct++] = obj_to_let(uarmh);
988 		if (uarms)
989 			lets[ct++] = obj_to_let(uarms);
990 		if (uarmg)
991 			lets[ct++] = obj_to_let(uarmg);
992 		lets[ct] = 0;
993 		doinv(lets);
994 	}
995 	return (0);
996 }
997 
998 int
999 doprring(void)
1000 {
1001 	if (!uleft && !uright)
1002 		pline("You are not wearing any rings.");
1003 	else {
1004 		char            lets[3];
1005 		int             ct = 0;
1006 
1007 		if (uleft)
1008 			lets[ct++] = obj_to_let(uleft);
1009 		if (uright)
1010 			lets[ct++] = obj_to_let(uright);
1011 		lets[ct] = 0;
1012 		doinv(lets);
1013 	}
1014 	return (0);
1015 }
1016 
1017 int
1018 digit(int c)
1019 {
1020 	return (c >= '0' && c <= '9');
1021 }
1022