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