1/* SCCS Id: @(#)spell.c 3.4 2003/01/17 */ 2/* Copyright (c) M. Stephenson 1988 */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5#include "hack.h" 6 7static NEARDATA schar delay; /* moves left for this spell */ 8static NEARDATA struct obj *book; /* last/current book being xscribed */ 9 10/* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */ 11#define SPELLMENU_CAST (-2) 12#define SPELLMENU_VIEW (-1) 13 14#define KEEN 20000 15#define MAX_SPELL_STUDY 3 16#define incrnknow(spell) spl_book[spell].sp_know = KEEN 17 18#define spellev(spell) spl_book[spell].sp_lev 19#define spellname(spell) OBJ_NAME(objects[spellid(spell)]) 20#define spellet(spell) \ 21 ((char)((spell < 26) ? ('a' + spell) : ('A' + spell - 26))) 22 23STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P)); 24STATIC_DCL boolean FDECL(cursed_book, (struct obj *bp)); 25STATIC_DCL boolean FDECL(confused_book, (struct obj *)); 26STATIC_DCL void FDECL(deadbook, (struct obj *)); 27STATIC_PTR int NDECL(learn); 28STATIC_DCL boolean FDECL(getspell, (int *)); 29STATIC_DCL boolean FDECL(dospellmenu, (const char *,int,int *)); 30STATIC_DCL int FDECL(percent_success, (int)); 31STATIC_DCL int NDECL(throwspell); 32STATIC_DCL void NDECL(cast_protection); 33STATIC_DCL void FDECL(spell_backfire, (int)); 34STATIC_DCL const char *FDECL(spelltypemnemonic, (int)); 35STATIC_DCL int FDECL(isqrt, (int)); 36 37/* The roles[] table lists the role-specific values for tuning 38 * percent_success(). 39 * 40 * Reasoning: 41 * spelbase, spelheal: 42 * Arc are aware of magic through historical research 43 * Bar abhor magic (Conan finds it "interferes with his animal instincts") 44 * Cav are ignorant to magic 45 * Hea are very aware of healing magic through medical research 46 * Kni are moderately aware of healing from Paladin training 47 * Mon use magic to attack and defend in lieu of weapons and armor 48 * Pri are very aware of healing magic through theological research 49 * Ran avoid magic, preferring to fight unseen and unheard 50 * Rog are moderately aware of magic through trickery 51 * Sam have limited magical awareness, prefering meditation to conjuring 52 * Tou are aware of magic from all the great films they have seen 53 * Val have limited magical awareness, prefering fighting 54 * Wiz are trained mages 55 * 56 * The arms penalty is lessened for trained fighters Bar, Kni, Ran, 57 * Sam, Val - 58 * the penalty is its metal interference, not encumbrance. 59 * The `spelspec' is a single spell which is fundamentally easier 60 * for that role to cast. 61 * 62 * spelspec, spelsbon: 63 * Arc map masters (SPE_MAGIC_MAPPING) 64 * Bar fugue/berserker (SPE_HASTE_SELF) 65 * Cav born to dig (SPE_DIG) 66 * Hea to heal (SPE_CURE_SICKNESS) 67 * Kni to turn back evil (SPE_TURN_UNDEAD) 68 * Mon to preserve their abilities (SPE_RESTORE_ABILITY) 69 * Pri to bless (SPE_REMOVE_CURSE) 70 * Ran to hide (SPE_INVISIBILITY) 71 * Rog to find loot (SPE_DETECT_TREASURE) 72 * Sam to be At One (SPE_CLAIRVOYANCE) 73 * Tou to smile (SPE_CHARM_MONSTER) 74 * Val control the cold (SPE_CONE_OF_COLD) 75 * Wiz all really, but SPE_MAGIC_MISSILE is their party trick 76 * 77 * See percent_success() below for more comments. 78 * 79 * uarmbon, uarmsbon, uarmhbon, uarmgbon, uarmfbon: 80 * Fighters find body armour & shield a little less limiting. 81 * Headgear, Gauntlets and Footwear are not role-specific (but 82 * still have an effect, except helm of brilliance, which is designed 83 * to permit magic-use). 84 */ 85 86#define uarmhbon 4 /* Metal helmets interfere with the mind */ 87#define uarmgbon 6 /* Casting channels through the hands */ 88#define uarmfbon 2 /* All metal interferes to some degree */ 89 90/* since the spellbook itself doesn't blow up, don't say just "explodes" */ 91static const char explodes[] = "radiates explosive energy"; 92 93/* convert a letter into a number in the range 0..51, or -1 if not a letter */ 94STATIC_OVL int 95spell_let_to_idx(ilet) 96char ilet; 97{ 98 int indx; 99 100 indx = ilet - 'a'; 101 if (indx >= 0 && indx < 26) return indx; 102 indx = ilet - 'A'; 103 if (indx >= 0 && indx < 26) return indx + 26; 104 return -1; 105} 106 107/* TRUE: book should be destroyed by caller */ 108STATIC_OVL boolean 109cursed_book(bp) 110 struct obj *bp; 111{ 112 int lev = objects[bp->otyp].oc_level; 113 114 switch(rn2(lev)) { 115 case 0: 116 You_feel("a wrenching sensation."); 117 tele(); /* teleport him */ 118 break; 119 case 1: 120 You_feel("threatened."); 121 aggravate(); 122 break; 123 case 2: 124 make_blinded(Blinded + rn1(100,250),TRUE); 125 break; 126 case 3: 127 take_gold(); 128 break; 129 case 4: 130 pline("These runes were just too much to comprehend."); 131 make_confused(HConfusion + rn1(7,16),FALSE); 132 break; 133 case 5: 134 pline_The("book was coated with contact poison!"); 135 if (uarmg) { 136 if (uarmg->oerodeproof || !is_corrodeable(uarmg)) { 137 Your("gloves seem unaffected."); 138 } else if (uarmg->oeroded2 < MAX_ERODE) { 139 if (uarmg->greased) { 140 grease_protect(uarmg, "gloves", &youmonst); 141 } else { 142 Your("gloves corrode%s!", 143 uarmg->oeroded2+1 == MAX_ERODE ? 144 " completely" : uarmg->oeroded2 ? 145 " further" : ""); 146 uarmg->oeroded2++; 147 } 148 } else 149 Your("gloves %s completely corroded.", 150 Blind ? "feel" : "look"); 151 break; 152 } 153 /* temp disable in_use; death should not destroy the book */ 154 bp->in_use = FALSE; 155 losestr(Poison_resistance ? rn1(2,1) : rn1(4,3)); 156 losehp(rnd(Poison_resistance ? 6 : 10), 157 "contact-poisoned spellbook", KILLED_BY_AN); 158 bp->in_use = TRUE; 159 break; 160 case 6: 161 if(Antimagic) { 162 shieldeff(u.ux, u.uy); 163 pline_The("book %s, but you are unharmed!", explodes); 164 } else { 165 pline("As you read the book, it %s in your %s!", 166 explodes, body_part(FACE)); 167 losehp(2*rnd(10)+5, "exploding rune", KILLED_BY_AN); 168 } 169 return TRUE; 170 default: 171 rndcurse(); 172 break; 173 } 174 return FALSE; 175} 176 177/* study while confused: returns TRUE if the book is destroyed */ 178STATIC_OVL boolean 179confused_book(spellbook) 180struct obj *spellbook; 181{ 182 boolean gone = FALSE; 183 184 if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { 185 spellbook->in_use = TRUE; /* in case called from learn */ 186 pline( 187 "Being confused you have difficulties in controlling your actions."); 188 display_nhwindow(WIN_MESSAGE, FALSE); 189 You("accidentally tear the spellbook to pieces."); 190 if (!objects[spellbook->otyp].oc_name_known && 191 !objects[spellbook->otyp].oc_uname) 192 docall(spellbook); 193 useup(spellbook); 194 gone = TRUE; 195 } else { 196 You("find yourself reading the %s line over and over again.", 197 spellbook == book ? "next" : "first"); 198 } 199 return gone; 200} 201 202/* special effects for The Book of the Dead */ 203STATIC_OVL void 204deadbook(book2) 205struct obj *book2; 206{ 207 struct monst *mtmp, *mtmp2; 208 coord mm; 209 210 You("turn the pages of the Book of the Dead..."); 211 makeknown(SPE_BOOK_OF_THE_DEAD); 212 /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */ 213 book2->known = 1; 214 if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { 215 register struct obj *otmp; 216 register boolean arti1_primed = FALSE, arti2_primed = FALSE, 217 arti_cursed = FALSE; 218 219 if(book2->cursed) { 220 pline_The("runes appear scrambled. You can't read them!"); 221 return; 222 } 223 224 if(!u.uhave.bell || !u.uhave.menorah) { 225 pline("A chill runs down your %s.", body_part(SPINE)); 226 if(!u.uhave.bell) You_hear("a faint chime..."); 227 if(!u.uhave.menorah) pline("Vlad's doppelganger is amused."); 228 return; 229 } 230 231 for(otmp = invent; otmp; otmp = otmp->nobj) { 232 if(otmp->otyp == CANDELABRUM_OF_INVOCATION && 233 otmp->spe == 7 && otmp->lamplit) { 234 if(!otmp->cursed) arti1_primed = TRUE; 235 else arti_cursed = TRUE; 236 } 237 if(otmp->otyp == BELL_OF_OPENING && 238 (moves - otmp->age) < 5L) { /* you rang it recently */ 239 if(!otmp->cursed) arti2_primed = TRUE; 240 else arti_cursed = TRUE; 241 } 242 } 243 244 if(arti_cursed) { 245 pline_The("invocation fails!"); 246 pline("At least one of your artifacts is cursed..."); 247 } else if(arti1_primed && arti2_primed) { 248 unsigned soon = (unsigned) d(2,6); /* time til next intervene() */ 249 250 /* successful invocation */ 251 mkinvokearea(); 252 u.uevent.invoked = 1; 253 /* in case you haven't killed the Wizard yet, behave as if 254 you just did */ 255 u.uevent.udemigod = 1; /* wizdead() */ 256 if (!u.udg_cnt || u.udg_cnt > soon) u.udg_cnt = soon; 257 } else { /* at least one artifact not prepared properly */ 258 You("have a feeling that %s is amiss...", something); 259 goto raise_dead; 260 } 261 return; 262 } 263 264 /* when not an invocation situation */ 265 if (book2->cursed) { 266raise_dead: 267 268 You("raised the dead!"); 269 /* first maybe place a dangerous adversary */ 270 if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH], 271 u.ux, u.uy, NO_MINVENT)) != 0 || 272 (mtmp = makemon(&mons[PM_NALFESHNEE], 273 u.ux, u.uy, NO_MINVENT)) != 0)) { 274 mtmp->mpeaceful = 0; 275 set_malign(mtmp); 276 } 277 /* next handle the affect on things you're carrying */ 278 (void) unturn_dead(&youmonst); 279 /* last place some monsters around you */ 280 mm.x = u.ux; 281 mm.y = u.uy; 282 mkundead(&mm, TRUE, NO_MINVENT); 283 } else if(book2->blessed) { 284 for(mtmp = fmon; mtmp; mtmp = mtmp2) { 285 mtmp2 = mtmp->nmon; /* tamedog() changes chain */ 286 if (DEADMONSTER(mtmp)) continue; 287 288 if (is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) { 289 mtmp->mpeaceful = TRUE; 290 if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type) 291 && distu(mtmp->mx, mtmp->my) < 4) 292 if (mtmp->mtame) { 293 if (mtmp->mtame < 20) 294 mtmp->mtame++; 295 } else 296 (void) tamedog(mtmp, (struct obj *)0); 297 else monflee(mtmp, 0, FALSE, TRUE); 298 } 299 } 300 } else { 301 switch(rn2(3)) { 302 case 0: 303 Your("ancestors are annoyed with you!"); 304 break; 305 case 1: 306 pline_The("headstones in the cemetery begin to move!"); 307 break; 308 default: 309 pline("Oh my! Your name appears in the book!"); 310 } 311 } 312 return; 313} 314 315STATIC_PTR int 316learn() 317{ 318 int i; 319 short booktype; 320 char splname[BUFSZ]; 321 boolean costly = TRUE; 322 323 /* JDS: lenses give 50% faster reading; 33% smaller read time */ 324 if (delay && ublindf && ublindf->otyp == LENSES && rn2(2)) delay++; 325 if (Confusion) { /* became confused while learning */ 326 (void) confused_book(book); 327 book = 0; /* no longer studying */ 328 nomul(delay); /* remaining delay is uninterrupted */ 329 delay = 0; 330 return(0); 331 } 332 if (delay) { /* not if (delay++), so at end delay == 0 */ 333 delay++; 334 return(1); /* still busy */ 335 } 336 exercise(A_WIS, TRUE); /* you're studying. */ 337 booktype = book->otyp; 338 if(booktype == SPE_BOOK_OF_THE_DEAD) { 339 deadbook(book); 340 return(0); 341 } 342 343 Sprintf(splname, objects[booktype].oc_name_known ? 344 "\"%s\"" : "the \"%s\" spell", 345 OBJ_NAME(objects[booktype])); 346 for (i = 0; i < MAXSPELL; i++) { 347 if (spellid(i) == booktype) { 348 if (book->spestudied > MAX_SPELL_STUDY) { 349 pline("This spellbook is too faint to be read any more."); 350 book->otyp = booktype = SPE_BLANK_PAPER; 351 } else if (spellknow(i) <= 1000) { 352 Your("knowledge of %s is keener.", splname); 353 incrnknow(i); 354 book->spestudied++; 355 exercise(A_WIS,TRUE); /* extra study */ 356 } else { /* 1000 < spellknow(i) <= MAX_SPELL_STUDY */ 357 You("know %s quite well already.", splname); 358 costly = FALSE; 359 } 360 /* make book become known even when spell is already 361 known, in case amnesia made you forget the book */ 362 makeknown((int)booktype); 363 break; 364 } else if (spellid(i) == NO_SPELL) { 365 spl_book[i].sp_id = booktype; 366 spl_book[i].sp_lev = objects[booktype].oc_level; 367 incrnknow(i); 368 book->spestudied++; 369 You(i > 0 ? "add %s to your repertoire." : "learn %s.", 370 splname); 371 makeknown((int)booktype); 372 break; 373 } 374 } 375 if (i == MAXSPELL) impossible("Too many spells memorized!"); 376 377 if (book->cursed) { /* maybe a demon cursed it */ 378 if (cursed_book(book)) { 379 useup(book); 380 book = 0; 381 return 0; 382 } 383 } 384 if (costly) check_unpaid(book); 385 book = 0; 386 return(0); 387} 388 389int 390study_book(spellbook) 391register struct obj *spellbook; 392{ 393 register int booktype = spellbook->otyp; 394 register boolean confused = (Confusion != 0); 395 boolean too_hard = FALSE; 396 397 if (delay && !confused && spellbook == book && 398 /* handle the sequence: start reading, get interrupted, 399 have book become erased somehow, resume reading it */ 400 booktype != SPE_BLANK_PAPER) { 401 You("continue your efforts to memorize the spell."); 402 } else { 403 /* KMH -- Simplified this code */ 404 if (booktype == SPE_BLANK_PAPER) { 405 pline("This spellbook is all blank."); 406 makeknown(booktype); 407 return(1); 408 } 409 switch (objects[booktype].oc_level) { 410 case 1: 411 case 2: 412 delay = -objects[booktype].oc_delay; 413 break; 414 case 3: 415 case 4: 416 delay = -(objects[booktype].oc_level - 1) * 417 objects[booktype].oc_delay; 418 break; 419 case 5: 420 case 6: 421 delay = -objects[booktype].oc_level * 422 objects[booktype].oc_delay; 423 break; 424 case 7: 425 delay = -8 * objects[booktype].oc_delay; 426 break; 427 default: 428 impossible("Unknown spellbook level %d, book %d;", 429 objects[booktype].oc_level, booktype); 430 return 0; 431 } 432 433 /* Books are often wiser than their readers (Rus.) */ 434 spellbook->in_use = TRUE; 435 if (!spellbook->blessed && 436 spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { 437 if (spellbook->cursed) { 438 too_hard = TRUE; 439 } else { 440 /* uncursed - chance to fail */ 441 int read_ability = ACURR(A_INT) + 4 + u.ulevel/2 442 - 2*objects[booktype].oc_level 443 + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0); 444 /* only wizards know if a spell is too difficult */ 445 if (Role_if(PM_WIZARD) && read_ability < 20 && 446 !confused) { 447 char qbuf[QBUFSZ]; 448 Sprintf(qbuf, 449 "This spellbook is %sdifficult to comprehend. Continue?", 450 (read_ability < 12 ? "very " : "")); 451 if (yn(qbuf) != 'y') { 452 spellbook->in_use = FALSE; 453 return(1); 454 } 455 } 456 /* its up to random luck now */ 457 if (rnd(20) > read_ability) { 458 too_hard = TRUE; 459 } 460 } 461 } 462 463 if (too_hard) { 464 boolean gone = cursed_book(spellbook); 465 466 nomul(delay); /* study time */ 467 delay = 0; 468 if(gone || !rn2(3)) { 469 if (!gone) pline_The("spellbook crumbles to dust!"); 470 if (!objects[spellbook->otyp].oc_name_known && 471 !objects[spellbook->otyp].oc_uname) 472 docall(spellbook); 473 useup(spellbook); 474 } else 475 spellbook->in_use = FALSE; 476 return(1); 477 } else if (confused) { 478 if (!confused_book(spellbook)) { 479 spellbook->in_use = FALSE; 480 } 481 nomul(delay); 482 delay = 0; 483 return(1); 484 } 485 spellbook->in_use = FALSE; 486 487 You("begin to %s the runes.", 488 spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : 489 "memorize"); 490 } 491 492 book = spellbook; 493 set_occupation(learn, "studying", 0); 494 return(1); 495} 496 497/* a spellbook has been destroyed or the character has changed levels; 498 the stored address for the current book is no longer valid */ 499void 500book_disappears(obj) 501struct obj *obj; 502{ 503 if (obj == book) book = (struct obj *)0; 504} 505 506/* renaming an object usually results in it having a different address; 507 so the sequence start reading, get interrupted, name the book, resume 508 reading would read the "new" book from scratch */ 509void 510book_substitution(old_obj, new_obj) 511struct obj *old_obj, *new_obj; 512{ 513 if (old_obj == book) book = new_obj; 514} 515 516/* called from moveloop() */ 517void 518age_spells() 519{ 520 int i; 521 /* 522 * The time relative to the hero (a pass through move 523 * loop) causes all spell knowledge to be decremented. 524 * The hero's speed, rest status, conscious status etc. 525 * does not alter the loss of memory. 526 */ 527 for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) 528 if (spellknow(i)) 529 decrnknow(i); 530 return; 531} 532 533/* 534 * Return TRUE if a spell was picked, with the spell index in the return 535 * parameter. Otherwise return FALSE. 536 */ 537STATIC_OVL boolean 538getspell(spell_no) 539 int *spell_no; 540{ 541 int nspells, idx; 542 char ilet, lets[BUFSZ], qbuf[QBUFSZ]; 543 544 if (spellid(0) == NO_SPELL) { 545 You("don't know any spells right now."); 546 return FALSE; 547 } 548 if (flags.menu_style == MENU_TRADITIONAL) { 549 /* we know there is at least 1 known spell */ 550 for (nspells = 1; nspells < MAXSPELL 551 && spellid(nspells) != NO_SPELL; nspells++) 552 continue; 553 554 if (nspells == 1) Strcpy(lets, "a"); 555 else if (nspells < 27) Sprintf(lets, "a-%c", 'a' + nspells - 1); 556 else if (nspells == 27) Sprintf(lets, "a-zA"); 557 else Sprintf(lets, "a-zA-%c", 'A' + nspells - 27); 558 559 for(;;) { 560 Sprintf(qbuf, "Cast which spell? [%s ?]", lets); 561 if ((ilet = yn_function(qbuf, (char *)0, '\0')) == '?') 562 break; 563 564 if (index(quitchars, ilet)) 565 return FALSE; 566 567 idx = spell_let_to_idx(ilet); 568 if (idx >= 0 && idx < nspells) { 569 *spell_no = idx; 570 return TRUE; 571 } else 572 You("don't know that spell."); 573 } 574 } 575 return dospellmenu("Choose which spell to cast", 576 SPELLMENU_CAST, spell_no); 577} 578 579/* the 'Z' command -- cast a spell */ 580int 581docast() 582{ 583 int spell_no; 584 585 if (getspell(&spell_no)) 586 return spelleffects(spell_no, FALSE); 587 return 0; 588} 589 590STATIC_OVL const char * 591spelltypemnemonic(skill) 592int skill; 593{ 594 switch (skill) { 595 case P_ATTACK_SPELL: 596 return "attack"; 597 case P_HEALING_SPELL: 598 return "healing"; 599 case P_DIVINATION_SPELL: 600 return "divination"; 601 case P_ENCHANTMENT_SPELL: 602 return "enchantment"; 603 case P_CLERIC_SPELL: 604 return "clerical"; 605 case P_ESCAPE_SPELL: 606 return "escape"; 607 case P_MATTER_SPELL: 608 return "matter"; 609 default: 610 impossible("Unknown spell skill, %d;", skill); 611 return ""; 612 } 613} 614 615int 616spell_skilltype(booktype) 617int booktype; 618{ 619 return (objects[booktype].oc_skill); 620} 621 622STATIC_OVL void 623cast_protection() 624{ 625 int loglev = 0; 626 int l = u.ulevel; 627 int natac = u.uac - u.uspellprot; 628 int gain; 629 630 /* loglev=log2(u.ulevel)+1 (1..5) */ 631 while (l) { 632 loglev++; 633 l /= 2; 634 } 635 636 /* The more u.uspellprot you already have, the less you get, 637 * and the better your natural ac, the less you get. 638 * 639 * LEVEL AC SPELLPROT from sucessive SPE_PROTECTION casts 640 * 1 10 0, 1, 2, 3, 4 641 * 1 0 0, 1, 2, 3 642 * 1 -10 0, 1, 2 643 * 2-3 10 0, 2, 4, 5, 6, 7, 8 644 * 2-3 0 0, 2, 4, 5, 6 645 * 2-3 -10 0, 2, 3, 4 646 * 4-7 10 0, 3, 6, 8, 9, 10, 11, 12 647 * 4-7 0 0, 3, 5, 7, 8, 9 648 * 4-7 -10 0, 3, 5, 6 649 * 7-15 -10 0, 3, 5, 6 650 * 8-15 10 0, 4, 7, 10, 12, 13, 14, 15, 16 651 * 8-15 0 0, 4, 7, 9, 10, 11, 12 652 * 8-15 -10 0, 4, 6, 7, 8 653 * 16-30 10 0, 5, 9, 12, 14, 16, 17, 18, 19, 20 654 * 16-30 0 0, 5, 9, 11, 13, 14, 15 655 * 16-30 -10 0, 5, 8, 9, 10 656 */ 657 gain = loglev - (int)u.uspellprot / (4 - min(3,(10 - natac)/10)); 658 659 if (gain > 0) { 660 if (!Blind) { 661 const char *hgolden = hcolor(NH_GOLDEN); 662 663 if (u.uspellprot) 664 pline_The("%s haze around you becomes more dense.", 665 hgolden); 666 else 667 pline_The("%s around you begins to shimmer with %s haze.", 668 /*[ what about being inside solid rock while polyd? ]*/ 669 (Underwater || Is_waterlevel(&u.uz)) ? "water" : "air", 670 an(hgolden)); 671 } 672 u.uspellprot += gain; 673 u.uspmtime = 674 P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT ? 20 : 10; 675 if (!u.usptime) 676 u.usptime = u.uspmtime; 677 find_ac(); 678 } else { 679 Your("skin feels warm for a moment."); 680 } 681} 682 683/* attempting to cast a forgotten spell will cause disorientation */ 684STATIC_OVL void 685spell_backfire(spell) 686int spell; 687{ 688 long duration = (long)((spellev(spell) + 1) * 3); /* 6..24 */ 689 690 /* prior to 3.4.1, the only effect was confusion; it still predominates */ 691 switch (rn2(10)) { 692 case 0: 693 case 1: 694 case 2: 695 case 3: make_confused(duration, FALSE); /* 40% */ 696 break; 697 case 4: 698 case 5: 699 case 6: make_confused(2L * duration / 3L, FALSE); /* 30% */ 700 make_stunned(duration / 3L, FALSE); 701 break; 702 case 7: 703 case 8: make_stunned(2L * duration / 3L, FALSE); /* 20% */ 704 make_confused(duration / 3L, FALSE); 705 break; 706 case 9: make_stunned(duration, FALSE); /* 10% */ 707 break; 708 } 709 return; 710} 711 712int 713spelleffects(spell, atme) 714int spell; 715boolean atme; 716{ 717 int energy, damage, chance, n, intell; 718 int skill, role_skill; 719 boolean confused = (Confusion != 0); 720 struct obj *pseudo; 721 coord cc; 722 723 /* 724 * Spell casting no longer affects knowledge of the spell. A 725 * decrement of spell knowledge is done every turn. 726 */ 727 if (spellknow(spell) <= 0) { 728 Your("knowledge of this spell is twisted."); 729 pline("It invokes nightmarish images in your mind..."); 730 spell_backfire(spell); 731 return(0); 732 } else if (spellknow(spell) <= 100) { 733 You("strain to recall the spell."); 734 } else if (spellknow(spell) <= 1000) { 735 Your("knowledge of this spell is growing faint."); 736 } 737 energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */ 738 739 if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) { 740 You("are too hungry to cast that spell."); 741 return(0); 742 } else if (ACURR(A_STR) < 4) { 743 You("lack the strength to cast spells."); 744 return(0); 745 } else if(check_capacity( 746 "Your concentration falters while carrying so much stuff.")) { 747 return (1); 748 } else if (!freehand()) { 749 Your("arms are not free to cast!"); 750 return (0); 751 } 752 753 if (u.uhave.amulet) { 754 You_feel("the amulet draining your energy away."); 755 energy += rnd(2*energy); 756 } 757 if(energy > u.uen) { 758 You("don't have enough energy to cast that spell."); 759 return(0); 760 } else { 761 if (spellid(spell) != SPE_DETECT_FOOD) { 762 int hungr = energy * 2; 763 764 /* If hero is a wizard, their current intelligence 765 * (bonuses + temporary + current) 766 * affects hunger reduction in casting a spell. 767 * 1. int = 17-18 no reduction 768 * 2. int = 16 1/4 hungr 769 * 3. int = 15 1/2 hungr 770 * 4. int = 1-14 normal reduction 771 * The reason for this is: 772 * a) Intelligence affects the amount of exertion 773 * in thinking. 774 * b) Wizards have spent their life at magic and 775 * understand quite well how to cast spells. 776 */ 777 intell = acurr(A_INT); 778 if (!Role_if(PM_WIZARD)) intell = 10; 779 switch (intell) { 780 case 25: case 24: case 23: case 22: 781 case 21: case 20: case 19: case 18: 782 case 17: hungr = 0; break; 783 case 16: hungr /= 4; break; 784 case 15: hungr /= 2; break; 785 } 786 /* don't put player (quite) into fainting from 787 * casting a spell, particularly since they might 788 * not even be hungry at the beginning; however, 789 * this is low enough that they must eat before 790 * casting anything else except detect food 791 */ 792 if (hungr > u.uhunger-3) 793 hungr = u.uhunger-3; 794 morehungry(hungr); 795 } 796 } 797 798 chance = percent_success(spell); 799 if (confused || (rnd(100) > chance)) { 800 You("fail to cast the spell correctly."); 801 u.uen -= energy / 2; 802 flags.botl = 1; 803 return(1); 804 } 805 806 u.uen -= energy; 807 flags.botl = 1; 808 exercise(A_WIS, TRUE); 809 /* pseudo is a temporary "false" object containing the spell stats */ 810 pseudo = mksobj(spellid(spell), FALSE, FALSE); 811 pseudo->blessed = pseudo->cursed = 0; 812 pseudo->quan = 20L; /* do not let useup get it */ 813 /* 814 * Find the skill the hero has in a spell type category. 815 * See spell_skilltype for categories. 816 */ 817 skill = spell_skilltype(pseudo->otyp); 818 role_skill = P_SKILL(skill); 819 820 switch(pseudo->otyp) { 821 /* 822 * At first spells act as expected. As the hero increases in skill 823 * with the appropriate spell type, some spells increase in their 824 * effects, e.g. more damage, further distance, and so on, without 825 * additional cost to the spellcaster. 826 */ 827 case SPE_CONE_OF_COLD: 828 case SPE_FIREBALL: 829 if (role_skill >= P_SKILLED) { 830 if (throwspell()) { 831 cc.x=u.dx;cc.y=u.dy; 832 n=rnd(8)+1; 833 while(n--) { 834 if(!u.dx && !u.dy && !u.dz) { 835 if ((damage = zapyourself(pseudo, TRUE)) != 0) { 836 char buf[BUFSZ]; 837 Sprintf(buf, "zapped %sself with a spell", uhim()); 838 losehp(damage, buf, NO_KILLER_PREFIX); 839 } 840 } else { 841 explode(u.dx, u.dy, 842 pseudo->otyp - SPE_MAGIC_MISSILE + 10, 843 u.ulevel/2 + 1 + spell_damage_bonus(), 0, 844 (pseudo->otyp == SPE_CONE_OF_COLD) ? 845 EXPL_FROSTY : EXPL_FIERY); 846 } 847 u.dx = cc.x+rnd(3)-2; u.dy = cc.y+rnd(3)-2; 848 if (!isok(u.dx,u.dy) || !cansee(u.dx,u.dy) || 849 IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) { 850 /* Spell is reflected back to center */ 851 u.dx = cc.x; 852 u.dy = cc.y; 853 } 854 } 855 } 856 break; 857 } /* else fall through... */ 858 859 /* these spells are all duplicates of wand effects */ 860 case SPE_FORCE_BOLT: 861 case SPE_SLEEP: 862 case SPE_MAGIC_MISSILE: 863 case SPE_KNOCK: 864 case SPE_SLOW_MONSTER: 865 case SPE_WIZARD_LOCK: 866 case SPE_DIG: 867 case SPE_TURN_UNDEAD: 868 case SPE_POLYMORPH: 869 case SPE_TELEPORT_AWAY: 870 case SPE_CANCELLATION: 871 case SPE_FINGER_OF_DEATH: 872 case SPE_LIGHT: 873 case SPE_DETECT_UNSEEN: 874 case SPE_HEALING: 875 case SPE_EXTRA_HEALING: 876 case SPE_DRAIN_LIFE: 877 case SPE_STONE_TO_FLESH: 878 if (!(objects[pseudo->otyp].oc_dir == NODIR)) { 879 if (atme) u.dx = u.dy = u.dz = 0; 880 else if (!getdir((char *)0)) { 881 /* getdir cancelled, re-use previous direction */ 882 pline_The("magical energy is released!"); 883 } 884 if(!u.dx && !u.dy && !u.dz) { 885 if ((damage = zapyourself(pseudo, TRUE)) != 0) { 886 char buf[BUFSZ]; 887 Sprintf(buf, "zapped %sself with a spell", uhim()); 888 losehp(damage, buf, NO_KILLER_PREFIX); 889 } 890 } else weffects(pseudo); 891 } else weffects(pseudo); 892 update_inventory(); /* spell may modify inventory */ 893 break; 894 895 /* these are all duplicates of scroll effects */ 896 case SPE_REMOVE_CURSE: 897 case SPE_CONFUSE_MONSTER: 898 case SPE_DETECT_FOOD: 899 case SPE_CAUSE_FEAR: 900 /* high skill yields effect equivalent to blessed scroll */ 901 if (role_skill >= P_SKILLED) pseudo->blessed = 1; 902 /* fall through */ 903 case SPE_CHARM_MONSTER: 904 case SPE_MAGIC_MAPPING: 905 case SPE_CREATE_MONSTER: 906 case SPE_IDENTIFY: 907 (void) seffects(pseudo); 908 break; 909 910 /* these are all duplicates of potion effects */ 911 case SPE_HASTE_SELF: 912 case SPE_DETECT_TREASURE: 913 case SPE_DETECT_MONSTERS: 914 case SPE_LEVITATION: 915 case SPE_RESTORE_ABILITY: 916 /* high skill yields effect equivalent to blessed potion */ 917 if (role_skill >= P_SKILLED) pseudo->blessed = 1; 918 /* fall through */ 919 case SPE_INVISIBILITY: 920 (void) peffects(pseudo); 921 break; 922 923 case SPE_CURE_BLINDNESS: 924 healup(0, 0, FALSE, TRUE); 925 break; 926 case SPE_CURE_SICKNESS: 927 if (Sick) You("are no longer ill."); 928 if (Slimed) { 929 pline_The("slime disappears!"); 930 Slimed = 0; 931 /* flags.botl = 1; -- healup() handles this */ 932 } 933 healup(0, 0, TRUE, FALSE); 934 break; 935 case SPE_CREATE_FAMILIAR: 936 (void) make_familiar((struct obj *)0, u.ux, u.uy, FALSE); 937 break; 938 case SPE_CLAIRVOYANCE: 939 if (!BClairvoyant) 940 do_vicinity_map(); 941 /* at present, only one thing blocks clairvoyance */ 942 else if (uarmh && uarmh->otyp == CORNUTHAUM) 943 You("sense a pointy hat on top of your %s.", 944 body_part(HEAD)); 945 break; 946 case SPE_PROTECTION: 947 cast_protection(); 948 break; 949 case SPE_JUMPING: 950 if (!jump(max(role_skill,1))) 951 pline(nothing_happens); 952 break; 953 default: 954 impossible("Unknown spell %d attempted.", spell); 955 obfree(pseudo, (struct obj *)0); 956 return(0); 957 } 958 959 /* gain skill for successful cast */ 960 use_skill(skill, spellev(spell)); 961 962 obfree(pseudo, (struct obj *)0); /* now, get rid of it */ 963 return(1); 964} 965 966/* Choose location where spell takes effect. */ 967STATIC_OVL int 968throwspell() 969{ 970 coord cc; 971 972 if (u.uinwater) { 973 pline("You're joking! In this weather?"); return 0; 974 } else if (Is_waterlevel(&u.uz)) { 975 You("had better wait for the sun to come out."); return 0; 976 } 977 978 pline("Where do you want to cast the spell?"); 979 cc.x = u.ux; 980 cc.y = u.uy; 981 if (getpos(&cc, TRUE, "the desired position") < 0) 982 return 0; /* user pressed ESC */ 983 /* The number of moves from hero to where the spell drops.*/ 984 if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) { 985 pline_The("spell dissipates over the distance!"); 986 return 0; 987 } else if (u.uswallow) { 988 pline_The("spell is cut short!"); 989 exercise(A_WIS, FALSE); /* What were you THINKING! */ 990 u.dx = 0; 991 u.dy = 0; 992 return 1; 993 } else if (!cansee(cc.x, cc.y) || IS_STWALL(levl[cc.x][cc.y].typ)) { 994 Your("mind fails to lock onto that location!"); 995 return 0; 996 } else { 997 u.dx=cc.x; 998 u.dy=cc.y; 999 return 1; 1000 } 1001} 1002 1003void 1004losespells() 1005{ 1006 boolean confused = (Confusion != 0); 1007 int n, nzap, i; 1008 1009 book = 0; 1010 for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; n++) 1011 continue; 1012 if (n) { 1013 nzap = rnd(n) + confused ? 1 : 0; 1014 if (nzap > n) nzap = n; 1015 for (i = n - nzap; i < n; i++) { 1016 spellid(i) = NO_SPELL; 1017 exercise(A_WIS, FALSE); /* ouch! */ 1018 } 1019 } 1020} 1021 1022/* the '+' command -- view known spells */ 1023int 1024dovspell() 1025{ 1026 char qbuf[QBUFSZ]; 1027 int splnum, othnum; 1028 struct spell spl_tmp; 1029 1030 if (spellid(0) == NO_SPELL) 1031 You("don't know any spells right now."); 1032 else { 1033 while (dospellmenu("Currently known spells", 1034 SPELLMENU_VIEW, &splnum)) { 1035 Sprintf(qbuf, "Reordering spells; swap '%c' with", 1036 spellet(splnum)); 1037 if (!dospellmenu(qbuf, splnum, &othnum)) break; 1038 1039 spl_tmp = spl_book[splnum]; 1040 spl_book[splnum] = spl_book[othnum]; 1041 spl_book[othnum] = spl_tmp; 1042 } 1043 } 1044 return 0; 1045} 1046 1047STATIC_OVL boolean 1048dospellmenu(prompt, splaction, spell_no) 1049const char *prompt; 1050int splaction; /* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */ 1051int *spell_no; 1052{ 1053 winid tmpwin; 1054 int i, n, how; 1055 char buf[BUFSZ]; 1056 menu_item *selected; 1057 anything any; 1058 1059 tmpwin = create_nhwindow(NHW_MENU); 1060 start_menu(tmpwin); 1061 any.a_void = 0; /* zero out all bits */ 1062 1063 /* 1064 * The correct spacing of the columns depends on the 1065 * following that (1) the font is monospaced and (2) 1066 * that selection letters are pre-pended to the given 1067 * string and are of the form "a - ". 1068 * 1069 * To do it right would require that we implement columns 1070 * in the window-ports (say via a tab character). 1071 */ 1072 if (!iflags.menu_tab_sep) 1073 Sprintf(buf, "%-20s Level %-12s Fail", " Name", "Category"); 1074 else 1075 Sprintf(buf, "Name\tLevel\tCategory\tFail"); 1076 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, buf, MENU_UNSELECTED); 1077 for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) { 1078 Sprintf(buf, iflags.menu_tab_sep ? 1079 "%s\t%-d%s\t%s\t%-d%%" : "%-20s %2d%s %-12s %3d%%", 1080 spellname(i), spellev(i), 1081 spellknow(i) ? " " : "*", 1082 spelltypemnemonic(spell_skilltype(spellid(i))), 1083 100 - percent_success(i)); 1084 1085 any.a_int = i+1; /* must be non-zero */ 1086 add_menu(tmpwin, NO_GLYPH, &any, 1087 spellet(i), 0, ATR_NONE, buf, 1088 (i == splaction) ? MENU_SELECTED : MENU_UNSELECTED); 1089 } 1090 end_menu(tmpwin, prompt); 1091 1092 how = PICK_ONE; 1093 if (splaction == SPELLMENU_VIEW && spellid(1) == NO_SPELL) 1094 how = PICK_NONE; /* only one spell => nothing to swap with */ 1095 n = select_menu(tmpwin, how, &selected); 1096 destroy_nhwindow(tmpwin); 1097 if (n > 0) { 1098 *spell_no = selected[0].item.a_int - 1; 1099 /* menu selection for `PICK_ONE' does not 1100 de-select any preselected entry */ 1101 if (n > 1 && *spell_no == splaction) 1102 *spell_no = selected[1].item.a_int - 1; 1103 free((genericptr_t)selected); 1104 /* default selection of preselected spell means that 1105 user chose not to swap it with anything */ 1106 if (*spell_no == splaction) return FALSE; 1107 return TRUE; 1108 } else if (splaction >= 0) { 1109 /* explicit de-selection of preselected spell means that 1110 user is still swapping but not for the current spell */ 1111 *spell_no = splaction; 1112 return TRUE; 1113 } 1114 return FALSE; 1115} 1116 1117/* Integer square root function without using floating point. */ 1118STATIC_OVL int 1119isqrt(val) 1120int val; 1121{ 1122 int rt = 0; 1123 int odd = 1; 1124 while(val >= odd) { 1125 val = val-odd; 1126 odd = odd+2; 1127 rt = rt + 1; 1128 } 1129 return rt; 1130} 1131 1132STATIC_OVL int 1133percent_success(spell) 1134int spell; 1135{ 1136 /* Intrinsic and learned ability are combined to calculate 1137 * the probability of player's success at cast a given spell. 1138 */ 1139 int chance, splcaster, special, statused; 1140 int difficulty; 1141 int skill; 1142 1143 /* Calculate intrinsic ability (splcaster) */ 1144 1145 splcaster = urole.spelbase; 1146 special = urole.spelheal; 1147 statused = ACURR(urole.spelstat); 1148 1149 if (uarm && is_metallic(uarm)) 1150 splcaster += (uarmc && uarmc->otyp == ROBE) ? 1151 urole.spelarmr/2 : urole.spelarmr; 1152 else if (uarmc && uarmc->otyp == ROBE) 1153 splcaster -= urole.spelarmr; 1154 if (uarms) splcaster += urole.spelshld; 1155 1156 if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE) 1157 splcaster += uarmhbon; 1158 if (uarmg && is_metallic(uarmg)) splcaster += uarmgbon; 1159 if (uarmf && is_metallic(uarmf)) splcaster += uarmfbon; 1160 1161 if (spellid(spell) == urole.spelspec) 1162 splcaster += urole.spelsbon; 1163 1164 1165 /* `healing spell' bonus */ 1166 if (spellid(spell) == SPE_HEALING || 1167 spellid(spell) == SPE_EXTRA_HEALING || 1168 spellid(spell) == SPE_CURE_BLINDNESS || 1169 spellid(spell) == SPE_CURE_SICKNESS || 1170 spellid(spell) == SPE_RESTORE_ABILITY || 1171 spellid(spell) == SPE_REMOVE_CURSE) splcaster += special; 1172 1173 if (splcaster > 20) splcaster = 20; 1174 1175 /* Calculate learned ability */ 1176 1177 /* Players basic likelihood of being able to cast any spell 1178 * is based of their `magic' statistic. (Int or Wis) 1179 */ 1180 chance = 11 * statused / 2; 1181 1182 /* 1183 * High level spells are harder. Easier for higher level casters. 1184 * The difficulty is based on the hero's level and their skill level 1185 * in that spell type. 1186 */ 1187 skill = P_SKILL(spell_skilltype(spellid(spell))); 1188 skill = max(skill,P_UNSKILLED) - 1; /* unskilled => 0 */ 1189 difficulty= (spellev(spell)-1) * 4 - ((skill * 6) + (u.ulevel/3) + 1); 1190 1191 if (difficulty > 0) { 1192 /* Player is too low level or unskilled. */ 1193 chance -= isqrt(900 * difficulty + 2000); 1194 } else { 1195 /* Player is above level. Learning continues, but the 1196 * law of diminishing returns sets in quickly for 1197 * low-level spells. That is, a player quickly gains 1198 * no advantage for raising level. 1199 */ 1200 int learning = 15 * -difficulty / spellev(spell); 1201 chance += learning > 20 ? 20 : learning; 1202 } 1203 1204 /* Clamp the chance: >18 stat and advanced learning only help 1205 * to a limit, while chances below "hopeless" only raise the 1206 * specter of overflowing 16-bit ints (and permit wearing a 1207 * shield to raise the chances :-). 1208 */ 1209 if (chance < 0) chance = 0; 1210 if (chance > 120) chance = 120; 1211 1212 /* Wearing anything but a light shield makes it very awkward 1213 * to cast a spell. The penalty is not quite so bad for the 1214 * player's role-specific spell. 1215 */ 1216 if (uarms && weight(uarms) > (int) objects[SMALL_SHIELD].oc_weight) { 1217 if (spellid(spell) == urole.spelspec) { 1218 chance /= 2; 1219 } else { 1220 chance /= 4; 1221 } 1222 } 1223 1224 /* Finally, chance (based on player intell/wisdom and level) is 1225 * combined with ability (based on player intrinsics and 1226 * encumbrances). No matter how intelligent/wise and advanced 1227 * a player is, intrinsics and encumbrance can prevent casting; 1228 * and no matter how able, learning is always required. 1229 */ 1230 chance = chance * (20-splcaster) / 15 - splcaster; 1231 1232 /* Clamp to percentile */ 1233 if (chance > 100) chance = 100; 1234 if (chance < 0) chance = 0; 1235 1236 return chance; 1237} 1238 1239 1240/* Learn a spell during creation of the initial inventory */ 1241void 1242initialspell(obj) 1243struct obj *obj; 1244{ 1245 int i; 1246 1247 for (i = 0; i < MAXSPELL; i++) { 1248 if (spellid(i) == obj->otyp) { 1249 pline("Error: Spell %s already known.", 1250 OBJ_NAME(objects[obj->otyp])); 1251 return; 1252 } 1253 if (spellid(i) == NO_SPELL) { 1254 spl_book[i].sp_id = obj->otyp; 1255 spl_book[i].sp_lev = objects[obj->otyp].oc_level; 1256 incrnknow(i); 1257 return; 1258 } 1259 } 1260 impossible("Too many spells memorized!"); 1261 return; 1262} 1263 1264/*spell.c*/ 1265