xref: /dragonfly/games/rogue/inventory.c (revision e1acdbad)
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.3 2003/08/26 23:52:50 drhodus 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 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 
75 	"teak ",
76 	"oak ",
77 	"cherry ",
78 	"birch ",
79 	"pine ",
80 	"cedar ",
81 	"redwood ",
82 	"balsa ",
83 	"ivory ",
84 	"walnut ",
85 	"maple ",
86 	"mahogany ",
87 	"elm ",
88 	"palm ",
89 	"wooden "
90 };
91 
92 const char *const gems[GEMS] = {
93 	"diamond ",
94 	"stibotantalite ",
95 	"lapi-lazuli ",
96 	"ruby ",
97 	"emerald ",
98 	"sapphire ",
99 	"amethyst ",
100 	"quartz ",
101 	"tiger-eye ",
102 	"opal ",
103 	"agate ",
104 	"turquoise ",
105 	"pearl ",
106 	"garnet "
107 };
108 
109 const char *const syllables[MAXSYLLABLES] = {
110 	"blech ",
111 	"foo ",
112 	"barf ",
113 	"rech ",
114 	"bar ",
115 	"blech ",
116 	"quo ",
117 	"bloto ",
118 	"oh ",
119 	"caca ",
120 	"blorp ",
121 	"erp ",
122 	"festr ",
123 	"rot ",
124 	"slie ",
125 	"snorf ",
126 	"iky ",
127 	"yuky ",
128 	"ooze ",
129 	"ah ",
130 	"bahl ",
131 	"zep ",
132 	"druhl ",
133 	"flem ",
134 	"behil ",
135 	"arek ",
136 	"mep ",
137 	"zihr ",
138 	"grit ",
139 	"kona ",
140 	"kini ",
141 	"ichi ",
142 	"tims ",
143 	"ogr ",
144 	"oo ",
145 	"ighr ",
146 	"coph ",
147 	"swerr ",
148 	"mihln ",
149 	"poxi "
150 };
151 
152 #define COMS 48
153 
154 struct id_com_s {
155 	short com_char;
156 	const char *com_desc;
157 };
158 
159 const struct id_com_s com_id_tab[COMS] = {
160 	'?',	"?       prints help",
161 	'r',	"r       read scroll",
162 	'/',	"/       identify object",
163 	'e',	"e       eat food",
164 	'h',	"h       left ",
165 	'w',	"w       wield a weapon",
166 	'j',	"j       down",
167 	'W',	"W       wear armor",
168 	'k',	"k       up",
169 	'T',	"T       take armor off",
170 	'l',	"l       right",
171 	'P',	"P       put on ring",
172 	'y',	"y       up & left",
173 	'R',	"R       remove ring",
174 	'u',	"u       up & right",
175 	'd',	"d       drop object",
176 	'b',	"b       down & left",
177 	'c',	"c       call object",
178 	'n',	"n       down & right",
179 	'\0',	"<SHIFT><dir>: run that way",
180 	')',	")       print current weapon",
181 	'\0',	"<CTRL><dir>: run till adjacent",
182 	']',	"]       print current armor",
183 	'f',	"f<dir>  fight till death or near death",
184 	'=',	"=       print current rings",
185 	't',	"t<dir>  throw something",
186 	'\001',	"^A      print Hp-raise average",
187 	'm',	"m<dir>  move onto without picking up",
188 	'z',	"z<dir>  zap a wand in a direction",
189 	'o',	"o       examine/set options",
190 	'^',	"^<dir>  identify trap type",
191 	'\022',	"^R      redraw screen",
192 	'&',	"&       save screen into 'rogue.screen'",
193 	's',	"s       search for trap/secret door",
194 	'\020',	"^P      repeat last message",
195 	'>',	">       go down a staircase",
196 	'\033',	"^[      cancel command",
197 	'<',	"<       go up a staircase",
198 	'S',	"S       save game",
199 	'.',	".       rest for a turn",
200 	'Q',	"Q       quit",
201 	',',	",       pick something up",
202 	'!',	"!       shell escape",
203 	'i',	"i       inventory",
204 	'F',	"F<dir>  fight till either of you dies",
205 	'I',	"I       inventory single item",
206 	'v',	"v       print version number",
207 	'q',	"q       quaff potion"
208 };
209 
210 extern boolean wizard;
211 extern char *m_names[], *more;
212 
213 inventory(pack, mask)
214 const object *pack;
215 unsigned short mask;
216 {
217 	object *obj;
218 	short i = 0, j, maxlen = 0, n;
219 	char descs[MAX_PACK_COUNT+1][DCOLS];
220 	short row, col;
221 
222 	obj = pack->next_object;
223 
224 	if (!obj) {
225 		message("your pack is empty", 0);
226 		return;
227 	}
228 	while (obj) {
229 		if (obj->what_is & mask) {
230 			descs[i][0] = ' ';
231 			descs[i][1] = obj->ichar;
232 			descs[i][2] = ((obj->what_is & ARMOR) && obj->is_protected)
233 				? '}' : ')';
234 			descs[i][3] = ' ';
235 			get_desc(obj, descs[i]+4);
236 			if ((n = strlen(descs[i])) > maxlen) {
237 				maxlen = n;
238 			}
239 		i++;
240 		}
241 		obj = obj->next_object;
242 	}
243 	(void) strcpy(descs[i++], press_space);
244 	if (maxlen < 27) maxlen = 27;
245 	col = DCOLS - (maxlen + 2);
246 
247 	for (row = 0; ((row < i) && (row < DROWS)); row++) {
248 		if (row > 0) {
249 			for (j = col; j < DCOLS; j++) {
250 				descs[row-1][j-col] = mvinch(row, j);
251 			}
252 			descs[row-1][j-col] = 0;
253 		}
254 		mvaddstr(row, col, descs[row]);
255 		clrtoeol();
256 	}
257 	refresh();
258 	wait_for_ack();
259 
260 	move(0, 0);
261 	clrtoeol();
262 
263 	for (j = 1; ((j < i) && (j < DROWS)); j++) {
264 		mvaddstr(j, col, descs[j-1]);
265 	}
266 }
267 
268 id_com()
269 {
270 	int ch = 0;
271 	short i, j, k;
272 
273 	while (ch != CANCEL) {
274 		check_message();
275 		message("Character you want help for (* for all):", 0);
276 
277 		refresh();
278 		ch = getchar();
279 
280 		switch(ch) {
281 		case LIST:
282 			{
283 				char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS];
284 				short rows = (((COMS / 2) + (COMS % 2)) + 1);
285 				boolean need_two_screens;
286 
287 				if (rows > LINES) {
288 					need_two_screens = 1;
289 					rows = LINES;
290 				}
291 				k = 0;
292 
293 				for (i = 0; i < rows; i++) {
294 					for (j = 0; j < DCOLS; j++) {
295 						save[i][j] = mvinch(i, j);
296 					}
297 				}
298 MORE:
299 				for (i = 0; i < rows; i++) {
300 					move(i, 0);
301 					clrtoeol();
302 				}
303 				for (i = 0; i < (rows-1); i++) {
304 					if (i < (LINES-1)) {
305 						if (((i + i) < COMS) && ((i+i+k) < COMS)) {
306 							mvaddstr(i, 0, com_id_tab[i+i+k].com_desc);
307 						}
308 						if (((i + i + 1) < COMS) && ((i+i+k+1) < COMS)) {
309 							mvaddstr(i, (DCOLS/2),
310 										com_id_tab[i+i+k+1].com_desc);
311 						}
312 					}
313 				}
314 				mvaddstr(rows - 1, 0, need_two_screens ? more : press_space);
315 				refresh();
316 				wait_for_ack();
317 
318 				if (need_two_screens) {
319 					k += ((rows-1) * 2);
320 					need_two_screens = 0;
321 					goto MORE;
322 				}
323 				for (i = 0; i < rows; i++) {
324 					move(i, 0);
325 					for (j = 0; j < DCOLS; j++) {
326 						addch(save[i][j]);
327 					}
328 				}
329 			}
330 			break;
331 		default:
332 			if (!pr_com_id(ch)) {
333 				if (!pr_motion_char(ch)) {
334 					check_message();
335 					message("unknown character", 0);
336 				}
337 			}
338 			ch = CANCEL;
339 			break;
340 		}
341 	}
342 }
343 
344 pr_com_id(ch)
345 int ch;
346 {
347 	int i;
348 
349 	if (!get_com_id(&i, ch)) {
350 		return(0);
351 	}
352 	check_message();
353 	message(com_id_tab[i].com_desc, 0);
354 	return(1);
355 }
356 
357 get_com_id(index, ch)
358 int *index;
359 short ch;
360 {
361 	short i;
362 
363 	for (i = 0; i < COMS; i++) {
364 		if (com_id_tab[i].com_char == ch) {
365 			*index = i;
366 			return(1);
367 		}
368 	}
369 	return(0);
370 }
371 
372 pr_motion_char(ch)
373 int ch;
374 {
375 	if (	(ch == 'J') ||
376 			(ch == 'K') ||
377 			(ch == 'L') ||
378 			(ch == 'H') ||
379 			(ch == 'Y') ||
380 			(ch == 'U') ||
381 			(ch == 'N') ||
382 			(ch == 'B') ||
383 			(ch == '\012') ||
384 			(ch == '\013') ||
385 			(ch == '\010') ||
386 			(ch == '\014') ||
387 			(ch == '\025') ||
388 			(ch == '\031') ||
389 			(ch == '\016') ||
390 			(ch == '\002')) {
391 		char until[18], buf[DCOLS];
392 		int n;
393 
394 		if (ch <= '\031') {
395 			ch += 96;
396 			(void) strcpy(until, "until adjascent");
397 		} else {
398 			ch += 32;
399 			until[0] = '\0';
400 		}
401 		(void) get_com_id(&n, ch);
402 		sprintf(buf, "run %s %s", com_id_tab[n].com_desc + 8, until);
403 		check_message();
404 		message(buf, 0);
405 		return(1);
406 	} else {
407 		return(0);
408 	}
409 }
410 
411 mix_colors()
412 {
413 	short i, j, k;
414 	char *t[MAX_ID_TITLE_LEN];
415 
416 	for (i = 0; i <= 32; i++) {
417 		j = get_rand(0, (POTIONS - 1));
418 		k = get_rand(0, (POTIONS - 1));
419 		memcpy(t, id_potions[j].title, MAX_ID_TITLE_LEN);
420 		memcpy(id_potions[j].title, id_potions[k].title, MAX_ID_TITLE_LEN);
421 	}
422 }
423 
424 make_scroll_titles()
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 		(void) strcpy(id_scrolls[i].title, "'");
432 
433 		for (j = 0; j < sylls; j++) {
434 			s = get_rand(1, (MAXSYLLABLES-1));
435 			(void) strcat(id_scrolls[i].title, syllables[s]);
436 		}
437 		n = strlen(id_scrolls[i].title);
438 		(void) strcpy(id_scrolls[i].title+(n-1), "' ");
439 	}
440 }
441 
442 get_desc(obj, desc)
443 const object *obj;
444 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 		(void) 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 			(void) 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 				(void) strcpy(desc, "some ");
475 			}
476 		} else {
477 			(void) strcpy(desc, "a ");
478 		}
479 		(void) 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 			(void) strcat(desc, item_name);
497 			(void) strcat(desc, "entitled: ");
498 			(void) strcat(desc, id_table[obj->which_kind].title);
499 			break;
500 		case POTION:
501 			(void) strcat(desc, id_table[obj->which_kind].title);
502 			(void) 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 			(void) strcat(desc, id_table[obj->which_kind].title);
514 			(void) strcat(desc, item_name);
515 			break;
516 		case ARMOR:
517 			if (obj->identified) {
518 				goto ID;
519 			}
520 			(void) strcpy(desc, id_table[obj->which_kind].title);
521 			break;
522 		case WEAPON:
523 			if (obj->identified) {
524 				goto ID;
525 			}
526 			(void) 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 			(void) strcat(desc, item_name);
537 			(void) strcat(desc, "called ");
538 			(void) 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 			(void) strcat(desc, item_name);
547 			(void) 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 					(void) strcat(desc, more_info);
556 				}
557 			}
558 			(void) strcat(desc, item_name);
559 			(void) strcat(desc, id_table[obj->which_kind].real);
560 			break;
561 		case WAND:
562 			(void) strcat(desc, item_name);
563 			(void) strcat(desc, id_table[obj->which_kind].real);
564 			if (wizard || obj->identified) {
565 				sprintf(more_info, "[%d]", obj->class);
566 				(void) strcat(desc, more_info);
567 			}
568 			break;
569 		case ARMOR:
570 			sprintf(desc, "%s%d ", ((obj->d_enchant >= 0) ? "+" : ""),
571 			obj->d_enchant);
572 			(void) strcat(desc, id_table[obj->which_kind].title);
573 			sprintf(more_info, "[%d] ", get_armor_class(obj));
574 			(void) 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 			(void) 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 		(void) strcat(desc, "in hand");
596 	} else if (obj->in_use_flags & BEING_WORN) {
597 		(void) strcat(desc, "being worn");
598 	} else if (obj->in_use_flags & ON_LEFT_HAND) {
599 		(void) strcat(desc, "on left hand");
600 	} else if (obj->in_use_flags & ON_RIGHT_HAND) {
601 		(void) strcat(desc, "on right hand");
602 	}
603 }
604 
605 get_wand_and_ring_materials()
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 		(void) 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 		(void) strcpy(id_rings[i].title, gems[j]);
630 	}
631 }
632 
633 single_inv(ichar)
634 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(obj)
659 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((struct id *) 0);
676 }
677 
678 inv_armor_weapon(is_weapon)
679 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 id_type()
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