Home | History | Annotate | Line # | Download | only in hack
      1 /*	$NetBSD: hack.invent.c,v 1.18 2011/08/07 06:03:45 dholland Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
      5  * Amsterdam
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions are
     10  * met:
     11  *
     12  * - Redistributions of source code must retain the above copyright notice,
     13  * this list of conditions and the following disclaimer.
     14  *
     15  * - Redistributions in binary form must reproduce the above copyright
     16  * notice, this list of conditions and the following disclaimer in the
     17  * documentation and/or other materials provided with the distribution.
     18  *
     19  * - Neither the name of the Stichting Centrum voor Wiskunde en
     20  * Informatica, nor the names of its contributors may be used to endorse or
     21  * promote products derived from this software without specific prior
     22  * written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
     28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     35  */
     36 
     37 /*
     38  * Copyright (c) 1982 Jay Fenlason <hack (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.18 2011/08/07 06:03:45 dholland Exp $");
     67 #endif				/* not lint */
     68 
     69 #include <assert.h>
     70 #include <stdlib.h>
     71 #include "hack.h"
     72 #include "extern.h"
     73 
     74 #ifndef NOWORM
     75 #include	"def.wseg.h"
     76 #endif	/* NOWORM */
     77 
     78 #define	NOINVSYM	'#'
     79 
     80 static int      lastinvnr = 51;	/* 0 ... 51 */
     81 
     82 static char *xprname(struct obj *, char);
     83 static void doinv(const char *);
     84 static int merged(struct obj *, struct obj *, int);
     85 
     86 static void
     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(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(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(NULL);
    503 			if (!(ilet = morc))
    504 				continue;
    505 			/* ... */
    506 		}
    507 		if (flags.invlet_constant) {
    508 			for (otmp = invent; otmp; otmp = otmp->nobj)
    509 				if (otmp->invlet == ilet)
    510 					break;
    511 		} else {
    512 			if (ilet >= 'A' && ilet <= 'Z')
    513 				ilet += 'z' - 'A' + 1;
    514 			ilet -= 'a';
    515 			for (otmp = invent; otmp && ilet;
    516 			     ilet--, otmp = otmp->nobj);
    517 		}
    518 		if (!otmp) {
    519 			pline("You don't have that object.");
    520 			continue;
    521 		}
    522 		if (cnt < 0 || otmp->quan < cnt) {
    523 			pline("You don't have that many! [You have %u]"
    524 			      ,otmp->quan);
    525 			continue;
    526 		}
    527 		break;
    528 	}
    529 	if (!allowall && let && !strchr(let, otmp->olet)) {
    530 		pline("That is a silly thing to %s.", word);
    531 		return (0);
    532 	}
    533 	if (allowcnt == 2) {	/* cnt given */
    534 		if (cnt == 0)
    535 			return (0);
    536 		if (cnt != otmp->quan) {
    537 			struct obj     *obj;
    538 			obj = splitobj(otmp, (int) cnt);
    539 			if (otmp == uwep)
    540 				setuwep(obj);
    541 		}
    542 	}
    543 	return (otmp);
    544 }
    545 
    546 static int
    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("%s", xprname(otmp, ilet));
    659 			addtopl(" [nyaq]? ");
    660 			sym = readchar();
    661 		} else
    662 			sym = 'y';
    663 
    664 		switch (sym) {
    665 		case 'a':
    666 			allflag = 1;
    667 			/* FALLTHROUGH */
    668 		case 'y':
    669 			cnt += (*fn) (otmp);
    670 			if (--max == 0)
    671 				goto ret;
    672 			break;
    673 		case 'n':
    674 		default:
    675 			break;
    676 		case 'q':
    677 			goto ret;
    678 		}
    679 	}
    680 	pline(cnt ? "That was all." : "No applicable objects.");
    681 ret:
    682 	return (cnt);
    683 }
    684 
    685 /* should of course only be called for things in invent */
    686 static char
    687 obj_to_let(struct obj *obj)
    688 {
    689 	struct obj     *otmp;
    690 	char            ilet;
    691 
    692 	if (flags.invlet_constant)
    693 		return (obj->invlet);
    694 	ilet = 'a';
    695 	for (otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
    696 		if (++ilet > 'z')
    697 			ilet = 'A';
    698 	return (otmp ? ilet : NOINVSYM);
    699 }
    700 
    701 void
    702 prinv(struct obj *obj)
    703 {
    704 	pline("%s", xprname(obj, obj_to_let(obj)));
    705 }
    706 
    707 static char *
    708 xprname(struct obj *obj, char let)
    709 {
    710 	static char     li[BUFSZ];
    711 
    712 	(void) snprintf(li, sizeof(li), "%c - %s.",
    713 		       flags.invlet_constant ? obj->invlet : let,
    714 		       doname(obj));
    715 	return (li);
    716 }
    717 
    718 int
    719 ddoinv(void)
    720 {
    721 	doinv(NULL);
    722 	return (0);
    723 }
    724 
    725 /* called with 0 or "": all objects in inventory */
    726 /* otherwise: all objects with (serial) letter in lets */
    727 static void
    728 doinv(const char *lets)
    729 {
    730 	struct obj     *otmp;
    731 	char            ilet;
    732 	unsigned        ct = 0;
    733 	char            any[BUFSZ];
    734 
    735 	morc = 0;		/* just to be sure */
    736 
    737 	if (!invent) {
    738 		pline("Not carrying anything.");
    739 		return;
    740 	}
    741 	cornline(0, NULL);
    742 	ilet = 'a';
    743 	for (otmp = invent; otmp; otmp = otmp->nobj) {
    744 		if (flags.invlet_constant)
    745 			ilet = otmp->invlet;
    746 		if (!lets || !*lets || strchr(lets, ilet)) {
    747 			cornline(1, xprname(otmp, ilet));
    748 			any[ct++] = ilet;
    749 		}
    750 		if (!flags.invlet_constant)
    751 			if (++ilet > 'z')
    752 				ilet = 'A';
    753 	}
    754 	any[ct] = 0;
    755 	assert(ct < sizeof(any));
    756 	cornline(2, any);
    757 }
    758 
    759 int
    760 dotypeinv(void)
    761 {				/* free after Robert Viduya */
    762 	/* Changed to one type only, so he doesnt have to type cr */
    763 	char            c, ilet;
    764 	char            stuff[BUFSZ];
    765 	unsigned        stct;
    766 	struct obj     *otmp;
    767 	boolean         billx = inshop() && doinvbill(0);
    768 	boolean         unpd = FALSE;
    769 
    770 	if (!invent && !u.ugold && !billx) {
    771 		pline("You aren't carrying anything.");
    772 		return (0);
    773 	}
    774 	stct = 0;
    775 	if (u.ugold)
    776 		stuff[stct++] = '$';
    777 	stuff[stct] = 0;
    778 	for (otmp = invent; otmp; otmp = otmp->nobj) {
    779 		if (!strchr(stuff, otmp->olet)) {
    780 			stuff[stct++] = otmp->olet;
    781 			stuff[stct] = 0;
    782 		}
    783 		if (otmp->unpaid)
    784 			unpd = TRUE;
    785 	}
    786 	if (unpd)
    787 		stuff[stct++] = 'u';
    788 	if (billx)
    789 		stuff[stct++] = 'x';
    790 	stuff[stct] = 0;
    791 	assert(stct < sizeof(stuff));
    792 
    793 	if (stct > 1) {
    794 		pline("What type of object [%s] do you want an inventory of? ",
    795 		      stuff);
    796 		c = readchar();
    797 		if (strchr(quitchars, c))
    798 			return (0);
    799 	} else
    800 		c = stuff[0];
    801 
    802 	if (c == '$')
    803 		return (doprgold());
    804 
    805 	if (c == 'x' || c == 'X') {
    806 		if (billx)
    807 			(void) doinvbill(1);
    808 		else
    809 			pline("No used-up objects on the shopping bill.");
    810 		return (0);
    811 	}
    812 	if ((c == 'u' || c == 'U') && !unpd) {
    813 		pline("You are not carrying any unpaid objects.");
    814 		return (0);
    815 	}
    816 	stct = 0;
    817 	ilet = 'a';
    818 	for (otmp = invent; otmp; otmp = otmp->nobj) {
    819 		if (flags.invlet_constant)
    820 			ilet = otmp->invlet;
    821 		if (c == otmp->olet || (c == 'u' && otmp->unpaid))
    822 			stuff[stct++] = ilet;
    823 		if (!flags.invlet_constant)
    824 			if (++ilet > 'z')
    825 				ilet = 'A';
    826 	}
    827 	stuff[stct] = '\0';
    828 	assert(stct < sizeof(stuff));
    829 
    830 	if (stct == 0)
    831 		pline("You have no such objects.");
    832 	else
    833 		doinv(stuff);
    834 
    835 	return (0);
    836 }
    837 
    838 /* look at what is here */
    839 int
    840 dolook(void)
    841 {
    842 	struct obj     *otmp = NULL, *otmp0 = NULL;
    843 	struct gold    *gold = NULL;
    844 	const char     *verb = Blind ? "feel" : "see";
    845 	int             ct = 0;
    846 
    847 	if (!u.uswallow) {
    848 		if (Blind) {
    849 			pline("You try to feel what is lying here on the floor.");
    850 			if (Levitation) {	/* ab@unido */
    851 				pline("You cannot reach the floor!");
    852 				return (1);
    853 			}
    854 		}
    855 		otmp0 = o_at(u.ux, u.uy);
    856 		gold = g_at(u.ux, u.uy);
    857 	}
    858 	if (u.uswallow || (!otmp0 && !gold)) {
    859 		pline("You %s no objects here.", verb);
    860 		return (!!Blind);
    861 	}
    862 	cornline(0, "Things that are here:");
    863 	for (otmp = otmp0; otmp; otmp = otmp->nobj) {
    864 		if (otmp->ox == u.ux && otmp->oy == u.uy) {
    865 			ct++;
    866 			cornline(1, doname(otmp));
    867 			if (Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) {
    868 				pline("Touching the dead cockatrice is a fatal mistake ...");
    869 				pline("You die ...");
    870 				killer = "dead cockatrice";
    871 				done("died");
    872 			}
    873 		}
    874 	}
    875 
    876 	if (gold) {
    877 		char            gbuf[30];
    878 
    879 		(void) snprintf(gbuf, sizeof(gbuf), "%ld gold piece%s",
    880 			       gold->amount, plur(gold->amount));
    881 		if (!ct++)
    882 			pline("You %s here %s.", verb, gbuf);
    883 		else
    884 			cornline(1, gbuf);
    885 	}
    886 	if (ct == 1 && !gold) {
    887 		pline("You %s here %s.", verb, doname(otmp0));
    888 		cornline(3, NULL);
    889 	}
    890 	if (ct > 1)
    891 		cornline(2, NULL);
    892 	return (!!Blind);
    893 }
    894 
    895 void
    896 stackobj(struct obj *obj)
    897 {
    898 	struct obj     *otmp = fobj;
    899 	for (otmp = fobj; otmp; otmp = otmp->nobj)
    900 		if (otmp != obj)
    901 			if (otmp->ox == obj->ox && otmp->oy == obj->oy &&
    902 			    merged(obj, otmp, 1))
    903 				return;
    904 }
    905 
    906 /* merge obj with otmp and delete obj if types agree */
    907 static int
    908 merged(struct obj *otmp, struct obj *obj, int lose)
    909 {
    910 	if (obj->otyp == otmp->otyp &&
    911 	    obj->unpaid == otmp->unpaid &&
    912 	    obj->spe == otmp->spe &&
    913 	    obj->dknown == otmp->dknown &&
    914 	    obj->cursed == otmp->cursed &&
    915 	    (strchr("%*?!", obj->olet) ||
    916 	     (obj->known == otmp->known &&
    917 	      (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) {
    918 		otmp->quan += obj->quan;
    919 		otmp->owt += obj->owt;
    920 		if (lose)
    921 			freeobj(obj);
    922 		obfree(obj, otmp);	/* free(obj), bill->otmp */
    923 		return (1);
    924 	} else
    925 		return (0);
    926 }
    927 
    928 static long goldcounted;
    929 /*
    930  * Gold is no longer displayed; in fact, when you have a lot of money,
    931  * it may take a while before you have counted it all.
    932  * [Bug: d$ and pickup still tell you how much it was.]
    933  */
    934 static int
    935 countgold(void)
    936 {
    937 	if ((goldcounted += 100 * (u.ulevel + 1)) >= u.ugold) {
    938 		long            eps = 0;
    939 		if (!rn2(2))
    940 			eps = rnd((int) (u.ugold / 100 + 1));
    941 		pline("You probably have about %ld gold pieces.",
    942 		      u.ugold + eps);
    943 		return (0);	/* done */
    944 	}
    945 	return (1);		/* continue */
    946 }
    947 
    948 int
    949 doprgold(void)
    950 {
    951 	if (!u.ugold)
    952 		pline("You do not carry any gold.");
    953 	else if (u.ugold <= 500)
    954 		pline("You are carrying %ld gold pieces.", u.ugold);
    955 	else {
    956 		pline("You sit down in order to count your gold pieces.");
    957 		goldcounted = 500;
    958 		occupation = countgold;
    959 		occtxt = "counting your gold";
    960 	}
    961 	return (1);
    962 }
    963 
    964 /* --- end of gold counting section --- */
    965 int
    966 doprwep(void)
    967 {
    968 	if (!uwep)
    969 		pline("You are empty handed.");
    970 	else
    971 		prinv(uwep);
    972 	return (0);
    973 }
    974 
    975 int
    976 doprarm(void)
    977 {
    978 	if (!uarm && !uarmg && !uarms && !uarmh)
    979 		pline("You are not wearing any armor.");
    980 	else {
    981 		char            lets[6];
    982 		int             ct = 0;
    983 
    984 		if (uarm)
    985 			lets[ct++] = obj_to_let(uarm);
    986 		if (uarm2)
    987 			lets[ct++] = obj_to_let(uarm2);
    988 		if (uarmh)
    989 			lets[ct++] = obj_to_let(uarmh);
    990 		if (uarms)
    991 			lets[ct++] = obj_to_let(uarms);
    992 		if (uarmg)
    993 			lets[ct++] = obj_to_let(uarmg);
    994 		lets[ct] = 0;
    995 		doinv(lets);
    996 	}
    997 	return (0);
    998 }
    999 
   1000 int
   1001 doprring(void)
   1002 {
   1003 	if (!uleft && !uright)
   1004 		pline("You are not wearing any rings.");
   1005 	else {
   1006 		char            lets[3];
   1007 		int             ct = 0;
   1008 
   1009 		if (uleft)
   1010 			lets[ct++] = obj_to_let(uleft);
   1011 		if (uright)
   1012 			lets[ct++] = obj_to_let(uright);
   1013 		lets[ct] = 0;
   1014 		doinv(lets);
   1015 	}
   1016 	return (0);
   1017 }
   1018 
   1019 int
   1020 digit(int c)
   1021 {
   1022 	return (c >= '0' && c <= '9');
   1023 }
   1024