xref: /netbsd/games/hack/hack.invent.c (revision 759cbe51)
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 <sys/cdefs.h>
65 #ifndef lint
66 __RCSID("$NetBSD: hack.invent.c,v 1.18 2011/08/07 06:03:45 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(const char *);
84 static int merged(struct obj *, struct obj *, int);
85 
86 static void
assigninvlet(struct obj * otmp)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     *
addinv(struct obj * 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
useup(struct obj * obj)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
freeinv(struct obj * obj)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
delobj(struct obj * obj)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
freeobj(struct obj * obj)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
freegold(struct gold * gold)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(gold);
241 }
242 
243 void
deltrap(struct trap * trap)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(trap);
255 }
256 
257 struct wseg    *m_atseg;
258 
259 struct monst   *
m_at(int x,int y)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     *
o_at(int x,int y)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     *
sobj_at(int n,int x,int y)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
carried(struct obj * obj)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
carrying(int type)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     *
o_on(unsigned int id,struct obj * objchn)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    *
t_at(int x,int y)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    *
g_at(int x,int y)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 *
mkgoldobj(long q)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     *
getobj(const char * let,const char * word)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(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 
546 static int
ckunpaid(struct obj * otmp)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
ggetobj(const char * word,int (* fn)(struct obj *),int max)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
askchain(struct obj * objchn,char * olets,int allflag,int (* fn)(struct obj *),int (* ckfn)(struct obj *),int max)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("%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.");
681 ret:
682 	return (cnt);
683 }
684 
685 /* should of course only be called for things in invent */
686 static char
obj_to_let(struct obj * obj)687 obj_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 
701 void
prinv(struct obj * obj)702 prinv(struct obj *obj)
703 {
704 	pline("%s", xprname(obj, obj_to_let(obj)));
705 }
706 
707 static char *
xprname(struct obj * obj,char let)708 xprname(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 
718 int
ddoinv(void)719 ddoinv(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 */
727 static void
doinv(const char * lets)728 doinv(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 
759 int
dotypeinv(void)760 dotypeinv(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 */
839 int
dolook(void)840 dolook(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 
895 void
stackobj(struct obj * obj)896 stackobj(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 */
907 static int
merged(struct obj * otmp,struct obj * obj,int lose)908 merged(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 
928 static 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  */
934 static int
countgold(void)935 countgold(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 
948 int
doprgold(void)949 doprgold(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 --- */
965 int
doprwep(void)966 doprwep(void)
967 {
968 	if (!uwep)
969 		pline("You are empty handed.");
970 	else
971 		prinv(uwep);
972 	return (0);
973 }
974 
975 int
doprarm(void)976 doprarm(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 
1000 int
doprring(void)1001 doprring(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 
1019 int
digit(int c)1020 digit(int c)
1021 {
1022 	return (c >= '0' && c <= '9');
1023 }
1024