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