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