1/*	SCCS Id: @(#)o_init.c	3.4	1999/12/09	*/
2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3/* NetHack may be freely redistributed.  See license for details. */
4
5#include "hack.h"
6#include "lev.h"	/* save & restore info */
7
8STATIC_DCL void FDECL(setgemprobs, (d_level*));
9STATIC_DCL void FDECL(shuffle,(int,int,BOOLEAN_P));
10STATIC_DCL void NDECL(shuffle_all);
11STATIC_DCL boolean FDECL(interesting_to_discover,(int));
12
13
14static NEARDATA short disco[NUM_OBJECTS] = DUMMY;
15
16#ifdef USE_TILES
17STATIC_DCL void NDECL(shuffle_tiles);
18extern short glyph2tile[];	/* from tile.c */
19
20/* Shuffle tile assignments to match descriptions, so a red potion isn't
21 * displayed with a blue tile and so on.
22 *
23 * Tile assignments are not saved, and shouldn't be so that a game can
24 * be resumed on an otherwise identical non-tile-using binary, so we have
25 * to reshuffle the assignments from oc_descr_idx information when a game
26 * is restored.  So might as well do that the first time instead of writing
27 * another routine.
28 */
29STATIC_OVL void
30shuffle_tiles()
31{
32	int i;
33	short tmp_tilemap[NUM_OBJECTS];
34
35	for (i = 0; i < NUM_OBJECTS; i++)
36		tmp_tilemap[i] =
37			glyph2tile[objects[i].oc_descr_idx + GLYPH_OBJ_OFF];
38
39	for (i = 0; i < NUM_OBJECTS; i++)
40		glyph2tile[i + GLYPH_OBJ_OFF] = tmp_tilemap[i];
41}
42#endif	/* USE_TILES */
43
44STATIC_OVL void
45setgemprobs(dlev)
46d_level *dlev;
47{
48	int j, first, lev;
49
50	if (dlev)
51	    lev = (ledger_no(dlev) > maxledgerno())
52				? maxledgerno() : ledger_no(dlev);
53	else
54	    lev = 0;
55	first = bases[GEM_CLASS];
56
57	for(j = 0; j < 9-lev/3; j++)
58		objects[first+j].oc_prob = 0;
59	first += j;
60	if (first > LAST_GEM || objects[first].oc_class != GEM_CLASS ||
61	    OBJ_NAME(objects[first]) == (char *)0) {
62		raw_printf("Not enough gems? - first=%d j=%d LAST_GEM=%d",
63			first, j, LAST_GEM);
64		wait_synch();
65	    }
66	for (j = first; j <= LAST_GEM; j++) {
67		objects[j].oc_prob = (171+j-first)/(LAST_GEM+1-first);
68        printf("setgemprobs[j %d] = %d\n", j, objects[j].oc_prob);
69    }
70}
71
72/* shuffle descriptions on objects o_low to o_high */
73STATIC_OVL void
74shuffle(o_low, o_high, domaterial)
75	int o_low, o_high;
76	boolean domaterial;
77{
78	int i, j, num_to_shuffle;
79	short sw;
80	int color;
81
82	for (num_to_shuffle = 0, j=o_low; j <= o_high; j++)
83		if (!objects[j].oc_name_known) num_to_shuffle++;
84	if (num_to_shuffle < 2) return;
85
86	for (j=o_low; j <= o_high; j++) {
87		if (objects[j].oc_name_known) continue;
88		do
89			i = j + rn2(o_high-j+1);
90		while (objects[i].oc_name_known);
91		sw = objects[j].oc_descr_idx;
92		objects[j].oc_descr_idx = objects[i].oc_descr_idx;
93		objects[i].oc_descr_idx = sw;
94		sw = objects[j].oc_tough;
95		objects[j].oc_tough = objects[i].oc_tough;
96		objects[i].oc_tough = sw;
97		color = objects[j].oc_color;
98		objects[j].oc_color = objects[i].oc_color;
99		objects[i].oc_color = color;
100
101		/* shuffle material */
102		if (domaterial) {
103			sw = objects[j].oc_material;
104			objects[j].oc_material = objects[i].oc_material;
105			objects[i].oc_material = sw;
106		}
107	}
108}
109
110void
111init_objects()
112{
113register int i, first, last, sum;
114register char oclass;
115#ifdef TEXTCOLOR
116# define COPY_OBJ_DESCR(o_dst,o_src) \
117			o_dst.oc_descr_idx = o_src.oc_descr_idx,\
118			o_dst.oc_color = o_src.oc_color
119#else
120# define COPY_OBJ_DESCR(o_dst,o_src) o_dst.oc_descr_idx = o_src.oc_descr_idx
121#endif
122
123	/* bug fix to prevent "initialization error" abort on Intel Xenix.
124	 * reported by mikew@semike
125	 */
126	for (i = 0; i < MAXOCLASSES; i++)
127		bases[i] = 0;
128	/* initialize object descriptions */
129	for (i = 0; i < NUM_OBJECTS; i++)
130		objects[i].oc_name_idx = objects[i].oc_descr_idx = i;
131	/* init base; if probs given check that they add up to 1000,
132	   otherwise compute probs */
133	first = 0;
134	while( first < NUM_OBJECTS ) {
135		oclass = objects[first].oc_class;
136		last = first+1;
137		while (last < NUM_OBJECTS && objects[last].oc_class == oclass) last++;
138		bases[(int)oclass] = first;
139
140		if (oclass == GEM_CLASS) {
141			setgemprobs((d_level *)0);
142
143			if (rn2(2)) { /* change turquoise from green to blue? */
144			    COPY_OBJ_DESCR(objects[TURQUOISE],objects[SAPPHIRE]);
145			}
146			if (rn2(2)) { /* change aquamarine from green to blue? */
147			    COPY_OBJ_DESCR(objects[AQUAMARINE],objects[SAPPHIRE]);
148			}
149			switch (rn2(4)) { /* change fluorite from violet? */
150			    case 0:  break;
151			    case 1:	/* blue */
152				COPY_OBJ_DESCR(objects[FLUORITE],objects[SAPPHIRE]);
153				break;
154			    case 2:	/* white */
155				COPY_OBJ_DESCR(objects[FLUORITE],objects[DIAMOND]);
156				break;
157			    case 3:	/* green */
158				COPY_OBJ_DESCR(objects[FLUORITE],objects[EMERALD]);
159				break;
160			}
161		}
162	check:
163		sum = 0;
164		for(i = first; i < last; i++) {
165            sum += objects[i].oc_prob;
166        }
167		if(sum == 0) {
168			for(i = first; i < last; i++)
169			    objects[i].oc_prob = (1000+i-first)/(last-first);
170			goto check;
171		}
172		if(sum != 1000) {
173			error("init-prob error for class %d (%d%%)", oclass, sum);
174        }
175		first = last;
176	}
177	/* shuffle descriptions */
178	shuffle_all();
179#ifdef USE_TILES
180	shuffle_tiles();
181#endif
182}
183
184STATIC_OVL void
185shuffle_all()
186{
187	int first, last, oclass;
188
189	for (oclass = 1; oclass < MAXOCLASSES; oclass++) {
190		first = bases[oclass];
191		last = first+1;
192		while (last < NUM_OBJECTS && objects[last].oc_class == oclass)
193			last++;
194
195		if (OBJ_DESCR(objects[first]) != (char *)0 &&
196				oclass != TOOL_CLASS &&
197				oclass != WEAPON_CLASS &&
198				oclass != ARMOR_CLASS &&
199				oclass != GEM_CLASS) {
200			int j = last-1;
201
202			if (oclass == POTION_CLASS)
203			    j -= 1;  /* only water has a fixed description */
204			else if (oclass == AMULET_CLASS ||
205				 oclass == SCROLL_CLASS ||
206				 oclass == SPBOOK_CLASS) {
207			    while (!objects[j].oc_magic || objects[j].oc_unique)
208				j--;
209			}
210
211			/* non-magical amulets, scrolls, and spellbooks
212			 * (ex. imitation Amulets, blank, scrolls of mail)
213			 * and one-of-a-kind magical artifacts at the end of
214			 * their class in objects[] have fixed descriptions.
215			 */
216			shuffle(first, j, TRUE);
217		}
218	}
219
220	/* shuffle the helmets */
221	shuffle(HELMET, HELM_OF_TELEPATHY, FALSE);
222
223	/* shuffle the gloves */
224	shuffle(LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY, FALSE);
225
226	/* shuffle the cloaks */
227	shuffle(CLOAK_OF_PROTECTION, CLOAK_OF_DISPLACEMENT, FALSE);
228
229	/* shuffle the boots [if they change, update find_skates() below] */
230	shuffle(SPEED_BOOTS, LEVITATION_BOOTS, FALSE);
231}
232
233/* find the object index for snow boots; used [once] by slippery ice code */
234int
235find_skates()
236{
237    register int i;
238    register const char *s;
239
240    for (i = SPEED_BOOTS; i <= LEVITATION_BOOTS; i++)
241	if ((s = OBJ_DESCR(objects[i])) != 0 && !strcmp(s, "snow boots"))
242	    return i;
243
244    impossible("snow boots not found?");
245    return -1;	/* not 0, or caller would try again each move */
246}
247
248void
249oinit()			/* level dependent initialization */
250{
251	setgemprobs(&u.uz);
252}
253
254void
255savenames(fd, mode)
256int fd, mode;
257{
258	register int i;
259	unsigned int len;
260
261	if (perform_bwrite(mode)) {
262	    bwrite(fd, (genericptr_t)bases, sizeof bases);
263	    bwrite(fd, (genericptr_t)disco, sizeof disco);
264	    bwrite(fd, (genericptr_t)objects,
265		   sizeof(struct objclass) * NUM_OBJECTS);
266	}
267	/* as long as we use only one version of Hack we
268	   need not save oc_name and oc_descr, but we must save
269	   oc_uname for all objects */
270	for (i = 0; i < NUM_OBJECTS; i++)
271	    if (objects[i].oc_uname) {
272		if (perform_bwrite(mode)) {
273		    len = strlen(objects[i].oc_uname)+1;
274		    bwrite(fd, (genericptr_t)&len, sizeof len);
275		    bwrite(fd, (genericptr_t)objects[i].oc_uname, len);
276		}
277		if (release_data(mode)) {
278		    free((genericptr_t)objects[i].oc_uname);
279		    objects[i].oc_uname = 0;
280		}
281	    }
282}
283
284void
285restnames(fd)
286register int fd;
287{
288	register int i;
289	unsigned int len;
290
291	mread(fd, (genericptr_t) bases, sizeof bases);
292	mread(fd, (genericptr_t) disco, sizeof disco);
293	mread(fd, (genericptr_t) objects, sizeof(struct objclass) * NUM_OBJECTS);
294	for (i = 0; i < NUM_OBJECTS; i++)
295	    if (objects[i].oc_uname) {
296		mread(fd, (genericptr_t) &len, sizeof len);
297		objects[i].oc_uname = (char *) alloc(len);
298		mread(fd, (genericptr_t)objects[i].oc_uname, len);
299	    }
300#ifdef USE_TILES
301	shuffle_tiles();
302#endif
303}
304
305void
306discover_object(oindx, mark_as_known, credit_hero)
307register int oindx;
308boolean mark_as_known;
309boolean credit_hero;
310{
311    if (!objects[oindx].oc_name_known) {
312	register int dindx, acls = objects[oindx].oc_class;
313
314	/* Loop thru disco[] 'til we find the target (which may have been
315	   uname'd) or the next open slot; one or the other will be found
316	   before we reach the next class...
317	 */
318	for (dindx = bases[acls]; disco[dindx] != 0; dindx++)
319	    if (disco[dindx] == oindx) break;
320	disco[dindx] = oindx;
321
322	if (mark_as_known) {
323	    objects[oindx].oc_name_known = 1;
324	    if (credit_hero) exercise(A_WIS, TRUE);
325	}
326	if (moves > 1L) update_inventory();
327    }
328}
329
330/* if a class name has been cleared, we may need to purge it from disco[] */
331void
332undiscover_object(oindx)
333register int oindx;
334{
335    if (!objects[oindx].oc_name_known) {
336	register int dindx, acls = objects[oindx].oc_class;
337	register boolean found = FALSE;
338
339	/* find the object; shift those behind it forward one slot */
340	for (dindx = bases[acls];
341	      dindx < NUM_OBJECTS && disco[dindx] != 0
342		&& objects[dindx].oc_class == acls; dindx++)
343	    if (found)
344		disco[dindx-1] = disco[dindx];
345	    else if (disco[dindx] == oindx)
346		found = TRUE;
347
348	/* clear last slot */
349	if (found) disco[dindx-1] = 0;
350	else impossible("named object not in disco");
351	update_inventory();
352    }
353}
354
355STATIC_OVL boolean
356interesting_to_discover(i)
357register int i;
358{
359	/* Pre-discovered objects are now printed with a '*' */
360    return((boolean)(objects[i].oc_uname != (char *)0 ||
361	    (objects[i].oc_name_known && OBJ_DESCR(objects[i]) != (char *)0)));
362}
363
364/* items that should stand out once they're known */
365static short uniq_objs[] = {
366	AMULET_OF_YENDOR,
367	SPE_BOOK_OF_THE_DEAD,
368	CANDELABRUM_OF_INVOCATION,
369	BELL_OF_OPENING,
370};
371
372int
373dodiscovered()				/* free after Robert Viduya */
374{
375    register int i, dis;
376    int	ct = 0;
377    char *s, oclass, prev_class, classes[MAXOCLASSES];
378    winid tmpwin;
379	char buf[BUFSZ];
380
381    tmpwin = create_nhwindow(NHW_MENU);
382    putstr(tmpwin, 0, "Discoveries");
383    putstr(tmpwin, 0, "");
384
385    /* gather "unique objects" into a pseudo-class; note that they'll
386       also be displayed individually within their regular class */
387    for (i = dis = 0; i < SIZE(uniq_objs); i++)
388	if (objects[uniq_objs[i]].oc_name_known) {
389	    if (!dis++)
390		putstr(tmpwin, iflags.menu_headings, "Unique Items");
391		Sprintf(buf, "  %s", OBJ_NAME(objects[uniq_objs[i]]));
392	    putstr(tmpwin, 0, buf);
393	    ++ct;
394	}
395    /* display any known artifacts as another pseudo-class */
396    ct += disp_artifact_discoveries(tmpwin);
397
398    /* several classes are omitted from packorder; one is of interest here */
399    Strcpy(classes, flags.inv_order);
400    if (!index(classes, VENOM_CLASS)) {
401	s = eos(classes);
402	*s++ = VENOM_CLASS;
403	*s = '\0';
404    }
405
406    for (s = classes; *s; s++) {
407	oclass = *s;
408	prev_class = oclass + 1;	/* forced different from oclass */
409	for (i = bases[(int)oclass];
410	     i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) {
411	    if ((dis = disco[i]) && interesting_to_discover(dis)) {
412		ct++;
413		if (oclass != prev_class) {
414		    putstr(tmpwin, iflags.menu_headings, let_to_name(oclass, FALSE));
415		    prev_class = oclass;
416		}
417		Sprintf(buf, "%s %s",(objects[dis].oc_pre_discovered ? "*" : " "),
418				obj_typename(dis));
419		putstr(tmpwin, 0, buf);
420	    }
421	}
422    }
423    if (ct == 0) {
424	You("haven't discovered anything yet...");
425    } else
426	display_nhwindow(tmpwin, TRUE);
427    destroy_nhwindow(tmpwin);
428
429    return 0;
430}
431
432/*o_init.c*/
433