1 /* NetHack 3.6 o_init.c $NHDT-Date: 1446892449 2015/11/07 10:34:09 $ $NHDT-Branch: master $:$NHDT-Revision: 1.20 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
6 #include "lev.h" /* save & restore info */
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 STATIC_DCL char *FDECL(oclass_to_name, (CHAR_P, char *));
14 static NEARDATA short disco[NUM_OBJECTS] = DUMMY;
17 STATIC_DCL void NDECL(shuffle_tiles);
18 extern short glyph2tile[]; /* from tile.c */
20 /* Shuffle tile assignments to match descriptions, so a red potion isn't
21 * displayed with a blue tile and so on.
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
33 short tmp_tilemap[NUM_OBJECTS];
35 for (i = 0; i < NUM_OBJECTS; i++)
36 tmp_tilemap[i] = glyph2tile[objects[i].oc_descr_idx + GLYPH_OBJ_OFF];
38 for (i = 0; i < NUM_OBJECTS; i++)
39 glyph2tile[i + GLYPH_OBJ_OFF] = tmp_tilemap[i];
41 #endif /* USE_TILES */
50 lev = (ledger_no(dlev) > maxledgerno()) ? maxledgerno()
54 first = bases[GEM_CLASS];
56 for (j = 0; j < 9 - lev / 3; j++)
57 objects[first + j].oc_prob = 0;
59 if (first > LAST_GEM || objects[first].oc_class != GEM_CLASS
60 || OBJ_NAME(objects[first]) == (char *) 0) {
61 raw_printf("Not enough gems? - first=%d j=%d LAST_GEM=%d", first, j,
65 for (j = first; j <= LAST_GEM; j++)
66 objects[j].oc_prob = (171 + j - first) / (LAST_GEM + 1 - first);
69 /* shuffle descriptions on objects o_low to o_high */
71 shuffle(o_low, o_high, domaterial)
75 int i, j, num_to_shuffle;
79 for (num_to_shuffle = 0, j = o_low; j <= o_high; j++)
80 if (!objects[j].oc_name_known)
82 if (num_to_shuffle < 2)
85 for (j = o_low; j <= o_high; j++) {
86 if (objects[j].oc_name_known)
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;
101 /* shuffle material */
103 sw = objects[j].oc_material;
104 objects[j].oc_material = objects[i].oc_material;
105 objects[i].oc_material = sw;
113 register int i, first, last, sum;
114 register char oclass;
116 #define COPY_OBJ_DESCR(o_dst, o_src) \
117 o_dst.oc_descr_idx = o_src.oc_descr_idx, o_dst.oc_color = o_src.oc_color
119 #define COPY_OBJ_DESCR(o_dst, o_src) o_dst.oc_descr_idx = o_src.oc_descr_idx
122 /* bug fix to prevent "initialization error" abort on Intel Xenix.
123 * reported by mikew@semike
125 for (i = 0; i < MAXOCLASSES; i++)
127 /* initialize object descriptions */
128 for (i = 0; i < NUM_OBJECTS; i++)
129 objects[i].oc_name_idx = objects[i].oc_descr_idx = i;
130 /* init base; if probs given check that they add up to 1000,
131 otherwise compute probs */
133 while (first < NUM_OBJECTS) {
134 oclass = objects[first].oc_class;
136 while (last < NUM_OBJECTS && objects[last].oc_class == oclass)
138 bases[(int) oclass] = first;
140 if (oclass == GEM_CLASS) {
141 setgemprobs((d_level *) 0);
143 if (rn2(2)) { /* change turquoise from green to blue? */
144 COPY_OBJ_DESCR(objects[TURQUOISE], objects[SAPPHIRE]);
146 if (rn2(2)) { /* change aquamarine from green to blue? */
147 COPY_OBJ_DESCR(objects[AQUAMARINE], objects[SAPPHIRE]);
149 switch (rn2(4)) { /* change fluorite from violet? */
153 COPY_OBJ_DESCR(objects[FLUORITE], objects[SAPPHIRE]);
156 COPY_OBJ_DESCR(objects[FLUORITE], objects[DIAMOND]);
159 COPY_OBJ_DESCR(objects[FLUORITE], objects[EMERALD]);
165 for (i = first; i < last; i++)
166 sum += objects[i].oc_prob;
168 for (i = first; i < last; i++)
169 objects[i].oc_prob = (1000 + i - first) / (last - first);
173 error("init-prob error for class %d (%d%%)", oclass, sum);
176 /* shuffle descriptions */
181 objects[WAN_NOTHING].oc_dir = rn2(2) ? NODIR : IMMEDIATE;
184 /* retrieve the range of objects that otyp shares descriptions with */
186 obj_shuffle_range(otyp, lo_p, hi_p)
187 int otyp; /* input: representative item */
188 int *lo_p, *hi_p; /* output: range that item belongs among */
190 int i, ocls = objects[otyp].oc_class;
192 /* default is just the object itself */
193 *lo_p = *hi_p = otyp;
197 if (otyp >= HELMET && otyp <= HELM_OF_TELEPATHY)
198 *lo_p = HELMET, *hi_p = HELM_OF_TELEPATHY;
199 else if (otyp >= LEATHER_GLOVES && otyp <= GAUNTLETS_OF_DEXTERITY)
200 *lo_p = LEATHER_GLOVES, *hi_p = GAUNTLETS_OF_DEXTERITY;
201 else if (otyp >= CLOAK_OF_PROTECTION && otyp <= CLOAK_OF_DISPLACEMENT)
202 *lo_p = CLOAK_OF_PROTECTION, *hi_p = CLOAK_OF_DISPLACEMENT;
203 else if (otyp >= SPEED_BOOTS && otyp <= LEVITATION_BOOTS)
204 *lo_p = SPEED_BOOTS, *hi_p = LEVITATION_BOOTS;
207 /* potion of water has the only fixed description */
208 *lo_p = bases[POTION_CLASS];
209 *hi_p = POT_WATER - 1;
214 /* exclude non-magic types and also unique ones */
216 for (i = *lo_p; objects[i].oc_class == ocls; i++)
217 if (objects[i].oc_unique || !objects[i].oc_magic)
226 for (i = *lo_p; objects[i].oc_class == ocls; i++)
232 /* artifact checking might ask about item which isn't part of any range
233 but fell within the classes that do have ranges specified above */
234 if (otyp < *lo_p || otyp > *hi_p)
235 *lo_p = *hi_p = otyp;
239 /* randomize object descriptions */
243 /* entire classes; obj_shuffle_range() handles their exceptions */
244 static char shuffle_classes[] = {
245 AMULET_CLASS, POTION_CLASS, RING_CLASS, SCROLL_CLASS,
246 SPBOOK_CLASS, WAND_CLASS, VENOM_CLASS,
248 /* sub-class type ranges (one item from each group) */
249 static short shuffle_types[] = {
250 HELMET, LEATHER_GLOVES, CLOAK_OF_PROTECTION, SPEED_BOOTS,
252 int first, last, idx;
254 /* do whole classes (amulets, &c) */
255 for (idx = 0; idx < SIZE(shuffle_classes); idx++) {
256 obj_shuffle_range(bases[(int) shuffle_classes[idx]], &first, &last);
257 shuffle(first, last, TRUE);
259 /* do type ranges (helms, &c) */
260 for (idx = 0; idx < SIZE(shuffle_types); idx++) {
261 obj_shuffle_range(shuffle_types[idx], &first, &last);
262 shuffle(first, last, FALSE);
267 /* find the object index for snow boots; used [once] by slippery ice code */
272 register const char *s;
274 for (i = SPEED_BOOTS; i <= LEVITATION_BOOTS; i++)
275 if ((s = OBJ_DESCR(objects[i])) != 0 && !strcmp(s, "snow boots"))
278 impossible("snow boots not found?");
279 return -1; /* not 0, or caller would try again each move */
282 /* level dependent initialization */
296 if (perform_bwrite(mode)) {
297 bwrite(fd, (genericptr_t) bases, sizeof bases);
298 bwrite(fd, (genericptr_t) disco, sizeof disco);
299 bwrite(fd, (genericptr_t) objects,
300 sizeof(struct objclass) * NUM_OBJECTS);
302 /* as long as we use only one version of Hack we
303 need not save oc_name and oc_descr, but we must save
304 oc_uname for all objects */
305 for (i = 0; i < NUM_OBJECTS; i++)
306 if (objects[i].oc_uname) {
307 if (perform_bwrite(mode)) {
308 len = strlen(objects[i].oc_uname) + 1;
309 bwrite(fd, (genericptr_t) &len, sizeof len);
310 bwrite(fd, (genericptr_t) objects[i].oc_uname, len);
312 if (release_data(mode)) {
313 free((genericptr_t) objects[i].oc_uname);
314 objects[i].oc_uname = 0;
326 mread(fd, (genericptr_t) bases, sizeof bases);
327 mread(fd, (genericptr_t) disco, sizeof disco);
328 mread(fd, (genericptr_t) objects, sizeof(struct objclass) * NUM_OBJECTS);
329 for (i = 0; i < NUM_OBJECTS; i++)
330 if (objects[i].oc_uname) {
331 mread(fd, (genericptr_t) &len, sizeof len);
332 objects[i].oc_uname = (char *) alloc(len);
333 mread(fd, (genericptr_t) objects[i].oc_uname, len);
341 discover_object(oindx, mark_as_known, credit_hero)
343 boolean mark_as_known;
346 if (!objects[oindx].oc_name_known) {
347 register int dindx, acls = objects[oindx].oc_class;
349 /* Loop thru disco[] 'til we find the target (which may have been
350 uname'd) or the next open slot; one or the other will be found
351 before we reach the next class...
353 for (dindx = bases[acls]; disco[dindx] != 0; dindx++)
354 if (disco[dindx] == oindx)
356 disco[dindx] = oindx;
359 objects[oindx].oc_name_known = 1;
361 exercise(A_WIS, TRUE);
368 /* if a class name has been cleared, we may need to purge it from disco[] */
370 undiscover_object(oindx)
373 if (!objects[oindx].oc_name_known) {
374 register int dindx, acls = objects[oindx].oc_class;
375 register boolean found = FALSE;
377 /* find the object; shift those behind it forward one slot */
378 for (dindx = bases[acls]; dindx < NUM_OBJECTS && disco[dindx] != 0
379 && objects[dindx].oc_class == acls;
382 disco[dindx - 1] = disco[dindx];
383 else if (disco[dindx] == oindx)
386 /* clear last slot */
388 disco[dindx - 1] = 0;
390 impossible("named object not in disco");
396 interesting_to_discover(i)
399 /* Pre-discovered objects are now printed with a '*' */
400 return (boolean) (objects[i].oc_uname != (char *) 0
401 || (objects[i].oc_name_known
402 && OBJ_DESCR(objects[i]) != (char *) 0));
405 /* items that should stand out once they're known */
406 static short uniq_objs[] = {
407 AMULET_OF_YENDOR, SPE_BOOK_OF_THE_DEAD, CANDELABRUM_OF_INVOCATION,
411 /* the '\' command - show discovered object types */
413 dodiscovered() /* free after Robert Viduya */
417 char *s, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ];
420 tmpwin = create_nhwindow(NHW_MENU);
421 putstr(tmpwin, 0, "Discoveries");
422 putstr(tmpwin, 0, "");
424 /* gather "unique objects" into a pseudo-class; note that they'll
425 also be displayed individually within their regular class */
426 for (i = dis = 0; i < SIZE(uniq_objs); i++)
427 if (objects[uniq_objs[i]].oc_name_known) {
429 putstr(tmpwin, iflags.menu_headings, "Unique items");
430 Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]]));
431 putstr(tmpwin, 0, buf);
434 /* display any known artifacts as another pseudo-class */
435 ct += disp_artifact_discoveries(tmpwin);
437 /* several classes are omitted from packorder; one is of interest here */
438 Strcpy(classes, flags.inv_order);
439 if (!index(classes, VENOM_CLASS)) {
445 for (s = classes; *s; s++) {
447 prev_class = oclass + 1; /* forced different from oclass */
448 for (i = bases[(int) oclass];
449 i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) {
450 if ((dis = disco[i]) != 0 && interesting_to_discover(dis)) {
452 if (oclass != prev_class) {
453 putstr(tmpwin, iflags.menu_headings,
454 let_to_name(oclass, FALSE, FALSE));
457 Sprintf(buf, "%s %s",
458 (objects[dis].oc_pre_discovered ? "*" : " "),
460 putstr(tmpwin, 0, buf);
465 You("haven't discovered anything yet...");
467 display_nhwindow(tmpwin, TRUE);
468 destroy_nhwindow(tmpwin);
473 /* lower case let_to_name() output, which differs from def_oc_syms[].name */
475 oclass_to_name(oclass, buf)
481 Strcpy(buf, let_to_name(oclass, FALSE, FALSE));
482 for (s = buf; *s; ++s)
487 /* the '`' command - show discovered object types for one class */
491 static NEARDATA const char
492 prompt[] = "View discoveries for which sort of objects?",
493 havent_discovered_any[] = "haven't discovered any %s yet.",
494 unique_items[] = "unique items",
495 artifact_items[] = "artifacts";
496 char *s, c, oclass, menulet, allclasses[MAXOCLASSES],
497 discosyms[2 + MAXOCLASSES + 1], buf[BUFSZ];
498 int i, ct, dis, xtras;
502 menu_item *pick_list = 0;
505 traditional = (flags.menu_style == MENU_TRADITIONAL
506 || flags.menu_style == MENU_COMBINATION);
507 tmpwin = !traditional ? create_nhwindow(NHW_MENU) : WIN_ERR;
511 /* check whether we've discovered any unique objects */
512 for (i = 0; i < SIZE(uniq_objs); i++)
513 if (objects[uniq_objs[i]].oc_name_known) {
514 Strcat(discosyms, "u");
517 add_menu(tmpwin, NO_GLYPH, &any, menulet++, 0, ATR_NONE,
518 unique_items, MENU_UNSELECTED);
523 /* check whether we've discovered any artifacts */
524 if (disp_artifact_discoveries(WIN_ERR) > 0) {
525 Strcat(discosyms, "a");
528 add_menu(tmpwin, NO_GLYPH, &any, menulet++, 0, ATR_NONE,
529 artifact_items, MENU_UNSELECTED);
533 /* collect classes with discoveries, in packorder ordering; several
534 classes are omitted from packorder and one is of interest here */
535 Strcpy(allclasses, flags.inv_order);
536 if (!index(allclasses, VENOM_CLASS))
537 Sprintf(eos(allclasses), "%c", VENOM_CLASS);
538 /* construct discosyms[] */
539 for (s = allclasses; *s; ++s) {
541 c = def_oc_syms[(int) oclass].sym;
542 for (i = bases[(int) oclass];
543 i < NUM_OBJECTS && objects[i].oc_class == oclass; ++i)
544 if ((dis = disco[i]) != 0 && interesting_to_discover(dis)) {
545 if (!index(discosyms, c)) {
546 Sprintf(eos(discosyms), "%c", c);
549 add_menu(tmpwin, NO_GLYPH, &any, menulet++, c,
550 ATR_NONE, oclass_to_name(oclass, buf),
557 /* there might not be anything for us to do... */
559 You(havent_discovered_any, "items");
560 if (tmpwin != WIN_ERR)
561 destroy_nhwindow(tmpwin);
565 /* have player choose a class */
566 c = '\0'; /* class not chosen yet */
568 /* we'll prompt even if there's only one viable class; we add all
569 nonviable classes as unseen acceptable choices so player can ask
570 for discoveries of any class whether it has discoveries or not */
571 for (s = allclasses, xtras = 0; *s; ++s) {
572 c = def_oc_syms[(int) *s].sym;
573 if (!index(discosyms, c)) {
575 Sprintf(eos(discosyms), "%c", '\033');
576 Sprintf(eos(discosyms), "%c", c);
579 /* get the class (via its symbol character) */
580 c = yn_function(prompt, discosyms, '\0');
583 clear_nhwindow(WIN_MESSAGE);
585 /* menustyle:full or menustyle:partial */
586 if (!discosyms[1] && flags.menu_style == MENU_PARTIAL) {
587 /* only one class; menustyle:partial normally jumps past class
588 filtering straight to final menu so skip class filter here */
591 /* more than one choice, or menustyle:full which normally has
592 an intermediate class selection menu before the final menu */
593 end_menu(tmpwin, prompt);
594 i = select_menu(tmpwin, PICK_ONE, &pick_list);
596 c = pick_list[0].item.a_int;
597 free((genericptr_t) pick_list);
598 } /* else c stays 0 */
600 destroy_nhwindow(tmpwin);
603 return 0; /* player declined to make a selection */
606 * show discoveries for object class c
608 tmpwin = create_nhwindow(NHW_MENU);
612 putstr(tmpwin, iflags.menu_headings,
613 upstart(strcpy(buf, unique_items)));
614 for (i = 0; i < SIZE(uniq_objs); i++)
615 if (objects[uniq_objs[i]].oc_name_known) {
616 Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]]));
617 putstr(tmpwin, 0, buf);
621 You(havent_discovered_any, unique_items);
624 /* disp_artifact_discoveries() includes a header */
625 ct = disp_artifact_discoveries(tmpwin);
627 You(havent_discovered_any, artifact_items);
630 oclass = def_char_to_objclass(c);
631 Sprintf(buf, "Discovered %s", let_to_name(oclass, FALSE, FALSE));
632 putstr(tmpwin, iflags.menu_headings, buf);
633 for (i = bases[(int) oclass];
634 i < NUM_OBJECTS && objects[i].oc_class == oclass; ++i) {
635 if ((dis = disco[i]) != 0 && interesting_to_discover(dis)) {
636 Sprintf(buf, "%s %s",
637 objects[dis].oc_pre_discovered ? "*" : " ",
639 putstr(tmpwin, 0, buf);
644 You(havent_discovered_any, oclass_to_name(oclass, buf));
648 display_nhwindow(tmpwin, TRUE);
649 destroy_nhwindow(tmpwin);
653 /* put up nameable subset of discoveries list as a menu */
658 int ct = 0, mn = 0, sl;
659 char *s, oclass, prev_class;
662 menu_item *selected = 0;
665 tmpwin = create_nhwindow(NHW_MENU);
669 * Skip the "unique objects" section (each will appear within its
670 * regular class if it is nameable) and the artifacts section.
671 * We assume that classes omitted from packorder aren't nameable
672 * so we skip venom too.
675 /* for each class, show discoveries in that class */
676 for (s = flags.inv_order; *s; s++) {
678 prev_class = oclass + 1; /* forced different from oclass */
679 for (i = bases[(int) oclass];
680 i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) {
682 if (!dis || !interesting_to_discover(dis))
685 if (!objtyp_is_callable(dis))
689 if (oclass != prev_class) {
691 add_menu(tmpwin, NO_GLYPH, &any, ' ', iflags.menu_headings,
692 ATR_NONE, let_to_name(oclass, FALSE, FALSE),
697 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
698 obj_typename(dis), MENU_UNSELECTED);
702 You("haven't discovered anything yet...");
703 } else if (mn == 0) {
704 pline("None of your discoveries can be assigned names...");
706 end_menu(tmpwin, "Pick an object type to name");
707 dis = STRANGE_OBJECT;
708 sl = select_menu(tmpwin, PICK_ONE, &selected);
710 dis = selected[0].item.a_int;
711 free((genericptr_t) selected);
713 if (dis != STRANGE_OBJECT) {
718 odummy.oclass = objects[dis].oc_class;
720 odummy.known = !objects[dis].oc_uses_known;
725 destroy_nhwindow(tmpwin);