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