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