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