Fix #H4411: Targeting detected monster through wall with fireball
[aNetHack.git] / src / spell.c
blob40969a62ed364076c1dc6829b94894b0f50f7d9e
1 /* NetHack 3.6 spell.c $NHDT-Date: 1450584420 2015/12/20 04:07:00 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.75 $ */
2 /* Copyright (c) M. Stephenson 1988 */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 /* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */
8 #define SPELLMENU_CAST (-2)
9 #define SPELLMENU_VIEW (-1)
10 #define SPELLMENU_SORT (MAXSPELL) /* special menu entry */
12 /* spell retention period, in turns; at 10% of this value, player becomes
13 eligible to reread the spellbook and regain 100% retention (the threshold
14 used to be 1000 turns, which was 10% of the original 10000 turn retention
15 period but didn't get adjusted when that period got doubled to 20000) */
16 #define KEEN 20000
17 /* x: need to add 1 when used for reading a spellbook rather than for hero
18 initialization; spell memory is decremented at the end of each turn,
19 including the turn on which the spellbook is read; without the extra
20 increment, the hero used to get cheated out of 1 turn of retention */
21 #define incrnknow(spell, x) (spl_book[spell].sp_know = KEEN + (x))
23 #define spellev(spell) spl_book[spell].sp_lev
24 #define spellname(spell) OBJ_NAME(objects[spellid(spell)])
25 #define spellet(spell) \
26 ((char) ((spell < 26) ? ('a' + spell) : ('A' + spell - 26)))
28 STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P));
29 STATIC_DCL boolean FDECL(cursed_book, (struct obj * bp));
30 STATIC_DCL boolean FDECL(confused_book, (struct obj *));
31 STATIC_DCL void FDECL(deadbook, (struct obj *));
32 STATIC_PTR int NDECL(learn);
33 STATIC_DCL boolean NDECL(rejectcasting);
34 STATIC_DCL boolean FDECL(getspell, (int *));
35 STATIC_PTR int FDECL(CFDECLSPEC spell_cmp, (const genericptr,
36 const genericptr));
37 STATIC_DCL void NDECL(sortspells);
38 STATIC_DCL boolean NDECL(spellsortmenu);
39 STATIC_DCL boolean FDECL(dospellmenu, (const char *, int, int *));
40 STATIC_DCL int FDECL(percent_success, (int));
41 STATIC_DCL char *FDECL(spellretention, (int, char *));
42 STATIC_DCL int NDECL(throwspell);
43 STATIC_DCL void NDECL(cast_protection);
44 STATIC_DCL void FDECL(spell_backfire, (int));
45 STATIC_DCL const char *FDECL(spelltypemnemonic, (int));
46 STATIC_DCL boolean FDECL(spell_hurtle_step, (genericptr_t, int, int));
48 /* The roles[] table lists the role-specific values for tuning
49 * percent_success().
51 * Reasoning:
52 * spelbase, spelheal:
53 * Arc are aware of magic through historical research
54 * Bar abhor magic (Conan finds it "interferes with his animal instincts")
55 * Cav are ignorant to magic
56 * Hea are very aware of healing magic through medical research
57 * Kni are moderately aware of healing from Paladin training
58 * Mon use magic to attack and defend in lieu of weapons and armor
59 * Pri are very aware of healing magic through theological research
60 * Ran avoid magic, preferring to fight unseen and unheard
61 * Rog are moderately aware of magic through trickery
62 * Sam have limited magical awareness, preferring meditation to conjuring
63 * Tou are aware of magic from all the great films they have seen
64 * Val have limited magical awareness, preferring fighting
65 * Wiz are trained mages
67 * The arms penalty is lessened for trained fighters Bar, Kni, Ran,
68 * Sam, Val -- the penalty is its metal interference, not encumbrance.
69 * The `spelspec' is a single spell which is fundamentally easier
70 * for that role to cast.
72 * spelspec, spelsbon:
73 * Arc map masters (SPE_MAGIC_MAPPING)
74 * Bar fugue/berserker (SPE_HASTE_SELF)
75 * Cav born to dig (SPE_DIG)
76 * Hea to heal (SPE_CURE_SICKNESS)
77 * Kni to turn back evil (SPE_TURN_UNDEAD)
78 * Mon to preserve their abilities (SPE_RESTORE_ABILITY)
79 * Pri to bless (SPE_REMOVE_CURSE)
80 * Ran to hide (SPE_INVISIBILITY)
81 * Rog to find loot (SPE_DETECT_TREASURE)
82 * Sam to be At One (SPE_CLAIRVOYANCE)
83 * Tou to smile (SPE_CHARM_MONSTER)
84 * Val control the cold (SPE_CONE_OF_COLD)
85 * Wiz all really, but SPE_MAGIC_MISSILE is their party trick
87 * See percent_success() below for more comments.
89 * uarmbon, uarmsbon, uarmhbon, uarmgbon, uarmfbon:
90 * Fighters find body armour & shield a little less limiting.
91 * Headgear, Gauntlets and Footwear are not role-specific (but
92 * still have an effect, except helm of brilliance, which is designed
93 * to permit magic-use).
96 #define uarmhbon 4 /* Metal helmets interfere with the mind */
97 #define uarmgbon 6 /* Casting channels through the hands */
98 #define uarmfbon 2 /* All metal interferes to some degree */
100 /* since the spellbook itself doesn't blow up, don't say just "explodes" */
101 static const char explodes[] = "radiates explosive energy";
103 /* convert a letter into a number in the range 0..51, or -1 if not a letter */
104 STATIC_OVL int
105 spell_let_to_idx(ilet)
106 char ilet;
108 int indx;
110 indx = ilet - 'a';
111 if (indx >= 0 && indx < 26)
112 return indx;
113 indx = ilet - 'A';
114 if (indx >= 0 && indx < 26)
115 return indx + 26;
116 return -1;
119 /* TRUE: book should be destroyed by caller */
120 STATIC_OVL boolean
121 cursed_book(bp)
122 struct obj *bp;
124 boolean was_in_use;
125 int lev = objects[bp->otyp].oc_level;
126 int dmg = 0;
128 switch (rn2(lev)) {
129 case 0:
130 You_feel("a wrenching sensation.");
131 tele(); /* teleport him */
132 break;
133 case 1:
134 You_feel("threatened.");
135 aggravate();
136 break;
137 case 2:
138 make_blinded(Blinded + rn1(100, 250), TRUE);
139 break;
140 case 3:
141 take_gold();
142 break;
143 case 4:
144 pline("These runes were just too much to comprehend.");
145 make_confused(HConfusion + rn1(7, 16), FALSE);
146 break;
147 case 5:
148 pline_The("book was coated with contact poison!");
149 if (uarmg) {
150 erode_obj(uarmg, "gloves", ERODE_CORRODE, EF_GREASE | EF_VERBOSE);
151 break;
153 /* temp disable in_use; death should not destroy the book */
154 was_in_use = bp->in_use;
155 bp->in_use = FALSE;
156 losestr(Poison_resistance ? rn1(2, 1) : rn1(4, 3));
157 losehp(rnd(Poison_resistance ? 6 : 10), "contact-poisoned spellbook",
158 KILLED_BY_AN);
159 bp->in_use = was_in_use;
160 break;
161 case 6:
162 if (Antimagic) {
163 shieldeff(u.ux, u.uy);
164 pline_The("book %s, but you are unharmed!", explodes);
165 } else {
166 pline("As you read the book, it %s in your %s!", explodes,
167 body_part(FACE));
168 dmg = 2 * rnd(10) + 5;
169 losehp(Maybe_Half_Phys(dmg), "exploding rune", KILLED_BY_AN);
171 return TRUE;
172 default:
173 rndcurse();
174 break;
176 return FALSE;
179 /* study while confused: returns TRUE if the book is destroyed */
180 STATIC_OVL boolean
181 confused_book(spellbook)
182 struct obj *spellbook;
184 boolean gone = FALSE;
186 if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
187 spellbook->in_use = TRUE; /* in case called from learn */
188 pline(
189 "Being confused you have difficulties in controlling your actions.");
190 display_nhwindow(WIN_MESSAGE, FALSE);
191 You("accidentally tear the spellbook to pieces.");
192 if (!objects[spellbook->otyp].oc_name_known
193 && !objects[spellbook->otyp].oc_uname)
194 docall(spellbook);
195 useup(spellbook);
196 gone = TRUE;
197 } else {
198 You("find yourself reading the %s line over and over again.",
199 spellbook == context.spbook.book ? "next" : "first");
201 return gone;
204 /* special effects for The Book of the Dead */
205 STATIC_OVL void
206 deadbook(book2)
207 struct obj *book2;
209 struct monst *mtmp, *mtmp2;
210 coord mm;
212 You("turn the pages of the Book of the Dead...");
213 makeknown(SPE_BOOK_OF_THE_DEAD);
214 /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */
215 book2->known = 1;
216 if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
217 register struct obj *otmp;
218 register boolean arti1_primed = FALSE, arti2_primed = FALSE,
219 arti_cursed = FALSE;
221 if (book2->cursed) {
222 pline_The("runes appear scrambled. You can't read them!");
223 return;
226 if (!u.uhave.bell || !u.uhave.menorah) {
227 pline("A chill runs down your %s.", body_part(SPINE));
228 if (!u.uhave.bell)
229 You_hear("a faint chime...");
230 if (!u.uhave.menorah)
231 pline("Vlad's doppelganger is amused.");
232 return;
235 for (otmp = invent; otmp; otmp = otmp->nobj) {
236 if (otmp->otyp == CANDELABRUM_OF_INVOCATION && otmp->spe == 7
237 && otmp->lamplit) {
238 if (!otmp->cursed)
239 arti1_primed = TRUE;
240 else
241 arti_cursed = TRUE;
243 if (otmp->otyp == BELL_OF_OPENING
244 && (moves - otmp->age) < 5L) { /* you rang it recently */
245 if (!otmp->cursed)
246 arti2_primed = TRUE;
247 else
248 arti_cursed = TRUE;
252 if (arti_cursed) {
253 pline_The("invocation fails!");
254 pline("At least one of your artifacts is cursed...");
255 } else if (arti1_primed && arti2_primed) {
256 unsigned soon =
257 (unsigned) d(2, 6); /* time til next intervene() */
259 /* successful invocation */
260 mkinvokearea();
261 u.uevent.invoked = 1;
262 /* in case you haven't killed the Wizard yet, behave as if
263 you just did */
264 u.uevent.udemigod = 1; /* wizdead() */
265 if (!u.udg_cnt || u.udg_cnt > soon)
266 u.udg_cnt = soon;
267 } else { /* at least one artifact not prepared properly */
268 You("have a feeling that %s is amiss...", something);
269 goto raise_dead;
271 return;
274 /* when not an invocation situation */
275 if (book2->cursed) {
276 raise_dead:
278 You("raised the dead!");
279 /* first maybe place a dangerous adversary */
280 if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH], u.ux, u.uy,
281 NO_MINVENT)) != 0
282 || (mtmp = makemon(&mons[PM_NALFESHNEE], u.ux, u.uy,
283 NO_MINVENT)) != 0)) {
284 mtmp->mpeaceful = 0;
285 set_malign(mtmp);
287 /* next handle the affect on things you're carrying */
288 (void) unturn_dead(&youmonst);
289 /* last place some monsters around you */
290 mm.x = u.ux;
291 mm.y = u.uy;
292 mkundead(&mm, TRUE, NO_MINVENT);
293 } else if (book2->blessed) {
294 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
295 mtmp2 = mtmp->nmon; /* tamedog() changes chain */
296 if (DEADMONSTER(mtmp))
297 continue;
299 if ((is_undead(mtmp->data) || is_vampshifter(mtmp))
300 && cansee(mtmp->mx, mtmp->my)) {
301 mtmp->mpeaceful = TRUE;
302 if (sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
303 && distu(mtmp->mx, mtmp->my) < 4)
304 if (mtmp->mtame) {
305 if (mtmp->mtame < 20)
306 mtmp->mtame++;
307 } else
308 (void) tamedog(mtmp, (struct obj *) 0);
309 else
310 monflee(mtmp, 0, FALSE, TRUE);
313 } else {
314 switch (rn2(3)) {
315 case 0:
316 Your("ancestors are annoyed with you!");
317 break;
318 case 1:
319 pline_The("headstones in the cemetery begin to move!");
320 break;
321 default:
322 pline("Oh my! Your name appears in the book!");
325 return;
328 /* 'book' has just become cursed; if we're reading it and realize it is
329 now cursed, interrupt */
330 void
331 book_cursed(book)
332 struct obj *book;
334 if (occupation == learn && context.spbook.book == book
335 && book->cursed && book->bknown && multi >= 0)
336 stop_occupation();
339 STATIC_PTR int
340 learn(VOID_ARGS)
342 int i;
343 short booktype;
344 char splname[BUFSZ];
345 boolean costly = TRUE;
346 struct obj *book = context.spbook.book;
348 /* JDS: lenses give 50% faster reading; 33% smaller read time */
349 if (context.spbook.delay && ublindf && ublindf->otyp == LENSES && rn2(2))
350 context.spbook.delay++;
351 if (Confusion) { /* became confused while learning */
352 (void) confused_book(book);
353 context.spbook.book = 0; /* no longer studying */
354 context.spbook.o_id = 0;
355 nomul(context.spbook.delay); /* remaining delay is uninterrupted */
356 multi_reason = "reading a book";
357 nomovemsg = 0;
358 context.spbook.delay = 0;
359 return 0;
361 if (context.spbook.delay) {
362 /* not if (context.spbook.delay++), so at end delay == 0 */
363 context.spbook.delay++;
364 return 1; /* still busy */
366 exercise(A_WIS, TRUE); /* you're studying. */
367 booktype = book->otyp;
368 if (booktype == SPE_BOOK_OF_THE_DEAD) {
369 deadbook(book);
370 return 0;
373 Sprintf(splname,
374 objects[booktype].oc_name_known ? "\"%s\"" : "the \"%s\" spell",
375 OBJ_NAME(objects[booktype]));
376 for (i = 0; i < MAXSPELL; i++)
377 if (spellid(i) == booktype || spellid(i) == NO_SPELL)
378 break;
380 if (i == MAXSPELL) {
381 impossible("Too many spells memorized!");
382 } else if (spellid(i) == booktype) {
383 /* normal book can be read and re-read a total of 4 times */
384 if (book->spestudied > MAX_SPELL_STUDY) {
385 pline("This spellbook is too faint to be read any more.");
386 book->otyp = booktype = SPE_BLANK_PAPER;
387 /* reset spestudied as if polymorph had taken place */
388 book->spestudied = rn2(book->spestudied);
389 } else if (spellknow(i) > KEEN / 10) {
390 You("know %s quite well already.", splname);
391 costly = FALSE;
392 } else { /* spellknow(i) <= KEEN/10 */
393 Your("knowledge of %s is %s.", splname,
394 spellknow(i) ? "keener" : "restored");
395 incrnknow(i, 1);
396 book->spestudied++;
397 exercise(A_WIS, TRUE); /* extra study */
399 /* make book become known even when spell is already
400 known, in case amnesia made you forget the book */
401 makeknown((int) booktype);
402 } else { /* (spellid(i) == NO_SPELL) */
403 /* for a normal book, spestudied will be zero, but for
404 a polymorphed one, spestudied will be non-zero and
405 one less reading is available than when re-learning */
406 if (book->spestudied >= MAX_SPELL_STUDY) {
407 /* pre-used due to being the product of polymorph */
408 pline("This spellbook is too faint to read even once.");
409 book->otyp = booktype = SPE_BLANK_PAPER;
410 /* reset spestudied as if polymorph had taken place */
411 book->spestudied = rn2(book->spestudied);
412 } else {
413 spl_book[i].sp_id = booktype;
414 spl_book[i].sp_lev = objects[booktype].oc_level;
415 incrnknow(i, 1);
416 book->spestudied++;
417 You(i > 0 ? "add %s to your repertoire." : "learn %s.", splname);
419 makeknown((int) booktype);
422 if (book->cursed) { /* maybe a demon cursed it */
423 if (cursed_book(book)) {
424 useup(book);
425 context.spbook.book = 0;
426 context.spbook.o_id = 0;
427 return 0;
430 if (costly)
431 check_unpaid(book);
432 context.spbook.book = 0;
433 context.spbook.o_id = 0;
434 return 0;
438 study_book(spellbook)
439 register struct obj *spellbook;
441 int booktype = spellbook->otyp;
442 boolean confused = (Confusion != 0);
443 boolean too_hard = FALSE;
445 /* attempting to read dull book may make hero fall asleep */
446 if (!confused && !Sleep_resistance
447 && !strcmp(OBJ_DESCR(objects[booktype]), "dull")) {
448 const char *eyes;
449 int dullbook = rnd(25) - ACURR(A_WIS);
451 /* adjust chance if hero stayed awake, got interrupted, retries */
452 if (context.spbook.delay && spellbook == context.spbook.book)
453 dullbook -= rnd(objects[booktype].oc_level);
455 if (dullbook > 0) {
456 eyes = body_part(EYE);
457 if (eyecount(youmonst.data) > 1)
458 eyes = makeplural(eyes);
459 pline("This book is so dull that you can't keep your %s open.",
460 eyes);
461 dullbook += rnd(2 * objects[booktype].oc_level);
462 fall_asleep(-dullbook, TRUE);
463 return 1;
467 if (context.spbook.delay && !confused && spellbook == context.spbook.book
468 /* handle the sequence: start reading, get interrupted, have
469 context.spbook.book become erased somehow, resume reading it */
470 && booktype != SPE_BLANK_PAPER) {
471 You("continue your efforts to %s.",
472 (booktype == SPE_NOVEL) ? "read the novel" : "memorize the spell");
473 } else {
474 /* KMH -- Simplified this code */
475 if (booktype == SPE_BLANK_PAPER) {
476 pline("This spellbook is all blank.");
477 makeknown(booktype);
478 return 1;
481 /* 3.6 tribute */
482 if (booktype == SPE_NOVEL) {
483 /* Obtain current Terry Pratchett book title */
484 const char *tribtitle = noveltitle(&spellbook->novelidx);
486 if (read_tribute("books", tribtitle, 0, (char *) 0, 0,
487 spellbook->o_id)) {
488 u.uconduct.literate++;
489 check_unpaid(spellbook);
490 makeknown(booktype);
491 if (!u.uevent.read_tribute) {
492 /* give bonus of 20 xp and 4*20+0 pts */
493 more_experienced(20, 0);
494 newexplevel();
495 u.uevent.read_tribute = 1; /* only once */
498 return 1;
501 switch (objects[booktype].oc_level) {
502 case 1:
503 case 2:
504 context.spbook.delay = -objects[booktype].oc_delay;
505 break;
506 case 3:
507 case 4:
508 context.spbook.delay = -(objects[booktype].oc_level - 1)
509 * objects[booktype].oc_delay;
510 break;
511 case 5:
512 case 6:
513 context.spbook.delay =
514 -objects[booktype].oc_level * objects[booktype].oc_delay;
515 break;
516 case 7:
517 context.spbook.delay = -8 * objects[booktype].oc_delay;
518 break;
519 default:
520 impossible("Unknown spellbook level %d, book %d;",
521 objects[booktype].oc_level, booktype);
522 return 0;
525 /* Books are often wiser than their readers (Rus.) */
526 spellbook->in_use = TRUE;
527 if (!spellbook->blessed && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
528 if (spellbook->cursed) {
529 too_hard = TRUE;
530 } else {
531 /* uncursed - chance to fail */
532 int read_ability = ACURR(A_INT) + 4 + u.ulevel / 2
533 - 2 * objects[booktype].oc_level
534 + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);
536 /* only wizards know if a spell is too difficult */
537 if (Role_if(PM_WIZARD) && read_ability < 20 && !confused) {
538 char qbuf[QBUFSZ];
540 Sprintf(qbuf,
541 "This spellbook is %sdifficult to comprehend. Continue?",
542 (read_ability < 12 ? "very " : ""));
543 if (yn(qbuf) != 'y') {
544 spellbook->in_use = FALSE;
545 return 1;
548 /* its up to random luck now */
549 if (rnd(20) > read_ability) {
550 too_hard = TRUE;
555 if (too_hard) {
556 boolean gone = cursed_book(spellbook);
558 nomul(context.spbook.delay); /* study time */
559 multi_reason = "reading a book";
560 nomovemsg = 0;
561 context.spbook.delay = 0;
562 if (gone || !rn2(3)) {
563 if (!gone)
564 pline_The("spellbook crumbles to dust!");
565 if (!objects[spellbook->otyp].oc_name_known
566 && !objects[spellbook->otyp].oc_uname)
567 docall(spellbook);
568 useup(spellbook);
569 } else
570 spellbook->in_use = FALSE;
571 return 1;
572 } else if (confused) {
573 if (!confused_book(spellbook)) {
574 spellbook->in_use = FALSE;
576 nomul(context.spbook.delay);
577 multi_reason = "reading a book";
578 nomovemsg = 0;
579 context.spbook.delay = 0;
580 return 1;
582 spellbook->in_use = FALSE;
584 You("begin to %s the runes.",
585 spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : "memorize");
588 context.spbook.book = spellbook;
589 if (context.spbook.book)
590 context.spbook.o_id = context.spbook.book->o_id;
591 set_occupation(learn, "studying", 0);
592 return 1;
595 /* a spellbook has been destroyed or the character has changed levels;
596 the stored address for the current book is no longer valid */
597 void
598 book_disappears(obj)
599 struct obj *obj;
601 if (obj == context.spbook.book) {
602 context.spbook.book = (struct obj *) 0;
603 context.spbook.o_id = 0;
607 /* renaming an object usually results in it having a different address;
608 so the sequence start reading, get interrupted, name the book, resume
609 reading would read the "new" book from scratch */
610 void
611 book_substitution(old_obj, new_obj)
612 struct obj *old_obj, *new_obj;
614 if (old_obj == context.spbook.book) {
615 context.spbook.book = new_obj;
616 if (context.spbook.book)
617 context.spbook.o_id = context.spbook.book->o_id;
621 /* called from moveloop() */
622 void
623 age_spells()
625 int i;
627 * The time relative to the hero (a pass through move
628 * loop) causes all spell knowledge to be decremented.
629 * The hero's speed, rest status, conscious status etc.
630 * does not alter the loss of memory.
632 for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++)
633 if (spellknow(i))
634 decrnknow(i);
635 return;
638 /* return True if spellcasting is inhibited;
639 only covers a small subset of reasons why casting won't work */
640 STATIC_OVL boolean
641 rejectcasting()
643 /* rejections which take place before selecting a particular spell */
644 if (Stunned) {
645 You("are too impaired to cast a spell.");
646 return TRUE;
647 } else if (!freehand()) {
648 /* Note: !freehand() occurs when weapon and shield (or two-handed
649 * weapon) are welded to hands, so "arms" probably doesn't need
650 * to be makeplural(bodypart(ARM)).
652 * But why isn't lack of free arms (for gesturing) an issue when
653 * poly'd hero has no limbs?
655 Your("arms are not free to cast!");
656 return TRUE;
658 return FALSE;
662 * Return TRUE if a spell was picked, with the spell index in the return
663 * parameter. Otherwise return FALSE.
665 STATIC_OVL boolean
666 getspell(spell_no)
667 int *spell_no;
669 int nspells, idx;
670 char ilet, lets[BUFSZ], qbuf[QBUFSZ];
672 if (spellid(0) == NO_SPELL) {
673 You("don't know any spells right now.");
674 return FALSE;
676 if (rejectcasting())
677 return FALSE; /* no spell chosen */
679 if (flags.menu_style == MENU_TRADITIONAL) {
680 /* we know there is at least 1 known spell */
681 for (nspells = 1; nspells < MAXSPELL && spellid(nspells) != NO_SPELL;
682 nspells++)
683 continue;
685 if (nspells == 1)
686 Strcpy(lets, "a");
687 else if (nspells < 27)
688 Sprintf(lets, "a-%c", 'a' + nspells - 1);
689 else if (nspells == 27)
690 Sprintf(lets, "a-zA");
691 /* this assumes that there are at most 52 spells... */
692 else
693 Sprintf(lets, "a-zA-%c", 'A' + nspells - 27);
695 for (;;) {
696 Sprintf(qbuf, "Cast which spell? [%s *?]", lets);
697 ilet = yn_function(qbuf, (char *) 0, '\0');
698 if (ilet == '*' || ilet == '?')
699 break; /* use menu mode */
700 if (index(quitchars, ilet))
701 return FALSE;
703 idx = spell_let_to_idx(ilet);
704 if (idx < 0 || idx >= nspells) {
705 You("don't know that spell.");
706 continue; /* ask again */
708 *spell_no = idx;
709 return TRUE;
712 return dospellmenu("Choose which spell to cast", SPELLMENU_CAST,
713 spell_no);
716 /* the 'Z' command -- cast a spell */
718 docast()
720 int spell_no;
722 if (getspell(&spell_no))
723 return spelleffects(spell_no, FALSE);
724 return 0;
727 STATIC_OVL const char *
728 spelltypemnemonic(skill)
729 int skill;
731 switch (skill) {
732 case P_ATTACK_SPELL:
733 return "attack";
734 case P_HEALING_SPELL:
735 return "healing";
736 case P_DIVINATION_SPELL:
737 return "divination";
738 case P_ENCHANTMENT_SPELL:
739 return "enchantment";
740 case P_CLERIC_SPELL:
741 return "clerical";
742 case P_ESCAPE_SPELL:
743 return "escape";
744 case P_MATTER_SPELL:
745 return "matter";
746 default:
747 impossible("Unknown spell skill, %d;", skill);
748 return "";
753 spell_skilltype(booktype)
754 int booktype;
756 return objects[booktype].oc_skill;
759 STATIC_OVL void
760 cast_protection()
762 int l = u.ulevel, loglev = 0,
763 gain, natac = u.uac + u.uspellprot;
764 /* note: u.uspellprot is subtracted when find_ac() factors it into u.uac,
765 so adding here factors it back out
766 (versions prior to 3.6 had this backwards) */
768 /* loglev=log2(u.ulevel)+1 (1..5) */
769 while (l) {
770 loglev++;
771 l /= 2;
774 /* The more u.uspellprot you already have, the less you get,
775 * and the better your natural ac, the less you get.
777 * LEVEL AC SPELLPROT from successive SPE_PROTECTION casts
778 * 1 10 0, 1, 2, 3, 4
779 * 1 0 0, 1, 2, 3
780 * 1 -10 0, 1, 2
781 * 2-3 10 0, 2, 4, 5, 6, 7, 8
782 * 2-3 0 0, 2, 4, 5, 6
783 * 2-3 -10 0, 2, 3, 4
784 * 4-7 10 0, 3, 6, 8, 9, 10, 11, 12
785 * 4-7 0 0, 3, 5, 7, 8, 9
786 * 4-7 -10 0, 3, 5, 6
787 * 7-15 -10 0, 3, 5, 6
788 * 8-15 10 0, 4, 7, 10, 12, 13, 14, 15, 16
789 * 8-15 0 0, 4, 7, 9, 10, 11, 12
790 * 8-15 -10 0, 4, 6, 7, 8
791 * 16-30 10 0, 5, 9, 12, 14, 16, 17, 18, 19, 20
792 * 16-30 0 0, 5, 9, 11, 13, 14, 15
793 * 16-30 -10 0, 5, 8, 9, 10
795 natac = (10 - natac) / 10; /* convert to positive and scale down */
796 gain = loglev - (int) u.uspellprot / (4 - min(3, natac));
798 if (gain > 0) {
799 if (!Blind) {
800 int rmtyp;
801 const char *hgolden = hcolor(NH_GOLDEN), *atmosphere;
803 if (u.uspellprot) {
804 pline_The("%s haze around you becomes more dense.", hgolden);
805 } else {
806 rmtyp = levl[u.ux][u.uy].typ;
807 atmosphere = u.uswallow
808 ? ((u.ustuck->data == &mons[PM_FOG_CLOUD])
809 ? "mist"
810 : is_whirly(u.ustuck->data)
811 ? "maelstrom"
812 : is_animal(u.ustuck->data)
813 ? "maw"
814 : "ooze")
815 : (u.uinwater
816 ? hliquid("water")
817 : (rmtyp == CLOUD)
818 ? "cloud"
819 : IS_TREE(rmtyp)
820 ? "vegetation"
821 : IS_STWALL(rmtyp)
822 ? "stone"
823 : "air");
824 pline_The("%s around you begins to shimmer with %s haze.",
825 atmosphere, an(hgolden));
828 u.uspellprot += gain;
829 u.uspmtime = (P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT)
830 ? 20 : 10;
831 if (!u.usptime)
832 u.usptime = u.uspmtime;
833 find_ac();
834 } else {
835 Your("skin feels warm for a moment.");
839 /* attempting to cast a forgotten spell will cause disorientation */
840 STATIC_OVL void
841 spell_backfire(spell)
842 int spell;
844 long duration = (long) ((spellev(spell) + 1) * 3), /* 6..24 */
845 old_stun = (HStun & TIMEOUT), old_conf = (HConfusion & TIMEOUT);
847 /* Prior to 3.4.1, only effect was confusion; it still predominates.
849 * 3.6.0: this used to override pre-existing confusion duration
850 * (cases 0..8) and pre-existing stun duration (cases 4..9);
851 * increase them instead. (Hero can no longer cast spells while
852 * Stunned, so the potential increment to stun duration here is
853 * just hypothetical.)
855 switch (rn2(10)) {
856 case 0:
857 case 1:
858 case 2:
859 case 3:
860 make_confused(old_conf + duration, FALSE); /* 40% */
861 break;
862 case 4:
863 case 5:
864 case 6:
865 make_confused(old_conf + 2L * duration / 3L, FALSE); /* 30% */
866 make_stunned(old_stun + duration / 3L, FALSE);
867 break;
868 case 7:
869 case 8:
870 make_stunned(old_stun + 2L * duration / 3L, FALSE); /* 20% */
871 make_confused(old_conf + duration / 3L, FALSE);
872 break;
873 case 9:
874 make_stunned(old_stun + duration, FALSE); /* 10% */
875 break;
877 return;
881 spelleffects(spell, atme)
882 int spell;
883 boolean atme;
885 int energy, damage, chance, n, intell;
886 int skill, role_skill;
887 boolean confused = (Confusion != 0);
888 boolean physical_damage = FALSE;
889 struct obj *pseudo;
890 coord cc;
893 * Reject attempting to cast while stunned or with no free hands.
894 * Already done in getspell() to stop casting before choosing
895 * which spell, but duplicated here for cases where spelleffects()
896 * gets called directly for ^T without intrinsic teleport capability
897 * or #turn for non-priest/non-knight.
898 * (There's no duplication of messages; when the rejection takes
899 * place in getspell(), we don't get called.)
901 if (rejectcasting()) {
902 return 0; /* no time elapses */
906 * Spell casting no longer affects knowledge of the spell. A
907 * decrement of spell knowledge is done every turn.
909 if (spellknow(spell) <= 0) {
910 Your("knowledge of this spell is twisted.");
911 pline("It invokes nightmarish images in your mind...");
912 spell_backfire(spell);
913 return 1;
914 } else if (spellknow(spell) <= KEEN / 200) { /* 100 turns left */
915 You("strain to recall the spell.");
916 } else if (spellknow(spell) <= KEEN / 40) { /* 500 turns left */
917 You("have difficulty remembering the spell.");
918 } else if (spellknow(spell) <= KEEN / 20) { /* 1000 turns left */
919 Your("knowledge of this spell is growing faint.");
920 } else if (spellknow(spell) <= KEEN / 10) { /* 2000 turns left */
921 Your("recall of this spell is gradually fading.");
923 energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */
925 if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) {
926 You("are too hungry to cast that spell.");
927 return 0;
928 } else if (ACURR(A_STR) < 4 && spellid(spell) != SPE_RESTORE_ABILITY) {
929 You("lack the strength to cast spells.");
930 return 0;
931 } else if (check_capacity(
932 "Your concentration falters while carrying so much stuff.")) {
933 return 1;
936 if (u.uhave.amulet) {
937 You_feel("the amulet draining your energy away.");
938 energy += rnd(2 * energy);
940 if (energy > u.uen) {
941 You("don't have enough energy to cast that spell.");
942 return 0;
943 } else {
944 if (spellid(spell) != SPE_DETECT_FOOD) {
945 int hungr = energy * 2;
947 /* If hero is a wizard, their current intelligence
948 * (bonuses + temporary + current)
949 * affects hunger reduction in casting a spell.
950 * 1. int = 17-18 no reduction
951 * 2. int = 16 1/4 hungr
952 * 3. int = 15 1/2 hungr
953 * 4. int = 1-14 normal reduction
954 * The reason for this is:
955 * a) Intelligence affects the amount of exertion
956 * in thinking.
957 * b) Wizards have spent their life at magic and
958 * understand quite well how to cast spells.
960 intell = acurr(A_INT);
961 if (!Role_if(PM_WIZARD))
962 intell = 10;
963 switch (intell) {
964 case 25:
965 case 24:
966 case 23:
967 case 22:
968 case 21:
969 case 20:
970 case 19:
971 case 18:
972 case 17:
973 hungr = 0;
974 break;
975 case 16:
976 hungr /= 4;
977 break;
978 case 15:
979 hungr /= 2;
980 break;
982 /* don't put player (quite) into fainting from
983 * casting a spell, particularly since they might
984 * not even be hungry at the beginning; however,
985 * this is low enough that they must eat before
986 * casting anything else except detect food
988 if (hungr > u.uhunger - 3)
989 hungr = u.uhunger - 3;
990 morehungry(hungr);
994 chance = percent_success(spell);
995 if (confused || (rnd(100) > chance)) {
996 You("fail to cast the spell correctly.");
997 u.uen -= energy / 2;
998 context.botl = 1;
999 return 1;
1002 u.uen -= energy;
1003 context.botl = 1;
1004 exercise(A_WIS, TRUE);
1005 /* pseudo is a temporary "false" object containing the spell stats */
1006 pseudo = mksobj(spellid(spell), FALSE, FALSE);
1007 pseudo->blessed = pseudo->cursed = 0;
1008 pseudo->quan = 20L; /* do not let useup get it */
1010 * Find the skill the hero has in a spell type category.
1011 * See spell_skilltype for categories.
1013 skill = spell_skilltype(pseudo->otyp);
1014 role_skill = P_SKILL(skill);
1016 switch (pseudo->otyp) {
1018 * At first spells act as expected. As the hero increases in skill
1019 * with the appropriate spell type, some spells increase in their
1020 * effects, e.g. more damage, further distance, and so on, without
1021 * additional cost to the spellcaster.
1023 case SPE_FIREBALL:
1024 case SPE_CONE_OF_COLD:
1025 if (role_skill >= P_SKILLED) {
1026 if (throwspell()) {
1027 cc.x = u.dx;
1028 cc.y = u.dy;
1029 n = rnd(8) + 1;
1030 while (n--) {
1031 if (!u.dx && !u.dy && !u.dz) {
1032 if ((damage = zapyourself(pseudo, TRUE)) != 0) {
1033 char buf[BUFSZ];
1034 Sprintf(buf, "zapped %sself with a spell",
1035 uhim());
1036 losehp(damage, buf, NO_KILLER_PREFIX);
1038 } else {
1039 explode(u.dx, u.dy,
1040 pseudo->otyp - SPE_MAGIC_MISSILE + 10,
1041 spell_damage_bonus(u.ulevel / 2 + 1), 0,
1042 (pseudo->otyp == SPE_CONE_OF_COLD)
1043 ? EXPL_FROSTY
1044 : EXPL_FIERY);
1046 u.dx = cc.x + rnd(3) - 2;
1047 u.dy = cc.y + rnd(3) - 2;
1048 if (!isok(u.dx, u.dy) || !cansee(u.dx, u.dy)
1049 || IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) {
1050 /* Spell is reflected back to center */
1051 u.dx = cc.x;
1052 u.dy = cc.y;
1056 break;
1057 } /* else fall through... */
1059 /* these spells are all duplicates of wand effects */
1060 case SPE_FORCE_BOLT:
1061 physical_damage = TRUE;
1062 /* fall through */
1063 case SPE_SLEEP:
1064 case SPE_MAGIC_MISSILE:
1065 case SPE_KNOCK:
1066 case SPE_SLOW_MONSTER:
1067 case SPE_WIZARD_LOCK:
1068 case SPE_DIG:
1069 case SPE_TURN_UNDEAD:
1070 case SPE_POLYMORPH:
1071 case SPE_TELEPORT_AWAY:
1072 case SPE_CANCELLATION:
1073 case SPE_FINGER_OF_DEATH:
1074 case SPE_LIGHT:
1075 case SPE_DETECT_UNSEEN:
1076 case SPE_HEALING:
1077 case SPE_EXTRA_HEALING:
1078 case SPE_DRAIN_LIFE:
1079 case SPE_STONE_TO_FLESH:
1080 if (!(objects[pseudo->otyp].oc_dir == NODIR)) {
1081 if (atme) {
1082 u.dx = u.dy = u.dz = 0;
1083 } else if (!getdir((char *) 0)) {
1084 /* getdir cancelled, re-use previous direction */
1086 * FIXME: reusing previous direction only makes sense
1087 * if there is an actual previous direction. When there
1088 * isn't one, the spell gets cast at self which is rarely
1089 * what the player intended. Unfortunately, the way
1090 * spelleffects() is organized means that aborting with
1091 * "nevermind" is not an option.
1093 pline_The("magical energy is released!");
1095 if (!u.dx && !u.dy && !u.dz) {
1096 if ((damage = zapyourself(pseudo, TRUE)) != 0) {
1097 char buf[BUFSZ];
1099 Sprintf(buf, "zapped %sself with a spell", uhim());
1100 if (physical_damage)
1101 damage = Maybe_Half_Phys(damage);
1102 losehp(damage, buf, NO_KILLER_PREFIX);
1104 } else
1105 weffects(pseudo);
1106 } else
1107 weffects(pseudo);
1108 update_inventory(); /* spell may modify inventory */
1109 break;
1111 /* these are all duplicates of scroll effects */
1112 case SPE_REMOVE_CURSE:
1113 case SPE_CONFUSE_MONSTER:
1114 case SPE_DETECT_FOOD:
1115 case SPE_CAUSE_FEAR:
1116 case SPE_IDENTIFY:
1117 /* high skill yields effect equivalent to blessed scroll */
1118 if (role_skill >= P_SKILLED)
1119 pseudo->blessed = 1;
1120 /* fall through */
1121 case SPE_CHARM_MONSTER:
1122 case SPE_MAGIC_MAPPING:
1123 case SPE_CREATE_MONSTER:
1124 (void) seffects(pseudo);
1125 break;
1127 /* these are all duplicates of potion effects */
1128 case SPE_HASTE_SELF:
1129 case SPE_DETECT_TREASURE:
1130 case SPE_DETECT_MONSTERS:
1131 case SPE_LEVITATION:
1132 case SPE_RESTORE_ABILITY:
1133 /* high skill yields effect equivalent to blessed potion */
1134 if (role_skill >= P_SKILLED)
1135 pseudo->blessed = 1;
1136 /* fall through */
1137 case SPE_INVISIBILITY:
1138 (void) peffects(pseudo);
1139 break;
1140 /* end of potion-like spells */
1142 case SPE_CURE_BLINDNESS:
1143 healup(0, 0, FALSE, TRUE);
1144 break;
1145 case SPE_CURE_SICKNESS:
1146 if (Sick)
1147 You("are no longer ill.");
1148 if (Slimed)
1149 make_slimed(0L, "The slime disappears!");
1150 healup(0, 0, TRUE, FALSE);
1151 break;
1152 case SPE_CREATE_FAMILIAR:
1153 (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE);
1154 break;
1155 case SPE_CLAIRVOYANCE:
1156 if (!BClairvoyant) {
1157 if (role_skill >= P_SKILLED)
1158 pseudo->blessed = 1; /* detect monsters as well as map */
1159 do_vicinity_map(pseudo);
1160 /* at present, only one thing blocks clairvoyance */
1161 } else if (uarmh && uarmh->otyp == CORNUTHAUM)
1162 You("sense a pointy hat on top of your %s.", body_part(HEAD));
1163 break;
1164 case SPE_PROTECTION:
1165 cast_protection();
1166 break;
1167 case SPE_JUMPING:
1168 if (!jump(max(role_skill, 1)))
1169 pline1(nothing_happens);
1170 break;
1171 default:
1172 impossible("Unknown spell %d attempted.", spell);
1173 obfree(pseudo, (struct obj *) 0);
1174 return 0;
1177 /* gain skill for successful cast */
1178 use_skill(skill, spellev(spell));
1180 obfree(pseudo, (struct obj *) 0); /* now, get rid of it */
1181 return 1;
1184 STATIC_OVL boolean
1185 spell_hurtle_step(arg, x, y)
1186 genericptr_t arg;
1187 int x, y;
1189 if (!isok(x,y)) return FALSE;
1190 if (!ZAP_POS(levl[x][y].typ)
1191 && !(IS_DOOR(levl[x][y].typ) && (levl[x][y].doormask & D_ISOPEN)))
1192 return FALSE;
1193 return TRUE;
1196 /* Choose location where spell takes effect. */
1197 STATIC_OVL int
1198 throwspell()
1200 coord cc, uc;
1201 struct monst *mtmp;
1203 if (u.uinwater) {
1204 pline("You're joking! In this weather?");
1205 return 0;
1206 } else if (Is_waterlevel(&u.uz)) {
1207 You("had better wait for the sun to come out.");
1208 return 0;
1211 pline("Where do you want to cast the spell?");
1212 cc.x = u.ux;
1213 cc.y = u.uy;
1214 if (getpos(&cc, TRUE, "the desired position") < 0)
1215 return 0; /* user pressed ESC */
1216 /* The number of moves from hero to where the spell drops.*/
1217 if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) {
1218 pline_The("spell dissipates over the distance!");
1219 return 0;
1220 } else if (u.uswallow) {
1221 pline_The("spell is cut short!");
1222 exercise(A_WIS, FALSE); /* What were you THINKING! */
1223 u.dx = 0;
1224 u.dy = 0;
1225 return 1;
1226 } else if ((!cansee(cc.x, cc.y)
1227 && (!(mtmp = m_at(cc.x, cc.y)) || !canspotmon(mtmp)))
1228 || IS_STWALL(levl[cc.x][cc.y].typ)) {
1229 Your("mind fails to lock onto that location!");
1230 return 0;
1233 uc.x = u.ux;
1234 uc.y = u.uy;
1236 walk_path(&uc, &cc, spell_hurtle_step, NULL);
1238 u.dx = cc.x;
1239 u.dy = cc.y;
1240 return 1;
1243 /* forget a random selection of known spells due to amnesia;
1244 they used to be lost entirely, as if never learned, but now we
1245 just set the memory retention to zero so that they can't be cast */
1246 void
1247 losespells()
1249 int n, nzap, i;
1251 /* in case reading has been interrupted earlier, discard context */
1252 context.spbook.book = 0;
1253 context.spbook.o_id = 0;
1254 /* count the number of known spells */
1255 for (n = 0; n < MAXSPELL; ++n)
1256 if (spellid(n) == NO_SPELL)
1257 break;
1259 /* lose anywhere from zero to all known spells;
1260 if confused, use the worse of two die rolls */
1261 nzap = rn2(n + 1);
1262 if (Confusion) {
1263 i = rn2(n + 1);
1264 if (i > nzap)
1265 nzap = i;
1267 /* good Luck might ameliorate spell loss */
1268 if (nzap > 1 && !rnl(7))
1269 nzap = rnd(nzap);
1272 * Forget 'nzap' out of 'n' known spells by setting their memory
1273 * retention to zero. Every spell has the same probability to be
1274 * forgotten, even if its retention is already zero.
1276 * Perhaps we should forget the corresponding book too?
1278 * (3.4.3 removed spells entirely from the list, but always did
1279 * so from its end, so the 'nzap' most recently learned spells
1280 * were the ones lost by default. Player had sort control over
1281 * the list, so could move the most useful spells to front and
1282 * only lose them if 'nzap' turned out to be a large value.
1284 * Discarding from the end of the list had the virtue of making
1285 * casting letters for lost spells become invalid and retaining
1286 * the original letter for the ones which weren't lost, so there
1287 * was no risk to the player of accidentally casting the wrong
1288 * spell when using a letter that was in use prior to amnesia.
1289 * That wouldn't be the case if we implemented spell loss spread
1290 * throughout the list of known spells; every spell located past
1291 * the first lost spell would end up with new letter assigned.)
1293 for (i = 0; nzap > 0; ++i) {
1294 /* when nzap is small relative to the number of spells left,
1295 the chance to lose spell [i] is small; as the number of
1296 remaining candidates shrinks, the chance per candidate
1297 gets bigger; overall, exactly nzap entries are affected */
1298 if (rn2(n - i) < nzap) {
1299 /* lose access to spell [i] */
1300 spellknow(i) = 0;
1301 #if 0
1302 /* also forget its book */
1303 forget_single_object(spellid(i));
1304 #endif
1305 /* and abuse wisdom */
1306 exercise(A_WIS, FALSE);
1307 /* there's now one less spell slated to be forgotten */
1308 --nzap;
1314 * Allow player to sort the list of known spells. Manually swapping
1315 * pairs of them becomes very tedious once the list reaches two pages.
1317 * Possible extensions:
1318 * provide means for player to control ordering of skill classes;
1319 * provide means to supply value N such that first N entries stick
1320 * while rest of list is being sorted;
1321 * make chosen sort order be persistent such that when new spells
1322 * are learned, they get inserted into sorted order rather than be
1323 * appended to the end of the list?
1325 static const char *spl_sortchoices[] = {
1326 "by casting letter",
1327 #define SORTBY_LETTER 0
1328 "alphabetically",
1329 #define SORTBY_ALPHA 1
1330 "by level, low to high",
1331 #define SORTBY_LVL_LO 2
1332 "by level, high to low",
1333 #define SORTBY_LVL_HI 3
1334 "by skill group, alphabetized within each group",
1335 #define SORTBY_SKL_AL 4
1336 "by skill group, low to high level within group",
1337 #define SORTBY_SKL_LO 5
1338 "by skill group, high to low level within group",
1339 #define SORTBY_SKL_HI 6
1340 "maintain current ordering",
1341 #define SORTBY_CURRENT 7
1342 /* a menu choice rather than a sort choice */
1343 "reassign casting letters to retain current order",
1344 #define SORTRETAINORDER 8
1346 static int spl_sortmode = 0; /* index into spl_sortchoices[] */
1347 static int *spl_orderindx = 0; /* array of spl_book[] indices */
1349 /* qsort callback routine */
1350 STATIC_PTR int CFDECLSPEC
1351 spell_cmp(vptr1, vptr2)
1352 const genericptr vptr1;
1353 const genericptr vptr2;
1356 * gather up all of the possible parameters except spell name
1357 * in advance, even though some might not be needed:
1358 * indx. = spl_orderindx[] index into spl_book[];
1359 * otyp. = spl_book[] index into objects[];
1360 * levl. = spell level;
1361 * skil. = skill group aka spell class.
1363 int indx1 = *(int *) vptr1, indx2 = *(int *) vptr2,
1364 otyp1 = spl_book[indx1].sp_id, otyp2 = spl_book[indx2].sp_id,
1365 levl1 = objects[otyp1].oc_level, levl2 = objects[otyp2].oc_level,
1366 skil1 = objects[otyp1].oc_skill, skil2 = objects[otyp2].oc_skill;
1368 switch (spl_sortmode) {
1369 case SORTBY_LETTER:
1370 return indx1 - indx2;
1371 case SORTBY_ALPHA:
1372 break;
1373 case SORTBY_LVL_LO:
1374 if (levl1 != levl2)
1375 return levl1 - levl2;
1376 break;
1377 case SORTBY_LVL_HI:
1378 if (levl1 != levl2)
1379 return levl2 - levl1;
1380 break;
1381 case SORTBY_SKL_AL:
1382 if (skil1 != skil2)
1383 return skil1 - skil2;
1384 break;
1385 case SORTBY_SKL_LO:
1386 if (skil1 != skil2)
1387 return skil1 - skil2;
1388 if (levl1 != levl2)
1389 return levl1 - levl2;
1390 break;
1391 case SORTBY_SKL_HI:
1392 if (skil1 != skil2)
1393 return skil1 - skil2;
1394 if (levl1 != levl2)
1395 return levl2 - levl1;
1396 break;
1397 case SORTBY_CURRENT:
1398 default:
1399 return (vptr1 < vptr2) ? -1
1400 : (vptr1 > vptr2); /* keep current order */
1402 /* tie-breaker for most sorts--alphabetical by spell name */
1403 return strcmpi(OBJ_NAME(objects[otyp1]), OBJ_NAME(objects[otyp2]));
1406 /* sort the index used for display order of the "view known spells"
1407 list (sortmode == SORTBY_xxx), or sort the spellbook itself to make
1408 the current display order stick (sortmode == SORTRETAINORDER) */
1409 STATIC_OVL void
1410 sortspells()
1412 int i;
1413 #if defined(SYSV) || defined(DGUX)
1414 unsigned n;
1415 #else
1416 int n;
1417 #endif
1419 if (spl_sortmode == SORTBY_CURRENT)
1420 return;
1421 for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; ++n)
1422 continue;
1423 if (n < 2)
1424 return; /* not enough entries to need sorting */
1426 if (!spl_orderindx) {
1427 /* we haven't done any sorting yet; list is in casting order */
1428 if (spl_sortmode == SORTBY_LETTER /* default */
1429 || spl_sortmode == SORTRETAINORDER)
1430 return;
1431 /* allocate enough for full spellbook rather than just N spells */
1432 spl_orderindx = (int *) alloc(MAXSPELL * sizeof(int));
1433 for (i = 0; i < MAXSPELL; i++)
1434 spl_orderindx[i] = i;
1437 if (spl_sortmode == SORTRETAINORDER) {
1438 struct spell tmp_book[MAXSPELL];
1440 /* sort spl_book[] rather than spl_orderindx[];
1441 this also updates the index to reflect the new ordering (we
1442 could just free it since that ordering becomes the default) */
1443 for (i = 0; i < MAXSPELL; i++)
1444 tmp_book[i] = spl_book[spl_orderindx[i]];
1445 for (i = 0; i < MAXSPELL; i++)
1446 spl_book[i] = tmp_book[i], spl_orderindx[i] = i;
1447 spl_sortmode = SORTBY_LETTER; /* reset */
1448 return;
1451 /* usual case, sort the index rather than the spells themselves */
1452 qsort((genericptr_t) spl_orderindx, n, sizeof *spl_orderindx, spell_cmp);
1453 return;
1456 /* called if the [sort spells] entry in the view spells menu gets chosen */
1457 STATIC_OVL boolean
1458 spellsortmenu()
1460 winid tmpwin;
1461 menu_item *selected;
1462 anything any;
1463 char let;
1464 int i, n, choice;
1466 tmpwin = create_nhwindow(NHW_MENU);
1467 start_menu(tmpwin);
1468 any = zeroany; /* zero out all bits */
1470 for (i = 0; i < SIZE(spl_sortchoices); i++) {
1471 if (i == SORTRETAINORDER) {
1472 let = 'z'; /* assumes fewer than 26 sort choices... */
1473 /* separate final choice from others with a blank line */
1474 any.a_int = 0;
1475 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
1476 MENU_UNSELECTED);
1477 } else {
1478 let = 'a' + i;
1480 any.a_int = i + 1;
1481 add_menu(tmpwin, NO_GLYPH, &any, let, 0, ATR_NONE, spl_sortchoices[i],
1482 (i == spl_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
1484 end_menu(tmpwin, "View known spells list sorted");
1486 n = select_menu(tmpwin, PICK_ONE, &selected);
1487 destroy_nhwindow(tmpwin);
1488 if (n > 0) {
1489 choice = selected[0].item.a_int - 1;
1490 /* skip preselected entry if we have more than one item chosen */
1491 if (n > 1 && choice == spl_sortmode)
1492 choice = selected[1].item.a_int - 1;
1493 free((genericptr_t) selected);
1494 spl_sortmode = choice;
1495 return TRUE;
1497 return FALSE;
1500 /* the '+' command -- view known spells */
1502 dovspell()
1504 char qbuf[QBUFSZ];
1505 int splnum, othnum;
1506 struct spell spl_tmp;
1508 if (spellid(0) == NO_SPELL) {
1509 You("don't know any spells right now.");
1510 } else {
1511 while (dospellmenu("Currently known spells",
1512 SPELLMENU_VIEW, &splnum)) {
1513 if (splnum == SPELLMENU_SORT) {
1514 if (spellsortmenu())
1515 sortspells();
1516 } else {
1517 Sprintf(qbuf, "Reordering spells; swap '%c' with",
1518 spellet(splnum));
1519 if (!dospellmenu(qbuf, splnum, &othnum))
1520 break;
1522 spl_tmp = spl_book[splnum];
1523 spl_book[splnum] = spl_book[othnum];
1524 spl_book[othnum] = spl_tmp;
1528 if (spl_orderindx) {
1529 free((genericptr_t) spl_orderindx);
1530 spl_orderindx = 0;
1532 spl_sortmode = SORTBY_LETTER; /* 0 */
1533 return 0;
1536 STATIC_OVL boolean
1537 dospellmenu(prompt, splaction, spell_no)
1538 const char *prompt;
1539 int splaction; /* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */
1540 int *spell_no;
1542 winid tmpwin;
1543 int i, n, how, splnum;
1544 char buf[BUFSZ], retentionbuf[24];
1545 const char *fmt;
1546 menu_item *selected;
1547 anything any;
1549 tmpwin = create_nhwindow(NHW_MENU);
1550 start_menu(tmpwin);
1551 any = zeroany; /* zero out all bits */
1554 * The correct spacing of the columns when not using
1555 * tab separation depends on the following:
1556 * (1) that the font is monospaced, and
1557 * (2) that selection letters are pre-pended to the
1558 * given string and are of the form "a - ".
1560 if (!iflags.menu_tab_sep) {
1561 Sprintf(buf, "%-20s Level %-12s Fail Retention", " Name",
1562 "Category");
1563 fmt = "%-20s %2d %-12s %3d%% %9s";
1564 } else {
1565 Sprintf(buf, "Name\tLevel\tCategory\tFail\tRetention");
1566 fmt = "%s\t%-d\t%s\t%-d%%\t%s";
1568 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, buf,
1569 MENU_UNSELECTED);
1570 for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) {
1571 splnum = !spl_orderindx ? i : spl_orderindx[i];
1572 Sprintf(buf, fmt, spellname(splnum), spellev(splnum),
1573 spelltypemnemonic(spell_skilltype(spellid(splnum))),
1574 100 - percent_success(splnum),
1575 spellretention(splnum, retentionbuf));
1577 any.a_int = splnum + 1; /* must be non-zero */
1578 add_menu(tmpwin, NO_GLYPH, &any, spellet(splnum), 0, ATR_NONE, buf,
1579 (splnum == splaction) ? MENU_SELECTED : MENU_UNSELECTED);
1581 how = PICK_ONE;
1582 if (splaction == SPELLMENU_VIEW) {
1583 if (spellid(1) == NO_SPELL) {
1584 /* only one spell => nothing to swap with */
1585 how = PICK_NONE;
1586 } else {
1587 /* more than 1 spell, add an extra menu entry */
1588 any.a_int = SPELLMENU_SORT + 1;
1589 add_menu(tmpwin, NO_GLYPH, &any, '+', 0, ATR_NONE,
1590 "[sort spells]", MENU_UNSELECTED);
1593 end_menu(tmpwin, prompt);
1595 n = select_menu(tmpwin, how, &selected);
1596 destroy_nhwindow(tmpwin);
1597 if (n > 0) {
1598 *spell_no = selected[0].item.a_int - 1;
1599 /* menu selection for `PICK_ONE' does not
1600 de-select any preselected entry */
1601 if (n > 1 && *spell_no == splaction)
1602 *spell_no = selected[1].item.a_int - 1;
1603 free((genericptr_t) selected);
1604 /* default selection of preselected spell means that
1605 user chose not to swap it with anything */
1606 if (*spell_no == splaction)
1607 return FALSE;
1608 return TRUE;
1609 } else if (splaction >= 0) {
1610 /* explicit de-selection of preselected spell means that
1611 user is still swapping but not for the current spell */
1612 *spell_no = splaction;
1613 return TRUE;
1615 return FALSE;
1618 STATIC_OVL int
1619 percent_success(spell)
1620 int spell;
1622 /* Intrinsic and learned ability are combined to calculate
1623 * the probability of player's success at cast a given spell.
1625 int chance, splcaster, special, statused;
1626 int difficulty;
1627 int skill;
1629 /* Calculate intrinsic ability (splcaster) */
1631 splcaster = urole.spelbase;
1632 special = urole.spelheal;
1633 statused = ACURR(urole.spelstat);
1635 if (uarm && is_metallic(uarm))
1636 splcaster += (uarmc && uarmc->otyp == ROBE) ? urole.spelarmr / 2
1637 : urole.spelarmr;
1638 else if (uarmc && uarmc->otyp == ROBE)
1639 splcaster -= urole.spelarmr;
1640 if (uarms)
1641 splcaster += urole.spelshld;
1643 if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE)
1644 splcaster += uarmhbon;
1645 if (uarmg && is_metallic(uarmg))
1646 splcaster += uarmgbon;
1647 if (uarmf && is_metallic(uarmf))
1648 splcaster += uarmfbon;
1650 if (spellid(spell) == urole.spelspec)
1651 splcaster += urole.spelsbon;
1653 /* `healing spell' bonus */
1654 if (spellid(spell) == SPE_HEALING || spellid(spell) == SPE_EXTRA_HEALING
1655 || spellid(spell) == SPE_CURE_BLINDNESS
1656 || spellid(spell) == SPE_CURE_SICKNESS
1657 || spellid(spell) == SPE_RESTORE_ABILITY
1658 || spellid(spell) == SPE_REMOVE_CURSE)
1659 splcaster += special;
1661 if (splcaster > 20)
1662 splcaster = 20;
1664 /* Calculate learned ability */
1666 /* Players basic likelihood of being able to cast any spell
1667 * is based of their `magic' statistic. (Int or Wis)
1669 chance = 11 * statused / 2;
1672 * High level spells are harder. Easier for higher level casters.
1673 * The difficulty is based on the hero's level and their skill level
1674 * in that spell type.
1676 skill = P_SKILL(spell_skilltype(spellid(spell)));
1677 skill = max(skill, P_UNSKILLED) - 1; /* unskilled => 0 */
1678 difficulty =
1679 (spellev(spell) - 1) * 4 - ((skill * 6) + (u.ulevel / 3) + 1);
1681 if (difficulty > 0) {
1682 /* Player is too low level or unskilled. */
1683 chance -= isqrt(900 * difficulty + 2000);
1684 } else {
1685 /* Player is above level. Learning continues, but the
1686 * law of diminishing returns sets in quickly for
1687 * low-level spells. That is, a player quickly gains
1688 * no advantage for raising level.
1690 int learning = 15 * -difficulty / spellev(spell);
1691 chance += learning > 20 ? 20 : learning;
1694 /* Clamp the chance: >18 stat and advanced learning only help
1695 * to a limit, while chances below "hopeless" only raise the
1696 * specter of overflowing 16-bit ints (and permit wearing a
1697 * shield to raise the chances :-).
1699 if (chance < 0)
1700 chance = 0;
1701 if (chance > 120)
1702 chance = 120;
1704 /* Wearing anything but a light shield makes it very awkward
1705 * to cast a spell. The penalty is not quite so bad for the
1706 * player's role-specific spell.
1708 if (uarms && weight(uarms) > (int) objects[SMALL_SHIELD].oc_weight) {
1709 if (spellid(spell) == urole.spelspec) {
1710 chance /= 2;
1711 } else {
1712 chance /= 4;
1716 /* Finally, chance (based on player intell/wisdom and level) is
1717 * combined with ability (based on player intrinsics and
1718 * encumbrances). No matter how intelligent/wise and advanced
1719 * a player is, intrinsics and encumbrance can prevent casting;
1720 * and no matter how able, learning is always required.
1722 chance = chance * (20 - splcaster) / 15 - splcaster;
1724 /* Clamp to percentile */
1725 if (chance > 100)
1726 chance = 100;
1727 if (chance < 0)
1728 chance = 0;
1730 return chance;
1733 STATIC_OVL char *
1734 spellretention(idx, outbuf)
1735 int idx;
1736 char *outbuf;
1738 long turnsleft, percent, accuracy;
1739 int skill;
1741 skill = P_SKILL(spell_skilltype(spellid(idx)));
1742 skill = max(skill, P_UNSKILLED); /* restricted same as unskilled */
1743 turnsleft = spellknow(idx);
1744 *outbuf = '\0'; /* lint suppression */
1746 if (turnsleft < 1L) {
1747 /* spell has expired; hero can't successfully cast it anymore */
1748 Strcpy(outbuf, "(gone)");
1749 } else if (turnsleft >= (long) KEEN) {
1750 /* full retention, first turn or immediately after reading book */
1751 Strcpy(outbuf, "100%");
1752 } else {
1754 * Retention is displayed as a range of percentages of
1755 * amount of time left until memory of the spell expires;
1756 * the precision of the range depends upon hero's skill
1757 * in this spell.
1758 * expert: 2% intervals; 1-2, 3-4, ..., 99-100;
1759 * skilled: 5% intervals; 1-5, 6-10, ..., 95-100;
1760 * basic: 10% intervals; 1-10, 11-20, ..., 91-100;
1761 * unskilled: 25% intervals; 1-25, 26-50, 51-75, 76-100.
1763 * At the low end of each range, a value of N% really means
1764 * (N-1)%+1 through N%; so 1% is "greater than 0, at most 200".
1765 * KEEN is a multiple of 100; KEEN/100 loses no precision.
1767 percent = (turnsleft - 1L) / ((long) KEEN / 100L) + 1L;
1768 accuracy =
1769 (skill == P_EXPERT) ? 2L : (skill == P_SKILLED)
1770 ? 5L
1771 : (skill == P_BASIC) ? 10L : 25L;
1772 /* round up to the high end of this range */
1773 percent = accuracy * ((percent - 1L) / accuracy + 1L);
1774 Sprintf(outbuf, "%ld%%-%ld%%", percent - accuracy + 1L, percent);
1776 return outbuf;
1779 /* Learn a spell during creation of the initial inventory */
1780 void
1781 initialspell(obj)
1782 struct obj *obj;
1784 int i, otyp = obj->otyp;
1786 for (i = 0; i < MAXSPELL; i++)
1787 if (spellid(i) == NO_SPELL || spellid(i) == otyp)
1788 break;
1790 if (i == MAXSPELL) {
1791 impossible("Too many spells memorized!");
1792 } else if (spellid(i) != NO_SPELL) {
1793 /* initial inventory shouldn't contain duplicate spellbooks */
1794 impossible("Spell %s already known.", OBJ_NAME(objects[otyp]));
1795 } else {
1796 spl_book[i].sp_id = otyp;
1797 spl_book[i].sp_lev = objects[otyp].oc_level;
1798 incrnknow(i, 0);
1800 return;
1803 /*spell.c*/