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