1 /* NetHack 3.6 o_init.c $NHDT-Date: 1450318588 2015/12/17 02:16:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.22 $ */
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
))
440 (void) strkitten(classes
, VENOM_CLASS
); /* append char to string */
442 for (s
= classes
; *s
; s
++) {
444 prev_class
= oclass
+ 1; /* forced different from oclass */
445 for (i
= bases
[(int) oclass
];
446 i
< NUM_OBJECTS
&& objects
[i
].oc_class
== oclass
; i
++) {
447 if ((dis
= disco
[i
]) != 0 && interesting_to_discover(dis
)) {
449 if (oclass
!= prev_class
) {
450 putstr(tmpwin
, iflags
.menu_headings
,
451 let_to_name(oclass
, FALSE
, FALSE
));
454 Sprintf(buf
, "%s %s",
455 (objects
[dis
].oc_pre_discovered
? "*" : " "),
457 putstr(tmpwin
, 0, buf
);
462 You("haven't discovered anything yet...");
464 display_nhwindow(tmpwin
, TRUE
);
465 destroy_nhwindow(tmpwin
);
470 /* lower case let_to_name() output, which differs from def_oc_syms[].name */
472 oclass_to_name(oclass
, buf
)
478 Strcpy(buf
, let_to_name(oclass
, FALSE
, FALSE
));
479 for (s
= buf
; *s
; ++s
)
484 /* the '`' command - show discovered object types for one class */
488 static NEARDATA
const char
489 prompt
[] = "View discoveries for which sort of objects?",
490 havent_discovered_any
[] = "haven't discovered any %s yet.",
491 unique_items
[] = "unique items",
492 artifact_items
[] = "artifacts";
493 char *s
, c
, oclass
, menulet
, allclasses
[MAXOCLASSES
],
494 discosyms
[2 + MAXOCLASSES
+ 1], buf
[BUFSZ
];
495 int i
, ct
, dis
, xtras
;
497 winid tmpwin
= WIN_ERR
;
499 menu_item
*pick_list
= 0;
502 traditional
= (flags
.menu_style
== MENU_TRADITIONAL
503 || flags
.menu_style
== MENU_COMBINATION
);
505 tmpwin
= create_nhwindow(NHW_MENU
);
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 (void) strkitten(allclasses
, VENOM_CLASS
); /* append char to string */
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 (void) strkitten(discosyms
, '\033');
576 (void) strkitten(discosyms
, 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
, 0, 0, iflags
.menu_headings
,
692 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
);