xref: /dragonfly/games/rogue/inventory.c (revision abf903a5)
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 }
772