xref: /dragonfly/games/rogue/inventory.c (revision ffe53622)
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 		memcpy(id_potions[k].title, t, MAX_ID_TITLE_LEN);
421 	}
422 }
423 
424 void
425 make_scroll_titles(void)
426 {
427 	short i, j, n;
428 	short sylls, s;
429 
430 	for (i = 0; i < SCROLS; i++) {
431 		sylls = get_rand(2, 5);
432 		strcpy(id_scrolls[i].title, "'");
433 
434 		for (j = 0; j < sylls; j++) {
435 			s = get_rand(1, (MAXSYLLABLES-1));
436 			strcat(id_scrolls[i].title, syllables[s]);
437 		}
438 		n = strlen(id_scrolls[i].title);
439 		strcpy(id_scrolls[i].title+(n-1), "' ");
440 	}
441 }
442 
443 void
444 get_desc(const object *obj, 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 		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 			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 				strcpy(desc, "some ");
475 			}
476 		} else {
477 			strcpy(desc, "a ");
478 		}
479 		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 			strcat(desc, item_name);
497 			strcat(desc, "entitled: ");
498 			strcat(desc, id_table[obj->which_kind].title);
499 			break;
500 		case POTION:
501 			strcat(desc, id_table[obj->which_kind].title);
502 			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 			strcat(desc, id_table[obj->which_kind].title);
514 			strcat(desc, item_name);
515 			break;
516 		case ARMOR:
517 			if (obj->identified) {
518 				goto ID;
519 			}
520 			strcpy(desc, id_table[obj->which_kind].title);
521 			break;
522 		case WEAPON:
523 			if (obj->identified) {
524 				goto ID;
525 			}
526 			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 			strcat(desc, item_name);
537 			strcat(desc, "called ");
538 			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 			strcat(desc, item_name);
547 			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 					strcat(desc, more_info);
556 				}
557 			}
558 			strcat(desc, item_name);
559 			strcat(desc, id_table[obj->which_kind].real);
560 			break;
561 		case WAND:
562 			strcat(desc, item_name);
563 			strcat(desc, id_table[obj->which_kind].real);
564 			if (wizard || obj->identified) {
565 				sprintf(more_info, "[%d]", obj->class);
566 				strcat(desc, more_info);
567 			}
568 			break;
569 		case ARMOR:
570 			sprintf(desc, "%s%d ", ((obj->d_enchant >= 0) ? "+" : ""),
571 			obj->d_enchant);
572 			strcat(desc, id_table[obj->which_kind].title);
573 			sprintf(more_info, "[%d] ", get_armor_class(obj));
574 			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 			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 		strcat(desc, "in hand");
596 	} else if (obj->in_use_flags & BEING_WORN) {
597 		strcat(desc, "being worn");
598 	} else if (obj->in_use_flags & ON_LEFT_HAND) {
599 		strcat(desc, "on left hand");
600 	} else if (obj->in_use_flags & ON_RIGHT_HAND) {
601 		strcat(desc, "on right hand");
602 	}
603 }
604 
605 void
606 get_wand_and_ring_materials(void)
607 {
608 	short i, j;
609 	boolean used[WAND_MATERIALS];
610 
611 	for (i = 0; i < WAND_MATERIALS; i++) {
612 		used[i] = 0;
613 	}
614 	for (i = 0; i < WANDS; i++) {
615 		do {
616 			j = get_rand(0, WAND_MATERIALS-1);
617 		} while (used[j]);
618 		used[j] = 1;
619 		strcpy(id_wands[i].title, wand_materials[j]);
620 		is_wood[i] = (j > MAX_METAL);
621 	}
622 	for (i = 0; i < GEMS; i++) {
623 		used[i] = 0;
624 	}
625 	for (i = 0; i < RINGS; i++) {
626 		do {
627 			j = get_rand(0, GEMS-1);
628 		} while (used[j]);
629 		used[j] = 1;
630 		strcpy(id_rings[i].title, gems[j]);
631 	}
632 }
633 
634 void
635 single_inv(short ichar)
636 {
637 	short ch;
638 	char desc[DCOLS];
639 	object *obj;
640 
641 	ch = ichar ? ichar : pack_letter("inventory what?", ALL_OBJECTS);
642 
643 	if (ch == CANCEL) {
644 		return;
645 	}
646 	if (!(obj = get_letter_object(ch))) {
647 		message("no such item.", 0);
648 		return;
649 	}
650 	desc[0] = ch;
651 	desc[1] = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')';
652 	desc[2] = ' ';
653 	desc[3] = 0;
654 	get_desc(obj, desc+3);
655 	message(desc, 0);
656 }
657 
658 struct id *
659 get_id_table(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(NULL);
676 }
677 
678 void
679 inv_armor_weapon(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 void
697 id_type(void)
698 {
699 	const char *id;
700 	int ch;
701 	char buf[DCOLS];
702 
703 	message("what do you want identified?", 0);
704 
705 	ch = rgetchar();
706 
707 	if ((ch >= 'A') && (ch <= 'Z')) {
708 		id = m_names[ch-'A'];
709 	} else if (ch < 32) {
710 		check_message();
711 		return;
712 	} else {
713 		switch(ch) {
714 		case '@':
715 			id = "you";
716 			break;
717 		case '%':
718 			id = "staircase";
719 			break;
720 		case '^':
721 			id = "trap";
722 			break;
723 		case '+':
724 			id = "door";
725 			break;
726 		case '-':
727 		case '|':
728 			id = "wall of a room";
729 			break;
730 		case '.':
731 			id = "floor";
732 			break;
733 		case '#':
734 			id = "passage";
735 			break;
736 		case ' ':
737 			id = "solid rock";
738 			break;
739 		case '=':
740 			id = "ring";
741 			break;
742 		case '?':
743 			id = "scroll";
744 			break;
745 		case '!':
746 			id = "potion";
747 			break;
748 		case '/':
749 			id = "wand or staff";
750 			break;
751 		case ')':
752 			id = "weapon";
753 			break;
754 		case ']':
755 			id = "armor";
756 			break;
757 		case '*':
758 			id = "gold";
759 			break;
760 		case ':':
761 			id = "food";
762 			break;
763 		case ',':
764 			id = "the Amulet of Yendor";
765 			break;
766 		default:
767 			id = "unknown character";
768 			break;
769 		}
770 	}
771 	check_message();
772 	sprintf(buf, "'%c': %s", ch, id);
773 	message(buf, 0);
774 }
775