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