xref: /original-bsd/games/rogue/object.c (revision ad93c43e)
1 /*
2  * object.c
3  *
4  * This source herein may be modified and/or distributed by anybody who
5  * so desires, with the following restrictions:
6  *    1.)  No portion of this notice shall be removed.
7  *    2.)  Credit shall not be taken for the creation of this source.
8  *    3.)  This code is not to be traded, sold, or used for personal
9  *         gain or profit.
10  *
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)object.c	5.1 (Berkeley) 11/25/87";
15 #endif /* not lint */
16 
17 #include "rogue.h"
18 
19 object level_objects;
20 unsigned short dungeon[DROWS][DCOLS];
21 short foods = 0;
22 object *free_list = (object *) 0;
23 char *fruit = (char *) 0;
24 
25 fighter rogue = {
26 	INIT_AW,	/* armor, weapon */
27 	INIT_RINGS,	/* rings */
28 	INIT_HP,	/* Hp current,max */
29 	INIT_STR,	/* Str current,max */
30 	INIT_PACK,	/* pack */
31 	INIT_GOLD,	/* gold */
32 	INIT_EXP,	/* exp level,points */
33 	0, 0,		/* row, col */
34 	INIT_CHAR,	/* char */
35 	INIT_MOVES	/* moves */
36 };
37 
38 struct id id_potions[POTIONS] = {
39 {100, "blue \0                           ", "of increase strength ", 0},
40 {250, "red \0                            ", "of restore strength ", 0},
41 {100, "green \0                          ", "of healing ", 0},
42 {200, "grey \0                           ", "of extra healing ", 0},
43  {10, "brown \0                          ", "of poison ", 0},
44 {300, "clear \0                          ", "of raise level ", 0},
45  {10, "pink \0                           ", "of blindness ", 0},
46  {25, "white \0                          ", "of hallucination ", 0},
47 {100, "purple \0                         ", "of detect monster ", 0},
48 {100, "black \0                          ", "of detect things ", 0},
49  {10, "yellow \0                         ", "of confusion ", 0},
50  {80, "plaid \0                          ", "of levitation ", 0},
51 {150, "burgundy \0                       ", "of haste self ", 0},
52 {145, "beige \0                          ", "of see invisible ", 0}
53 };
54 
55 struct id id_scrolls[SCROLS] = {
56 {505, "                                   ", "of protect armor ", 0},
57 {200, "                                   ", "of hold monster ", 0},
58 {235, "                                   ", "of enchant weapon ", 0},
59 {235, "                                   ", "of enchant armor ", 0},
60 {175, "                                   ", "of identify ", 0},
61 {190, "                                   ", "of teleportation ", 0},
62  {25, "                                   ", "of sleep ", 0},
63 {610, "                                   ", "of scare monster ", 0},
64 {210, "                                   ", "of remove curse ", 0},
65  {80, "                                   ", "of create monster ",0},
66  {25, "                                   ", "of aggravate monster ",0},
67 {180, "                                   ", "of magic mapping ", 0},
68  {90, "                                   ", "of confuse monster ", 0}
69 };
70 
71 struct id id_weapons[WEAPONS] = {
72 	{150, "short bow ", "", 0},
73 	  {8, "darts ", "", 0},
74 	 {15, "arrows ", "", 0},
75 	 {27, "daggers ", "", 0},
76 	 {35, "shurikens ", "", 0},
77 	{360, "mace ", "", 0},
78 	{470, "long sword ", "", 0},
79 	{580, "two-handed sword ", "", 0}
80 };
81 
82 struct id id_armors[ARMORS] = {
83 	{300, "leather armor ", "", (UNIDENTIFIED)},
84 	{300, "ring mail ", "", (UNIDENTIFIED)},
85 	{400, "scale mail ", "", (UNIDENTIFIED)},
86 	{500, "chain mail ", "", (UNIDENTIFIED)},
87 	{600, "banded mail ", "", (UNIDENTIFIED)},
88 	{600, "splint mail ", "", (UNIDENTIFIED)},
89 	{700, "plate mail ", "", (UNIDENTIFIED)}
90 };
91 
92 struct id id_wands[WANDS] = {
93 	 {25, "                                 ", "of teleport away ",0},
94 	 {50, "                                 ", "of slow monster ", 0},
95 	  {8, "                                 ", "of invisibility ",0},
96 	 {55, "                                 ", "of polymorph ",0},
97 	  {2, "                                 ", "of haste monster ",0},
98 	 {20, "                                 ", "of magic missile ",0},
99 	 {20, "                                 ", "of cancellation ",0},
100 	  {0, "                                 ", "of do nothing ",0},
101 	 {35, "                                 ", "of drain life ",0},
102 	 {20, "                                 ", "of cold ",0},
103 	 {20, "                                 ", "of fire ",0}
104 };
105 
106 struct id id_rings[RINGS] = {
107 	 {250, "                                 ", "of stealth ",0},
108 	 {100, "                                 ", "of teleportation ", 0},
109 	 {255, "                                 ", "of regeneration ",0},
110 	 {295, "                                 ", "of slow digestion ",0},
111 	 {200, "                                 ", "of add strength ",0},
112 	 {250, "                                 ", "of sustain strength ",0},
113 	 {250, "                                 ", "of dexterity ",0},
114 	  {25, "                                 ", "of adornment ",0},
115 	 {300, "                                 ", "of see invisible ",0},
116 	 {290, "                                 ", "of maintain armor ",0},
117 	 {270, "                                 ", "of searching ",0},
118 };
119 
120 extern short cur_level, max_level;
121 extern short party_room;
122 extern char *error_file;
123 extern boolean is_wood[];
124 
125 put_objects()
126 {
127 	short i, n;
128 	object *obj;
129 
130 	if (cur_level < max_level) {
131 		return;
132 	}
133 	n = coin_toss() ? get_rand(2, 4) : get_rand(3, 5);
134 	while (rand_percent(33)) {
135 		n++;
136 	}
137 	if (party_room != NO_ROOM) {
138 		make_party();
139 	}
140 	for (i = 0; i < n; i++) {
141 		obj = gr_object();
142 		rand_place(obj);
143 	}
144 	put_gold();
145 }
146 
147 put_gold()
148 {
149 	short i, j;
150 	short row,col;
151 	boolean is_maze, is_room;
152 
153 	for (i = 0; i < MAXROOMS; i++) {
154 		is_maze = (rooms[i].is_room & R_MAZE) ? 1 : 0;
155 		is_room = (rooms[i].is_room & R_ROOM) ? 1 : 0;
156 
157 		if (!(is_room || is_maze)) {
158 			continue;
159 		}
160 		if (is_maze || rand_percent(GOLD_PERCENT)) {
161 			for (j = 0; j < 50; j++) {
162 				row = get_rand(rooms[i].top_row+1,
163 				rooms[i].bottom_row-1);
164 				col = get_rand(rooms[i].left_col+1,
165 				rooms[i].right_col-1);
166 				if ((dungeon[row][col] == FLOOR) ||
167 					(dungeon[row][col] == TUNNEL)) {
168 					plant_gold(row, col, is_maze);
169 					break;
170 				}
171 			}
172 		}
173 	}
174 }
175 
176 plant_gold(row, col, is_maze)
177 short row, col;
178 boolean is_maze;
179 {
180 	object *obj;
181 
182 	obj = alloc_object();
183 	obj->row = row; obj->col = col;
184 	obj->what_is = GOLD;
185 	obj->quantity = get_rand((2 * cur_level), (16 * cur_level));
186 	if (is_maze) {
187 		obj->quantity += obj->quantity / 2;
188 	}
189 	dungeon[row][col] |= OBJECT;
190 	(void) add_to_pack(obj, &level_objects, 0);
191 }
192 
193 place_at(obj, row, col)
194 object *obj;
195 {
196 	obj->row = row;
197 	obj->col = col;
198 	dungeon[row][col] |= OBJECT;
199 	(void) add_to_pack(obj, &level_objects, 0);
200 }
201 
202 object *
203 object_at(pack, row, col)
204 register object *pack;
205 short row, col;
206 {
207 	object *obj = (object *) 0;
208 
209 	if (dungeon[row][col] & (MONSTER | OBJECT)) {
210 		obj = pack->next_object;
211 
212 		while (obj && ((obj->row != row) || (obj->col != col))) {
213 			obj = obj->next_object;
214 		}
215 		if (!obj) {
216 			message("object_at(): inconsistent", 1);
217 		}
218 	}
219 	return(obj);
220 }
221 
222 object *
223 get_letter_object(ch)
224 {
225 	object *obj;
226 
227 	obj = rogue.pack.next_object;
228 
229 	while (obj && (obj->ichar != ch)) {
230 		obj = obj->next_object;
231 	}
232 	return(obj);
233 }
234 
235 free_stuff(objlist)
236 object *objlist;
237 {
238 	object *obj;
239 
240 	while (objlist->next_object) {
241 		obj = objlist->next_object;
242 		objlist->next_object =
243 			objlist->next_object->next_object;
244 		free_object(obj);
245 	}
246 }
247 
248 char *
249 name_of(obj)
250 object *obj;
251 {
252 	char *retstring;
253 
254 	switch(obj->what_is) {
255 	case SCROL:
256 		retstring = obj->quantity > 1 ? "scrolls " : "scroll ";
257 		break;
258 	case POTION:
259 		retstring = obj->quantity > 1 ? "potions " : "potion ";
260 		break;
261 	case FOOD:
262 		if (obj->which_kind == RATION) {
263 			retstring = "food ";
264 		} else {
265 			retstring = fruit;
266 		}
267 		break;
268 	case WAND:
269 		retstring = is_wood[obj->which_kind] ? "staff " : "wand ";
270 		break;
271 	case WEAPON:
272 		switch(obj->which_kind) {
273 		case DART:
274 			retstring=obj->quantity > 1 ? "darts " : "dart ";
275 			break;
276 		case ARROW:
277 			retstring=obj->quantity > 1 ? "arrows " : "arrow ";
278 			break;
279 		case DAGGER:
280 			retstring=obj->quantity > 1 ? "daggers " : "dagger ";
281 			break;
282 		case SHURIKEN:
283 			retstring=obj->quantity > 1?"shurikens ":"shuriken ";
284 			break;
285 		default:
286 			retstring = id_weapons[obj->which_kind].title;
287 		}
288 		break;
289 	case ARMOR:
290 		retstring = "armor ";
291 		break;
292 	case RING:
293 			retstring = "ring ";
294 		break;
295 	case AMULET:
296 		retstring = "amulet ";
297 		break;
298 	default:
299 		retstring = "unknown ";
300 		break;
301 	}
302 	return(retstring);
303 }
304 
305 object *
306 gr_object()
307 {
308 	object *obj;
309 
310 	obj = alloc_object();
311 
312 	if (foods < (cur_level / 3)) {
313 		obj->what_is = FOOD;
314 		foods++;
315 	} else {
316 		obj->what_is = gr_what_is();
317 	}
318 	switch(obj->what_is) {
319 	case SCROL:
320 		gr_scroll(obj);
321 		break;
322 	case POTION:
323 		gr_potion(obj);
324 		break;
325 	case WEAPON:
326 		gr_weapon(obj, 1);
327 		break;
328 	case ARMOR:
329 		gr_armor(obj);
330 		break;
331 	case WAND:
332 		gr_wand(obj);
333 		break;
334 	case FOOD:
335 		get_food(obj, 0);
336 		break;
337 	case RING:
338 		gr_ring(obj, 1);
339 		break;
340 	}
341 	return(obj);
342 }
343 
344 unsigned short
345 gr_what_is()
346 {
347 	short percent;
348 	unsigned short what_is;
349 
350 	percent = get_rand(1, 91);
351 
352 	if (percent <= 30) {
353 		what_is = SCROL;
354 	} else if (percent <= 60) {
355 		what_is = POTION;
356 	} else if (percent <= 64) {
357 		what_is = WAND;
358 	} else if (percent <= 74) {
359 		what_is = WEAPON;
360 	} else if (percent <= 83) {
361 		what_is = ARMOR;
362 	} else if (percent <= 88) {
363 		what_is = FOOD;
364 	} else {
365 		what_is = RING;
366 	}
367 	return(what_is);
368 }
369 
370 gr_scroll(obj)
371 object *obj;
372 {
373 	short percent;
374 
375 	percent = get_rand(0, 91);
376 
377 	obj->what_is = SCROL;
378 
379 	if (percent <= 5) {
380 		obj->which_kind = PROTECT_ARMOR;
381 	} else if (percent <= 10) {
382 		obj->which_kind = HOLD_MONSTER;
383 	} else if (percent <= 20) {
384 		obj->which_kind = CREATE_MONSTER;
385 	} else if (percent <= 35) {
386 		obj->which_kind = IDENTIFY;
387 	} else if (percent <= 43) {
388 		obj->which_kind = TELEPORT;
389 	} else if (percent <= 50) {
390 		obj->which_kind = SLEEP;
391 	} else if (percent <= 55) {
392 		obj->which_kind = SCARE_MONSTER;
393 	} else if (percent <= 64) {
394 		obj->which_kind = REMOVE_CURSE;
395 	} else if (percent <= 69) {
396 		obj->which_kind = ENCH_ARMOR;
397 	} else if (percent <= 74) {
398 		obj->which_kind = ENCH_WEAPON;
399 	} else if (percent <= 80) {
400 		obj->which_kind = AGGRAVATE_MONSTER;
401 	} else if (percent <= 86) {
402 		obj->which_kind = CON_MON;
403 	} else {
404 		obj->which_kind = MAGIC_MAPPING;
405 	}
406 }
407 
408 gr_potion(obj)
409 object *obj;
410 {
411 	short percent;
412 
413 	percent = get_rand(1, 118);
414 
415 	obj->what_is = POTION;
416 
417 	if (percent <= 5) {
418 		obj->which_kind = RAISE_LEVEL;
419 	} else if (percent <= 15) {
420 		obj->which_kind = DETECT_OBJECTS;
421 	} else if (percent <= 25) {
422 		obj->which_kind = DETECT_MONSTER;
423 	} else if (percent <= 35) {
424 		obj->which_kind = INCREASE_STRENGTH;
425 	} else if (percent <= 45) {
426 		obj->which_kind = RESTORE_STRENGTH;
427 	} else if (percent <= 55) {
428 		obj->which_kind = HEALING;
429 	} else if (percent <= 65) {
430 		obj->which_kind = EXTRA_HEALING;
431 	} else if (percent <= 75) {
432 		obj->which_kind = BLINDNESS;
433 	} else if (percent <= 85) {
434 		obj->which_kind = HALLUCINATION;
435 	} else if (percent <= 95) {
436 		obj->which_kind = CONFUSION;
437 	} else if (percent <= 105) {
438 		obj->which_kind = POISON;
439 	} else if (percent <= 110) {
440 		obj->which_kind = LEVITATION;
441 	} else if (percent <= 114) {
442 		obj->which_kind = HASTE_SELF;
443 	} else {
444 		obj->which_kind = SEE_INVISIBLE;
445 	}
446 }
447 
448 gr_weapon(obj, assign_wk)
449 object *obj;
450 int assign_wk;
451 {
452 	short percent;
453 	short i;
454 	short blessing, increment;
455 
456 	obj->what_is = WEAPON;
457 	if (assign_wk) {
458 		obj->which_kind = get_rand(0, (WEAPONS - 1));
459 	}
460 	if ((obj->which_kind == ARROW) || (obj->which_kind == DAGGER) ||
461 		(obj->which_kind == SHURIKEN) | (obj->which_kind == DART)) {
462 		obj->quantity = get_rand(3, 15);
463 		obj->quiver = get_rand(0, 126);
464 	} else {
465 		obj->quantity = 1;
466 	}
467 	obj->hit_enchant = obj->d_enchant = 0;
468 
469 	percent = get_rand(1, 96);
470 	blessing = get_rand(1, 3);
471 
472 	if (percent <= 16) {
473 		increment = 1;
474 	} else if (percent <= 32) {
475 		increment = -1;
476 		obj->is_cursed = 1;
477 	}
478 	if (percent <= 32) {
479 		for (i = 0; i < blessing; i++) {
480 			if (coin_toss()) {
481 				obj->hit_enchant += increment;
482 			} else {
483 				obj->d_enchant += increment;
484 			}
485 		}
486 	}
487 	switch(obj->which_kind) {
488 	case BOW:
489 	case DART:
490 		obj->damage = "1d1";
491 		break;
492 	case ARROW:
493 		obj->damage = "1d2";
494 		break;
495 	case DAGGER:
496 		obj->damage = "1d3";
497 		break;
498 	case SHURIKEN:
499 		obj->damage = "1d4";
500 		break;
501 	case MACE:
502 		obj->damage = "2d3";
503 		break;
504 	case LONG_SWORD:
505 		obj->damage = "3d4";
506 		break;
507 	case TWO_HANDED_SWORD:
508 		obj->damage = "4d5";
509 		break;
510 	}
511 }
512 
513 gr_armor(obj)
514 object *obj;
515 {
516 	short percent;
517 	short blessing;
518 
519 	obj->what_is = ARMOR;
520 	obj->which_kind = get_rand(0, (ARMORS - 1));
521 	obj->class = obj->which_kind + 2;
522 	if ((obj->which_kind == PLATE) || (obj->which_kind == SPLINT)) {
523 		obj->class--;
524 	}
525 	obj->is_protected = 0;
526 	obj->d_enchant = 0;
527 
528 	percent = get_rand(1, 100);
529 	blessing = get_rand(1, 3);
530 
531 	if (percent <= 16) {
532 		obj->is_cursed = 1;
533 		obj->d_enchant -= blessing;
534 	} else if (percent <= 33) {
535 		obj->d_enchant += blessing;
536 	}
537 }
538 
539 gr_wand(obj)
540 object *obj;
541 {
542 	obj->what_is = WAND;
543 	obj->which_kind = get_rand(0, (WANDS - 1));
544 	obj->class = get_rand(3, 7);
545 }
546 
547 get_food(obj, force_ration)
548 object *obj;
549 boolean force_ration;
550 {
551 	obj->what_is = FOOD;
552 
553 	if (force_ration || rand_percent(80)) {
554 		obj->which_kind = RATION;
555 	} else {
556 		obj->which_kind = FRUIT;
557 	}
558 }
559 
560 put_stairs()
561 {
562 	short row, col;
563 
564 	gr_row_col(&row, &col, (FLOOR | TUNNEL));
565 	dungeon[row][col] |= STAIRS;
566 }
567 
568 get_armor_class(obj)
569 object *obj;
570 {
571 	if (obj) {
572 		return(obj->class + obj->d_enchant);
573 	}
574 	return(0);
575 }
576 
577 object *
578 alloc_object()
579 {
580 	object *obj;
581 
582 	if (free_list) {
583 		obj = free_list;
584 		free_list = free_list->next_object;
585 	} else if (!(obj = (object *) md_malloc(sizeof(object)))) {
586 			message("cannot allocate object, saving game", 0);
587 			save_into_file(error_file);
588 	}
589 	obj->quantity = 1;
590 	obj->ichar = 'L';
591 	obj->picked_up = obj->is_cursed = 0;
592 	obj->in_use_flags = NOT_USED;
593 	obj->identified = UNIDENTIFIED;
594 	obj->damage = "1d1";
595 	return(obj);
596 }
597 
598 free_object(obj)
599 object *obj;
600 {
601 	obj->next_object = free_list;
602 	free_list = obj;
603 }
604 
605 make_party()
606 {
607 	short n;
608 
609 	party_room = gr_room();
610 
611 	n = rand_percent(99) ? party_objects(party_room) : 11;
612 	if (rand_percent(99)) {
613 		party_monsters(party_room, n);
614 	}
615 }
616 
617 show_objects()
618 {
619 	object *obj;
620 	short mc, rc, row, col;
621 	object *monster;
622 
623 	obj = level_objects.next_object;
624 
625 	while (obj) {
626 		row = obj->row;
627 		col = obj->col;
628 
629 		rc = get_mask_char(obj->what_is);
630 
631 		if (dungeon[row][col] & MONSTER) {
632 			if (monster = object_at(&level_monsters, row, col)) {
633 				monster->trail_char = rc;
634 			}
635 		}
636 		mc = mvinch(row, col);
637 		if (((mc < 'A') || (mc > 'Z')) &&
638 			((row != rogue.row) || (col != rogue.col))) {
639 			mvaddch(row, col, rc);
640 		}
641 		obj = obj->next_object;
642 	}
643 
644 	monster = level_monsters.next_object;
645 
646 	while (monster) {
647 		if (monster->m_flags & IMITATES) {
648 			mvaddch(monster->row, monster->col, (int) monster->disguise);
649 		}
650 		monster = monster->next_monster;
651 	}
652 }
653 
654 put_amulet()
655 {
656 	object *obj;
657 
658 	obj = alloc_object();
659 	obj->what_is = AMULET;
660 	rand_place(obj);
661 }
662 
663 rand_place(obj)
664 object *obj;
665 {
666 	short row, col;
667 
668 	gr_row_col(&row, &col, (FLOOR | TUNNEL));
669 	place_at(obj, row, col);
670 }
671 
672 c_object_for_wizard()
673 {
674 	short ch, max, wk;
675 	object *obj;
676 	char buf[80];
677 
678 	if (pack_count((object *) 0) >= MAX_PACK_COUNT) {
679 		message("pack full", 0);
680 		return;
681 	}
682 	message("type of object?", 0);
683 
684 	while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) {
685 		sound_bell();
686 	}
687 	check_message();
688 
689 	if (ch == '\033') {
690 		return;
691 	}
692 	obj = alloc_object();
693 
694 	switch(ch) {
695 	case '!':
696 		obj->what_is = POTION;
697 		max = POTIONS - 1;
698 		break;
699 	case '?':
700 		obj->what_is = SCROL;
701 		max = SCROLS - 1;
702 		break;
703 	case ',':
704 		obj->what_is = AMULET;
705 		break;
706 	case ':':
707 		get_food(obj, 0);
708 		break;
709 	case ')':
710 		gr_weapon(obj, 0);
711 		max = WEAPONS - 1;
712 		break;
713 	case ']':
714 		gr_armor(obj);
715 		max = ARMORS - 1;
716 		break;
717 	case '/':
718 		gr_wand(obj);
719 		max = WANDS - 1;
720 		break;
721 	case '=':
722 		max = RINGS - 1;
723 		obj->what_is = RING;
724 		break;
725 	}
726 	if ((ch != ',') && (ch != ':')) {
727 GIL:
728 		if (get_input_line("which kind?", "", buf, "", 0, 1)) {
729 			wk = get_number(buf);
730 			if ((wk >= 0) && (wk <= max)) {
731 				obj->which_kind = (unsigned short) wk;
732 				if (obj->what_is == RING) {
733 					gr_ring(obj, 0);
734 				}
735 			} else {
736 				sound_bell();
737 				goto GIL;
738 			}
739 		} else {
740 			free_object(obj);
741 			return;
742 		}
743 	}
744 	get_desc(obj, buf);
745 	message(buf, 0);
746 	(void) add_to_pack(obj, &rogue.pack, 1);
747 }
748