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