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*/