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