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@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
assigninvlet(struct obj * otmp)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     *
addinv(struct obj * 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
useup(struct obj * obj)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
freeinv(struct obj * obj)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
delobj(struct obj * obj)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
freeobj(struct obj * obj)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
freegold(struct gold * gold)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
deltrap(struct trap * trap)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   *
m_at(int x,int y)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     *
o_at(int x,int y)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     *
sobj_at(int n,int x,int y)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
carried(struct obj * obj)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
carrying(int type)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     *
o_on(unsigned int id,struct obj * objchn)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    *
t_at(int x,int y)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    *
g_at(int x,int y)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 *
mkgoldobj(long q)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     *
getobj(const char * let,const char * word)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
ckunpaid(struct obj * otmp)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
ggetobj(const char * word,int (* fn)(struct obj *),int max)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
askchain(struct obj * objchn,char * olets,int allflag,int (* fn)(struct obj *),int (* ckfn)(struct obj *),int max)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
obj_to_let(struct obj * obj)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
prinv(struct obj * obj)702 prinv(struct obj *obj)
703 {
704           pline("%s", xprname(obj, obj_to_let(obj)));
705 }
706 
707 static char *
xprname(struct obj * obj,char let)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
ddoinv(void)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
doinv(const char * lets)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
dotypeinv(void)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
dolook(void)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
stackobj(struct obj * obj)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
merged(struct obj * otmp,struct obj * obj,int lose)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
countgold(void)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
doprgold(void)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
doprwep(void)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
doprarm(void)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
doprring(void)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
digit(int c)1020 digit(int c)
1021 {
1022           return (c >= '0' && c <= '9');
1023 }
1024