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