xref: /dragonfly/games/rogue/inventory.c (revision 3536f98c1c75c45bd29f481cf18b0f90cea395f7)
1 /*-
2  * Copyright (c) 1988, 1993
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Timothy C. Stoehr.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)inventory.c  8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/rogue/inventory.c,v 1.4 1999/11/30 03:49:23 billf Exp $
34  */
35 
36 /*
37  * inventory.c
38  *
39  * This source herein may be modified and/or distributed by anybody who
40  * so desires, with the following restrictions:
41  *    1.)  No portion of this notice shall be removed.
42  *    2.)  Credit shall not be taken for the creation of this source.
43  *    3.)  This code is not to be traded, sold, or used for personal
44  *         gain or profit.
45  *
46  */
47 
48 #include "rogue.h"
49 
50 boolean is_wood[WANDS];
51 const char *press_space = " --press space to continue--";
52 
53 static const char *const wand_materials[WAND_MATERIALS] = {
54           "steel ",
55           "bronze ",
56           "gold ",
57           "silver ",
58           "copper ",
59           "nickel ",
60           "cobalt ",
61           "tin ",
62           "iron ",
63           "magnesium ",
64           "chrome ",
65           "carbon ",
66           "platinum ",
67           "silicon ",
68           "titanium ",
69           "teak ",
70           "oak ",
71           "cherry ",
72           "birch ",
73           "pine ",
74           "cedar ",
75           "redwood ",
76           "balsa ",
77           "ivory ",
78           "walnut ",
79           "maple ",
80           "mahogany ",
81           "elm ",
82           "palm ",
83           "wooden "
84 };
85 
86 static const char *const gems[GEMS] = {
87           "diamond ",
88           "stibotantalite ",
89           "lapi-lazuli ",
90           "ruby ",
91           "emerald ",
92           "sapphire ",
93           "amethyst ",
94           "quartz ",
95           "tiger-eye ",
96           "opal ",
97           "agate ",
98           "turquoise ",
99           "pearl ",
100           "garnet "
101 };
102 
103 static const char *const syllables[MAXSYLLABLES] = {
104           "blech ",
105           "foo ",
106           "barf ",
107           "rech ",
108           "bar ",
109           "blech ",
110           "quo ",
111           "bloto ",
112           "oh ",
113           "caca ",
114           "blorp ",
115           "erp ",
116           "festr ",
117           "rot ",
118           "slie ",
119           "snorf ",
120           "iky ",
121           "yuky ",
122           "ooze ",
123           "ah ",
124           "bahl ",
125           "zep ",
126           "druhl ",
127           "flem ",
128           "behil ",
129           "arek ",
130           "mep ",
131           "zihr ",
132           "grit ",
133           "kona ",
134           "kini ",
135           "ichi ",
136           "tims ",
137           "ogr ",
138           "oo ",
139           "ighr ",
140           "coph ",
141           "swerr ",
142           "mihln ",
143           "poxi "
144 };
145 
146 #define COMS 48
147 
148 struct id_com_s {
149           short com_char;
150           const char *com_desc;
151 };
152 
153 static const struct id_com_s com_id_tab[COMS] = {
154           { '?',              "?       prints help" },
155           { 'r',              "r       read scroll" },
156           { '/',              "/         identify object" },
157           { 'e',              "e       eat food" },
158           { 'h',              "h       left " },
159           { 'w',              "w       wield a weapon" },
160           { 'j',              "j       down" },
161           { 'W',              "W       wear armor" },
162           { 'k',              "k       up" },
163           { 'T',              "T       take armor off" },
164           { 'l',              "l       right" },
165           { 'P',              "P       put on ring" },
166           { 'y',              "y       up & left" },
167           { 'R',              "R       remove ring" },
168           { 'u',              "u       up & right" },
169           { 'd',              "d       drop object" },
170           { 'b',              "b       down & left" },
171           { 'c',              "c       call object" },
172           { 'n',              "n       down & right" },
173           { '\0',             "<SHIFT><dir>: run that way" },
174           { ')',              ")       print current weapon" },
175           { '\0',             "<CTRL><dir>: run till adjacent" },
176           { ']',              "]       print current armor" },
177           { 'f',              "f<dir>  fight till death or near death" },
178           { '=',              "=       print current rings" },
179           { 't',              "t<dir>  throw something" },
180           { '\001', "^A      print Hp-raise average" },
181           { 'm',              "m<dir>  move onto without picking up" },
182           { 'z',              "z<dir>  zap a wand in a direction" },
183           { 'o',              "o       examine/set options" },
184           { '^',              "^<dir>  identify trap type" },
185           { '\022', "^R      redraw screen" },
186           { '&',              "&       save screen into 'rogue.screen'" },
187           { 's',              "s       search for trap/secret door" },
188           { '\020', "^P      repeat last message" },
189           { '>',              ">       go down a staircase" },
190           { '\033', "^[      cancel command" },
191           { '<',              "<       go up a staircase" },
192           { 'S',              "S       save game" },
193           { '.',              ".       rest for a turn" },
194           { 'Q',              "Q       quit" },
195           { ',',              ",       pick something up" },
196           { '!',              "!       shell escape" },
197           { 'i',              "i       inventory" },
198           { 'F',              "F<dir>  fight till either of you dies" },
199           { 'I',              "I       inventory single item" },
200           { 'v',              "v       print version number" },
201           { 'q',              "q       quaff potion" }
202 };
203 
204 static boolean get_com_id(int *, short);
205 static boolean pr_com_id(int);
206 static boolean pr_motion_char(int);
207 
208 void
inventory(const object * pack,unsigned short mask)209 inventory(const object *pack, unsigned short mask)
210 {
211           object *obj;
212           short i = 0, j, maxlen = 0, n;
213           char descs[MAX_PACK_COUNT+1][DCOLS];
214           short row, col;
215 
216           obj = pack->next_object;
217 
218           if (!obj) {
219                     message("your pack is empty", 0);
220                     return;
221           }
222           while (obj) {
223                     if (obj->what_is & mask) {
224                               descs[i][0] = ' ';
225                               descs[i][1] = obj->ichar;
226                               descs[i][2] = ((obj->what_is & ARMOR) && obj->is_protected)
227                                         ? '}' : ')';
228                               descs[i][3] = ' ';
229                               get_desc(obj, descs[i]+4);
230                               if ((n = strlen(descs[i])) > maxlen) {
231                                         maxlen = n;
232                               }
233                     i++;
234                     }
235                     obj = obj->next_object;
236           }
237           strcpy(descs[i++], press_space);
238           if (maxlen < 27) maxlen = 27;
239           col = DCOLS - (maxlen + 2);
240 
241           for (row = 0; ((row < i) && (row < DROWS)); row++) {
242                     if (row > 0) {
243                               for (j = col; j < DCOLS; j++) {
244                                         descs[row-1][j-col] = mvinch(row, j);
245                               }
246                               descs[row-1][j-col] = 0;
247                     }
248                     mvaddstr(row, col, descs[row]);
249                     clrtoeol();
250           }
251           refresh();
252           wait_for_ack();
253 
254           move(0, 0);
255           clrtoeol();
256 
257           for (j = 1; ((j < i) && (j < DROWS)); j++) {
258                     mvaddstr(j, col, descs[j-1]);
259           }
260 }
261 
262 void
id_com(void)263 id_com(void)
264 {
265           int ch = 0;
266           short i, j, k;
267 
268           while (ch != CANCEL) {
269                     check_message();
270                     message("Character you want help for (* for all):", 0);
271 
272                     refresh();
273                     ch = getchar();
274 
275                     switch(ch) {
276                     case LIST:
277                               {
278                                         char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS];
279                                         short rows = (((COMS / 2) + (COMS % 2)) + 1);
280                                         boolean need_two_screens = FALSE;
281 
282                                         if (rows > LINES) {
283                                                   need_two_screens = 1;
284                                                   rows = LINES;
285                                         }
286                                         k = 0;
287 
288                                         for (i = 0; i < rows; i++) {
289                                                   for (j = 0; j < DCOLS; j++) {
290                                                             save[i][j] = mvinch(i, j);
291                                                   }
292                                         }
293 MORE:
294                                         for (i = 0; i < rows; i++) {
295                                                   move(i, 0);
296                                                   clrtoeol();
297                                         }
298                                         for (i = 0; i < (rows-1); i++) {
299                                                   if (i < (LINES-1)) {
300                                                             if (((i + i) < COMS) && ((i+i+k) < COMS)) {
301                                                                       mvaddstr(i, 0, com_id_tab[i+i+k].com_desc);
302                                                             }
303                                                             if (((i + i + 1) < COMS) && ((i+i+k+1) < COMS)) {
304                                                                       mvaddstr(i, (DCOLS/2),
305                                                                                                     com_id_tab[i+i+k+1].com_desc);
306                                                             }
307                                                   }
308                                         }
309                                         mvaddstr(rows - 1, 0, need_two_screens ? more : press_space);
310                                         refresh();
311                                         wait_for_ack();
312 
313                                         if (need_two_screens) {
314                                                   k += ((rows-1) * 2);
315                                                   need_two_screens = 0;
316                                                   goto MORE;
317                                         }
318                                         for (i = 0; i < rows; i++) {
319                                                   move(i, 0);
320                                                   for (j = 0; j < DCOLS; j++) {
321                                                             addch(save[i][j]);
322                                                   }
323                                         }
324                               }
325                               break;
326                     default:
327                               if (!pr_com_id(ch)) {
328                                         if (!pr_motion_char(ch)) {
329                                                   check_message();
330                                                   message("unknown character", 0);
331                                         }
332                               }
333                               ch = CANCEL;
334                               break;
335                     }
336           }
337 }
338 
339 static boolean
pr_com_id(int ch)340 pr_com_id(int ch)
341 {
342           int i;
343 
344           if (!get_com_id(&i, ch)) {
345                     return(0);
346           }
347           check_message();
348           message(com_id_tab[i].com_desc, 0);
349           return(1);
350 }
351 
352 static boolean
get_com_id(int * idx,short ch)353 get_com_id(int *idx, short ch)
354 {
355           short i;
356 
357           for (i = 0; i < COMS; i++) {
358                     if (com_id_tab[i].com_char == ch) {
359                               *idx = i;
360                               return(1);
361                     }
362           }
363           return(0);
364 }
365 
366 static boolean
pr_motion_char(int ch)367 pr_motion_char(int ch)
368 {
369           if (      (ch == 'J') ||
370                               (ch == 'K') ||
371                               (ch == 'L') ||
372                               (ch == 'H') ||
373                               (ch == 'Y') ||
374                               (ch == 'U') ||
375                               (ch == 'N') ||
376                               (ch == 'B') ||
377                               (ch == '\012') ||
378                               (ch == '\013') ||
379                               (ch == '\010') ||
380                               (ch == '\014') ||
381                               (ch == '\025') ||
382                               (ch == '\031') ||
383                               (ch == '\016') ||
384                               (ch == '\002')) {
385                     char until[18], buf[DCOLS];
386                     int n;
387 
388                     n = 0;
389                     if (ch <= '\031') {
390                               ch += 96;
391                               strcpy(until, "until adjascent");
392                     } else {
393                               ch += 32;
394                               until[0] = '\0';
395                     }
396                     get_com_id(&n, ch);
397                     sprintf(buf, "run %s %s", com_id_tab[n].com_desc + 8, until);
398                     check_message();
399                     message(buf, 0);
400                     return(1);
401           } else {
402                     return(0);
403           }
404 }
405 
406 void
mix_colors(void)407 mix_colors(void)
408 {
409           short i, j, k;
410           char *t[MAX_ID_TITLE_LEN];
411 
412           for (i = 0; i <= 32; i++) {
413                     j = get_rand(0, (POTIONS - 1));
414                     k = get_rand(0, (POTIONS - 1));
415                     memcpy(t, id_potions[j].title, MAX_ID_TITLE_LEN);
416                     memcpy(id_potions[j].title, id_potions[k].title, MAX_ID_TITLE_LEN);
417                     memcpy(id_potions[k].title, t, MAX_ID_TITLE_LEN);
418           }
419 }
420 
421 void
make_scroll_titles(void)422 make_scroll_titles(void)
423 {
424           short i, j, n;
425           short sylls, s;
426 
427           for (i = 0; i < SCROLS; i++) {
428                     sylls = get_rand(2, 5);
429                     strcpy(id_scrolls[i].title, "'");
430 
431                     for (j = 0; j < sylls; j++) {
432                               s = get_rand(1, (MAXSYLLABLES-1));
433                               strcat(id_scrolls[i].title, syllables[s]);
434                     }
435                     n = strlen(id_scrolls[i].title);
436                     strcpy(id_scrolls[i].title+(n-1), "' ");
437           }
438 }
439 
440 void
get_desc(const object * obj,char * desc)441 get_desc(const object *obj, char *desc)
442 {
443           const char *item_name;
444           struct id *id_table;
445           char more_info[32];
446           short i;
447 
448           if (obj->what_is == AMULET) {
449                     strcpy(desc, "the amulet of Yendor ");
450                     return;
451           }
452           item_name = name_of(obj);
453 
454           if (obj->what_is == GOLD) {
455                     sprintf(desc, "%d pieces of gold", obj->quantity);
456                     return;
457           }
458 
459           if (obj->what_is != ARMOR) {
460                     if (obj->quantity == 1) {
461                               strcpy(desc, "a ");
462                     } else {
463                               sprintf(desc, "%d ", obj->quantity);
464                     }
465           }
466           if (obj->what_is == FOOD) {
467                     if (obj->which_kind == RATION) {
468                               if (obj->quantity > 1) {
469                                         sprintf(desc, "%d rations of ", obj->quantity);
470                               } else {
471                                         strcpy(desc, "some ");
472                               }
473                     } else {
474                               strcpy(desc, "a ");
475                     }
476                     strcat(desc, item_name);
477                     goto ANA;
478           }
479           id_table = get_id_table(obj);
480 
481           if (wizard) {
482                     goto ID;
483           }
484           if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) {
485                     goto CHECK;
486           }
487 
488           switch(id_table[obj->which_kind].id_status) {
489           case UNIDENTIFIED:
490 CHECK:
491                     switch(obj->what_is) {
492                     case SCROL:
493                               strcat(desc, item_name);
494                               strcat(desc, "entitled: ");
495                               strcat(desc, id_table[obj->which_kind].title);
496                               break;
497                     case POTION:
498                               strcat(desc, id_table[obj->which_kind].title);
499                               strcat(desc, item_name);
500                               break;
501                     case WAND:
502                     case RING:
503                               if (obj->identified ||
504                               (id_table[obj->which_kind].id_status == IDENTIFIED)) {
505                                         goto ID;
506                               }
507                               if (id_table[obj->which_kind].id_status == CALLED) {
508                                         goto CALL;
509                               }
510                               strcat(desc, id_table[obj->which_kind].title);
511                               strcat(desc, item_name);
512                               break;
513                     case ARMOR:
514                               if (obj->identified) {
515                                         goto ID;
516                               }
517                               strcpy(desc, id_table[obj->which_kind].title);
518                               break;
519                     case WEAPON:
520                               if (obj->identified) {
521                                         goto ID;
522                               }
523                               strcat(desc, name_of(obj));
524                               break;
525                     }
526                     break;
527           case CALLED:
528 CALL:     switch(obj->what_is) {
529                     case SCROL:
530                     case POTION:
531                     case WAND:
532                     case RING:
533                               strcat(desc, item_name);
534                               strcat(desc, "called ");
535                               strcat(desc, id_table[obj->which_kind].title);
536                               break;
537                     }
538                     break;
539           case IDENTIFIED:
540 ID:                 switch(obj->what_is) {
541                     case SCROL:
542                     case POTION:
543                               strcat(desc, item_name);
544                               strcat(desc, id_table[obj->which_kind].real);
545                               break;
546                     case RING:
547                               if (wizard || obj->identified) {
548                                         if ((obj->which_kind == DEXTERITY) ||
549                                                   (obj->which_kind == ADD_STRENGTH)) {
550                                                   sprintf(more_info, "%s%d ", ((obj->class > 0) ? "+" : ""),
551                                                             obj->class);
552                                                   strcat(desc, more_info);
553                                         }
554                               }
555                               strcat(desc, item_name);
556                               strcat(desc, id_table[obj->which_kind].real);
557                               break;
558                     case WAND:
559                               strcat(desc, item_name);
560                               strcat(desc, id_table[obj->which_kind].real);
561                               if (wizard || obj->identified) {
562                                         sprintf(more_info, "[%d]", obj->class);
563                                         strcat(desc, more_info);
564                               }
565                               break;
566                     case ARMOR:
567                               sprintf(desc, "%s%d ", ((obj->d_enchant >= 0) ? "+" : ""),
568                               obj->d_enchant);
569                               strcat(desc, id_table[obj->which_kind].title);
570                               sprintf(more_info, "[%d] ", get_armor_class(obj));
571                               strcat(desc, more_info);
572                               break;
573                     case WEAPON:
574                               sprintf(desc+strlen(desc), "%s%d,%s%d ",
575                               ((obj->hit_enchant >= 0) ? "+" : ""), obj->hit_enchant,
576                               ((obj->d_enchant >= 0) ? "+" : ""), obj->d_enchant);
577                               strcat(desc, name_of(obj));
578                               break;
579                     }
580                     break;
581           }
582 ANA:
583           if (!strncmp(desc, "a ", 2)) {
584                     if (is_vowel(desc[2])) {
585                               for (i = strlen(desc) + 1; i > 1; i--) {
586                                         desc[i] = desc[i-1];
587                               }
588                               desc[1] = 'n';
589                     }
590           }
591           if (obj->in_use_flags & BEING_WIELDED) {
592                     strcat(desc, "in hand");
593           } else if (obj->in_use_flags & BEING_WORN) {
594                     strcat(desc, "being worn");
595           } else if (obj->in_use_flags & ON_LEFT_HAND) {
596                     strcat(desc, "on left hand");
597           } else if (obj->in_use_flags & ON_RIGHT_HAND) {
598                     strcat(desc, "on right hand");
599           }
600 }
601 
602 void
get_wand_and_ring_materials(void)603 get_wand_and_ring_materials(void)
604 {
605           short i, j;
606           boolean used[WAND_MATERIALS];
607 
608           for (i = 0; i < WAND_MATERIALS; i++) {
609                     used[i] = 0;
610           }
611           for (i = 0; i < WANDS; i++) {
612                     do {
613                               j = get_rand(0, WAND_MATERIALS-1);
614                     } while (used[j]);
615                     used[j] = 1;
616                     strcpy(id_wands[i].title, wand_materials[j]);
617                     is_wood[i] = (j > MAX_METAL);
618           }
619           for (i = 0; i < GEMS; i++) {
620                     used[i] = 0;
621           }
622           for (i = 0; i < RINGS; i++) {
623                     do {
624                               j = get_rand(0, GEMS-1);
625                     } while (used[j]);
626                     used[j] = 1;
627                     strcpy(id_rings[i].title, gems[j]);
628           }
629 }
630 
631 void
single_inv(short ichar)632 single_inv(short ichar)
633 {
634           short ch;
635           char desc[DCOLS];
636           object *obj;
637 
638           ch = ichar ? ichar : pack_letter("inventory what?", ALL_OBJECTS);
639 
640           if (ch == CANCEL) {
641                     return;
642           }
643           if (!(obj = get_letter_object(ch))) {
644                     message("no such item.", 0);
645                     return;
646           }
647           desc[0] = ch;
648           desc[1] = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')';
649           desc[2] = ' ';
650           desc[3] = 0;
651           get_desc(obj, desc+3);
652           message(desc, 0);
653 }
654 
655 struct id *
get_id_table(const object * obj)656 get_id_table(const object *obj)
657 {
658           switch(obj->what_is) {
659           case SCROL:
660                     return(id_scrolls);
661           case POTION:
662                     return(id_potions);
663           case WAND:
664                     return(id_wands);
665           case RING:
666                     return(id_rings);
667           case WEAPON:
668                     return(id_weapons);
669           case ARMOR:
670                     return(id_armors);
671           }
672           return(NULL);
673 }
674 
675 void
inv_armor_weapon(boolean is_weapon)676 inv_armor_weapon(boolean is_weapon)
677 {
678           if (is_weapon) {
679                     if (rogue.weapon) {
680                               single_inv(rogue.weapon->ichar);
681                     } else {
682                               message("not wielding anything", 0);
683                     }
684           } else {
685                     if (rogue.armor) {
686                               single_inv(rogue.armor->ichar);
687                     } else {
688                               message("not wearing anything", 0);
689                     }
690           }
691 }
692 
693 void
id_type(void)694 id_type(void)
695 {
696           const char *id;
697           int ch;
698           char buf[DCOLS];
699 
700           message("what do you want identified?", 0);
701 
702           ch = rgetchar();
703 
704           if ((ch >= 'A') && (ch <= 'Z')) {
705                     id = m_names[ch-'A'];
706           } else if (ch < 32) {
707                     check_message();
708                     return;
709           } else {
710                     switch(ch) {
711                     case '@':
712                               id = "you";
713                               break;
714                     case '%':
715                               id = "staircase";
716                               break;
717                     case '^':
718                               id = "trap";
719                               break;
720                     case '+':
721                               id = "door";
722                               break;
723                     case '-':
724                     case '|':
725                               id = "wall of a room";
726                               break;
727                     case '.':
728                               id = "floor";
729                               break;
730                     case '#':
731                               id = "passage";
732                               break;
733                     case ' ':
734                               id = "solid rock";
735                               break;
736                     case '=':
737                               id = "ring";
738                               break;
739                     case '?':
740                               id = "scroll";
741                               break;
742                     case '!':
743                               id = "potion";
744                               break;
745                     case '/':
746                               id = "wand or staff";
747                               break;
748                     case ')':
749                               id = "weapon";
750                               break;
751                     case ']':
752                               id = "armor";
753                               break;
754                     case '*':
755                               id = "gold";
756                               break;
757                     case ':':
758                               id = "food";
759                               break;
760                     case ',':
761                               id = "the Amulet of Yendor";
762                               break;
763                     default:
764                               id = "unknown character";
765                               break;
766                     }
767           }
768           check_message();
769           sprintf(buf, "'%c': %s", ch, id);
770           message(buf, 0);
771 }
772