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