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