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