1 /* $OpenBSD: hack.read.c,v 1.6 2003/05/19 06:30:56 pjanzen Exp $ */ 2 3 /* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 #ifndef lint 65 static const char rcsid[] = "$OpenBSD: hack.read.c,v 1.6 2003/05/19 06:30:56 pjanzen Exp $"; 66 #endif /* not lint */ 67 68 #include <stdlib.h> 69 #include "hack.h" 70 71 static boolean monstersym(char); 72 73 int 74 doread() 75 { 76 struct obj *scroll; 77 boolean confused = (Confusion != 0); 78 boolean known = FALSE; 79 80 scroll = getobj("?", "read"); 81 if(!scroll) return(0); 82 if(!scroll->dknown && Blind) { 83 pline("Being blind, you cannot read the formula on the scroll."); 84 return(0); 85 } 86 if(Blind) 87 pline("As you pronounce the formula on it, the scroll disappears."); 88 else 89 pline("As you read the scroll, it disappears."); 90 if(confused) 91 pline("Being confused, you mispronounce the magic words ... "); 92 93 switch(scroll->otyp) { 94 #ifdef MAIL 95 case SCR_MAIL: 96 readmail(/* scroll */); 97 break; 98 #endif /* MAIL */ 99 case SCR_ENCHANT_ARMOR: 100 { struct obj *otmp = some_armor(); 101 if(!otmp) { 102 strange_feeling(scroll,"Your skin glows then fades."); 103 return(1); 104 } 105 if(confused) { 106 pline("Your %s glows silver for a moment.", 107 objects[otmp->otyp].oc_name); 108 otmp->rustfree = 1; 109 break; 110 } 111 if(otmp->spe > 3 && rn2(otmp->spe)) { 112 pline("Your %s glows violently green for a while, then evaporates.", 113 objects[otmp->otyp].oc_name); 114 useup(otmp); 115 break; 116 } 117 pline("Your %s glows green for a moment.", 118 objects[otmp->otyp].oc_name); 119 otmp->cursed = 0; 120 otmp->spe++; 121 break; 122 } 123 case SCR_DESTROY_ARMOR: 124 if(confused) { 125 struct obj *otmp = some_armor(); 126 if(!otmp) { 127 strange_feeling(scroll,"Your bones itch."); 128 return(1); 129 } 130 pline("Your %s glows purple for a moment.", 131 objects[otmp->otyp].oc_name); 132 otmp->rustfree = 0; 133 break; 134 } 135 if(uarm) { 136 pline("Your armor turns to dust and falls to the floor!"); 137 useup(uarm); 138 } else if(uarmh) { 139 pline("Your helmet turns to dust and is blown away!"); 140 useup(uarmh); 141 } else if(uarmg) { 142 pline("Your gloves vanish!"); 143 useup(uarmg); 144 selftouch("You"); 145 } else { 146 strange_feeling(scroll,"Your skin itches."); 147 return(1); 148 } 149 break; 150 case SCR_CONFUSE_MONSTER: 151 if(confused) { 152 pline("Your hands begin to glow purple."); 153 Confusion += rnd(100); 154 } else { 155 pline("Your hands begin to glow blue."); 156 u.umconf = 1; 157 } 158 break; 159 case SCR_SCARE_MONSTER: 160 { int ct = 0; 161 struct monst *mtmp; 162 163 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 164 if(cansee(mtmp->mx,mtmp->my)) { 165 if(confused) 166 mtmp->mflee = mtmp->mfroz = 167 mtmp->msleep = 0; 168 else 169 mtmp->mflee = 1; 170 ct++; 171 } 172 if(!ct) { 173 if(confused) 174 pline("You hear sad wailing in the distance."); 175 else 176 pline("You hear maniacal laughter in the distance."); 177 } 178 break; 179 } 180 case SCR_BLANK_PAPER: 181 if(confused) 182 pline("You see strange patterns on this scroll."); 183 else 184 pline("This scroll seems to be blank."); 185 break; 186 case SCR_REMOVE_CURSE: 187 { struct obj *obj; 188 if(confused) 189 pline("You feel like you need some help."); 190 else 191 pline("You feel like someone is helping you."); 192 for(obj = invent; obj ; obj = obj->nobj) 193 if(obj->owornmask) 194 obj->cursed = confused; 195 if(Punished && !confused) { 196 Punished = 0; 197 freeobj(uchain); 198 unpobj(uchain); 199 free((char *) uchain); 200 uball->spe = 0; 201 uball->owornmask &= ~W_BALL; 202 uchain = uball = (struct obj *) 0; 203 } 204 break; 205 } 206 case SCR_CREATE_MONSTER: 207 { int cnt = 1; 208 209 if(!rn2(73)) cnt += rnd(4); 210 if(confused) cnt += 12; 211 while(cnt--) 212 (void) makemon(confused ? PM_ACID_BLOB : 213 (struct permonst *) 0, u.ux, u.uy); 214 break; 215 } 216 case SCR_ENCHANT_WEAPON: 217 if(uwep && confused) { 218 pline("Your %s glows silver for a moment.", 219 objects[uwep->otyp].oc_name); 220 uwep->rustfree = 1; 221 } else 222 if(!chwepon(scroll, 1)) /* tests for !uwep */ 223 return(1); 224 break; 225 case SCR_DAMAGE_WEAPON: 226 if(uwep && confused) { 227 pline("Your %s glows purple for a moment.", 228 objects[uwep->otyp].oc_name); 229 uwep->rustfree = 0; 230 } else 231 if(!chwepon(scroll, -1)) /* tests for !uwep */ 232 return(1); 233 break; 234 case SCR_TAMING: 235 { int i,j; 236 int bd = confused ? 5 : 1; 237 struct monst *mtmp; 238 239 for(i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++) 240 if ((mtmp = m_at(u.ux+i, u.uy+j))) 241 (void) tamedog(mtmp, NULL); 242 break; 243 } 244 case SCR_GENOCIDE: 245 { extern char genocided[], fut_geno[]; 246 char buf[BUFSZ]; 247 struct monst *mtmp, *mtmp2; 248 249 pline("You have found a scroll of genocide!"); 250 known = TRUE; 251 if(confused) 252 *buf = u.usym; 253 else do { 254 pline("What monster do you want to genocide (Type the letter)? "); 255 getlin(buf); 256 } while(strlen(buf) != 1 || !monstersym(*buf)); 257 if(!strchr(fut_geno, *buf)) 258 charcat(fut_geno, *buf); 259 if(!strchr(genocided, *buf)) 260 charcat(genocided, *buf); 261 else { 262 pline("Such monsters do not exist in this world."); 263 break; 264 } 265 for(mtmp = fmon; mtmp; mtmp = mtmp2){ 266 mtmp2 = mtmp->nmon; 267 if(mtmp->data->mlet == *buf) 268 mondead(mtmp); 269 } 270 pline("Wiped out all %c's.", *buf); 271 if(*buf == u.usym) { 272 killer = "scroll of genocide"; 273 u.uhp = -1; 274 } 275 break; 276 } 277 case SCR_LIGHT: 278 if(!Blind) known = TRUE; 279 litroom(!confused); 280 break; 281 case SCR_TELEPORTATION: 282 if(confused) 283 level_tele(); 284 else { 285 #ifdef QUEST 286 int oux = u.ux, ouy = u.uy; 287 tele(); 288 if(dist(oux, ouy) > 100) known = TRUE; 289 #else /* QUEST */ 290 int uroom = inroom(u.ux, u.uy); 291 tele(); 292 if(uroom != inroom(u.ux, u.uy)) known = TRUE; 293 #endif /* QUEST */ 294 } 295 break; 296 case SCR_GOLD_DETECTION: 297 /* Unfortunately this code has become slightly less elegant, 298 now that gold and traps no longer are of the same type. */ 299 if(confused) { 300 struct trap *ttmp; 301 302 if(!ftrap) { 303 strange_feeling(scroll, "Your toes stop itching."); 304 return(1); 305 } else { 306 for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) 307 if(ttmp->tx != u.ux || ttmp->ty != u.uy) 308 goto outtrapmap; 309 /* only under me - no separate display required */ 310 pline("Your toes itch!"); 311 break; 312 outtrapmap: 313 cls(); 314 for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) 315 at(ttmp->tx, ttmp->ty, '$'); 316 prme(); 317 pline("You feel very greedy!"); 318 } 319 } else { 320 struct gold *gtmp; 321 322 if(!fgold) { 323 strange_feeling(scroll, "You feel materially poor."); 324 return(1); 325 } else { 326 known = TRUE; 327 for(gtmp = fgold; gtmp; gtmp = gtmp->ngold) 328 if(gtmp->gx != u.ux || gtmp->gy != u.uy) 329 goto outgoldmap; 330 /* only under me - no separate display required */ 331 pline("You notice some gold between your feet."); 332 break; 333 outgoldmap: 334 cls(); 335 for(gtmp = fgold; gtmp; gtmp = gtmp->ngold) 336 at(gtmp->gx, gtmp->gy, '$'); 337 prme(); 338 pline("You feel very greedy, and sense gold!"); 339 } 340 } 341 /* common sequel */ 342 more(); 343 docrt(); 344 break; 345 case SCR_FOOD_DETECTION: 346 { int ct = 0, ctu = 0; 347 struct obj *obj; 348 char foodsym = confused ? POTION_SYM : FOOD_SYM; 349 350 for(obj = fobj; obj; obj = obj->nobj) 351 if(obj->olet == FOOD_SYM) { 352 if(obj->ox == u.ux && obj->oy == u.uy) ctu++; 353 else ct++; 354 } 355 if(!ct && !ctu) { 356 strange_feeling(scroll,"Your nose twitches."); 357 return(1); 358 } else if(!ct) { 359 known = TRUE; 360 pline("You smell %s close nearby.", 361 confused ? "something" : "food"); 362 363 } else { 364 known = TRUE; 365 cls(); 366 for(obj = fobj; obj; obj = obj->nobj) 367 if(obj->olet == foodsym) 368 at(obj->ox, obj->oy, FOOD_SYM); 369 prme(); 370 pline("Your nose tingles and you smell %s!", 371 confused ? "something" : "food"); 372 more(); 373 docrt(); 374 } 375 break; 376 } 377 case SCR_IDENTIFY: 378 /* known = TRUE; */ 379 if(confused) 380 pline("You identify this as an identify scroll."); 381 else 382 pline("This is an identify scroll."); 383 useup(scroll); 384 objects[SCR_IDENTIFY].oc_name_known = 1; 385 if(!confused) 386 while( 387 !ggetobj("identify", identify, rn2(5) ? 1 : rn2(5)) 388 && invent 389 ); 390 return(1); 391 case SCR_MAGIC_MAPPING: 392 { struct rm *lev; 393 int num, zx, zy; 394 395 known = TRUE; 396 pline("On this scroll %s a map!", 397 confused ? "was" : "is"); 398 for(zy = 0; zy < ROWNO; zy++) 399 for(zx = 0; zx < COLNO; zx++) { 400 if(confused && rn2(7)) continue; 401 lev = &(levl[zx][zy]); 402 if((num = lev->typ) == 0) 403 continue; 404 if(num == SCORR) { 405 lev->typ = CORR; 406 lev->scrsym = CORR_SYM; 407 } else 408 if(num == SDOOR) { 409 lev->typ = DOOR; 410 lev->scrsym = '+'; 411 /* do sth in doors ? */ 412 } else if(lev->seen) continue; 413 #ifndef QUEST 414 if(num != ROOM) 415 #endif /* QUEST */ 416 { 417 lev->seen = lev->new = 1; 418 if(lev->scrsym == ' ' || !lev->scrsym) 419 newsym(zx,zy); 420 else 421 on_scr(zx,zy); 422 } 423 } 424 break; 425 } 426 case SCR_AMNESIA: 427 { int zx, zy; 428 429 known = TRUE; 430 for(zx = 0; zx < COLNO; zx++) for(zy = 0; zy < ROWNO; zy++) 431 if(!confused || rn2(7)) 432 if(!cansee(zx,zy)) 433 levl[zx][zy].seen = 0; 434 docrt(); 435 pline("Thinking of Maud you forget everything else."); 436 break; 437 } 438 case SCR_FIRE: 439 { int num; 440 struct monst *mtmp; 441 442 known = TRUE; 443 if(confused) { 444 pline("The scroll catches fire and you burn your hands."); 445 losehp(1, "scroll of fire"); 446 } else { 447 pline("The scroll erupts in a tower of flame!"); 448 if(Fire_resistance) 449 pline("You are uninjured."); 450 else { 451 num = rnd(6); 452 u.uhpmax -= num; 453 losehp(num, "scroll of fire"); 454 } 455 } 456 num = (2*num + 1)/3; 457 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 458 if(dist(mtmp->mx,mtmp->my) < 3) { 459 mtmp->mhp -= num; 460 if(strchr("FY", mtmp->data->mlet)) 461 mtmp->mhp -= 3*num; /* this might well kill 'F's */ 462 if(mtmp->mhp < 1) { 463 killed(mtmp); 464 break; /* primitive */ 465 } 466 } 467 } 468 break; 469 } 470 case SCR_PUNISHMENT: 471 known = TRUE; 472 if(confused) { 473 pline("You feel guilty."); 474 break; 475 } 476 pline("You are being punished for your misbehaviour!"); 477 if(Punished){ 478 pline("Your iron ball gets heavier."); 479 uball->owt += 15; 480 break; 481 } 482 Punished = INTRINSIC; 483 setworn(mkobj_at(CHAIN_SYM, u.ux, u.uy), W_CHAIN); 484 setworn(mkobj_at(BALL_SYM, u.ux, u.uy), W_BALL); 485 uball->spe = 1; /* special ball (see save) */ 486 break; 487 default: 488 impossible("What weird language is this written in? (%u)", 489 scroll->otyp); 490 } 491 if(!objects[scroll->otyp].oc_name_known) { 492 if(known && !confused) { 493 objects[scroll->otyp].oc_name_known = 1; 494 more_experienced(0,10); 495 } else if(!objects[scroll->otyp].oc_uname) 496 docall(scroll); 497 } 498 useup(scroll); 499 return(1); 500 } 501 502 /* also called by newmail() */ 503 int 504 identify(struct obj *otmp) 505 { 506 objects[otmp->otyp].oc_name_known = 1; 507 otmp->known = otmp->dknown = 1; 508 prinv(otmp); 509 return(1); 510 } 511 512 void 513 litroom(boolean on) 514 { 515 int num,zx,zy; 516 517 /* first produce the text (provided he is not blind) */ 518 if(Blind) goto do_it; 519 if(!on) { 520 if(u.uswallow || !xdnstair || 521 levl[(int)u.ux][(int)u.uy].typ == CORR || 522 !levl[(int)u.ux][(int)u.uy].lit) { 523 pline("It seems even darker in here than before."); 524 return; 525 } else 526 pline("It suddenly becomes dark in here."); 527 } else { 528 if(u.uswallow){ 529 pline("%s's stomach is lit.", Monnam(u.ustuck)); 530 return; 531 } 532 if(!xdnstair){ 533 pline("Nothing Happens."); 534 return; 535 } 536 #ifdef QUEST 537 pline("The cave lights up around you, then fades."); 538 return; 539 #else /* QUEST */ 540 if (levl[(int)u.ux][(int)u.uy].typ == CORR) { 541 pline("The corridor lights up around you, then fades."); 542 return; 543 } else if (levl[(int)u.ux][(int)u.uy].lit) { 544 pline("The light here seems better now."); 545 return; 546 } else 547 pline("The room is lit."); 548 #endif /* QUEST */ 549 } 550 551 do_it: 552 #ifdef QUEST 553 return; 554 #else /* QUEST */ 555 if (levl[(int)u.ux][(int)u.uy].lit == on) 556 return; 557 if (levl[(int)u.ux][(int)u.uy].typ == DOOR) { 558 if (IS_ROOM(levl[(int)u.ux][(int)u.uy+1].typ)) 559 zy = u.uy+1; 560 else if(IS_ROOM(levl[(int)u.ux][u.uy-1].typ)) 561 zy = u.uy-1; 562 else zy = u.uy; 563 if(IS_ROOM(levl[u.ux+1][(int)u.uy].typ)) 564 zx = u.ux+1; 565 else if(IS_ROOM(levl[u.ux-1][(int)u.uy].typ)) 566 zx = u.ux-1; 567 else zx = u.ux; 568 } else { 569 zx = u.ux; 570 zy = u.uy; 571 } 572 for(seelx = u.ux; (num = levl[seelx-1][zy].typ) != CORR && num != 0; 573 seelx--); 574 for(seehx = u.ux; (num = levl[seehx+1][zy].typ) != CORR && num != 0; 575 seehx++); 576 for(seely = u.uy; (num = levl[zx][seely-1].typ) != CORR && num != 0; 577 seely--); 578 for(seehy = u.uy; (num = levl[zx][seehy+1].typ) != CORR && num != 0; 579 seehy++); 580 for(zy = seely; zy <= seehy; zy++) 581 for(zx = seelx; zx <= seehx; zx++) { 582 levl[zx][zy].lit = on; 583 if (!Blind && dist(zx,zy) > 2) { 584 if(on) 585 prl(zx,zy); 586 else 587 nosee(zx,zy); 588 } 589 } 590 if(!on) 591 seehx = 0; 592 #endif /* QUEST */ 593 } 594 595 /* Test whether we may genocide all monsters with symbol ch */ 596 /* arnold@ucsfcgl */ 597 static boolean 598 monstersym(char ch) 599 { 600 struct permonst *mp; 601 extern struct permonst pm_eel; 602 603 /* 604 * can't genocide certain monsters 605 */ 606 if (strchr("12 &:", ch)) 607 return FALSE; 608 609 if (ch == pm_eel.mlet) 610 return TRUE; 611 for (mp = mons; mp < &mons[CMNUM+2]; mp++) 612 if (mp->mlet == ch) 613 return TRUE; 614 return FALSE; 615 } 616