Home | History | Annotate | Line # | Download | only in hack
hack.invent.c revision 1.14
      1 /*	$NetBSD: hack.invent.c,v 1.14 2009/08/12 07:28:40 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 (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.14 2009/08/12 07:28:40 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(char *);
     84 static int merged(struct obj *, struct obj *, int);
     85 
     86 static void
     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     *
    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
    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
    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
    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
    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
    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((char *) gold);
    241 }
    242 
    243 void
    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((char *) trap);
    255 }
    256 
    257 struct wseg    *m_atseg;
    258 
    259 struct monst   *
    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     *
    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     *
    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
    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
    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     *
    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    *
    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    *
    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 *
    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     *
    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((char *) 0);
    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
    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
    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
    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(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 		case 'y':
    668 			cnt += (*fn) (otmp);
    669 			if (--max == 0)
    670 				goto ret;
    671 		case 'n':
    672 		default:
    673 			break;
    674 		case 'q':
    675 			goto ret;
    676 		}
    677 	}
    678 	pline(cnt ? "That was all." : "No applicable objects.");
    679 ret:
    680 	return (cnt);
    681 }
    682 
    683 /* should of course only be called for things in invent */
    684 static char
    685 obj_to_let(struct obj *obj)
    686 {
    687 	struct obj     *otmp;
    688 	char            ilet;
    689 
    690 	if (flags.invlet_constant)
    691 		return (obj->invlet);
    692 	ilet = 'a';
    693 	for (otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
    694 		if (++ilet > 'z')
    695 			ilet = 'A';
    696 	return (otmp ? ilet : NOINVSYM);
    697 }
    698 
    699 void
    700 prinv(struct obj *obj)
    701 {
    702 	pline(xprname(obj, obj_to_let(obj)));
    703 }
    704 
    705 static char *
    706 xprname(struct obj *obj, char let)
    707 {
    708 	static char     li[BUFSZ];
    709 
    710 	(void) snprintf(li, sizeof(li), "%c - %s.",
    711 		       flags.invlet_constant ? obj->invlet : let,
    712 		       doname(obj));
    713 	return (li);
    714 }
    715 
    716 int
    717 ddoinv(void)
    718 {
    719 	doinv((char *) 0);
    720 	return (0);
    721 }
    722 
    723 /* called with 0 or "": all objects in inventory */
    724 /* otherwise: all objects with (serial) letter in lets */
    725 static void
    726 doinv(char *lets)
    727 {
    728 	struct obj     *otmp;
    729 	char            ilet;
    730 	unsigned        ct = 0;
    731 	char            any[BUFSZ];
    732 
    733 	morc = 0;		/* just to be sure */
    734 
    735 	if (!invent) {
    736 		pline("Not carrying anything.");
    737 		return;
    738 	}
    739 	cornline(0, (char *) 0);
    740 	ilet = 'a';
    741 	for (otmp = invent; otmp; otmp = otmp->nobj) {
    742 		if (flags.invlet_constant)
    743 			ilet = otmp->invlet;
    744 		if (!lets || !*lets || strchr(lets, ilet)) {
    745 			cornline(1, xprname(otmp, ilet));
    746 			any[ct++] = ilet;
    747 		}
    748 		if (!flags.invlet_constant)
    749 			if (++ilet > 'z')
    750 				ilet = 'A';
    751 	}
    752 	any[ct] = 0;
    753 	assert(ct < sizeof(any));
    754 	cornline(2, any);
    755 }
    756 
    757 int
    758 dotypeinv(void)
    759 {				/* free after Robert Viduya */
    760 	/* Changed to one type only, so he doesnt have to type cr */
    761 	char            c, ilet;
    762 	char            stuff[BUFSZ];
    763 	unsigned        stct;
    764 	struct obj     *otmp;
    765 	boolean         billx = inshop() && doinvbill(0);
    766 	boolean         unpd = FALSE;
    767 
    768 	if (!invent && !u.ugold && !billx) {
    769 		pline("You aren't carrying anything.");
    770 		return (0);
    771 	}
    772 	stct = 0;
    773 	if (u.ugold)
    774 		stuff[stct++] = '$';
    775 	stuff[stct] = 0;
    776 	for (otmp = invent; otmp; otmp = otmp->nobj) {
    777 		if (!strchr(stuff, otmp->olet)) {
    778 			stuff[stct++] = otmp->olet;
    779 			stuff[stct] = 0;
    780 		}
    781 		if (otmp->unpaid)
    782 			unpd = TRUE;
    783 	}
    784 	if (unpd)
    785 		stuff[stct++] = 'u';
    786 	if (billx)
    787 		stuff[stct++] = 'x';
    788 	stuff[stct] = 0;
    789 	assert(stct < sizeof(stuff));
    790 
    791 	if (stct > 1) {
    792 		pline("What type of object [%s] do you want an inventory of? ",
    793 		      stuff);
    794 		c = readchar();
    795 		if (strchr(quitchars, c))
    796 			return (0);
    797 	} else
    798 		c = stuff[0];
    799 
    800 	if (c == '$')
    801 		return (doprgold());
    802 
    803 	if (c == 'x' || c == 'X') {
    804 		if (billx)
    805 			(void) doinvbill(1);
    806 		else
    807 			pline("No used-up objects on the shopping bill.");
    808 		return (0);
    809 	}
    810 	if ((c == 'u' || c == 'U') && !unpd) {
    811 		pline("You are not carrying any unpaid objects.");
    812 		return (0);
    813 	}
    814 	stct = 0;
    815 	ilet = 'a';
    816 	for (otmp = invent; otmp; otmp = otmp->nobj) {
    817 		if (flags.invlet_constant)
    818 			ilet = otmp->invlet;
    819 		if (c == otmp->olet || (c == 'u' && otmp->unpaid))
    820 			stuff[stct++] = ilet;
    821 		if (!flags.invlet_constant)
    822 			if (++ilet > 'z')
    823 				ilet = 'A';
    824 	}
    825 	stuff[stct] = '\0';
    826 	assert(stct < sizeof(stuff));
    827 
    828 	if (stct == 0)
    829 		pline("You have no such objects.");
    830 	else
    831 		doinv(stuff);
    832 
    833 	return (0);
    834 }
    835 
    836 /* look at what is here */
    837 int
    838 dolook(void)
    839 {
    840 	struct obj     *otmp = NULL, *otmp0 = NULL;
    841 	struct gold    *gold = NULL;
    842 	const char     *verb = Blind ? "feel" : "see";
    843 	int             ct = 0;
    844 
    845 	if (!u.uswallow) {
    846 		if (Blind) {
    847 			pline("You try to feel what is lying here on the floor.");
    848 			if (Levitation) {	/* ab@unido */
    849 				pline("You cannot reach the floor!");
    850 				return (1);
    851 			}
    852 		}
    853 		otmp0 = o_at(u.ux, u.uy);
    854 		gold = g_at(u.ux, u.uy);
    855 	}
    856 	if (u.uswallow || (!otmp0 && !gold)) {
    857 		pline("You %s no objects here.", verb);
    858 		return (!!Blind);
    859 	}
    860 	cornline(0, "Things that are here:");
    861 	for (otmp = otmp0; otmp; otmp = otmp->nobj) {
    862 		if (otmp->ox == u.ux && otmp->oy == u.uy) {
    863 			ct++;
    864 			cornline(1, doname(otmp));
    865 			if (Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) {
    866 				pline("Touching the dead cockatrice is a fatal mistake ...");
    867 				pline("You die ...");
    868 				killer = "dead cockatrice";
    869 				done("died");
    870 			}
    871 		}
    872 	}
    873 
    874 	if (gold) {
    875 		char            gbuf[30];
    876 
    877 		(void) snprintf(gbuf, sizeof(gbuf), "%ld gold piece%s",
    878 			       gold->amount, plur(gold->amount));
    879 		if (!ct++)
    880 			pline("You %s here %s.", verb, gbuf);
    881 		else
    882 			cornline(1, gbuf);
    883 	}
    884 	if (ct == 1 && !gold) {
    885 		pline("You %s here %s.", verb, doname(otmp0));
    886 		cornline(3, (char *) 0);
    887 	}
    888 	if (ct > 1)
    889 		cornline(2, (char *) 0);
    890 	return (!!Blind);
    891 }
    892 
    893 void
    894 stackobj(struct obj *obj)
    895 {
    896 	struct obj     *otmp = fobj;
    897 	for (otmp = fobj; otmp; otmp = otmp->nobj)
    898 		if (otmp != obj)
    899 			if (otmp->ox == obj->ox && otmp->oy == obj->oy &&
    900 			    merged(obj, otmp, 1))
    901 				return;
    902 }
    903 
    904 /* merge obj with otmp and delete obj if types agree */
    905 static int
    906 merged(struct obj *otmp, struct obj *obj, int lose)
    907 {
    908 	if (obj->otyp == otmp->otyp &&
    909 	    obj->unpaid == otmp->unpaid &&
    910 	    obj->spe == otmp->spe &&
    911 	    obj->dknown == otmp->dknown &&
    912 	    obj->cursed == otmp->cursed &&
    913 	    (strchr("%*?!", obj->olet) ||
    914 	     (obj->known == otmp->known &&
    915 	      (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) {
    916 		otmp->quan += obj->quan;
    917 		otmp->owt += obj->owt;
    918 		if (lose)
    919 			freeobj(obj);
    920 		obfree(obj, otmp);	/* free(obj), bill->otmp */
    921 		return (1);
    922 	} else
    923 		return (0);
    924 }
    925 
    926 static long goldcounted;
    927 /*
    928  * Gold is no longer displayed; in fact, when you have a lot of money,
    929  * it may take a while before you have counted it all.
    930  * [Bug: d$ and pickup still tell you how much it was.]
    931  */
    932 static int
    933 countgold(void)
    934 {
    935 	if ((goldcounted += 100 * (u.ulevel + 1)) >= u.ugold) {
    936 		long            eps = 0;
    937 		if (!rn2(2))
    938 			eps = rnd((int) (u.ugold / 100 + 1));
    939 		pline("You probably have about %ld gold pieces.",
    940 		      u.ugold + eps);
    941 		return (0);	/* done */
    942 	}
    943 	return (1);		/* continue */
    944 }
    945 
    946 int
    947 doprgold(void)
    948 {
    949 	if (!u.ugold)
    950 		pline("You do not carry any gold.");
    951 	else if (u.ugold <= 500)
    952 		pline("You are carrying %ld gold pieces.", u.ugold);
    953 	else {
    954 		pline("You sit down in order to count your gold pieces.");
    955 		goldcounted = 500;
    956 		occupation = countgold;
    957 		occtxt = "counting your gold";
    958 	}
    959 	return (1);
    960 }
    961 
    962 /* --- end of gold counting section --- */
    963 int
    964 doprwep(void)
    965 {
    966 	if (!uwep)
    967 		pline("You are empty handed.");
    968 	else
    969 		prinv(uwep);
    970 	return (0);
    971 }
    972 
    973 int
    974 doprarm(void)
    975 {
    976 	if (!uarm && !uarmg && !uarms && !uarmh)
    977 		pline("You are not wearing any armor.");
    978 	else {
    979 		char            lets[6];
    980 		int             ct = 0;
    981 
    982 		if (uarm)
    983 			lets[ct++] = obj_to_let(uarm);
    984 		if (uarm2)
    985 			lets[ct++] = obj_to_let(uarm2);
    986 		if (uarmh)
    987 			lets[ct++] = obj_to_let(uarmh);
    988 		if (uarms)
    989 			lets[ct++] = obj_to_let(uarms);
    990 		if (uarmg)
    991 			lets[ct++] = obj_to_let(uarmg);
    992 		lets[ct] = 0;
    993 		doinv(lets);
    994 	}
    995 	return (0);
    996 }
    997 
    998 int
    999 doprring(void)
   1000 {
   1001 	if (!uleft && !uright)
   1002 		pline("You are not wearing any rings.");
   1003 	else {
   1004 		char            lets[3];
   1005 		int             ct = 0;
   1006 
   1007 		if (uleft)
   1008 			lets[ct++] = obj_to_let(uleft);
   1009 		if (uright)
   1010 			lets[ct++] = obj_to_let(uright);
   1011 		lets[ct] = 0;
   1012 		doinv(lets);
   1013 	}
   1014 	return (0);
   1015 }
   1016 
   1017 int
   1018 digit(int c)
   1019 {
   1020 	return (c >= '0' && c <= '9');
   1021 }
   1022