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