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