1/* SCCS Id: @(#)read.c 3.4 2003/10/22 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5#include "hack.h" 6 7/* KMH -- Copied from pray.c; this really belongs in a header file */ 8#define DEVOUT 14 9#define STRIDENT 4 10 11#define Your_Own_Role(mndx) \ 12 ((mndx) == urole.malenum || \ 13 (urole.femalenum != NON_PM && (mndx) == urole.femalenum)) 14#define Your_Own_Race(mndx) \ 15 ((mndx) == urace.malenum || \ 16 (urace.femalenum != NON_PM && (mndx) == urace.femalenum)) 17 18#ifdef OVLB 19 20boolean known; 21 22static NEARDATA const char readable[] = 23 { ALL_CLASSES, SCROLL_CLASS, SPBOOK_CLASS, 0 }; 24static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; 25 26static void FDECL(wand_explode, (struct obj *)); 27static void NDECL(do_class_genocide); 28static void FDECL(stripspe,(struct obj *)); 29static void FDECL(p_glow1,(struct obj *)); 30static void FDECL(p_glow2,(struct obj *,const char *)); 31static void FDECL(randomize,(int *, int)); 32static void FDECL(forget_single_object, (int)); 33static void FDECL(forget, (int)); 34static void FDECL(maybe_tame, (struct monst *,struct obj *)); 35 36STATIC_PTR void FDECL(set_lit, (int,int,genericptr_t)); 37 38int 39doread() 40{ 41 register struct obj *scroll; 42 register boolean confused; 43 44 known = FALSE; 45 if(check_capacity((char *)0)) return (0); 46 scroll = getobj(readable, "read"); 47 if(!scroll) return(0); 48 49 /* outrumor has its own blindness check */ 50 if(scroll->otyp == FORTUNE_COOKIE) { 51 if(flags.verbose) 52 You("break up the cookie and throw away the pieces."); 53 outrumor(bcsign(scroll), BY_COOKIE); 54 if (!Blind) u.uconduct.literate++; 55 useup(scroll); 56 return(1); 57#ifdef TOURIST 58 } else if (scroll->otyp == T_SHIRT) { 59 static const char *shirt_msgs[] = { /* Scott Bigham */ 60 "I explored the Dungeons of Doom and all I got was this lousy T-shirt!", 61 "Is that Mjollnir in your pocket or are you just happy to see me?", 62 "It's not the size of your sword, it's how #enhance'd you are with it.", 63 "Madame Elvira's House O' Succubi Lifetime Customer", 64 "Madame Elvira's House O' Succubi Employee of the Month", 65 "Ludios Vault Guards Do It In Small, Dark Rooms", 66 "Yendor Military Soldiers Do It In Large Groups", 67 "I survived Yendor Military Boot Camp", 68 "Ludios Accounting School Intra-Mural Lacrosse Team", 69 "Oracle(TM) Fountains 10th Annual Wet T-Shirt Contest", 70 "Hey, black dragon! Disintegrate THIS!", 71 "I'm With Stupid -->", 72 "Don't blame me, I voted for Izchak!", 73 "Don't Panic", /* HHGTTG */ 74 "Furinkan High School Athletic Dept.", /* Ranma 1/2 */ 75 "Hel-LOOO, Nurse!", /* Animaniacs */ 76 }; 77 char buf[BUFSZ]; 78 int erosion; 79 80 if (Blind) { 81 You_cant("feel any Braille writing."); 82 return 0; 83 } 84 u.uconduct.literate++; 85 if(flags.verbose) 86 pline("It reads:"); 87 Strcpy(buf, shirt_msgs[scroll->o_id % SIZE(shirt_msgs)]); 88 erosion = greatest_erosion(scroll); 89 if (erosion) 90 wipeout_text(buf, 91 (int)(strlen(buf) * erosion / (2*MAX_ERODE)), 92 scroll->o_id ^ (unsigned)u.ubirthday); 93 pline("\"%s\"", buf); 94 return 1; 95#endif /* TOURIST */ 96 } else if (scroll->oclass != SCROLL_CLASS 97 && scroll->oclass != SPBOOK_CLASS) { 98 pline(silly_thing_to, "read"); 99 return(0); 100 } else if (Blind) { 101 const char *what = 0; 102 if (scroll->oclass == SPBOOK_CLASS) 103 what = "mystic runes"; 104 else if (!scroll->dknown) 105 what = "formula on the scroll"; 106 if (what) { 107 pline("Being blind, you cannot read the %s.", what); 108 return(0); 109 } 110 } 111 112 /* Actions required to win the game aren't counted towards conduct */ 113 if (scroll->otyp != SPE_BOOK_OF_THE_DEAD && 114 scroll->otyp != SPE_BLANK_PAPER && 115 scroll->otyp != SCR_BLANK_PAPER) 116 u.uconduct.literate++; 117 118 confused = (Confusion != 0); 119#ifdef MAIL 120 if (scroll->otyp == SCR_MAIL) confused = FALSE; 121#endif 122 if(scroll->oclass == SPBOOK_CLASS) { 123 return(study_book(scroll)); 124 } 125 scroll->in_use = TRUE; /* scroll, not spellbook, now being read */ 126 if(scroll->otyp != SCR_BLANK_PAPER) { 127 if(Blind) 128 pline("As you %s the formula on it, the scroll disappears.", 129 is_silent(youmonst.data) ? "cogitate" : "pronounce"); 130 else 131 pline("As you read the scroll, it disappears."); 132 if(confused) { 133 if (Hallucination) 134 pline("Being so trippy, you screw up..."); 135 else 136 pline("Being confused, you mis%s the magic words...", 137 is_silent(youmonst.data) ? "understand" : "pronounce"); 138 } 139 } 140 if(!seffects(scroll)) { 141 if(!objects[scroll->otyp].oc_name_known) { 142 if(known) { 143 makeknown(scroll->otyp); 144 more_experienced(0,10); 145 } else if(!objects[scroll->otyp].oc_uname) 146 docall(scroll); 147 } 148 if(scroll->otyp != SCR_BLANK_PAPER) 149 useup(scroll); 150 else scroll->in_use = FALSE; 151 } 152 return(1); 153} 154 155static void 156stripspe(obj) 157register struct obj *obj; 158{ 159 if (obj->blessed) pline(nothing_happens); 160 else { 161 if (obj->spe > 0) { 162 obj->spe = 0; 163 if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN) 164 obj->age = 0; 165 Your("%s %s briefly.",xname(obj), otense(obj, "vibrate")); 166 } else pline(nothing_happens); 167 } 168} 169 170static void 171p_glow1(otmp) 172register struct obj *otmp; 173{ 174 Your("%s %s briefly.", xname(otmp), 175 otense(otmp, Blind ? "vibrate" : "glow")); 176} 177 178static void 179p_glow2(otmp,color) 180register struct obj *otmp; 181register const char *color; 182{ 183 Your("%s %s%s%s for a moment.", 184 xname(otmp), 185 otense(otmp, Blind ? "vibrate" : "glow"), 186 Blind ? "" : " ", 187 Blind ? nul : hcolor(color)); 188} 189 190/* Is the object chargeable? For purposes of inventory display; it is */ 191/* possible to be able to charge things for which this returns FALSE. */ 192boolean 193is_chargeable(obj) 194struct obj *obj; 195{ 196 if (obj->oclass == WAND_CLASS) return TRUE; 197 /* known && !uname is possible after amnesia/mind flayer */ 198 if (obj->oclass == RING_CLASS) 199 return (boolean)(objects[obj->otyp].oc_charged && 200 (obj->known || objects[obj->otyp].oc_uname)); 201 if (is_weptool(obj)) /* specific check before general tools */ 202 return FALSE; 203 if (obj->oclass == TOOL_CLASS) 204 return (boolean)(objects[obj->otyp].oc_charged); 205 return FALSE; /* why are weapons/armor considered charged anyway? */ 206} 207 208/* 209 * recharge an object; curse_bless is -1 if the recharging implement 210 * was cursed, +1 if blessed, 0 otherwise. 211 */ 212void 213recharge(obj, curse_bless) 214struct obj *obj; 215int curse_bless; 216{ 217 register int n; 218 boolean is_cursed, is_blessed; 219 220 is_cursed = curse_bless < 0; 221 is_blessed = curse_bless > 0; 222 223 if (obj->oclass == WAND_CLASS) { 224 /* undo any prior cancellation, even when is_cursed */ 225 if (obj->spe == -1) obj->spe = 0; 226 227 /* 228 * Recharging might cause wands to explode. 229 * v = number of previous recharges 230 * v = percentage chance to explode on this attempt 231 * v = cumulative odds for exploding 232 * 0 : 0 0 233 * 1 : 0.29 0.29 234 * 2 : 2.33 2.62 235 * 3 : 7.87 10.28 236 * 4 : 18.66 27.02 237 * 5 : 36.44 53.62 238 * 6 : 62.97 82.83 239 * 7 : 100 100 240 */ 241 n = (int)obj->recharged; 242 if (n > 0 && (obj->otyp == WAN_WISHING || 243 (n * n * n > rn2(7*7*7)))) { /* recharge_limit */ 244 wand_explode(obj); 245 return; 246 } 247 /* didn't explode, so increment the recharge count */ 248 obj->recharged = (unsigned)(n + 1); 249 250 /* now handle the actual recharging */ 251 if (is_cursed) { 252 stripspe(obj); 253 } else { 254 int lim = (obj->otyp == WAN_WISHING) ? 3 : 255 (objects[obj->otyp].oc_dir != NODIR) ? 8 : 15; 256 257 n = (lim == 3) ? 3 : rn1(5, lim + 1 - 5); 258 if (!is_blessed) n = rnd(n); 259 260 if (obj->spe < n) obj->spe = n; 261 else obj->spe++; 262 if (obj->otyp == WAN_WISHING && obj->spe > 3) { 263 wand_explode(obj); 264 return; 265 } 266 if (obj->spe >= lim) p_glow2(obj, NH_BLUE); 267 else p_glow1(obj); 268 } 269 270 } else if (obj->oclass == RING_CLASS && 271 objects[obj->otyp].oc_charged) { 272 /* charging does not affect ring's curse/bless status */ 273 int s = is_blessed ? rnd(3) : is_cursed ? -rnd(2) : 1; 274 boolean is_on = (obj == uleft || obj == uright); 275 276 /* destruction depends on current state, not adjustment */ 277 if (obj->spe > rn2(7) || obj->spe <= -5) { 278 Your("%s %s momentarily, then %s!", 279 xname(obj), otense(obj,"pulsate"), otense(obj,"explode")); 280 if (is_on) Ring_gone(obj); 281 s = rnd(3 * abs(obj->spe)); /* amount of damage */ 282 useup(obj); 283 losehp(s, "exploding ring", KILLED_BY_AN); 284 } else { 285 long mask = is_on ? (obj == uleft ? LEFT_RING : 286 RIGHT_RING) : 0L; 287 Your("%s spins %sclockwise for a moment.", 288 xname(obj), s < 0 ? "counter" : ""); 289 /* cause attributes and/or properties to be updated */ 290 if (is_on) Ring_off(obj); 291 obj->spe += s; /* update the ring while it's off */ 292 if (is_on) setworn(obj, mask), Ring_on(obj); 293 /* oartifact: if a touch-sensitive artifact ring is 294 ever created the above will need to be revised */ 295 } 296 297 } else if (obj->oclass == TOOL_CLASS) { 298 int rechrg = (int)obj->recharged; 299 300 if (objects[obj->otyp].oc_charged) { 301 /* tools don't have a limit, but the counter used does */ 302 if (rechrg < 7) /* recharge_limit */ 303 obj->recharged++; 304 } 305 switch(obj->otyp) { 306 case BELL_OF_OPENING: 307 if (is_cursed) stripspe(obj); 308 else if (is_blessed) obj->spe += rnd(3); 309 else obj->spe += 1; 310 if (obj->spe > 5) obj->spe = 5; 311 break; 312 case MAGIC_MARKER: 313 case TINNING_KIT: 314#ifdef TOURIST 315 case EXPENSIVE_CAMERA: 316#endif 317 if (is_cursed) stripspe(obj); 318 else if (rechrg && obj->otyp == MAGIC_MARKER) { /* previously recharged */ 319 obj->recharged = 1; /* override increment done above */ 320 if (obj->spe < 3) 321 Your("marker seems permanently dried out."); 322 else 323 pline(nothing_happens); 324 } else if (is_blessed) { 325 n = rn1(16,15); /* 15..30 */ 326 if (obj->spe + n <= 50) 327 obj->spe = 50; 328 else if (obj->spe + n <= 75) 329 obj->spe = 75; 330 else { 331 int chrg = (int)obj->spe; 332 if ((chrg + n) > 127) 333 obj->spe = 127; 334 else 335 obj->spe += n; 336 } 337 p_glow2(obj, NH_BLUE); 338 } else { 339 n = rn1(11,10); /* 10..20 */ 340 if (obj->spe + n <= 50) 341 obj->spe = 50; 342 else { 343 int chrg = (int)obj->spe; 344 if ((chrg + n) > 127) 345 obj->spe = 127; 346 else 347 obj->spe += n; 348 } 349 p_glow2(obj, NH_WHITE); 350 } 351 break; 352 case OIL_LAMP: 353 case BRASS_LANTERN: 354 if (is_cursed) { 355 stripspe(obj); 356 if (obj->lamplit) { 357 if (!Blind) 358 pline("%s out!", Tobjnam(obj, "go")); 359 end_burn(obj, TRUE); 360 } 361 } else if (is_blessed) { 362 obj->spe = 1; 363 obj->age = 1500; 364 p_glow2(obj, NH_BLUE); 365 } else { 366 obj->spe = 1; 367 obj->age += 750; 368 if (obj->age > 1500) obj->age = 1500; 369 p_glow1(obj); 370 } 371 break; 372 case CRYSTAL_BALL: 373 if (is_cursed) stripspe(obj); 374 else if (is_blessed) { 375 obj->spe = 6; 376 p_glow2(obj, NH_BLUE); 377 } else { 378 if (obj->spe < 5) { 379 obj->spe++; 380 p_glow1(obj); 381 } else pline(nothing_happens); 382 } 383 break; 384 case HORN_OF_PLENTY: 385 case BAG_OF_TRICKS: 386 case CAN_OF_GREASE: 387 if (is_cursed) stripspe(obj); 388 else if (is_blessed) { 389 if (obj->spe <= 10) 390 obj->spe += rn1(10, 6); 391 else obj->spe += rn1(5, 6); 392 if (obj->spe > 50) obj->spe = 50; 393 p_glow2(obj, NH_BLUE); 394 } else { 395 obj->spe += rnd(5); 396 if (obj->spe > 50) obj->spe = 50; 397 p_glow1(obj); 398 } 399 break; 400 case MAGIC_FLUTE: 401 case MAGIC_HARP: 402 case FROST_HORN: 403 case FIRE_HORN: 404 case DRUM_OF_EARTHQUAKE: 405 if (is_cursed) { 406 stripspe(obj); 407 } else if (is_blessed) { 408 obj->spe += d(2,4); 409 if (obj->spe > 20) obj->spe = 20; 410 p_glow2(obj, NH_BLUE); 411 } else { 412 obj->spe += rnd(4); 413 if (obj->spe > 20) obj->spe = 20; 414 p_glow1(obj); 415 } 416 break; 417 default: 418 goto not_chargable; 419 /*NOTREACHED*/ 420 break; 421 } /* switch */ 422 423 } else { 424 not_chargable: 425 You("have a feeling of loss."); 426 } 427} 428 429 430/* Forget known information about this object class. */ 431static void 432forget_single_object(obj_id) 433 int obj_id; 434{ 435 objects[obj_id].oc_name_known = 0; 436 objects[obj_id].oc_pre_discovered = 0; /* a discovery when relearned */ 437 if (objects[obj_id].oc_uname) { 438 free((genericptr_t)objects[obj_id].oc_uname); 439 objects[obj_id].oc_uname = 0; 440 } 441 undiscover_object(obj_id); /* after clearing oc_name_known */ 442 443 /* clear & free object names from matching inventory items too? */ 444} 445 446 447#if 0 /* here if anyone wants it.... */ 448/* Forget everything known about a particular object class. */ 449static void 450forget_objclass(oclass) 451 int oclass; 452{ 453 int i; 454 455 for (i=bases[oclass]; 456 i < NUM_OBJECTS && objects[i].oc_class==oclass; i++) 457 forget_single_object(i); 458} 459#endif 460 461 462/* randomize the given list of numbers 0 <= i < count */ 463static void 464randomize(indices, count) 465 int *indices; 466 int count; 467{ 468 int i, iswap, temp; 469 470 for (i = count - 1; i > 0; i--) { 471 if ((iswap = rn2(i + 1)) == i) continue; 472 temp = indices[i]; 473 indices[i] = indices[iswap]; 474 indices[iswap] = temp; 475 } 476} 477 478 479/* Forget % of known objects. */ 480void 481forget_objects(percent) 482 int percent; 483{ 484 int i, count; 485 int indices[NUM_OBJECTS]; 486 487 if (percent == 0) return; 488 if (percent <= 0 || percent > 100) { 489 impossible("forget_objects: bad percent %d", percent); 490 return; 491 } 492 493 for (count = 0, i = 1; i < NUM_OBJECTS; i++) 494 if (OBJ_DESCR(objects[i]) && 495 (objects[i].oc_name_known || objects[i].oc_uname)) 496 indices[count++] = i; 497 498 randomize(indices, count); 499 500 /* forget first % of randomized indices */ 501 count = ((count * percent) + 50) / 100; 502 for (i = 0; i < count; i++) 503 forget_single_object(indices[i]); 504} 505 506 507/* Forget some or all of map (depends on parameters). */ 508void 509forget_map(howmuch) 510 int howmuch; 511{ 512 register int zx, zy; 513 514 if (In_sokoban(&u.uz)) 515 return; 516 517 known = TRUE; 518 for(zx = 0; zx < COLNO; zx++) for(zy = 0; zy < ROWNO; zy++) 519 if (howmuch & ALL_MAP || rn2(7)) { 520 /* Zonk all memory of this location. */ 521 levl[zx][zy].seenv = 0; 522 levl[zx][zy].waslit = 0; 523 levl[zx][zy].glyph = cmap_to_glyph(S_stone); 524 } 525} 526 527/* Forget all traps on the level. */ 528void 529forget_traps() 530{ 531 register struct trap *trap; 532 533 /* forget all traps (except the one the hero is in :-) */ 534 for (trap = ftrap; trap; trap = trap->ntrap) 535 if ((trap->tx != u.ux || trap->ty != u.uy) && (trap->ttyp != HOLE)) 536 trap->tseen = 0; 537} 538 539/* 540 * Forget given % of all levels that the hero has visited and not forgotten, 541 * except this one. 542 */ 543void 544forget_levels(percent) 545 int percent; 546{ 547 int i, count; 548 xchar maxl, this_lev; 549 int indices[MAXLINFO]; 550 551 if (percent == 0) return; 552 553 if (percent <= 0 || percent > 100) { 554 impossible("forget_levels: bad percent %d", percent); 555 return; 556 } 557 558 this_lev = ledger_no(&u.uz); 559 maxl = maxledgerno(); 560 561 /* count & save indices of non-forgotten visited levels */ 562 /* Sokoban levels are pre-mapped for the player, and should stay 563 * so, or they become nearly impossible to solve. But try to 564 * shift the forgetting elsewhere by fiddling with percent 565 * instead of forgetting fewer levels. 566 */ 567 for (count = 0, i = 0; i <= maxl; i++) 568 if ((level_info[i].flags & VISITED) && 569 !(level_info[i].flags & FORGOTTEN) && i != this_lev) { 570 if (ledger_to_dnum(i) == sokoban_dnum) 571 percent += 2; 572 else 573 indices[count++] = i; 574 } 575 576 if (percent > 100) percent = 100; 577 578 randomize(indices, count); 579 580 /* forget first % of randomized indices */ 581 count = ((count * percent) + 50) / 100; 582 for (i = 0; i < count; i++) { 583 level_info[indices[i]].flags |= FORGOTTEN; 584 } 585} 586 587/* 588 * Forget some things (e.g. after reading a scroll of amnesia). When called, 589 * the following are always forgotten: 590 * 591 * - felt ball & chain 592 * - traps 593 * - part (6 out of 7) of the map 594 * 595 * Other things are subject to flags: 596 * 597 * howmuch & ALL_MAP = forget whole map 598 * howmuch & ALL_SPELLS = forget all spells 599 */ 600static void 601forget(howmuch) 602int howmuch; 603{ 604 605 if (Punished) u.bc_felt = 0; /* forget felt ball&chain */ 606 607 forget_map(howmuch); 608 forget_traps(); 609 610 /* 1 in 3 chance of forgetting some levels */ 611 if (!rn2(3)) forget_levels(rn2(25)); 612 613 /* 1 in 3 chance of forgeting some objects */ 614 if (!rn2(3)) forget_objects(rn2(25)); 615 616 if (howmuch & ALL_SPELLS) losespells(); 617 /* 618 * Make sure that what was seen is restored correctly. To do this, 619 * we need to go blind for an instant --- turn off the display, 620 * then restart it. All this work is needed to correctly handle 621 * walls which are stone on one side and wall on the other. Turning 622 * off the seen bits above will make the wall revert to stone, but 623 * there are cases where we don't want this to happen. The easiest 624 * thing to do is to run it through the vision system again, which 625 * is always correct. 626 */ 627 docrt(); /* this correctly will reset vision */ 628} 629 630/* monster is hit by scroll of taming's effect */ 631static void 632maybe_tame(mtmp, sobj) 633struct monst *mtmp; 634struct obj *sobj; 635{ 636 if (sobj->cursed) { 637 setmangry(mtmp); 638 } else { 639 if (mtmp->isshk) 640 make_happy_shk(mtmp, FALSE); 641 else if (!resist(mtmp, sobj->oclass, 0, NOTELL)) 642 (void) tamedog(mtmp, (struct obj *) 0); 643 } 644} 645 646int 647seffects(sobj) 648register struct obj *sobj; 649{ 650 register int cval; 651 register boolean confused = (Confusion != 0); 652 register struct obj *otmp; 653 654 if (objects[sobj->otyp].oc_magic) 655 exercise(A_WIS, TRUE); /* just for trying */ 656 switch(sobj->otyp) { 657#ifdef MAIL 658 case SCR_MAIL: 659 known = TRUE; 660 if (sobj->spe) 661 pline("This seems to be junk mail addressed to the finder of the Eye of Larn."); 662 /* note to the puzzled: the game Larn actually sends you junk 663 * mail if you win! 664 */ 665 else readmail(sobj); 666 break; 667#endif 668 case SCR_ENCHANT_ARMOR: 669 { 670 register schar s; 671 boolean special_armor; 672 boolean same_color; 673 674 otmp = some_armor(&youmonst); 675 if(!otmp) { 676 strange_feeling(sobj, 677 !Blind ? "Your skin glows then fades." : 678 "Your skin feels warm for a moment."); 679 exercise(A_CON, !sobj->cursed); 680 exercise(A_STR, !sobj->cursed); 681 return(1); 682 } 683 if(confused) { 684 otmp->oerodeproof = !(sobj->cursed); 685 if(Blind) { 686 otmp->rknown = FALSE; 687 Your("%s %s warm for a moment.", 688 xname(otmp), otense(otmp, "feel")); 689 } else { 690 otmp->rknown = TRUE; 691 Your("%s %s covered by a %s %s %s!", 692 xname(otmp), otense(otmp, "are"), 693 sobj->cursed ? "mottled" : "shimmering", 694 hcolor(sobj->cursed ? NH_BLACK : NH_GOLDEN), 695 sobj->cursed ? "glow" : 696 (is_shield(otmp) ? "layer" : "shield")); 697 } 698 if (otmp->oerodeproof && 699 (otmp->oeroded || otmp->oeroded2)) { 700 otmp->oeroded = otmp->oeroded2 = 0; 701 Your("%s %s as good as new!", 702 xname(otmp), 703 otense(otmp, Blind ? "feel" : "look")); 704 } 705 break; 706 } 707 /* elven armor vibrates warningly when enchanted beyond a limit */ 708 special_armor = is_elven_armor(otmp) || 709 (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM); 710 if (sobj->cursed) 711 same_color = 712 (otmp->otyp == BLACK_DRAGON_SCALE_MAIL || 713 otmp->otyp == BLACK_DRAGON_SCALES); 714 else 715 same_color = 716 (otmp->otyp == SILVER_DRAGON_SCALE_MAIL || 717 otmp->otyp == SILVER_DRAGON_SCALES || 718 otmp->otyp == SHIELD_OF_REFLECTION); 719 if (Blind) same_color = FALSE; 720 721 /* KMH -- catch underflow */ 722 s = sobj->cursed ? -otmp->spe : otmp->spe; 723 if (s > (special_armor ? 5 : 3) && rn2(s)) { 724 Your("%s violently %s%s%s for a while, then %s.", 725 xname(otmp), 726 otense(otmp, Blind ? "vibrate" : "glow"), 727 (!Blind && !same_color) ? " " : nul, 728 (Blind || same_color) ? nul : 729 hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), 730 otense(otmp, "evaporate")); 731 if(is_cloak(otmp)) (void) Cloak_off(); 732 if(is_boots(otmp)) (void) Boots_off(); 733 if(is_helmet(otmp)) (void) Helmet_off(); 734 if(is_gloves(otmp)) (void) Gloves_off(); 735 if(is_shield(otmp)) (void) Shield_off(); 736 if(otmp == uarm) (void) Armor_gone(); 737 useup(otmp); 738 break; 739 } 740 s = sobj->cursed ? -1 : 741 otmp->spe >= 9 ? (rn2(otmp->spe) == 0) : 742 sobj->blessed ? rnd(3-otmp->spe/3) : 1; 743 if (s >= 0 && otmp->otyp >= GRAY_DRAGON_SCALES && 744 otmp->otyp <= YELLOW_DRAGON_SCALES) { 745 /* dragon scales get turned into dragon scale mail */ 746 Your("%s merges and hardens!", xname(otmp)); 747 setworn((struct obj *)0, W_ARM); 748 /* assumes same order */ 749 otmp->otyp = GRAY_DRAGON_SCALE_MAIL + 750 otmp->otyp - GRAY_DRAGON_SCALES; 751 otmp->cursed = 0; 752 if (sobj->blessed) { 753 otmp->spe++; 754 otmp->blessed = 1; 755 } 756 otmp->known = 1; 757 setworn(otmp, W_ARM); 758 break; 759 } 760 Your("%s %s%s%s%s for a %s.", 761 xname(otmp), 762 s == 0 ? "violently " : nul, 763 otense(otmp, Blind ? "vibrate" : "glow"), 764 (!Blind && !same_color) ? " " : nul, 765 (Blind || same_color) ? nul : hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), 766 (s*s>1) ? "while" : "moment"); 767 otmp->cursed = sobj->cursed; 768 if (!otmp->blessed || sobj->cursed) 769 otmp->blessed = sobj->blessed; 770 if (s) { 771 otmp->spe += s; 772 adj_abon(otmp, s); 773 known = otmp->known; 774 } 775 776 if ((otmp->spe > (special_armor ? 5 : 3)) && 777 (special_armor || !rn2(7))) 778 Your("%s suddenly %s %s.", 779 xname(otmp), otense(otmp, "vibrate"), 780 Blind ? "again" : "unexpectedly"); 781 break; 782 } 783 case SCR_DESTROY_ARMOR: 784 { 785 otmp = some_armor(&youmonst); 786 if(confused) { 787 if(!otmp) { 788 strange_feeling(sobj,"Your bones itch."); 789 exercise(A_STR, FALSE); 790 exercise(A_CON, FALSE); 791 return(1); 792 } 793 otmp->oerodeproof = sobj->cursed; 794 p_glow2(otmp, NH_PURPLE); 795 break; 796 } 797 if(!sobj->cursed || !otmp || !otmp->cursed) { 798 if(!destroy_arm(otmp)) { 799 strange_feeling(sobj,"Your skin itches."); 800 exercise(A_STR, FALSE); 801 exercise(A_CON, FALSE); 802 return(1); 803 } else 804 known = TRUE; 805 } else { /* armor and scroll both cursed */ 806 Your("%s %s.", xname(otmp), otense(otmp, "vibrate")); 807 if (otmp->spe >= -6) otmp->spe--; 808 make_stunned(HStun + rn1(10, 10), TRUE); 809 } 810 } 811 break; 812 case SCR_CONFUSE_MONSTER: 813 case SPE_CONFUSE_MONSTER: 814 if(youmonst.data->mlet != S_HUMAN || sobj->cursed) { 815 if(!HConfusion) You_feel("confused."); 816 make_confused(HConfusion + rnd(100),FALSE); 817 } else if(confused) { 818 if(!sobj->blessed) { 819 Your("%s begin to %s%s.", 820 makeplural(body_part(HAND)), 821 Blind ? "tingle" : "glow ", 822 Blind ? nul : hcolor(NH_PURPLE)); 823 make_confused(HConfusion + rnd(100),FALSE); 824 } else { 825 pline("A %s%s surrounds your %s.", 826 Blind ? nul : hcolor(NH_RED), 827 Blind ? "faint buzz" : " glow", 828 body_part(HEAD)); 829 make_confused(0L,TRUE); 830 } 831 } else { 832 if (!sobj->blessed) { 833 Your("%s%s %s%s.", 834 makeplural(body_part(HAND)), 835 Blind ? "" : " begin to glow", 836 Blind ? (const char *)"tingle" : hcolor(NH_RED), 837 u.umconf ? " even more" : ""); 838 u.umconf++; 839 } else { 840 if (Blind) 841 Your("%s tingle %s sharply.", 842 makeplural(body_part(HAND)), 843 u.umconf ? "even more" : "very"); 844 else 845 Your("%s glow a%s brilliant %s.", 846 makeplural(body_part(HAND)), 847 u.umconf ? "n even more" : "", 848 hcolor(NH_RED)); 849 /* after a while, repeated uses become less effective */ 850 if (u.umconf >= 40) 851 u.umconf++; 852 else 853 u.umconf += rn1(8, 2); 854 } 855 } 856 break; 857 case SCR_SCARE_MONSTER: 858 case SPE_CAUSE_FEAR: 859 { register int ct = 0; 860 register struct monst *mtmp; 861 862 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 863 if (DEADMONSTER(mtmp)) continue; 864 if(cansee(mtmp->mx,mtmp->my)) { 865 if(confused || sobj->cursed) { 866 mtmp->mflee = mtmp->mfrozen = mtmp->msleeping = 0; 867 mtmp->mcanmove = 1; 868 } else 869 if (! resist(mtmp, sobj->oclass, 0, NOTELL)) 870 monflee(mtmp, 0, FALSE, FALSE); 871 if(!mtmp->mtame) ct++; /* pets don't laugh at you */ 872 } 873 } 874 if(!ct) 875 You_hear("%s in the distance.", 876 (confused || sobj->cursed) ? "sad wailing" : 877 "maniacal laughter"); 878 else if(sobj->otyp == SCR_SCARE_MONSTER) 879 You_hear("%s close by.", 880 (confused || sobj->cursed) ? "sad wailing" : 881 "maniacal laughter"); 882 break; 883 } 884 case SCR_BLANK_PAPER: 885 if (Blind) 886 You("don't remember there being any magic words on this scroll."); 887 else 888 pline("This scroll seems to be blank."); 889 known = TRUE; 890 break; 891 case SCR_REMOVE_CURSE: 892 case SPE_REMOVE_CURSE: 893 { register struct obj *obj; 894 if(confused) 895 if (Hallucination) 896 You_feel("the power of the Force against you!"); 897 else 898 You_feel("like you need some help."); 899 else 900 if (Hallucination) 901 You_feel("in touch with the Universal Oneness."); 902 else 903 You_feel("like someone is helping you."); 904 905 if (sobj->cursed) { 906 pline_The("scroll disintegrates."); 907 } else { 908 for (obj = invent; obj; obj = obj->nobj) { 909 long wornmask; 910#ifdef GOLDOBJ 911 /* gold isn't subject to cursing and blessing */ 912 if (obj->oclass == COIN_CLASS) continue; 913#endif 914 wornmask = (obj->owornmask & ~(W_BALL|W_ART|W_ARTI)); 915 if (wornmask && !sobj->blessed) { 916 /* handle a couple of special cases; we don't 917 allow auxiliary weapon slots to be used to 918 artificially increase number of worn items */ 919 if (obj == uswapwep) { 920 if (!u.twoweap) wornmask = 0L; 921 } else if (obj == uquiver) { 922 if (obj->oclass == WEAPON_CLASS) { 923 /* mergeable weapon test covers ammo, 924 missiles, spears, daggers & knives */ 925 if (!objects[obj->otyp].oc_merge) 926 wornmask = 0L; 927 } else if (obj->oclass == GEM_CLASS) { 928 /* possibly ought to check whether 929 alternate weapon is a sling... */ 930 if (!uslinging()) wornmask = 0L; 931 } else { 932 /* weptools don't merge and aren't 933 reasonable quivered weapons */ 934 wornmask = 0L; 935 } 936 } 937 } 938 if (sobj->blessed || wornmask || 939 obj->otyp == LOADSTONE || 940 (obj->otyp == LEASH && obj->leashmon)) { 941 if(confused) blessorcurse(obj, 2); 942 else uncurse(obj); 943 } 944 } 945 } 946 if(Punished && !confused) unpunish(); 947 update_inventory(); 948 break; 949 } 950 case SCR_CREATE_MONSTER: 951 case SPE_CREATE_MONSTER: 952 if (create_critters(1 + ((confused || sobj->cursed) ? 12 : 0) + 953 ((sobj->blessed || rn2(73)) ? 0 : rnd(4)), 954 confused ? &mons[PM_ACID_BLOB] : (struct permonst *)0)) 955 known = TRUE; 956 /* no need to flush monsters; we ask for identification only if the 957 * monsters are not visible 958 */ 959 break; 960 case SCR_ENCHANT_WEAPON: 961 if(uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)) 962 && confused) { 963 /* oclass check added 10/25/86 GAN */ 964 uwep->oerodeproof = !(sobj->cursed); 965 if (Blind) { 966 uwep->rknown = FALSE; 967 Your("weapon feels warm for a moment."); 968 } else { 969 uwep->rknown = TRUE; 970 Your("%s covered by a %s %s %s!", 971 aobjnam(uwep, "are"), 972 sobj->cursed ? "mottled" : "shimmering", 973 hcolor(sobj->cursed ? NH_PURPLE : NH_GOLDEN), 974 sobj->cursed ? "glow" : "shield"); 975 } 976 if (uwep->oerodeproof && (uwep->oeroded || uwep->oeroded2)) { 977 uwep->oeroded = uwep->oeroded2 = 0; 978 Your("%s as good as new!", 979 aobjnam(uwep, Blind ? "feel" : "look")); 980 } 981 } else return !chwepon(sobj, 982 sobj->cursed ? -1 : 983 !uwep ? 1 : 984 uwep->spe >= 9 ? (rn2(uwep->spe) == 0) : 985 sobj->blessed ? rnd(3-uwep->spe/3) : 1); 986 break; 987 case SCR_TAMING: 988 case SPE_CHARM_MONSTER: 989 if (u.uswallow) { 990 maybe_tame(u.ustuck, sobj); 991 } else { 992 int i, j, bd = confused ? 5 : 1; 993 struct monst *mtmp; 994 995 for (i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++) { 996 if (!isok(u.ux + i, u.uy + j)) continue; 997 if ((mtmp = m_at(u.ux + i, u.uy + j)) != 0) 998 maybe_tame(mtmp, sobj); 999 } 1000 } 1001 break; 1002 case SCR_GENOCIDE: 1003 You("have found a scroll of genocide!"); 1004 known = TRUE; 1005 if (sobj->blessed) do_class_genocide(); 1006 else do_genocide(!sobj->cursed | (2 * !!Confusion)); 1007 break; 1008 case SCR_LIGHT: 1009 if(!Blind) known = TRUE; 1010 litroom(!confused && !sobj->cursed, sobj); 1011 break; 1012 case SCR_TELEPORTATION: 1013 if(confused || sobj->cursed) level_tele(); 1014 else { 1015 if (sobj->blessed && !Teleport_control) { 1016 known = TRUE; 1017 if (yn("Do you wish to teleport?")=='n') 1018 break; 1019 } 1020 tele(); 1021 if(Teleport_control || !couldsee(u.ux0, u.uy0) || 1022 (distu(u.ux0, u.uy0) >= 16)) 1023 known = TRUE; 1024 } 1025 break; 1026 case SCR_GOLD_DETECTION: 1027 if (confused || sobj->cursed) return(trap_detect(sobj)); 1028 else return(gold_detect(sobj)); 1029 case SCR_FOOD_DETECTION: 1030 case SPE_DETECT_FOOD: 1031 if (food_detect(sobj)) 1032 return(1); /* nothing detected */ 1033 break; 1034 case SPE_IDENTIFY: 1035 cval = rn2(5); 1036 goto id; 1037 case SCR_IDENTIFY: 1038 /* known = TRUE; */ 1039 if(confused) 1040 You("identify this as an identify scroll."); 1041 else 1042 pline("This is an identify scroll."); 1043 if (sobj->blessed || (!sobj->cursed && !rn2(5))) { 1044 cval = rn2(5); 1045 /* Note: if rn2(5)==0, identify all items */ 1046 if (cval == 1 && sobj->blessed && Luck > 0) ++cval; 1047 } else cval = 1; 1048 if(!objects[sobj->otyp].oc_name_known) more_experienced(0,10); 1049 useup(sobj); 1050 makeknown(SCR_IDENTIFY); 1051 id: 1052 if(invent && !confused) { 1053 identify_pack(cval); 1054 } 1055 return(1); 1056 case SCR_CHARGING: 1057 if (confused) { 1058 You_feel("charged up!"); 1059 if (u.uen < u.uenmax) 1060 u.uen = u.uenmax; 1061 else 1062 u.uen = (u.uenmax += d(5,4)); 1063 flags.botl = 1; 1064 break; 1065 } 1066 known = TRUE; 1067 pline("This is a charging scroll."); 1068 otmp = getobj(all_count, "charge"); 1069 if (!otmp) break; 1070 recharge(otmp, sobj->cursed ? -1 : (sobj->blessed ? 1 : 0)); 1071 break; 1072 case SCR_MAGIC_MAPPING: 1073 if (level.flags.nommap) { 1074 Your("mind is filled with crazy lines!"); 1075 if (Hallucination) 1076 pline("Wow! Modern art."); 1077 else 1078 Your("%s spins in bewilderment.", body_part(HEAD)); 1079 make_confused(HConfusion + rnd(30), FALSE); 1080 break; 1081 } 1082 if (sobj->blessed) { 1083 register int x, y; 1084 1085 for (x = 1; x < COLNO; x++) 1086 for (y = 0; y < ROWNO; y++) 1087 if (levl[x][y].typ == SDOOR) 1088 cvt_sdoor_to_door(&levl[x][y]); 1089 /* do_mapping() already reveals secret passages */ 1090 } 1091 known = TRUE; 1092 case SPE_MAGIC_MAPPING: 1093 if (level.flags.nommap) { 1094 Your("%s spins as %s blocks the spell!", body_part(HEAD), something); 1095 make_confused(HConfusion + rnd(30), FALSE); 1096 break; 1097 } 1098 pline("A map coalesces in your mind!"); 1099 cval = (sobj->cursed && !confused); 1100 if(cval) HConfusion = 1; /* to screw up map */ 1101 do_mapping(); 1102 if(cval) { 1103 HConfusion = 0; /* restore */ 1104 pline("Unfortunately, you can't grasp the details."); 1105 } 1106 break; 1107 case SCR_AMNESIA: 1108 known = TRUE; 1109 forget( (!sobj->blessed ? ALL_SPELLS : 0) | 1110 (!confused || sobj->cursed ? ALL_MAP : 0) ); 1111 if (Hallucination) /* Ommmmmm! */ 1112 Your("mind releases itself from mundane concerns."); 1113 else if (!strncmpi(plname, "Maud", 4)) 1114 pline("As your mind turns inward on itself, you forget everything else."); 1115 else if (rn2(2)) 1116 pline("Who was that Maud person anyway?"); 1117 else 1118 pline("Thinking of Maud you forget everything else."); 1119 exercise(A_WIS, FALSE); 1120 break; 1121 case SCR_FIRE: 1122 /* 1123 * Note: Modifications have been made as of 3.0 to allow for 1124 * some damage under all potential cases. 1125 */ 1126 cval = bcsign(sobj); 1127 if(!objects[sobj->otyp].oc_name_known) more_experienced(0,10); 1128 useup(sobj); 1129 makeknown(SCR_FIRE); 1130 if(confused) { 1131 if(Fire_resistance) { 1132 shieldeff(u.ux, u.uy); 1133 if(!Blind) 1134 pline("Oh, look, what a pretty fire in your %s.", 1135 makeplural(body_part(HAND))); 1136 else You_feel("a pleasant warmth in your %s.", 1137 makeplural(body_part(HAND))); 1138 } else { 1139 pline_The("scroll catches fire and you burn your %s.", 1140 makeplural(body_part(HAND))); 1141 losehp(1, "scroll of fire", KILLED_BY_AN); 1142 } 1143 return(1); 1144 } 1145 if (Underwater) 1146 pline_The("water around you vaporizes violently!"); 1147 else { 1148 pline_The("scroll erupts in a tower of flame!"); 1149 burn_away_slime(); 1150 } 1151 explode(u.ux, u.uy, 11, (2*(rn1(3, 3) + 2 * cval) + 1)/3, 1152 SCROLL_CLASS, EXPL_FIERY); 1153 return(1); 1154 case SCR_EARTH: 1155 /* TODO: handle steeds */ 1156 if ( 1157#ifdef REINCARNATION 1158 !Is_rogue_level(&u.uz) && 1159#endif 1160 (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) { 1161 register int x, y; 1162 1163 /* Identify the scroll */ 1164 pline_The("%s rumbles %s you!", ceiling(u.ux,u.uy), 1165 sobj->blessed ? "around" : "above"); 1166 known = 1; 1167 if (In_sokoban(&u.uz)) 1168 change_luck(-1); /* Sokoban guilt */ 1169 1170 /* Loop through the surrounding squares */ 1171 if (!sobj->cursed) for (x = u.ux-1; x <= u.ux+1; x++) { 1172 for (y = u.uy-1; y <= u.uy+1; y++) { 1173 1174 /* Is this a suitable spot? */ 1175 if (isok(x, y) && !closed_door(x, y) && 1176 !IS_ROCK(levl[x][y].typ) && 1177 !IS_AIR(levl[x][y].typ) && 1178 (x != u.ux || y != u.uy)) { 1179 register struct obj *otmp2; 1180 register struct monst *mtmp; 1181 1182 /* Make the object(s) */ 1183 otmp2 = mksobj(confused ? ROCK : BOULDER, 1184 FALSE, FALSE); 1185 if (!otmp2) continue; /* Shouldn't happen */ 1186 otmp2->quan = confused ? rn1(5,2) : 1; 1187 otmp2->owt = weight(otmp2); 1188 1189 /* Find the monster here (won't be player) */ 1190 mtmp = m_at(x, y); 1191 if (mtmp && !amorphous(mtmp->data) && 1192 !passes_walls(mtmp->data) && 1193 !noncorporeal(mtmp->data) && 1194 !unsolid(mtmp->data)) { 1195 struct obj *helmet = which_armor(mtmp, W_ARMH); 1196 int mdmg; 1197 1198 if (cansee(mtmp->mx, mtmp->my)) { 1199 pline("%s is hit by %s!", Monnam(mtmp), 1200 doname(otmp2)); 1201 if (mtmp->minvis && !canspotmon(mtmp)) 1202 map_invisible(mtmp->mx, mtmp->my); 1203 } 1204 mdmg = dmgval(otmp2, mtmp) * otmp2->quan; 1205 if (helmet) { 1206 if(is_metallic(helmet)) { 1207 if (canspotmon(mtmp)) 1208 pline("Fortunately, %s is wearing a hard helmet.", mon_nam(mtmp)); 1209 else if (flags.soundok) 1210 You_hear("a clanging sound."); 1211 if (mdmg > 2) mdmg = 2; 1212 } else { 1213 if (canspotmon(mtmp)) 1214 pline("%s's %s does not protect %s.", 1215 Monnam(mtmp), xname(helmet), 1216 mhim(mtmp)); 1217 } 1218 } 1219 mtmp->mhp -= mdmg; 1220 if (mtmp->mhp <= 0) 1221 xkilled(mtmp, 1); 1222 } 1223 /* Drop the rock/boulder to the floor */ 1224 if (!flooreffects(otmp2, x, y, "fall")) { 1225 place_object(otmp2, x, y); 1226 stackobj(otmp2); 1227 newsym(x, y); /* map the rock */ 1228 } 1229 } 1230 } 1231 } 1232 /* Attack the player */ 1233 if (!sobj->blessed) { 1234 int dmg; 1235 struct obj *otmp2; 1236 1237 /* Okay, _you_ write this without repeating the code */ 1238 otmp2 = mksobj(confused ? ROCK : BOULDER, 1239 FALSE, FALSE); 1240 if (!otmp2) break; 1241 otmp2->quan = confused ? rn1(5,2) : 1; 1242 otmp2->owt = weight(otmp2); 1243 if (!amorphous(youmonst.data) && 1244 !Passes_walls && 1245 !noncorporeal(youmonst.data) && 1246 !unsolid(youmonst.data)) { 1247 You("are hit by %s!", doname(otmp2)); 1248 dmg = dmgval(otmp2, &youmonst) * otmp2->quan; 1249 if (uarmh && !sobj->cursed) { 1250 if(is_metallic(uarmh)) { 1251 pline("Fortunately, you are wearing a hard helmet."); 1252 if (dmg > 2) dmg = 2; 1253 } else if (flags.verbose) { 1254 Your("%s does not protect you.", 1255 xname(uarmh)); 1256 } 1257 } 1258 } else 1259 dmg = 0; 1260 /* Must be before the losehp(), for bones files */ 1261 if (!flooreffects(otmp2, u.ux, u.uy, "fall")) { 1262 place_object(otmp2, u.ux, u.uy); 1263 stackobj(otmp2); 1264 newsym(u.ux, u.uy); 1265 } 1266 if (dmg) losehp(dmg, "scroll of earth", KILLED_BY_AN); 1267 } 1268 } 1269 break; 1270 case SCR_PUNISHMENT: 1271 known = TRUE; 1272 if(confused || sobj->blessed) { 1273 You_feel("guilty."); 1274 break; 1275 } 1276 punish(sobj); 1277 break; 1278 case SCR_STINKING_CLOUD: { 1279 coord cc; 1280 1281 You("have found a scroll of stinking cloud!"); 1282 known = TRUE; 1283 pline("Where do you want to center the cloud?"); 1284 cc.x = u.ux; 1285 cc.y = u.uy; 1286 if (getpos(&cc, TRUE, "the desired position") < 0) { 1287 pline(Never_mind); 1288 return 0; 1289 } 1290 if (!cansee(cc.x, cc.y) || distu(cc.x, cc.y) >= 32) { 1291 You("smell rotten eggs."); 1292 return 0; 1293 } 1294 (void) create_gas_cloud(cc.x, cc.y, 3+bcsign(sobj), 1295 8+4*bcsign(sobj)); 1296 break; 1297 } 1298 default: 1299 impossible("What weird effect is this? (%u)", sobj->otyp); 1300 } 1301 return(0); 1302} 1303 1304static void 1305wand_explode(obj) 1306register struct obj *obj; 1307{ 1308 obj->in_use = TRUE; /* in case losehp() is fatal */ 1309 Your("%s vibrates violently, and explodes!",xname(obj)); 1310 nhbell(); 1311 losehp(rnd(2*(u.uhpmax+1)/3), "exploding wand", KILLED_BY_AN); 1312 useup(obj); 1313 exercise(A_STR, FALSE); 1314} 1315 1316/* 1317 * Low-level lit-field update routine. 1318 */ 1319STATIC_PTR void 1320set_lit(x,y,val) 1321int x, y; 1322genericptr_t val; 1323{ 1324 if (val) 1325 levl[x][y].lit = 1; 1326 else { 1327 levl[x][y].lit = 0; 1328 snuff_light_source(x, y); 1329 } 1330} 1331 1332void 1333litroom(on,obj) 1334register boolean on; 1335struct obj *obj; 1336{ 1337 char is_lit; /* value is irrelevant; we use its address 1338 as a `not null' flag for set_lit() */ 1339 1340 /* first produce the text (provided you're not blind) */ 1341 if(!on) { 1342 register struct obj *otmp; 1343 1344 if (!Blind) { 1345 if(u.uswallow) { 1346 pline("It seems even darker in here than before."); 1347 return; 1348 } 1349 if (uwep && artifact_light(uwep) && uwep->lamplit) 1350 pline("Suddenly, the only light left comes from %s!", 1351 the(xname(uwep))); 1352 else 1353 You("are surrounded by darkness!"); 1354 } 1355 1356 /* the magic douses lamps, et al, too */ 1357 for(otmp = invent; otmp; otmp = otmp->nobj) 1358 if (otmp->lamplit) 1359 (void) snuff_lit(otmp); 1360 if (Blind) goto do_it; 1361 } else { 1362 if (Blind) goto do_it; 1363 if(u.uswallow){ 1364 if (is_animal(u.ustuck->data)) 1365 pline("%s %s is lit.", 1366 s_suffix(Monnam(u.ustuck)), 1367 mbodypart(u.ustuck, STOMACH)); 1368 else 1369 if (is_whirly(u.ustuck->data)) 1370 pline("%s shines briefly.", 1371 Monnam(u.ustuck)); 1372 else 1373 pline("%s glistens.", Monnam(u.ustuck)); 1374 return; 1375 } 1376 pline("A lit field surrounds you!"); 1377 } 1378 1379do_it: 1380 /* No-op in water - can only see the adjacent squares and that's it! */ 1381 if (Underwater || Is_waterlevel(&u.uz)) return; 1382 /* 1383 * If we are darkening the room and the hero is punished but not 1384 * blind, then we have to pick up and replace the ball and chain so 1385 * that we don't remember them if they are out of sight. 1386 */ 1387 if (Punished && !on && !Blind) 1388 move_bc(1, 0, uball->ox, uball->oy, uchain->ox, uchain->oy); 1389 1390#ifdef REINCARNATION 1391 if (Is_rogue_level(&u.uz)) { 1392 /* Can't use do_clear_area because MAX_RADIUS is too small */ 1393 /* rogue lighting must light the entire room */ 1394 int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET; 1395 int rx, ry; 1396 if(rnum >= 0) { 1397 for(rx = rooms[rnum].lx-1; rx <= rooms[rnum].hx+1; rx++) 1398 for(ry = rooms[rnum].ly-1; ry <= rooms[rnum].hy+1; ry++) 1399 set_lit(rx, ry, 1400 (genericptr_t)(on ? &is_lit : (char *)0)); 1401 rooms[rnum].rlit = on; 1402 } 1403 /* hallways remain dark on the rogue level */ 1404 } else 1405#endif 1406 do_clear_area(u.ux,u.uy, 1407 (obj && obj->oclass==SCROLL_CLASS && obj->blessed) ? 9 : 5, 1408 set_lit, (genericptr_t)(on ? &is_lit : (char *)0)); 1409 1410 /* 1411 * If we are not blind, then force a redraw on all positions in sight 1412 * by temporarily blinding the hero. The vision recalculation will 1413 * correctly update all previously seen positions *and* correctly 1414 * set the waslit bit [could be messed up from above]. 1415 */ 1416 if (!Blind) { 1417 vision_recalc(2); 1418 1419 /* replace ball&chain */ 1420 if (Punished && !on) 1421 move_bc(0, 0, uball->ox, uball->oy, uchain->ox, uchain->oy); 1422 } 1423 1424 vision_full_recalc = 1; /* delayed vision recalculation */ 1425} 1426 1427static void 1428do_class_genocide() 1429{ 1430 int i, j, immunecnt, gonecnt, goodcnt, class, feel_dead = 0; 1431 char buf[BUFSZ]; 1432 boolean gameover = FALSE; /* true iff killed self */ 1433 1434 for(j=0; ; j++) { 1435 if (j >= 5) { 1436 pline(thats_enough_tries); 1437 return; 1438 } 1439 do { 1440 getlin("What class of monsters do you wish to genocide?", 1441 buf); 1442 (void)mungspaces(buf); 1443 } while (buf[0]=='\033' || !buf[0]); 1444 /* choosing "none" preserves genocideless conduct */ 1445 if (!strcmpi(buf, "none") || 1446 !strcmpi(buf, "nothing")) return; 1447 1448 if (strlen(buf) == 1) { 1449 if (buf[0] == ILLOBJ_SYM) 1450 buf[0] = def_monsyms[S_MIMIC]; 1451 class = def_char_to_monclass(buf[0]); 1452 } else { 1453 char buf2[BUFSZ]; 1454 1455 class = 0; 1456 Strcpy(buf2, makesingular(buf)); 1457 Strcpy(buf, buf2); 1458 } 1459 immunecnt = gonecnt = goodcnt = 0; 1460 for (i = LOW_PM; i < NUMMONS; i++) { 1461 if (class == 0 && 1462 strstri(monexplain[(int)mons[i].mlet], buf) != 0) 1463 class = mons[i].mlet; 1464 if (mons[i].mlet == class) { 1465 if (!(mons[i].geno & G_GENO)) immunecnt++; 1466 else if(mvitals[i].mvflags & G_GENOD) gonecnt++; 1467 else goodcnt++; 1468 } 1469 } 1470 /* 1471 * TODO[?]: If user's input doesn't match any class 1472 * description, check individual species names. 1473 */ 1474 if (!goodcnt && class != mons[urole.malenum].mlet && 1475 class != mons[urace.malenum].mlet) { 1476 if (gonecnt) 1477 pline("All such monsters are already nonexistent."); 1478 else if (immunecnt || 1479 (buf[0] == DEF_INVISIBLE && buf[1] == '\0')) 1480 You("aren't permitted to genocide such monsters."); 1481 else 1482#ifdef WIZARD /* to aid in topology testing; remove pesky monsters */ 1483 if (wizard && buf[0] == '*') { 1484 register struct monst *mtmp, *mtmp2; 1485 1486 gonecnt = 0; 1487 for (mtmp = fmon; mtmp; mtmp = mtmp2) { 1488 mtmp2 = mtmp->nmon; 1489 if (DEADMONSTER(mtmp)) continue; 1490 mongone(mtmp); 1491 gonecnt++; 1492 } 1493 pline("Eliminated %d monster%s.", gonecnt, plur(gonecnt)); 1494 return; 1495 } else 1496#endif 1497 pline("That symbol does not represent any monster."); 1498 continue; 1499 } 1500 1501 for (i = LOW_PM; i < NUMMONS; i++) { 1502 if(mons[i].mlet == class) { 1503 char nam[BUFSZ]; 1504 1505 Strcpy(nam, makeplural(mons[i].mname)); 1506 /* Although "genus" is Latin for race, the hero benefits 1507 * from both race and role; thus genocide affects either. 1508 */ 1509 if (Your_Own_Role(i) || Your_Own_Race(i) || 1510 ((mons[i].geno & G_GENO) 1511 && !(mvitals[i].mvflags & G_GENOD))) { 1512 /* This check must be first since player monsters might 1513 * have G_GENOD or !G_GENO. 1514 */ 1515 mvitals[i].mvflags |= (G_GENOD|G_NOCORPSE); 1516 reset_rndmonst(i); 1517 kill_genocided_monsters(); 1518 update_inventory(); /* eggs & tins */ 1519 pline("Wiped out all %s.", nam); 1520 if (Upolyd && i == u.umonnum) { 1521 u.mh = -1; 1522 if (Unchanging) { 1523 if (!feel_dead++) You("die."); 1524 /* finish genociding this class of 1525 monsters before ultimately dying */ 1526 gameover = TRUE; 1527 } else 1528 rehumanize(); 1529 } 1530 /* Self-genocide if it matches either your race 1531 or role. Assumption: male and female forms 1532 share same monster class. */ 1533 if (i == urole.malenum || i == urace.malenum) { 1534 u.uhp = -1; 1535 if (Upolyd) { 1536 if (!feel_dead++) You_feel("dead inside."); 1537 } else { 1538 if (!feel_dead++) You("die."); 1539 gameover = TRUE; 1540 } 1541 } 1542 } else if (mvitals[i].mvflags & G_GENOD) { 1543 if (!gameover) 1544 pline("All %s are already nonexistent.", nam); 1545 } else if (!gameover) { 1546 /* suppress feedback about quest beings except 1547 for those applicable to our own role */ 1548 if ((mons[i].msound != MS_LEADER || 1549 quest_info(MS_LEADER) == i) 1550 && (mons[i].msound != MS_NEMESIS || 1551 quest_info(MS_NEMESIS) == i) 1552 && (mons[i].msound != MS_GUARDIAN || 1553 quest_info(MS_GUARDIAN) == i) 1554 /* non-leader/nemesis/guardian role-specific monster */ 1555 && (i != PM_NINJA || /* nuisance */ 1556 Role_if(PM_SAMURAI))) { 1557 boolean named, uniq; 1558 1559 named = type_is_pname(&mons[i]) ? TRUE : FALSE; 1560 uniq = (mons[i].geno & G_UNIQ) ? TRUE : FALSE; 1561 /* one special case */ 1562 if (i == PM_HIGH_PRIEST) uniq = FALSE; 1563 1564 You("aren't permitted to genocide %s%s.", 1565 (uniq && !named) ? "the " : "", 1566 (uniq || named) ? mons[i].mname : nam); 1567 } 1568 } 1569 } 1570 } 1571 if (gameover || u.uhp == -1) { 1572 killer_format = KILLED_BY_AN; 1573 killer = "scroll of genocide"; 1574 if (gameover) done(GENOCIDED); 1575 } 1576 return; 1577 } 1578} 1579 1580#define REALLY 1 1581#define PLAYER 2 1582#define ONTHRONE 4 1583void 1584do_genocide(how) 1585int how; 1586/* 0 = no genocide; create monsters (cursed scroll) */ 1587/* 1 = normal genocide */ 1588/* 3 = forced genocide of player */ 1589/* 5 (4 | 1) = normal genocide from throne */ 1590{ 1591 char buf[BUFSZ]; 1592 register int i, killplayer = 0; 1593 register int mndx; 1594 register struct permonst *ptr; 1595 const char *which; 1596 1597 if (how & PLAYER) { 1598 mndx = u.umonster; /* non-polymorphed mon num */ 1599 ptr = &mons[mndx]; 1600 Strcpy(buf, ptr->mname); 1601 killplayer++; 1602 } else { 1603 for(i = 0; ; i++) { 1604 if(i >= 5) { 1605 pline(thats_enough_tries); 1606 return; 1607 } 1608 getlin("What monster do you want to genocide? [type the name]", 1609 buf); 1610 (void)mungspaces(buf); 1611 /* choosing "none" preserves genocideless conduct */ 1612 if (!strcmpi(buf, "none") || !strcmpi(buf, "nothing")) { 1613 /* ... but no free pass if cursed */ 1614 if (!(how & REALLY)) { 1615 ptr = rndmonst(); 1616 if (!ptr) return; /* no message, like normal case */ 1617 mndx = monsndx(ptr); 1618 break; /* remaining checks don't apply */ 1619 } else return; 1620 } 1621 1622 mndx = name_to_mon(buf); 1623 if (mndx == NON_PM || (mvitals[mndx].mvflags & G_GENOD)) { 1624 pline("Such creatures %s exist in this world.", 1625 (mndx == NON_PM) ? "do not" : "no longer"); 1626 continue; 1627 } 1628 ptr = &mons[mndx]; 1629 /* Although "genus" is Latin for race, the hero benefits 1630 * from both race and role; thus genocide affects either. 1631 */ 1632 if (Your_Own_Role(mndx) || Your_Own_Race(mndx)) { 1633 killplayer++; 1634 break; 1635 } 1636 if (is_human(ptr)) adjalign(-sgn(u.ualign.type)); 1637 if (is_demon(ptr)) adjalign(sgn(u.ualign.type)); 1638 1639 if(!(ptr->geno & G_GENO)) { 1640 if(flags.soundok) { 1641 /* fixme: unconditional "caverns" will be silly in some circumstances */ 1642 if(flags.verbose) 1643 pline("A thunderous voice booms through the caverns:"); 1644 verbalize("No, mortal! That will not be done."); 1645 } 1646 continue; 1647 } 1648 /* KMH -- Unchanging prevents rehumanization */ 1649 if (Unchanging && ptr == youmonst.data) 1650 killplayer++; 1651 break; 1652 } 1653 } 1654 1655 which = "all "; 1656 if (Hallucination) { 1657 if (Upolyd) 1658 Strcpy(buf,youmonst.data->mname); 1659 else { 1660 Strcpy(buf, (flags.female && urole.name.f) ? 1661 urole.name.f : urole.name.m); 1662 buf[0] = lowc(buf[0]); 1663 } 1664 } else { 1665 Strcpy(buf, ptr->mname); /* make sure we have standard singular */ 1666 if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_PRIEST]) 1667 which = !type_is_pname(ptr) ? "the " : ""; 1668 } 1669 if (how & REALLY) { 1670 /* setting no-corpse affects wishing and random tin generation */ 1671 mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE); 1672 pline("Wiped out %s%s.", which, 1673 (*which != 'a') ? buf : makeplural(buf)); 1674 1675 if (killplayer) { 1676 /* might need to wipe out dual role */ 1677 if (urole.femalenum != NON_PM && mndx == urole.malenum) 1678 mvitals[urole.femalenum].mvflags |= (G_GENOD | G_NOCORPSE); 1679 if (urole.femalenum != NON_PM && mndx == urole.femalenum) 1680 mvitals[urole.malenum].mvflags |= (G_GENOD | G_NOCORPSE); 1681 if (urace.femalenum != NON_PM && mndx == urace.malenum) 1682 mvitals[urace.femalenum].mvflags |= (G_GENOD | G_NOCORPSE); 1683 if (urace.femalenum != NON_PM && mndx == urace.femalenum) 1684 mvitals[urace.malenum].mvflags |= (G_GENOD | G_NOCORPSE); 1685 1686 u.uhp = -1; 1687 if (how & PLAYER) { 1688 killer_format = KILLED_BY; 1689 killer = "genocidal confusion"; 1690 } else if (how & ONTHRONE) { 1691 /* player selected while on a throne */ 1692 killer_format = KILLED_BY_AN; 1693 killer = "imperious order"; 1694 } else { /* selected player deliberately, not confused */ 1695 killer_format = KILLED_BY_AN; 1696 killer = "scroll of genocide"; 1697 } 1698 1699 /* Polymorphed characters will die as soon as they're rehumanized. */ 1700 /* KMH -- Unchanging prevents rehumanization */ 1701 if (Upolyd && ptr != youmonst.data) { 1702 delayed_killer = killer; 1703 killer = 0; 1704 You_feel("dead inside."); 1705 } else 1706 done(GENOCIDED); 1707 } else if (ptr == youmonst.data) { 1708 rehumanize(); 1709 } 1710 reset_rndmonst(mndx); 1711 kill_genocided_monsters(); 1712 update_inventory(); /* in case identified eggs were affected */ 1713 } else { 1714 int cnt = 0; 1715 1716 if (!(mons[mndx].geno & G_UNIQ) && 1717 !(mvitals[mndx].mvflags & (G_GENOD | G_EXTINCT))) 1718 for (i = rn1(3, 4); i > 0; i--) { 1719 if (!makemon(ptr, u.ux, u.uy, NO_MINVENT)) 1720 break; /* couldn't make one */ 1721 ++cnt; 1722 if (mvitals[mndx].mvflags & G_EXTINCT) 1723 break; /* just made last one */ 1724 } 1725 if (cnt) 1726 pline("Sent in some %s.", makeplural(buf)); 1727 else 1728 pline(nothing_happens); 1729 } 1730} 1731 1732void 1733punish(sobj) 1734register struct obj *sobj; 1735{ 1736 /* KMH -- Punishment is still okay when you are riding */ 1737 You("are being punished for your misbehavior!"); 1738 if(Punished){ 1739 Your("iron ball gets heavier."); 1740 uball->owt += 160 * (1 + sobj->cursed); 1741 return; 1742 } 1743 if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) { 1744 pline("A ball and chain appears, then falls away."); 1745 dropy(mkobj(BALL_CLASS, TRUE)); 1746 return; 1747 } 1748 setworn(mkobj(CHAIN_CLASS, TRUE), W_CHAIN); 1749 setworn(mkobj(BALL_CLASS, TRUE), W_BALL); 1750 uball->spe = 1; /* special ball (see save) */ 1751 1752 /* 1753 * Place ball & chain if not swallowed. If swallowed, the ball & 1754 * chain variables will be set at the next call to placebc(). 1755 */ 1756 if (!u.uswallow) { 1757 placebc(); 1758 if (Blind) set_bc(1); /* set up ball and chain variables */ 1759 newsym(u.ux,u.uy); /* see ball&chain if can't see self */ 1760 } 1761} 1762 1763void 1764unpunish() 1765{ /* remove the ball and chain */ 1766 struct obj *savechain = uchain; 1767 1768 obj_extract_self(uchain); 1769 newsym(uchain->ox,uchain->oy); 1770 setworn((struct obj *)0, W_CHAIN); 1771 dealloc_obj(savechain); 1772 uball->spe = 0; 1773 setworn((struct obj *)0, W_BALL); 1774} 1775 1776/* some creatures have special data structures that only make sense in their 1777 * normal locations -- if the player tries to create one elsewhere, or to revive 1778 * one, the disoriented creature becomes a zombie 1779 */ 1780boolean 1781cant_create(mtype, revival) 1782int *mtype; 1783boolean revival; 1784{ 1785 1786 /* SHOPKEEPERS can be revived now */ 1787 if (*mtype==PM_GUARD || (*mtype==PM_SHOPKEEPER && !revival) 1788 || *mtype==PM_ALIGNED_PRIEST || *mtype==PM_ANGEL) { 1789 *mtype = PM_HUMAN_ZOMBIE; 1790 return TRUE; 1791 } else if (*mtype==PM_LONG_WORM_TAIL) { /* for create_particular() */ 1792 *mtype = PM_LONG_WORM; 1793 return TRUE; 1794 } 1795 return FALSE; 1796} 1797 1798#ifdef WIZARD 1799/* 1800 * Make a new monster with the type controlled by the user. 1801 * 1802 * Note: when creating a monster by class letter, specifying the 1803 * "strange object" (']') symbol produces a random monster rather 1804 * than a mimic; this behavior quirk is useful so don't "fix" it... 1805 */ 1806boolean 1807create_particular() 1808{ 1809 char buf[BUFSZ], *bufp, monclass = MAXMCLASSES; 1810 int which, tries, i; 1811 struct permonst *whichpm; 1812 struct monst *mtmp; 1813 boolean madeany = FALSE; 1814 boolean maketame, makepeaceful, makehostile; 1815 1816 tries = 0; 1817 do { 1818 which = urole.malenum; /* an arbitrary index into mons[] */ 1819 maketame = makepeaceful = makehostile = FALSE; 1820 getlin("Create what kind of monster? [type the name or symbol]", 1821 buf); 1822 bufp = mungspaces(buf); 1823 if (*bufp == '\033') return FALSE; 1824 /* allow the initial disposition to be specified */ 1825 if (!strncmpi(bufp, "tame ", 5)) { 1826 bufp += 5; 1827 maketame = TRUE; 1828 } else if (!strncmpi(bufp, "peaceful ", 9)) { 1829 bufp += 9; 1830 makepeaceful = TRUE; 1831 } else if (!strncmpi(bufp, "hostile ", 8)) { 1832 bufp += 8; 1833 makehostile = TRUE; 1834 } 1835 /* decide whether a valid monster was chosen */ 1836 if (strlen(bufp) == 1) { 1837 monclass = def_char_to_monclass(*bufp); 1838 if (monclass != MAXMCLASSES) break; /* got one */ 1839 } else { 1840 which = name_to_mon(bufp); 1841 if (which >= LOW_PM) break; /* got one */ 1842 } 1843 /* no good; try again... */ 1844 pline("I've never heard of such monsters."); 1845 } while (++tries < 5); 1846 1847 if (tries == 5) { 1848 pline(thats_enough_tries); 1849 } else { 1850 (void) cant_create(&which, FALSE); 1851 whichpm = &mons[which]; 1852 for (i = 0; i <= multi; i++) { 1853 if (monclass != MAXMCLASSES) 1854 whichpm = mkclass(monclass, 0); 1855 if (maketame) { 1856 mtmp = makemon(whichpm, u.ux, u.uy, MM_EDOG); 1857 if (mtmp) { 1858 initedog(mtmp); 1859 set_malign(mtmp); 1860 } 1861 } else { 1862 mtmp = makemon(whichpm, u.ux, u.uy, NO_MM_FLAGS); 1863 if ((makepeaceful || makehostile) && mtmp) { 1864 mtmp->mtame = 0; /* sanity precaution */ 1865 mtmp->mpeaceful = makepeaceful ? 1 : 0; 1866 set_malign(mtmp); 1867 } 1868 } 1869 if (mtmp) madeany = TRUE; 1870 } 1871 } 1872 return madeany; 1873} 1874#endif /* WIZARD */ 1875 1876#endif /* OVLB */ 1877 1878/*read.c*/ 1879