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