xref: /dragonfly/games/rogue/inventory.c (revision bcb3e04d)
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  * $DragonFly: src/games/rogue/inventory.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
35  */
36 
37 /*
38  * inventory.c
39  *
40  * This source herein may be modified and/or distributed by anybody who
41  * so desires, with the following restrictions:
42  *    1.)  No portion of this notice shall be removed.
43  *    2.)  Credit shall not be taken for the creation of this source.
44  *    3.)  This code is not to be traded, sold, or used for personal
45  *         gain or profit.
46  *
47  */
48 
49 #include "rogue.h"
50 
51 static boolean	pr_com_id(int);
52 static boolean	get_com_id(int *, short);
53 static boolean	pr_motion_char(int);
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 	"teak ",
75 	"oak ",
76 	"cherry ",
77 	"birch ",
78 	"pine ",
79 	"cedar ",
80 	"redwood ",
81 	"balsa ",
82 	"ivory ",
83 	"walnut ",
84 	"maple ",
85 	"mahogany ",
86 	"elm ",
87 	"palm ",
88 	"wooden "
89 };
90 
91 const char *const gems[GEMS] = {
92 	"diamond ",
93 	"stibotantalite ",
94 	"lapi-lazuli ",
95 	"ruby ",
96 	"emerald ",
97 	"sapphire ",
98 	"amethyst ",
99 	"quartz ",
100 	"tiger-eye ",
101 	"opal ",
102 	"agate ",
103 	"turquoise ",
104 	"pearl ",
105 	"garnet "
106 };
107 
108 const char *const syllables[MAXSYLLABLES] = {
109 	"blech ",
110 	"foo ",
111 	"barf ",
112 	"rech ",
113 	"bar ",
114 	"blech ",
115 	"quo ",
116 	"bloto ",
117 	"oh ",
118 	"caca ",
119 	"blorp ",
120 	"erp ",
121 	"festr ",
122 	"rot ",
123 	"slie ",
124 	"snorf ",
125 	"iky ",
126 	"yuky ",
127 	"ooze ",
128 	"ah ",
129 	"bahl ",
130 	"zep ",
131 	"druhl ",
132 	"flem ",
133 	"behil ",
134 	"arek ",
135 	"mep ",
136 	"zihr ",
137 	"grit ",
138 	"kona ",
139 	"kini ",
140 	"ichi ",
141 	"tims ",
142 	"ogr ",
143 	"oo ",
144 	"ighr ",
145 	"coph ",
146 	"swerr ",
147 	"mihln ",
148 	"poxi "
149 };
150 
151 #define COMS 48
152 
153 struct id_com_s {
154 	short com_char;
155 	const char *com_desc;
156 };
157 
158 const struct id_com_s com_id_tab[COMS] = {
159 	{ '?',		"?       prints help" },
160 	{ 'r',		"r       read scroll" },
161 	{ '/',		"/     	 identify object" },
162 	{ 'e',		"e       eat food" },
163 	{ 'h',		"h       left " },
164 	{ 'w',		"w       wield a weapon" },
165 	{ 'j',		"j       down" },
166 	{ 'W',		"W       wear armor" },
167 	{ 'k',		"k       up" },
168 	{ 'T',		"T       take armor off" },
169 	{ 'l',		"l       right" },
170 	{ 'P',		"P       put on ring" },
171 	{ 'y',		"y       up & left" },
172 	{ 'R',		"R       remove ring" },
173 	{ 'u',		"u       up & right" },
174 	{ 'd',		"d       drop object" },
175 	{ 'b',		"b       down & left" },
176 	{ 'c',		"c       call object" },
177 	{ 'n',		"n       down & right" },
178 	{ '\0',		"<SHIFT><dir>: run that way" },
179 	{ ')',		")       print current weapon" },
180 	{ '\0',		"<CTRL><dir>: run till adjacent" },
181 	{ ']',		"]       print current armor" },
182 	{ 'f',		"f<dir>  fight till death or near death" },
183 	{ '=',		"=       print current rings" },
184 	{ 't',		"t<dir>  throw something" },
185 	{ '\001',	"^A      print Hp-raise average" },
186 	{ 'm',		"m<dir>  move onto without picking up" },
187 	{ 'z',		"z<dir>  zap a wand in a direction" },
188 	{ 'o',		"o       examine/set options" },
189 	{ '^',		"^<dir>  identify trap type" },
190 	{ '\022',	"^R      redraw screen" },
191 	{ '&',		"&       save screen into 'rogue.screen'" },
192 	{ 's',		"s       search for trap/secret door" },
193 	{ '\020',	"^P      repeat last message" },
194 	{ '>',		">       go down a staircase" },
195 	{ '\033',	"^[      cancel command" },
196 	{ '<',		"<       go up a staircase" },
197 	{ 'S',		"S       save game" },
198 	{ '.',		".       rest for a turn" },
199 	{ 'Q',		"Q       quit" },
200 	{ ',',		",       pick something up" },
201 	{ '!',		"!       shell escape" },
202 	{ 'i',		"i       inventory" },
203 	{ 'F',		"F<dir>  fight till either of you dies" },
204 	{ 'I',		"I       inventory single item" },
205 	{ 'v',		"v       print version number" },
206 	{ 'q',		"q       quaff potion" }
207 };
208 
209 extern boolean wizard;
210 extern char *m_names[], *more;
211 
212 void
213 inventory(const object *pack, unsigned short mask)
214 {
215 	object *obj;
216 	short i = 0, j, maxlen = 0, n;
217 	char descs[MAX_PACK_COUNT+1][DCOLS];
218 	short row, col;
219 
220 	obj = pack->next_object;
221 
222 	if (!obj) {
223 		message("your pack is empty", 0);
224 		return;
225 	}
226 	while (obj) {
227 		if (obj->what_is & mask) {
228 			descs[i][0] = ' ';
229 			descs[i][1] = obj->ichar;
230 			descs[i][2] = ((obj->what_is & ARMOR) && obj->is_protected)
231 				? '}' : ')';
232 			descs[i][3] = ' ';
233 			get_desc(obj, descs[i]+4);
234 			if ((n = strlen(descs[i])) > maxlen) {
235 				maxlen = n;
236 			}
237 		i++;
238 		}
239 		obj = obj->next_object;
240 	}
241 	strcpy(descs[i++], press_space);
242 	if (maxlen < 27) maxlen = 27;
243 	col = DCOLS - (maxlen + 2);
244 
245 	for (row = 0; ((row < i) && (row < DROWS)); row++) {
246 		if (row > 0) {
247 			for (j = col; j < DCOLS; j++) {
248 				descs[row-1][j-col] = mvinch(row, j);
249 			}
250 			descs[row-1][j-col] = 0;
251 		}
252 		mvaddstr(row, col, descs[row]);
253 		clrtoeol();
254 	}
255 	refresh();
256 	wait_for_ack();
257 
258 	move(0, 0);
259 	clrtoeol();
260 
261 	for (j = 1; ((j < i) && (j < DROWS)); j++) {
262 		mvaddstr(j, col, descs[j-1]);
263 	}
264 }
265 
266 void
267 id_com(void)
268 {
269 	int ch = 0;
270 	short i, j, k;
271 
272 	while (ch != CANCEL) {
273 		check_message();
274 		message("Character you want help for (* for all):", 0);
275 
276 		refresh();
277 		ch = getchar();
278 
279 		switch(ch) {
280 		case LIST:
281 			{
282 				char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS];
283 				short rows = (((COMS / 2) + (COMS % 2)) + 1);
284 				boolean need_two_screens = FALSE;
285 
286 				if (rows > LINES) {
287 					need_two_screens = 1;
288 					rows = LINES;
289 				}
290 				k = 0;
291 
292 				for (i = 0; i < rows; i++) {
293 					for (j = 0; j < DCOLS; j++) {
294 						save[i][j] = mvinch(i, j);
295 					}
296 				}
297 MORE:
298 				for (i = 0; i < rows; i++) {
299 					move(i, 0);
300 					clrtoeol();
301 				}
302 				for (i = 0; i < (rows-1); i++) {
303 					if (i < (LINES-1)) {
304 						if (((i + i) < COMS) && ((i+i+k) < COMS)) {
305 							mvaddstr(i, 0, com_id_tab[i+i+k].com_desc);
306 						}
307 						if (((i + i + 1) < COMS) && ((i+i+k+1) < COMS)) {
308 							mvaddstr(i, (DCOLS/2),
309 										com_id_tab[i+i+k+1].com_desc);
310 						}
311 					}
312 				}
313 				mvaddstr(rows - 1, 0, need_two_screens ? more : press_space);
314 				refresh();
315 				wait_for_ack();
316 
317 				if (need_two_screens) {
318 					k += ((rows-1) * 2);
319 					need_two_screens = 0;
320 					goto MORE;
321 				}
322 				for (i = 0; i < rows; i++) {
323 					move(i, 0);
324 					for (j = 0; j < DCOLS; j++) {
325 						addch(save[i][j]);
326 					}
327 				}
328 			}
329 			break;
330 		default:
331 			if (!pr_com_id(ch)) {
332 				if (!pr_motion_char(ch)) {
333 					check_message();
334 					message("unknown character", 0);
335 				}
336 			}
337 			ch = CANCEL;
338 			break;
339 		}
340 	}
341 }
342 
343 static boolean
344 pr_com_id(int ch)
345 {
346 	int i;
347 
348 	if (!get_com_id(&i, ch)) {
349 		return(0);
350 	}
351 	check_message();
352 	message(com_id_tab[i].com_desc, 0);
353 	return(1);
354 }
355 
356 static boolean
357 get_com_id(int *idx, short ch)
358 {
359 	short i;
360 
361 	for (i = 0; i < COMS; i++) {
362 		if (com_id_tab[i].com_char == ch) {
363 			*idx = i;
364 			return(1);
365 		}
366 	}
367 	return(0);
368 }
369 
370 static boolean
371 pr_motion_char(int ch)
372 {
373 	if (	(ch == 'J') ||
374 			(ch == 'K') ||
375 			(ch == 'L') ||
376 			(ch == 'H') ||
377 			(ch == 'Y') ||
378 			(ch == 'U') ||
379 			(ch == 'N') ||
380 			(ch == 'B') ||
381 			(ch == '\012') ||
382 			(ch == '\013') ||
383 			(ch == '\010') ||
384 			(ch == '\014') ||
385 			(ch == '\025') ||
386 			(ch == '\031') ||
387 			(ch == '\016') ||
388 			(ch == '\002')) {
389 		char until[18], buf[DCOLS];
390 		int n;
391 
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