1 /* NetHack 3.7 spell.c $NHDT-Date: 1611522041 2021/01/24 21:00:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.113 $ */
2 /* Copyright (c) M. Stephenson 1988 */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include "hack.h"
6
7 /* spellmenu arguments; 0 thru n-1 used as g.spl_book[] index when swapping */
8 #define SPELLMENU_CAST (-2)
9 #define SPELLMENU_VIEW (-1)
10 #define SPELLMENU_SORT (MAXSPELL) /* special menu entry */
11
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) (g.spl_book[spell].sp_know = KEEN + (x))
22
23 #define spellev(spell) g.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)))
27
28 static int spell_let_to_idx(char);
29 static boolean cursed_book(struct obj * bp);
30 static boolean confused_book(struct obj *);
31 static void deadbook(struct obj *);
32 static int learn(void);
33 static boolean rejectcasting(void);
34 static boolean getspell(int *);
35 static int QSORTCALLBACK spell_cmp(const genericptr, const genericptr);
36 static void sortspells(void);
37 static boolean spellsortmenu(void);
38 static boolean dospellmenu(const char *, int, int *);
39 static int percent_success(int);
40 static int energy_cost(int);
41 static int throwspell(void);
42 static void cast_protection(void);
43 static void spell_backfire(int);
44 static int spell_hunger(int);
45 static boolean spell_aim_step(genericptr_t, int, int);
46
47 /* The roles[] table lists the role-specific values for tuning
48 * percent_success().
49 *
50 * Reasoning for spelbase:
51 * Arc are aware of magic through historical research
52 * Bar abhor magic (Conan finds it "interferes with his animal instincts")
53 * Cav are ignorant to magic
54 * Hea are very aware of healing magic through medical research
55 * Kni are moderately aware of healing from Paladin training
56 * Mon use magic to attack and defend in lieu of weapons and armor
57 * Pri are very aware of healing magic through theological research
58 * Ran avoid magic, preferring to fight unseen and unheard
59 * Rog are moderately aware of magic through trickery
60 * Sam have limited magical awareness, preferring meditation to conjuring
61 * Tou are aware of magic from all the great films they have seen
62 * Val have limited magical awareness, preferring fighting
63 * Wiz are trained mages
64 *
65 * The arms penalty is lessened for trained fighters Bar, Kni, Ran,
66 * Sam, Val -- the penalty is its metal interference, not encumbrance.
67 * The `spelspec' is a single spell which is fundamentally easier
68 * for that role to cast.
69 *
70 */
71
72 /* since the spellbook itself doesn't blow up, don't say just "explodes" */
73 static const char explodes[] = "radiates explosive energy";
74
75 /* convert a letter into a number in the range 0..51, or -1 if not a letter */
76 static int
spell_let_to_idx(char ilet)77 spell_let_to_idx(char ilet)
78 {
79 int indx;
80
81 indx = ilet - 'a';
82 if (indx >= 0 && indx < 26)
83 return indx;
84 indx = ilet - 'A';
85 if (indx >= 0 && indx < 26)
86 return indx + 26;
87 return -1;
88 }
89
90 /* TRUE: book should be destroyed by caller */
91 static boolean
cursed_book(struct obj * bp)92 cursed_book(struct obj* bp)
93 {
94 boolean was_in_use;
95 int lev = objects[bp->otyp].oc_level;
96 int dmg = 0;
97 boolean already_cursed = bp->cursed;
98
99 switch (rn2(lev)) {
100 case 0:
101 if (already_cursed || rn2(4)) {
102 You_feel("a wrenching sensation.");
103 tele(); /* teleport him */
104 }
105 else {
106 if (bp->blessed) {
107 unbless(bp);
108 pline_The("book glows %s.", hcolor("brown"));
109 }
110 else {
111 /* can't call curse() here because it will call cursed_book */
112 bp->cursed = 1;
113 pline_The("book glows %s.", hcolor(NH_BLACK));
114 }
115 bp->bknown = TRUE;
116 }
117 break;
118 case 1:
119 You_feel("threatened.");
120 aggravate();
121 break;
122 case 2:
123 make_blinded(Blinded + rn1(30, 10), TRUE);
124 break;
125 case 3:
126 pline_The("book develops a huge set of teeth and bites you!");
127 /* temp disable in_use; death should not destroy the book */
128 was_in_use = bp->in_use;
129 bp->in_use = FALSE;
130 losehp(rn1(5, 3), "carnivorous book", KILLED_BY_AN);
131 bp->in_use = was_in_use;
132 break;
133 case 4:
134 make_paralyzed(rn1(16,16), TRUE, "frozen by a book");
135 break;
136 case 5:
137 pline_The("book was coated with contact poison!");
138 if (uarmg) {
139 erode_obj(uarmg, "gloves", ERODE_CORRODE, EF_GREASE | EF_VERBOSE);
140 break;
141 }
142 /* temp disable in_use; death should not destroy the book */
143 was_in_use = bp->in_use;
144 bp->in_use = FALSE;
145 losestr(Poison_resistance ? rn1(2, 1) : rn1(4, 3));
146 losehp(rnd(Poison_resistance ? 6 : 10), "contact-poisoned spellbook",
147 KILLED_BY_AN);
148 bp->in_use = was_in_use;
149 break;
150 case 6:
151 if (Antimagic) {
152 shieldeff(u.ux, u.uy);
153 pline_The("book %s, but you are unharmed!", explodes);
154 } else {
155 pline("As you read the book, it %s in your %s!", explodes,
156 body_part(FACE));
157 dmg = 2 * rnd(10) + 5;
158 losehp(Maybe_Half_Phys(dmg), "exploding rune", KILLED_BY_AN);
159 }
160 return TRUE;
161 default:
162 impossible("spellbook level out of bounds");
163 rndcurse();
164 break;
165 }
166 if (already_cursed) {
167 pline_The("spellbook crumbles to dust!");
168 return TRUE;
169 }
170 return FALSE;
171 }
172
173 /* study while confused: returns TRUE if the book is destroyed */
174 static boolean
confused_book(struct obj * spellbook)175 confused_book(struct obj* spellbook)
176 {
177 if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
178 spellbook->in_use = TRUE; /* in case called from learn */
179 pline(
180 "Being confused you have difficulties in controlling your actions.");
181 display_nhwindow(WIN_MESSAGE, FALSE);
182 You("accidentally tear the spellbook to pieces.");
183 if (!objects[spellbook->otyp].oc_name_known
184 && !objects[spellbook->otyp].oc_uname)
185 docall(spellbook);
186 useup(spellbook);
187 return TRUE;
188 } else {
189 You("find yourself reading the %s line over and over again.",
190 spellbook == g.context.spbook.book ? "next" : "first");
191 }
192 return FALSE;
193 }
194
195 /* special effects for The Book of the Dead; reading it while blind is
196 allowed so that needs to be taken into account too */
197 static void
deadbook(struct obj * book2)198 deadbook(struct obj* book2)
199 {
200 struct monst *mtmp, *mtmp2;
201 coord mm;
202
203 You("turn the pages of the Book of the Dead...");
204 makeknown(SPE_BOOK_OF_THE_DEAD);
205 book2->dknown = 1; /* in case blind now and hasn't been seen yet */
206 /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */
207 book2->known = 1;
208 if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
209 register struct obj *otmp;
210 register boolean arti1_primed = FALSE, arti2_primed = FALSE,
211 arti_cursed = FALSE;
212
213 if (book2->cursed) {
214 pline_The("%s!",
215 Blind ? "Book seems to be ignoring you"
216 : "runes appear scrambled. You can't read them");
217 return;
218 }
219
220 if (!u.uhave.bell || !u.uhave.menorah) {
221 pline("A chill runs down your %s.", body_part(SPINE));
222 if (!u.uhave.bell)
223 You_hear("a faint chime...");
224 if (!u.uhave.menorah)
225 pline("Vlad's doppelganger is amused.");
226 return;
227 }
228
229 for (otmp = g.invent; otmp; otmp = otmp->nobj) {
230 if (otmp->otyp == CANDELABRUM_OF_INVOCATION && otmp->spe == 7
231 && otmp->lamplit) {
232 if (!otmp->cursed)
233 arti1_primed = TRUE;
234 else
235 arti_cursed = TRUE;
236 }
237 if (otmp->otyp == BELL_OF_OPENING
238 && (g.moves - otmp->age) < 5L) { /* you rang it recently */
239 if (!otmp->cursed)
240 arti2_primed = TRUE;
241 else
242 arti_cursed = TRUE;
243 }
244 }
245
246 if (arti_cursed) {
247 pline_The("invocation fails!");
248 /* this used to say "your artifacts" but the invocation tools
249 are not artifacts */
250 pline("At least one of your relics is cursed...");
251 } else if (arti1_primed && arti2_primed) {
252 unsigned soon =
253 (unsigned) d(2, 6); /* time til next intervene() */
254
255 /* successful invocation */
256 mkinvokearea();
257 u.uevent.invoked = 1;
258 record_achievement(ACH_INVK);
259 /* in case you haven't killed the Wizard yet, behave as if
260 you just did */
261 u.uevent.udemigod = 1; /* wizdead() */
262 if (!u.udg_cnt || u.udg_cnt > soon)
263 u.udg_cnt = soon;
264 } else { /* at least one relic not prepared properly */
265 You("have a feeling that %s is amiss...", something);
266 goto raise_dead;
267 }
268 return;
269 }
270
271 /* when not an invocation situation */
272 if (book2->cursed) {
273 raise_dead:
274
275 You("raised the dead!");
276 /* first maybe place a dangerous adversary */
277 if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH], u.ux, u.uy,
278 NO_MINVENT)) != 0
279 || (mtmp = makemon(&mons[PM_NALFESHNEE], u.ux, u.uy,
280 NO_MINVENT)) != 0)) {
281 mtmp->mpeaceful = 0;
282 set_malign(mtmp);
283 }
284 /* next handle the affect on things you're carrying */
285 (void) unturn_dead(&g.youmonst);
286 /* last place some monsters around you */
287 mm.x = u.ux;
288 mm.y = u.uy;
289 mkundead(&mm, TRUE, NO_MINVENT);
290 } else if (book2->blessed) {
291 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
292 mtmp2 = mtmp->nmon; /* tamedog() changes chain */
293 if (DEADMONSTER(mtmp))
294 continue;
295
296 if ((is_undead(mtmp->data) || is_vampshifter(mtmp))
297 && cansee(mtmp->mx, mtmp->my)) {
298 mtmp->mpeaceful = TRUE;
299 if (sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
300 && distu(mtmp->mx, mtmp->my) < 4)
301 if (mtmp->mtame) {
302 if (mtmp->mtame < 20)
303 mtmp->mtame++;
304 } else
305 (void) tamedog(mtmp, (struct obj *) 0, FALSE);
306 else
307 monflee(mtmp, 0, FALSE, TRUE);
308 }
309 }
310 } else {
311 switch (rn2(3)) {
312 case 0:
313 Your("ancestors are annoyed with you!");
314 break;
315 case 1:
316 pline_The("headstones in the cemetery begin to move!");
317 break;
318 default:
319 pline("Oh my! Your name appears in the book!");
320 }
321 }
322 return;
323 }
324
325 /* 'book' has just become cursed; if we're reading it and realize it is
326 now cursed, interrupt */
327 void
book_cursed(struct obj * book)328 book_cursed(struct obj* book)
329 {
330 if (g.occupation == learn && g.context.spbook.book == book
331 && book->cursed && book->bknown && g.multi >= 0)
332 stop_occupation();
333 }
334
335 DISABLE_WARNING_FORMAT_NONLITERAL
336
337 static int
learn(void)338 learn(void)
339 {
340 int i;
341 short booktype;
342 char splname[BUFSZ];
343 boolean costly = TRUE;
344 struct obj *book = g.context.spbook.book;
345
346 /* JDS: lenses give 50% faster reading; 33% smaller read time */
347 if (g.context.spbook.delay && ublindf && ublindf->otyp == LENSES && rn2(2))
348 g.context.spbook.delay++;
349 if (Confusion) { /* became confused while learning */
350 if (!confused_book(book)) {
351 You("can no longer focus on the book.");
352 }
353 g.context.spbook.book = 0; /* no longer studying */
354 g.context.spbook.o_id = 0;
355 g.context.spbook.delay = 0;
356 return 0;
357 }
358 if (g.context.spbook.delay) {
359 /* not if (g.context.spbook.delay++), so at end delay == 0 */
360 g.context.spbook.delay++;
361 return 1; /* still busy */
362 }
363 exercise(A_WIS, TRUE); /* you're studying. */
364 booktype = book->otyp;
365 if (booktype == SPE_BOOK_OF_THE_DEAD) {
366 deadbook(book);
367 return 0;
368 }
369
370 Sprintf(splname,
371 objects[booktype].oc_name_known ? "\"%s\"" : "the \"%s\" spell",
372 OBJ_NAME(objects[booktype]));
373 for (i = 0; i < MAXSPELL; i++)
374 if (spellid(i) == booktype || spellid(i) == NO_SPELL)
375 break;
376
377 if (i == MAXSPELL) {
378 impossible("Too many spells memorized!");
379 } else if (spellid(i) == booktype) {
380 /* normal book can be read and re-read a total of 4 times */
381 if (book->spestudied > MAX_SPELL_STUDY) {
382 pline("This spellbook is too faint to be read any more.");
383 book->otyp = booktype = SPE_BLANK_PAPER;
384 /* reset spestudied as if polymorph had taken place */
385 book->spestudied = rn2(book->spestudied);
386 } else {
387 Your("knowledge of %s is %s.", splname,
388 spellknow(i) ? "keener" : "restored");
389 incrnknow(i, 1);
390 book->spestudied++;
391 exercise(A_WIS, TRUE); /* extra study */
392 }
393 makeknown((int) booktype);
394 } else { /* (spellid(i) == NO_SPELL) */
395 /* for a normal book, spestudied will be zero, but for
396 a polymorphed one, spestudied will be non-zero and
397 one less reading is available than when re-learning */
398 if (book->spestudied >= MAX_SPELL_STUDY) {
399 /* pre-used due to being the product of polymorph */
400 pline("This spellbook is too faint to read even once.");
401 book->otyp = booktype = SPE_BLANK_PAPER;
402 /* reset spestudied as if polymorph had taken place */
403 book->spestudied = rn2(book->spestudied);
404 } else {
405 g.spl_book[i].sp_id = booktype;
406 g.spl_book[i].sp_lev = objects[booktype].oc_level;
407 incrnknow(i, 1);
408 book->spestudied++;
409 if (!i)
410 /* first is always 'a', so no need to mention the letter */
411 You("learn %s.", splname);
412 else
413 You("add %s to your repertoire, as '%c'.",
414 splname, spellet(i));
415 }
416 makeknown((int) booktype);
417 }
418
419 if (book->cursed) { /* maybe a demon cursed it */
420 if (cursed_book(book)) {
421 useup(book);
422 g.context.spbook.book = 0;
423 g.context.spbook.o_id = 0;
424 return 0;
425 }
426 }
427 if (costly)
428 check_unpaid(book);
429 g.context.spbook.book = 0;
430 g.context.spbook.o_id = 0;
431 return 0;
432 }
433
434 RESTORE_WARNING_FORMAT_NONLITERAL
435
436 int
study_book(register struct obj * spellbook)437 study_book(register struct obj* spellbook)
438 {
439 int booktype = spellbook->otyp, i;
440 boolean confused = (Confusion != 0);
441 boolean too_hard = FALSE;
442
443 /* attempting to read dull book may make hero fall asleep */
444 if (!confused && !Sleep_resistance
445 && objdescr_is(spellbook, "dull")) {
446 const char *eyes;
447 int dullbook = rnd(25) - ACURR(A_WIS);
448
449 /* adjust chance if hero stayed awake, got interrupted, retries */
450 if (g.context.spbook.delay && spellbook == g.context.spbook.book)
451 dullbook -= rnd(objects[booktype].oc_level);
452
453 if (dullbook > 0) {
454 eyes = body_part(EYE);
455 if (eyecount(g.youmonst.data) > 1)
456 eyes = makeplural(eyes);
457 pline("This book is so dull that you can't keep your %s open.",
458 eyes);
459 dullbook += rnd(2 * objects[booktype].oc_level);
460 fall_asleep(-dullbook, TRUE);
461 return 1;
462 }
463 }
464
465 if (g.context.spbook.delay && !confused
466 && spellbook == g.context.spbook.book
467 /* handle the sequence: start reading, get interrupted, have
468 g.context.spbook.book become erased somehow, resume reading it */
469 && booktype != SPE_BLANK_PAPER) {
470 You("continue your efforts to %s.",
471 (booktype == SPE_NOVEL) ? "read the novel" : "memorize the spell");
472 } else {
473 /* KMH -- Simplified this code */
474 if (booktype == SPE_BLANK_PAPER) {
475 pline("This spellbook is all blank.");
476 makeknown(booktype);
477 return 1;
478 }
479
480 /* 3.6 tribute */
481 if (booktype == SPE_NOVEL) {
482 /* Obtain current Terry Pratchett book title */
483 const char *tribtitle = noveltitle(&spellbook->novelidx);
484
485 if (read_tribute("books", tribtitle, 0, (char *) 0, 0,
486 spellbook->o_id)) {
487 if (!u.uconduct.literate++)
488 livelog_printf(LL_CONDUCT,
489 "became literate by reading %s", tribtitle);
490 check_unpaid(spellbook);
491 makeknown(booktype);
492 if (!u.uevent.read_tribute) {
493 record_achievement(ACH_NOVL);
494 /* give bonus of 20 xp and 4*20+0 pts */
495 more_experienced(20, 0);
496 newexplevel();
497 u.uevent.read_tribute = 1; /* only once */
498 }
499 }
500 return 1;
501 }
502
503 if (objects[booktype].oc_level >= 8) {
504 impossible("Unknown spellbook level %d, book %d;",
505 objects[booktype].oc_level, booktype);
506 return 0;
507 }
508
509 /* currently level * 10 */
510 g.context.spbook.delay = -objects[booktype].oc_delay;
511
512 /* check to see if we already know it and want to refresh our memory */
513 for (i = 0; i < MAXSPELL; i++)
514 if (spellid(i) == booktype || spellid(i) == NO_SPELL)
515 break;
516 if (spellid(i) == booktype && spellknow(i) > KEEN / 10) {
517 You("know \"%s\" quite well already.", OBJ_NAME(objects[booktype]));
518 if (yn("Refresh your memory anyway?") == 'n')
519 return 0;
520 }
521
522 /* Books are often wiser than their readers (Rus.) */
523 spellbook->in_use = TRUE;
524 if (spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
525 /* uncursed - chance to fail */
526 int read_ability = ACURR(A_INT) + 4 + u.ulevel / 2
527 - 2 * objects[booktype].oc_level
528 + (spellbook->blessed ? 10 : 0)
529 - (spellbook->cursed ? 10 : 0)
530 + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);
531
532 /* only wizards know if a spell is too difficult */
533 if (Role_if(PM_WIZARD) && read_ability < 20 && !confused) {
534 char qbuf[QBUFSZ];
535
536 Sprintf(qbuf,
537 "This spellbook is %sdifficult to comprehend. Continue?",
538 (read_ability < 12 ? "very " : ""));
539 if (yn(qbuf) != 'y') {
540 spellbook->in_use = FALSE;
541 return 1;
542 }
543 }
544 /* its up to random luck now */
545 if (rnd(20) > read_ability) {
546 too_hard = TRUE;
547 }
548 }
549
550 if (too_hard) {
551 You("can't comprehend the runes!");
552 make_confused(-g.context.spbook.delay, FALSE); /* study time */
553 bot(); /* show Conf on status line before tele, crumbling, etc */
554
555 boolean gone = cursed_book(spellbook);
556 if (gone) {
557 if (!objects[spellbook->otyp].oc_name_known
558 && !objects[spellbook->otyp].oc_uname)
559 docall(spellbook);
560 useup(spellbook);
561 } else
562 spellbook->in_use = FALSE;
563 return 1;
564 } else if (confused) {
565 if (!confused_book(spellbook)) {
566 spellbook->in_use = FALSE;
567 }
568 return 1;
569 }
570 spellbook->in_use = FALSE;
571
572 You("begin to %s the runes.",
573 spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : "memorize");
574 }
575
576 g.context.spbook.book = spellbook;
577 if (g.context.spbook.book)
578 g.context.spbook.o_id = g.context.spbook.book->o_id;
579 set_occupation(learn, "studying", 0);
580 return 1;
581 }
582
583 /* a spellbook has been destroyed or the character has changed levels;
584 the stored address for the current book is no longer valid */
585 void
book_disappears(struct obj * obj)586 book_disappears(struct obj* obj)
587 {
588 if (obj == g.context.spbook.book) {
589 g.context.spbook.book = (struct obj *) 0;
590 g.context.spbook.o_id = 0;
591 }
592 }
593
594 /* renaming an object usually results in it having a different address;
595 so the sequence start reading, get interrupted, name the book, resume
596 reading would read the "new" book from scratch */
597 void
book_substitution(struct obj * old_obj,struct obj * new_obj)598 book_substitution(struct obj* old_obj, struct obj* new_obj)
599 {
600 if (old_obj == g.context.spbook.book) {
601 g.context.spbook.book = new_obj;
602 if (g.context.spbook.book)
603 g.context.spbook.o_id = g.context.spbook.book->o_id;
604 }
605 }
606
607 /* called from moveloop() */
608 void
age_spells(void)609 age_spells(void)
610 {
611 int i;
612 /*
613 * The time relative to the hero (a pass through move
614 * loop) causes all spell knowledge to be decremented.
615 * The hero's speed, rest status, conscious status etc.
616 * does not alter the loss of memory.
617 */
618 for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++)
619 if (spellknow(i))
620 decrnknow(i);
621 return;
622 }
623
624 /* return True if spellcasting is inhibited;
625 only covers a small subset of reasons why casting won't work */
626 static boolean
rejectcasting(void)627 rejectcasting(void)
628 {
629 /* rejections which take place before selecting a particular spell */
630 if (Stunned) {
631 You("are too impaired to cast a spell.");
632 return TRUE;
633 } else if (!can_chant(&g.youmonst)) {
634 You("are unable to chant the incantation.");
635 return TRUE;
636 } else if (!freehand() && !(uwep && uwep->otyp == QUARTERSTAFF)) {
637 /* Note: !freehand() occurs when weapon and shield (or two-handed
638 * weapon) are welded to hands, so "arms" probably doesn't need
639 * to be makeplural(bodypart(ARM)).
640 *
641 * But why isn't lack of free arms (for gesturing) an issue when
642 * poly'd hero has no limbs?
643 */
644 Your("arms are not free to cast!");
645 return TRUE;
646 }
647 return FALSE;
648 }
649
650 /*
651 * Return TRUE if a spell was picked, with the spell index in the return
652 * parameter. Otherwise return FALSE.
653 */
654 static boolean
getspell(int * spell_no)655 getspell(int* spell_no)
656 {
657 int nspells, idx;
658 char ilet, lets[BUFSZ], qbuf[QBUFSZ];
659
660 if (spellid(0) == NO_SPELL) {
661 You("don't know any spells right now.");
662 return FALSE;
663 }
664 if (rejectcasting())
665 return FALSE; /* no spell chosen */
666
667 if (flags.menu_style == MENU_TRADITIONAL) {
668 /* we know there is at least 1 known spell */
669 for (nspells = 1; nspells < MAXSPELL && spellid(nspells) != NO_SPELL;
670 nspells++)
671 continue;
672
673 if (nspells == 1)
674 Strcpy(lets, "a");
675 else if (nspells < 27)
676 Sprintf(lets, "a-%c", 'a' + nspells - 1);
677 else if (nspells == 27)
678 Sprintf(lets, "a-zA");
679 /* this assumes that there are at most 52 spells... */
680 else
681 Sprintf(lets, "a-zA-%c", 'A' + nspells - 27);
682
683 for (;;) {
684 Snprintf(qbuf, sizeof(qbuf), "Cast which spell? [%s *?]",
685 lets);
686 ilet = yn_function(qbuf, (char *) 0, '\0');
687 if (ilet == '*' || ilet == '?')
688 break; /* use menu mode */
689 if (index(quitchars, ilet))
690 return FALSE;
691
692 idx = spell_let_to_idx(ilet);
693 if (idx < 0 || idx >= nspells) {
694 You("don't know that spell.");
695 continue; /* ask again */
696 }
697 *spell_no = idx;
698 return TRUE;
699 }
700 }
701 return dospellmenu("Choose which spell to cast", SPELLMENU_CAST,
702 spell_no);
703 }
704
705 /* the 'Z' command -- cast a spell */
706 int
docast(void)707 docast(void)
708 {
709 int spell_no;
710
711 if (getspell(&spell_no))
712 return spelleffects(spell_no, FALSE);
713 return 0;
714 }
715
716 const char *
spelltypemnemonic(int skill)717 spelltypemnemonic(int skill)
718 {
719 switch (skill) {
720 case P_ATTACK_SPELL:
721 return "attack";
722 case P_HEALING_SPELL:
723 return "healing";
724 case P_DIVINATION_SPELL:
725 return "divination";
726 case P_ENCHANTMENT_SPELL:
727 return "enchantment";
728 case P_CLERIC_SPELL:
729 return "clerical";
730 case P_ESCAPE_SPELL:
731 return "escape";
732 case P_MATTER_SPELL:
733 return "matter";
734 default:
735 impossible("Unknown spell skill, %d;", skill);
736 return "";
737 }
738 }
739
740 int
spell_skilltype(int booktype)741 spell_skilltype(int booktype)
742 {
743 return objects[booktype].oc_skill;
744 }
745
746 static void
cast_protection(void)747 cast_protection(void)
748 {
749 int l = u.ulevel, loglev = 0,
750 gain, natac = u.uac + u.uspellprot;
751 /* note: u.uspellprot is subtracted when find_ac() factors it into u.uac,
752 so adding here factors it back out
753 (versions prior to 3.6 had this backwards) */
754
755 /* loglev=log2(u.ulevel)+1 (1..5) */
756 while (l) {
757 loglev++;
758 l /= 2;
759 }
760
761 /* The more u.uspellprot you already have, the less you get,
762 * and the better your natural ac, the less you get.
763 *
764 * LEVEL AC SPELLPROT from successive SPE_PROTECTION casts
765 * 1 10 0, 1, 2, 3, 4
766 * 1 0 0, 1, 2, 3
767 * 1 -10 0, 1, 2
768 * 2-3 10 0, 2, 4, 5, 6, 7, 8
769 * 2-3 0 0, 2, 4, 5, 6
770 * 2-3 -10 0, 2, 3, 4
771 * 4-7 10 0, 3, 6, 8, 9, 10, 11, 12
772 * 4-7 0 0, 3, 5, 7, 8, 9
773 * 4-7 -10 0, 3, 5, 6
774 * 7-15 -10 0, 3, 5, 6
775 * 8-15 10 0, 4, 7, 10, 12, 13, 14, 15, 16
776 * 8-15 0 0, 4, 7, 9, 10, 11, 12
777 * 8-15 -10 0, 4, 6, 7, 8
778 * 16-30 10 0, 5, 9, 12, 14, 16, 17, 18, 19, 20
779 * 16-30 0 0, 5, 9, 11, 13, 14, 15
780 * 16-30 -10 0, 5, 8, 9, 10
781 */
782 natac = (10 - natac) / 10; /* convert to positive and scale down */
783 gain = loglev - (int) u.uspellprot / (4 - min(3, natac));
784
785 if (gain > 0) {
786 if (!Blind) {
787 int rmtyp;
788 const char *hgolden = hcolor(NH_GOLDEN), *atmosphere;
789
790 if (u.uspellprot) {
791 pline_The("%s haze around you becomes more dense.", hgolden);
792 } else {
793 rmtyp = levl[u.ux][u.uy].typ;
794 atmosphere = u.uswallow
795 ? ((u.ustuck->data == &mons[PM_FOG_CLOUD])
796 ? "mist"
797 : is_whirly(u.ustuck->data)
798 ? "maelstrom"
799 : is_animal(u.ustuck->data)
800 ? "maw"
801 : "ooze")
802 : (u.uinwater
803 ? hliquid("water")
804 : (rmtyp == CLOUD)
805 ? "cloud"
806 : IS_TREE(rmtyp)
807 ? "vegetation"
808 : IS_STWALL(rmtyp)
809 ? "stone"
810 : "air");
811 pline_The("%s around you begins to shimmer with %s haze.",
812 atmosphere, an(hgolden));
813 }
814 }
815 u.uspellprot += gain;
816 u.uspmtime = (P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT)
817 ? 20 : 10;
818 if (!u.usptime)
819 u.usptime = u.uspmtime;
820 find_ac();
821 } else {
822 Your("skin feels warm for a moment.");
823 }
824 }
825
826 /* attempting to cast a forgotten spell will cause disorientation */
827 static void
spell_backfire(int spell)828 spell_backfire(int spell)
829 {
830 long duration = (long) ((spellev(spell) + 1) * 3), /* 6..24 */
831 old_stun = (HStun & TIMEOUT), old_conf = (HConfusion & TIMEOUT);
832
833 /* Prior to 3.4.1, only effect was confusion; it still predominates.
834 *
835 * 3.6.0: this used to override pre-existing confusion duration
836 * (cases 0..8) and pre-existing stun duration (cases 4..9);
837 * increase them instead. (Hero can no longer cast spells while
838 * Stunned, so the potential increment to stun duration here is
839 * just hypothetical.)
840 */
841 switch (rn2(10)) {
842 case 0:
843 case 1:
844 case 2:
845 case 3:
846 make_confused(old_conf + duration, FALSE); /* 40% */
847 break;
848 case 4:
849 case 5:
850 case 6:
851 make_confused(old_conf + 2L * duration / 3L, FALSE); /* 30% */
852 make_stunned(old_stun + duration / 3L, FALSE);
853 break;
854 case 7:
855 case 8:
856 make_stunned(old_stun + 2L * duration / 3L, FALSE); /* 20% */
857 make_confused(old_conf + duration / 3L, FALSE);
858 break;
859 case 9:
860 make_stunned(old_stun + duration, FALSE); /* 10% */
861 break;
862 }
863 return;
864 }
865
866 /* Given an expected amount of hunger for a spell, return the amount it should
867 * be reduced to. High-intelligence Wizards get to cast spells with less or no
868 * hunger penalty. */
869 static int
spell_hunger(int hungr)870 spell_hunger(int hungr)
871 {
872 /* If hero is a wizard, their current intelligence
873 * (bonuses + temporary + current)
874 * affects hunger reduction in casting a spell.
875 * 1. int = 17-18 no reduction
876 * 2. int = 16 1/4 hungr
877 * 3. int = 15 1/2 hungr
878 * 4. int = 1-14 normal reduction
879 * The reason for this is:
880 * a) Intelligence affects the amount of exertion
881 * in thinking.
882 * b) Wizards have spent their life at magic and
883 * understand quite well how to cast spells.
884 */
885 if (Role_if(PM_WIZARD)) {
886 int intel = acurr(A_INT);
887 if (intel >= 17)
888 return 0;
889 else if (intel == 16)
890 return hungr / 4;
891 else if (intel == 15)
892 return hungr / 2;
893 }
894 /* no adjustment */
895 return hungr;
896 }
897 /* for using this function to test whether hunger would be eliminated */
898 #define spell_would_hunger() (spell_hunger(100) > 0)
899
900 int
spelleffects(int spell,boolean atme)901 spelleffects(int spell, boolean atme)
902 {
903 int energy, damage, n;
904 int otyp, skill, role_skill, res = 0;
905 boolean confused = (Confusion != 0);
906 boolean physical_damage = FALSE;
907 struct obj *pseudo;
908 coord cc;
909
910 /*
911 * Reject attempting to cast while stunned or with no free hands.
912 * Already done in getspell() to stop casting before choosing
913 * which spell, but duplicated here for cases where spelleffects()
914 * gets called directly for ^T without intrinsic teleport capability
915 * or #turn for non-priest/non-knight.
916 * (There's no duplication of messages; when the rejection takes
917 * place in getspell(), we don't get called.)
918 */
919 if (rejectcasting()) {
920 return 0; /* no time elapses */
921 }
922
923 /*
924 * Spell casting no longer affects knowledge of the spell. A
925 * decrement of spell knowledge is done every turn.
926 */
927 if (spellknow(spell) <= 0) {
928 Your("knowledge of this spell is twisted.");
929 pline("It invokes nightmarish images in your mind...");
930 spell_backfire(spell);
931 return 1;
932 } else if (spellknow(spell) <= KEEN / 200) { /* 100 turns left */
933 You("strain to recall the spell.");
934 } else if (spellknow(spell) <= KEEN / 40) { /* 500 turns left */
935 You("have difficulty remembering the spell.");
936 } else if (spellknow(spell) <= KEEN / 20) { /* 1000 turns left */
937 Your("knowledge of this spell is growing faint.");
938 } else if (spellknow(spell) <= KEEN / 10) { /* 2000 turns left */
939 Your("recall of this spell is gradually fading.");
940 }
941
942 if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD
943 && spell_would_hunger() > 0) {
944 You("are too hungry to cast that spell.");
945 return 0;
946 } else if (ACURR(A_STR) < 4 && spellid(spell) != SPE_RESTORE_ABILITY) {
947 You("lack the strength to cast spells.");
948 return 0;
949 } else if (check_capacity(
950 "Your concentration falters while carrying so much stuff.")) {
951 return 1;
952 }
953
954 /*
955 * Note: dotele() also calculates energy use and checks nutrition
956 * and strength requirements; it any of these change, update it too.
957 */
958 energy = energy_cost(spell);
959 /* if spell is impossible to cast, kludge this to u.uen + 1 to make it fail
960 * the checks below as not having enough energy. */
961 if (energy < 0) {
962 energy = u.uen + 1;
963 }
964
965 /* if the cast attempt is already going to fail due to insufficient
966 energy (ie, u.uen < energy), the Amulet's drain effect won't kick
967 in and no turn will be consumed; however, when it does kick in,
968 the attempt may fail due to lack of energy after the draining, in
969 which case a turn will be used up in addition to the energy loss */
970 if (u.uhave.amulet && u.uen >= energy) {
971 You_feel("the amulet draining your energy away.");
972 /* this used to be 'energy += rnd(2 * energy)' (without 'res'),
973 so if amulet-induced cost was more than u.uen, nothing
974 (except the "don't have enough energy" message) happened
975 and player could just try again (and again and again...);
976 now we drain some energy immediately, which has a
977 side-effect of not increasing the hunger aspect of casting */
978 u.uen -= rnd(2 * energy);
979 if (u.uen < 0)
980 u.uen = 0;
981 g.context.botl = 1;
982 res = 1; /* time is going to elapse even if spell doesn't get cast */
983 }
984
985 if (energy > u.uen) {
986 You("don't have enough energy to cast that spell.");
987 return res;
988 } else {
989 if (spellid(spell) != SPE_DETECT_FOOD) {
990 int hungr = spell_hunger(energy * 2);
991 /* don't put player (quite) into fainting from
992 * casting a spell, particularly since they might
993 * not even be hungry at the beginning; however,
994 * this is low enough that they must eat before
995 * casting anything else except detect food
996 */
997 if (hungr > u.uhunger - 3)
998 hungr = u.uhunger - 3;
999 morehungry(hungr);
1000 }
1001 }
1002
1003 /* confused casting always fails, and is assessed after hunger penalty */
1004 if (confused) {
1005 You("fail to cast the spell correctly.");
1006 u.uen -= energy / 2;
1007 g.context.botl = 1;
1008 return 1;
1009 }
1010
1011 u.uen -= energy;
1012 g.context.botl = 1;
1013 exercise(A_WIS, TRUE);
1014 /* pseudo is a temporary "false" object containing the spell stats */
1015 pseudo = mksobj(spellid(spell), FALSE, FALSE);
1016 pseudo->blessed = pseudo->cursed = 0;
1017 pseudo->quan = 20L; /* do not let useup get it */
1018 /*
1019 * Find the skill the hero has in a spell type category.
1020 * See spell_skilltype for categories.
1021 */
1022 otyp = pseudo->otyp;
1023 skill = spell_skilltype(otyp);
1024 role_skill = P_SKILL(skill);
1025
1026 switch (otyp) {
1027 /*
1028 * At first spells act as expected. As the hero increases in skill
1029 * with the appropriate spell type, some spells increase in their
1030 * effects, e.g. more damage, further distance, and so on, without
1031 * additional cost to the spellcaster.
1032 */
1033 case SPE_FIREBALL:
1034 case SPE_CONE_OF_COLD:
1035 if (role_skill >= P_SKILLED && yn("Cast advanced spell?") == 'y') {
1036 if (throwspell()) {
1037 cc.x = u.dx;
1038 cc.y = u.dy;
1039 n = rnd(8) + 1;
1040 while (n--) {
1041 if (!u.dx && !u.dy && !u.dz) {
1042 if ((damage = zapyourself(pseudo, TRUE)) != 0) {
1043 char buf[BUFSZ];
1044 Sprintf(buf, "zapped %sself with a spell",
1045 uhim());
1046 losehp(damage, buf, NO_KILLER_PREFIX);
1047 }
1048 } else {
1049 explode(u.dx, u.dy,
1050 otyp - SPE_MAGIC_MISSILE + 10,
1051 spell_damage_bonus(u.ulevel / 2 + 1), 0,
1052 (otyp == SPE_CONE_OF_COLD)
1053 ? EXPL_FROSTY
1054 : EXPL_FIERY);
1055 }
1056 u.dx = cc.x + rnd(3) - 2;
1057 u.dy = cc.y + rnd(3) - 2;
1058 if (!isok(u.dx, u.dy) || !cansee(u.dx, u.dy)
1059 || IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) {
1060 /* Spell is reflected back to center */
1061 u.dx = cc.x;
1062 u.dy = cc.y;
1063 }
1064 }
1065 }
1066 break;
1067 } else if (role_skill >= P_SKILLED) {
1068 /* player said not to cast advanced spell; return up to half of the
1069 * magical energy */
1070 u.uen += rnd(energy / 2);
1071 }
1072 /*FALLTHRU*/
1073
1074 /* these spells are all duplicates of wand effects */
1075 case SPE_FORCE_BOLT:
1076 physical_damage = TRUE;
1077 /*FALLTHRU*/
1078 case SPE_SLEEP:
1079 case SPE_MAGIC_MISSILE:
1080 case SPE_KNOCK:
1081 case SPE_SLOW_MONSTER:
1082 case SPE_WIZARD_LOCK:
1083 case SPE_DIG:
1084 case SPE_TURN_UNDEAD:
1085 case SPE_POLYMORPH:
1086 case SPE_TELEPORT_AWAY:
1087 case SPE_CANCELLATION:
1088 case SPE_FINGER_OF_DEATH:
1089 case SPE_LIGHT:
1090 case SPE_DETECT_UNSEEN:
1091 case SPE_HEALING:
1092 case SPE_EXTRA_HEALING:
1093 case SPE_DRAIN_LIFE:
1094 case SPE_STONE_TO_FLESH:
1095 if (objects[otyp].oc_dir != NODIR) {
1096 if (otyp == SPE_HEALING || otyp == SPE_EXTRA_HEALING) {
1097 /* healing and extra healing are actually potion effects,
1098 but they've been extended to take a direction like wands */
1099 if (role_skill >= P_SKILLED)
1100 pseudo->blessed = 1;
1101 }
1102 if (atme) {
1103 u.dx = u.dy = u.dz = 0;
1104 } else if (!getdir((char *) 0)) {
1105 /* getdir cancelled, re-use previous direction */
1106 /*
1107 * FIXME: reusing previous direction only makes sense
1108 * if there is an actual previous direction. When there
1109 * isn't one, the spell gets cast at self which is rarely
1110 * what the player intended. Unfortunately, the way
1111 * spelleffects() is organized means that aborting with
1112 * "nevermind" is not an option.
1113 */
1114 pline_The("magical energy is released!");
1115 }
1116 if (!u.dx && !u.dy && !u.dz) {
1117 if ((damage = zapyourself(pseudo, TRUE)) != 0) {
1118 char buf[BUFSZ];
1119
1120 Sprintf(buf, "zapped %sself with a spell", uhim());
1121 if (physical_damage)
1122 damage = Maybe_Half_Phys(damage);
1123 losehp(damage, buf, NO_KILLER_PREFIX);
1124 }
1125 } else
1126 weffects(pseudo);
1127 } else
1128 weffects(pseudo);
1129 update_inventory(); /* spell may modify inventory */
1130 break;
1131
1132 /* these are all duplicates of scroll effects */
1133 case SPE_REMOVE_CURSE:
1134 case SPE_CONFUSE_MONSTER:
1135 case SPE_DETECT_FOOD:
1136 case SPE_CAUSE_FEAR:
1137 /* high skill yields effect equivalent to blessed scroll */
1138 if (role_skill >= P_SKILLED)
1139 pseudo->blessed = 1;
1140 /*FALLTHRU*/
1141 case SPE_CHARM_MONSTER:
1142 case SPE_MAGIC_MAPPING:
1143 case SPE_CREATE_MONSTER:
1144 (void) seffects(pseudo);
1145 break;
1146
1147 /* these are all duplicates of potion effects */
1148 case SPE_HASTE_SELF:
1149 case SPE_DETECT_TREASURE:
1150 case SPE_DETECT_MONSTERS:
1151 case SPE_LEVITATION:
1152 case SPE_RESTORE_ABILITY:
1153 /* high skill yields effect equivalent to blessed potion */
1154 if (role_skill >= P_SKILLED)
1155 pseudo->blessed = 1;
1156 /*FALLTHRU*/
1157 case SPE_INVISIBILITY:
1158 (void) peffects(pseudo);
1159 break;
1160 /* end of potion-like spells */
1161
1162 case SPE_CURE_BLINDNESS:
1163 healup(0, 0, FALSE, TRUE);
1164 break;
1165 case SPE_CURE_SICKNESS:
1166 if (Sick)
1167 You("are no longer ill.");
1168 if (Slimed)
1169 make_slimed(0L, "The slime disappears!");
1170 healup(0, 0, TRUE, FALSE);
1171 break;
1172 case SPE_CREATE_FAMILIAR:
1173 (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE);
1174 break;
1175 case SPE_CLAIRVOYANCE:
1176 if (!BClairvoyant) {
1177 if (role_skill >= P_SKILLED)
1178 pseudo->blessed = 1; /* detect monsters as well as map */
1179 do_vicinity_map(pseudo);
1180 /* at present, only one thing blocks clairvoyance */
1181 } else if (uarmh && uarmh->otyp == CORNUTHAUM)
1182 You("sense a pointy hat on top of your %s.", body_part(HEAD));
1183 break;
1184 case SPE_PROTECTION:
1185 cast_protection();
1186 break;
1187 case SPE_JUMPING:
1188 if (!jump(max(role_skill, 1)))
1189 pline1(nothing_happens);
1190 break;
1191 default:
1192 impossible("Unknown spell %d attempted.", spell);
1193 obfree(pseudo, (struct obj *) 0);
1194 return 0;
1195 }
1196
1197 /* gain skill for successful cast */
1198 use_skill(skill, spellev(spell));
1199
1200 obfree(pseudo, (struct obj *) 0); /* now, get rid of it */
1201 return 1;
1202 }
1203
1204 /*ARGSUSED*/
1205 static boolean
spell_aim_step(genericptr_t arg UNUSED,int x,int y)1206 spell_aim_step(genericptr_t arg UNUSED, int x, int y)
1207 {
1208 if (!isok(x,y))
1209 return FALSE;
1210 if (!ZAP_POS(levl[x][y].typ)
1211 && !(IS_DOOR(levl[x][y].typ) && doorstate(&levl[x][y]) == D_ISOPEN))
1212 return FALSE;
1213 return TRUE;
1214 }
1215
1216 /* Choose location where spell takes effect. */
1217 static int
throwspell(void)1218 throwspell(void)
1219 {
1220 coord cc, uc;
1221 struct monst *mtmp;
1222
1223 if (u.uinwater) {
1224 pline("You're joking! In this weather?");
1225 return 0;
1226 } else if (Is_waterlevel(&u.uz)) {
1227 You("had better wait for the sun to come out.");
1228 return 0;
1229 }
1230
1231 pline("Where do you want to cast the spell?");
1232 cc.x = u.ux;
1233 cc.y = u.uy;
1234 if (getpos(&cc, TRUE, "the desired position") < 0)
1235 return 0; /* user pressed ESC */
1236 clear_nhwindow(WIN_MESSAGE); /* discard any autodescribe feedback */
1237
1238 /* The number of moves from hero to where the spell drops.*/
1239 if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) {
1240 pline_The("spell dissipates over the distance!");
1241 return 0;
1242 } else if (u.uswallow) {
1243 pline_The("spell is cut short!");
1244 exercise(A_WIS, FALSE); /* What were you THINKING! */
1245 u.dx = 0;
1246 u.dy = 0;
1247 return 1;
1248 } else if (((cc.x != u.ux || cc.y != u.uy) && !cansee(cc.x, cc.y)
1249 && (!(mtmp = m_at(cc.x, cc.y)) || !canspotmon(mtmp)))
1250 || IS_STWALL(levl[cc.x][cc.y].typ)) {
1251 Your("mind fails to lock onto that location!");
1252 return 0;
1253 }
1254
1255 uc.x = u.ux;
1256 uc.y = u.uy;
1257
1258 walk_path(&uc, &cc, spell_aim_step, (genericptr_t) 0);
1259
1260 u.dx = cc.x;
1261 u.dy = cc.y;
1262 return 1;
1263 }
1264
1265 /* add/hide/remove/unhide teleport-away on behalf of dotelecmd() to give
1266 more control to behavior of ^T when used in wizard mode */
1267 int
tport_spell(int what)1268 tport_spell(int what)
1269 {
1270 static struct tport_hideaway {
1271 struct spell savespell;
1272 int tport_indx;
1273 } save_tport;
1274 int i;
1275 /* also defined in teleport.c */
1276 #define NOOP_SPELL 0
1277 #define HIDE_SPELL 1
1278 #define ADD_SPELL 2
1279 #define UNHIDESPELL 3
1280 #define REMOVESPELL 4
1281
1282 for (i = 0; i < MAXSPELL; i++)
1283 if (spellid(i) == SPE_TELEPORT_AWAY || spellid(i) == NO_SPELL)
1284 break;
1285 if (i == MAXSPELL) {
1286 impossible("tport_spell: spellbook full");
1287 /* wizard mode ^T is not able to honor player's menu choice */
1288 } else if (spellid(i) == NO_SPELL) {
1289 if (what == HIDE_SPELL || what == REMOVESPELL) {
1290 save_tport.tport_indx = MAXSPELL;
1291 } else if (what == UNHIDESPELL) {
1292 /*assert( save_tport.savespell.sp_id == SPE_TELEPORT_AWAY );*/
1293 g.spl_book[save_tport.tport_indx] = save_tport.savespell;
1294 save_tport.tport_indx = MAXSPELL; /* burn bridge... */
1295 } else if (what == ADD_SPELL) {
1296 save_tport.savespell = g.spl_book[i];
1297 save_tport.tport_indx = i;
1298 g.spl_book[i].sp_id = SPE_TELEPORT_AWAY;
1299 g.spl_book[i].sp_lev = objects[SPE_TELEPORT_AWAY].oc_level;
1300 g.spl_book[i].sp_know = KEEN;
1301 return REMOVESPELL; /* operation needed to reverse */
1302 }
1303 } else { /* spellid(i) == SPE_TELEPORT_AWAY */
1304 if (what == ADD_SPELL || what == UNHIDESPELL) {
1305 save_tport.tport_indx = MAXSPELL;
1306 } else if (what == REMOVESPELL) {
1307 /*assert( i == save_tport.tport_indx );*/
1308 g.spl_book[i] = save_tport.savespell;
1309 save_tport.tport_indx = MAXSPELL;
1310 } else if (what == HIDE_SPELL) {
1311 save_tport.savespell = g.spl_book[i];
1312 save_tport.tport_indx = i;
1313 g.spl_book[i].sp_id = NO_SPELL;
1314 return UNHIDESPELL; /* operation needed to reverse */
1315 }
1316 }
1317 return NOOP_SPELL;
1318 }
1319
1320 /* forget a random selection of known spells due to amnesia;
1321 they used to be lost entirely, as if never learned, but now we
1322 just set the memory retention to zero so that they can't be cast */
1323 void
losespells(void)1324 losespells(void)
1325 {
1326 int n, nzap, i;
1327
1328 /* in case reading has been interrupted earlier, discard context */
1329 g.context.spbook.book = 0;
1330 g.context.spbook.o_id = 0;
1331 /* count the number of known spells */
1332 for (n = 0; n < MAXSPELL; ++n)
1333 if (spellid(n) == NO_SPELL)
1334 break;
1335
1336 /* lose anywhere from zero to all known spells;
1337 if confused, use the worse of two die rolls */
1338 nzap = rn2(n + 1);
1339 if (Confusion) {
1340 i = rn2(n + 1);
1341 if (i > nzap)
1342 nzap = i;
1343 }
1344 /* good Luck might ameliorate spell loss */
1345 if (nzap > 1 && !rnl(7))
1346 nzap = rnd(nzap);
1347
1348 /*
1349 * Forget 'nzap' out of 'n' known spells by setting their memory
1350 * retention to zero. Every spell has the same probability to be
1351 * forgotten, even if its retention is already zero.
1352 *
1353 * Perhaps we should forget the corresponding book too?
1354 *
1355 * (3.4.3 removed spells entirely from the list, but always did
1356 * so from its end, so the 'nzap' most recently learned spells
1357 * were the ones lost by default. Player had sort control over
1358 * the list, so could move the most useful spells to front and
1359 * only lose them if 'nzap' turned out to be a large value.
1360 *
1361 * Discarding from the end of the list had the virtue of making
1362 * casting letters for lost spells become invalid and retaining
1363 * the original letter for the ones which weren't lost, so there
1364 * was no risk to the player of accidentally casting the wrong
1365 * spell when using a letter that was in use prior to amnesia.
1366 * That wouldn't be the case if we implemented spell loss spread
1367 * throughout the list of known spells; every spell located past
1368 * the first lost spell would end up with new letter assigned.)
1369 */
1370 for (i = 0; nzap > 0; ++i) {
1371 /* when nzap is small relative to the number of spells left,
1372 the chance to lose spell [i] is small; as the number of
1373 remaining candidates shrinks, the chance per candidate
1374 gets bigger; overall, exactly nzap entries are affected */
1375 if (rn2(n - i) < nzap) {
1376 /* lose access to spell [i] */
1377 spellknow(i) = 0;
1378 #if 0
1379 /* also forget its book */
1380 forget_single_object(spellid(i));
1381 #endif
1382 /* and abuse wisdom */
1383 exercise(A_WIS, FALSE);
1384 /* there's now one less spell slated to be forgotten */
1385 --nzap;
1386 }
1387 }
1388 }
1389
1390 /*
1391 * Allow player to sort the list of known spells. Manually swapping
1392 * pairs of them becomes very tedious once the list reaches two pages.
1393 *
1394 * Possible extensions:
1395 * provide means for player to control ordering of skill classes;
1396 * provide means to supply value N such that first N entries stick
1397 * while rest of list is being sorted;
1398 * make chosen sort order be persistent such that when new spells
1399 * are learned, they get inserted into sorted order rather than be
1400 * appended to the end of the list?
1401 */
1402 enum spl_sort_types {
1403 SORTBY_LETTER = 0,
1404 SORTBY_ALPHA,
1405 SORTBY_LVL_LO,
1406 SORTBY_LVL_HI,
1407 SORTBY_SKL_AL,
1408 SORTBY_SKL_LO,
1409 SORTBY_SKL_HI,
1410 SORTBY_CURRENT,
1411 SORTRETAINORDER,
1412
1413 NUM_SPELL_SORTBY
1414 };
1415
1416 static const char *spl_sortchoices[NUM_SPELL_SORTBY] = {
1417 "by casting letter",
1418 "alphabetically",
1419 "by level, low to high",
1420 "by level, high to low",
1421 "by skill group, alphabetized within each group",
1422 "by skill group, low to high level within group",
1423 "by skill group, high to low level within group",
1424 "maintain current ordering",
1425 /* a menu choice rather than a sort choice */
1426 "reassign casting letters to retain current order",
1427 };
1428
1429 /* qsort callback routine */
1430 static int QSORTCALLBACK
spell_cmp(const genericptr vptr1,const genericptr vptr2)1431 spell_cmp(const genericptr vptr1, const genericptr vptr2)
1432 {
1433 /*
1434 * gather up all of the possible parameters except spell name
1435 * in advance, even though some might not be needed:
1436 * indx. = spl_orderindx[] index into g.spl_book[];
1437 * otyp. = g.spl_book[] index into objects[];
1438 * levl. = spell level;
1439 * skil. = skill group aka spell class.
1440 */
1441 int indx1 = *(int *) vptr1, indx2 = *(int *) vptr2,
1442 otyp1 = g.spl_book[indx1].sp_id, otyp2 = g.spl_book[indx2].sp_id,
1443 levl1 = objects[otyp1].oc_level, levl2 = objects[otyp2].oc_level,
1444 skil1 = objects[otyp1].oc_skill, skil2 = objects[otyp2].oc_skill;
1445
1446 switch (g.spl_sortmode) {
1447 case SORTBY_LETTER:
1448 return indx1 - indx2;
1449 case SORTBY_ALPHA:
1450 break;
1451 case SORTBY_LVL_LO:
1452 if (levl1 != levl2)
1453 return levl1 - levl2;
1454 break;
1455 case SORTBY_LVL_HI:
1456 if (levl1 != levl2)
1457 return levl2 - levl1;
1458 break;
1459 case SORTBY_SKL_AL:
1460 if (skil1 != skil2)
1461 return skil1 - skil2;
1462 break;
1463 case SORTBY_SKL_LO:
1464 if (skil1 != skil2)
1465 return skil1 - skil2;
1466 if (levl1 != levl2)
1467 return levl1 - levl2;
1468 break;
1469 case SORTBY_SKL_HI:
1470 if (skil1 != skil2)
1471 return skil1 - skil2;
1472 if (levl1 != levl2)
1473 return levl2 - levl1;
1474 break;
1475 case SORTBY_CURRENT:
1476 default:
1477 return (vptr1 < vptr2) ? -1
1478 : (vptr1 > vptr2); /* keep current order */
1479 }
1480 /* tie-breaker for most sorts--alphabetical by spell name */
1481 return strcmpi(OBJ_NAME(objects[otyp1]), OBJ_NAME(objects[otyp2]));
1482 }
1483
1484 /* sort the index used for display order of the "view known spells"
1485 list (sortmode == SORTBY_xxx), or sort the spellbook itself to make
1486 the current display order stick (sortmode == SORTRETAINORDER) */
1487 static void
sortspells(void)1488 sortspells(void)
1489 {
1490 int i;
1491 #if defined(SYSV) || defined(DGUX)
1492 unsigned n;
1493 #else
1494 int n;
1495 #endif
1496
1497 if (g.spl_sortmode == SORTBY_CURRENT)
1498 return;
1499 for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; ++n)
1500 continue;
1501 if (n < 2)
1502 return; /* not enough entries to need sorting */
1503
1504 if (!g.spl_orderindx) {
1505 /* we haven't done any sorting yet; list is in casting order */
1506 if (g.spl_sortmode == SORTBY_LETTER /* default */
1507 || g.spl_sortmode == SORTRETAINORDER)
1508 return;
1509 /* allocate enough for full spellbook rather than just N spells */
1510 g.spl_orderindx = (int *) alloc(MAXSPELL * sizeof(int));
1511 for (i = 0; i < MAXSPELL; i++)
1512 g.spl_orderindx[i] = i;
1513 }
1514
1515 if (g.spl_sortmode == SORTRETAINORDER) {
1516 struct spell tmp_book[MAXSPELL];
1517
1518 /* sort g.spl_book[] rather than spl_orderindx[];
1519 this also updates the index to reflect the new ordering (we
1520 could just free it since that ordering becomes the default) */
1521 for (i = 0; i < MAXSPELL; i++)
1522 tmp_book[i] = g.spl_book[g.spl_orderindx[i]];
1523 for (i = 0; i < MAXSPELL; i++)
1524 g.spl_book[i] = tmp_book[i], g.spl_orderindx[i] = i;
1525 g.spl_sortmode = SORTBY_LETTER; /* reset */
1526 return;
1527 }
1528
1529 /* usual case, sort the index rather than the spells themselves */
1530 qsort((genericptr_t) g.spl_orderindx, n, sizeof *g.spl_orderindx, spell_cmp);
1531 return;
1532 }
1533
1534 /* called if the [sort spells] entry in the view spells menu gets chosen */
1535 static boolean
spellsortmenu(void)1536 spellsortmenu(void)
1537 {
1538 winid tmpwin;
1539 menu_item *selected;
1540 anything any;
1541 char let;
1542 int i, n, choice;
1543
1544 tmpwin = create_nhwindow(NHW_MENU);
1545 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
1546 any = cg.zeroany; /* zero out all bits */
1547
1548 for (i = 0; i < SIZE(spl_sortchoices); i++) {
1549 if (i == SORTRETAINORDER) {
1550 let = 'z'; /* assumes fewer than 26 sort choices... */
1551 /* separate final choice from others with a blank line */
1552 any.a_int = 0;
1553 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
1554 ATR_NONE, "", MENU_ITEMFLAGS_NONE);
1555 } else {
1556 let = 'a' + i;
1557 }
1558 any.a_int = i + 1;
1559 add_menu(tmpwin, &nul_glyphinfo, &any, let, 0,
1560 ATR_NONE, spl_sortchoices[i],
1561 (i == g.spl_sortmode) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
1562 }
1563 end_menu(tmpwin, "View known spells list sorted");
1564
1565 n = select_menu(tmpwin, PICK_ONE, &selected);
1566 destroy_nhwindow(tmpwin);
1567 if (n > 0) {
1568 choice = selected[0].item.a_int - 1;
1569 /* skip preselected entry if we have more than one item chosen */
1570 if (n > 1 && choice == g.spl_sortmode)
1571 choice = selected[1].item.a_int - 1;
1572 free((genericptr_t) selected);
1573 g.spl_sortmode = choice;
1574 return TRUE;
1575 }
1576 return FALSE;
1577 }
1578
1579 /* the '+' command -- view known spells */
1580 int
dovspell(void)1581 dovspell(void)
1582 {
1583 char qbuf[QBUFSZ];
1584 int splnum, othnum;
1585 struct spell spl_tmp;
1586
1587 if (spellid(0) == NO_SPELL) {
1588 You("don't know any spells right now.");
1589 } else {
1590 while (dospellmenu("Currently known spells",
1591 SPELLMENU_VIEW, &splnum)) {
1592 if (splnum == SPELLMENU_SORT) {
1593 if (spellsortmenu())
1594 sortspells();
1595 } else {
1596 Sprintf(qbuf, "Reordering spells; swap '%c' with",
1597 spellet(splnum));
1598 if (!dospellmenu(qbuf, splnum, &othnum))
1599 break;
1600
1601 spl_tmp = g.spl_book[splnum];
1602 g.spl_book[splnum] = g.spl_book[othnum];
1603 g.spl_book[othnum] = spl_tmp;
1604 }
1605 }
1606 }
1607 if (g.spl_orderindx) {
1608 free((genericptr_t) g.spl_orderindx);
1609 g.spl_orderindx = 0;
1610 }
1611 g.spl_sortmode = SORTBY_LETTER; /* 0 */
1612 return 0;
1613 }
1614
1615 DISABLE_WARNING_FORMAT_NONLITERAL
1616
1617 static boolean
dospellmenu(const char * prompt,int splaction,int * spell_no)1618 dospellmenu(
1619 const char *prompt,
1620 int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, or g.spl_book[] index */
1621 int *spell_no)
1622 {
1623 winid tmpwin;
1624 int i, n, how, splnum;
1625 char buf[BUFSZ], pw_buf[5];
1626 const char *fmt;
1627 menu_item *selected;
1628 anything any;
1629
1630 tmpwin = create_nhwindow(NHW_MENU);
1631 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
1632 any = cg.zeroany; /* zero out all bits */
1633
1634 /*
1635 * The correct spacing of the columns when not using
1636 * tab separation depends on the following:
1637 * (1) that the font is monospaced, and
1638 * (2) that selection letters are pre-pended to the
1639 * given string and are of the form "a - ".
1640 */
1641 if (!iflags.menu_tab_sep) {
1642 Sprintf(buf, "%-20s Level %-12s Pw Retention", " Name",
1643 "Category");
1644 fmt = "%-20s %2d %-12s %4s %5d";
1645 } else {
1646 Sprintf(buf, "Name\tLevel\tCategory\tPw\tRetention");
1647 fmt = "%s\t%-d\t%s\t%s\t%d";
1648 }
1649 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
1650 iflags.menu_headings, buf, MENU_ITEMFLAGS_NONE);
1651 for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) {
1652 splnum = !g.spl_orderindx ? i : g.spl_orderindx[i];
1653 if (energy_cost(splnum) < 0) {
1654 strcpy(pw_buf, "Inf");
1655 } else {
1656 /* maximum possible should be 3500 */
1657 Sprintf(pw_buf, "%d", energy_cost(splnum));
1658 }
1659 Sprintf(buf, fmt, spellname(splnum), spellev(splnum),
1660 spelltypemnemonic(spell_skilltype(spellid(splnum))),
1661 pw_buf, spellknow(splnum));
1662
1663 any.a_int = splnum + 1; /* must be non-zero */
1664 add_menu(tmpwin, &nul_glyphinfo, &any, spellet(splnum), 0,
1665 ATR_NONE, buf,
1666 (splnum == splaction)
1667 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
1668 }
1669 how = PICK_ONE;
1670 if (splaction == SPELLMENU_VIEW) {
1671 if (spellid(1) == NO_SPELL) {
1672 /* only one spell => nothing to swap with */
1673 how = PICK_NONE;
1674 } else {
1675 /* more than 1 spell, add an extra menu entry */
1676 any.a_int = SPELLMENU_SORT + 1;
1677 add_menu(tmpwin, &nul_glyphinfo, &any, '+', 0,
1678 ATR_NONE, "[sort spells]", MENU_ITEMFLAGS_NONE);
1679 }
1680 }
1681 end_menu(tmpwin, prompt);
1682
1683 n = select_menu(tmpwin, how, &selected);
1684 destroy_nhwindow(tmpwin);
1685 if (n > 0) {
1686 *spell_no = selected[0].item.a_int - 1;
1687 /* menu selection for `PICK_ONE' does not
1688 de-select any preselected entry */
1689 if (n > 1 && *spell_no == splaction)
1690 *spell_no = selected[1].item.a_int - 1;
1691 free((genericptr_t) selected);
1692 /* default selection of preselected spell means that
1693 user chose not to swap it with anything */
1694 if (*spell_no == splaction)
1695 return FALSE;
1696 return TRUE;
1697 } else if (splaction >= 0) {
1698 /* explicit de-selection of preselected spell means that
1699 user is still swapping but not for the current spell */
1700 *spell_no = splaction;
1701 return TRUE;
1702 }
1703 return FALSE;
1704 }
1705
1706 RESTORE_WARNING_FORMAT_NONLITERAL
1707
1708 struct spellwand {
1709 short spell;
1710 short wand; /* both of these are otyps */
1711 };
1712 static const struct spellwand wand_combos[] = {
1713 { SPE_LIGHT, WAN_LIGHT },
1714 { SPE_FIREBALL, WAN_FIRE },
1715 { SPE_CONE_OF_COLD, WAN_COLD },
1716 { SPE_CANCELLATION, WAN_CANCELLATION },
1717 { SPE_FINGER_OF_DEATH, WAN_DEATH },
1718 { SPE_SLEEP, WAN_SLEEP },
1719 { SPE_DIG, WAN_DIGGING },
1720 { SPE_KNOCK, WAN_OPENING },
1721 { SPE_WIZARD_LOCK, WAN_LOCKING },
1722 { SPE_DETECT_UNSEEN, WAN_SECRET_DOOR_DETECTION },
1723 { SPE_MAGIC_MISSILE, WAN_MAGIC_MISSILE },
1724 { SPE_INVISIBILITY, WAN_MAKE_INVISIBLE },
1725 { SPE_SLOW_MONSTER, WAN_SLOW_MONSTER },
1726 { SPE_HASTE_SELF, WAN_SPEED_MONSTER },
1727 { SPE_FORCE_BOLT, WAN_STRIKING },
1728 { SPE_TURN_UNDEAD, WAN_UNDEAD_TURNING },
1729 { SPE_CREATE_MONSTER, WAN_CREATE_MONSTER },
1730 { SPE_POLYMORPH, WAN_POLYMORPH },
1731 { SPE_TELEPORT_AWAY, WAN_TELEPORTATION },
1732 { 0, 0}
1733 };
1734
1735 /* Compute a percentage chance of a spell succeeding, based on skill,
1736 * Intelligence, XL, armor getting in the way, the spell level, and base role
1737 * spellcasting ability.
1738 * In xNetHack this number isn't actually used directly, but rather influences
1739 * how much extra Pw a difficult spell will take to cast.
1740 * Original formula by FIQ, with some modifications. */
1741 static int
percent_success(int spell)1742 percent_success(int spell)
1743 {
1744 int chance;
1745 int skill;
1746 int cap;
1747
1748 /* Calculate effective Int: may be boosted by certain items */
1749 unsigned char intel = ACURR(A_INT);
1750
1751 boolean wield_wand_bonus, wield_book_bonus, gear_bonus;
1752 wield_wand_bonus = wield_book_bonus = gear_bonus = FALSE;
1753
1754 if (uwep && uwep->oclass == WAND_CLASS && uwep->otyp != WAN_NOTHING) {
1755 /* can get a boost from a wand whose magic is similar
1756 * however, you need to have formally identified it, otherwise you can
1757 * cheese wand ID by wielding different wands and seeing if this stat
1758 * changes */
1759 int i = 0;
1760 for (i = 0; wand_combos[i].spell != 0; ++i) {
1761 if (spellid(spell) == wand_combos[i].spell
1762 && uwep->otyp == wand_combos[i].wand
1763 && objects[uwep->otyp].oc_name_known) {
1764 wield_wand_bonus = TRUE;
1765 break;
1766 }
1767 }
1768 }
1769 /* can't be an else if - otherwise, wielding an unidentified non-nothing
1770 * wand will prevent this bonus from being assessed */
1771 if (uwep && uwep->oclass == SPBOOK_CLASS && uwep->otyp == spellid(spell)) {
1772 /* Wielding the spellbook containing the spell that we're trying to cast
1773 * gives the same benefit as the wand.
1774 * No check for spellbook identification here, largely because if the
1775 * hero knows a spell they should know its corresponding book's
1776 * appearance. */
1777 wield_book_bonus = TRUE;
1778 }
1779 if (uarmc && uarmc->otyp == ROBE) {
1780 gear_bonus = TRUE;
1781 }
1782 if (uwep && (uwep->otyp == QUARTERSTAFF || uwep->otyp == WAN_NOTHING)) {
1783 gear_bonus = TRUE;
1784 }
1785
1786 if (wield_wand_bonus) {
1787 intel += 7;
1788 }
1789 else if (gear_bonus) {
1790 intel += 5;
1791 }
1792
1793 /* Don't get over-powerful with these boosts */
1794 if (intel >= 20) {
1795 intel = 20;
1796 }
1797
1798 /* At base, chance is your base role spellcasting ability. */
1799 chance = g.urole.spelbase;
1800
1801 /* Int and XL increase this. */
1802 chance += (intel * 5) + (u.ulevel * 5);
1803
1804 /* Higher level spells will reduce this chance, though. */
1805 chance -= 25 * spellev(spell);
1806
1807 /* Calculate penalty from armor. Metal armor and shields hurt chance. */
1808 if (uarm && is_metallic(uarm)) {
1809 chance -= 50;
1810 }
1811 if (uarms && !is_quest_artifact(uarms)) {
1812 /* quest artifact check allows Archeologists to wear Itlachiayaque
1813 * without penalty; other roles get normal penalty */
1814 if (objects[uarms->otyp].oc_bulky)
1815 chance -= 30;
1816 else
1817 chance -= 15;
1818
1819 if (is_metallic(uarms))
1820 chance -= 15;
1821 }
1822 if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE) {
1823 chance -= 20;
1824 }
1825 if (uarmg && is_metallic(uarmg)) {
1826 chance -= 35;
1827 }
1828 if (uarmf && is_metallic(uarmf)) {
1829 chance -= 10;
1830 }
1831
1832 /* The less skilled you are, the worse the cap on your spellcasting ability. */
1833 cap = 30; /* restricted */
1834 skill = P_SKILL(spell_skilltype(spellid(spell)));
1835 if (skill == P_EXPERT)
1836 cap = 100;
1837 else if (skill == P_SKILLED)
1838 cap = 80;
1839 else if (skill == P_BASIC)
1840 cap = 60;
1841 else if (skill == P_UNSKILLED)
1842 cap = 40;
1843
1844 /* Clamp to percentile */
1845 if (chance > cap)
1846 chance = cap;
1847 if (chance < 0)
1848 chance = 0;
1849
1850 return chance;
1851 }
1852
1853 /* Return the amount of energy a spell will take to cast.
1854 Spells can no longer fail. Instead, the percent_success() function is used
1855 to increase the required energy of the spell, so a spell with a 100% success
1856 chance costs the same as always, whereas a spell with a 50% success chance
1857 costs twice as much Pw, and a spell with a 1% success chance costs 100 times
1858 as much Pw.
1859 Return -1 if the success rate would be 0 and the spell cannot be cast.
1860 */
1861 static int
energy_cost(int spell)1862 energy_cost(int spell)
1863 {
1864 int base_energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */
1865 int energy = base_energy;
1866 int old_success_rate = percent_success(spell);
1867
1868 if (old_success_rate == 0) {
1869 /* With a 0% success chance, the spell should take infinite power to
1870 * cast, and is thus still uncastable. However, this should work well
1871 * enough to prevent it from being cast. */
1872 return -1;
1873 } else {
1874 energy = (energy * 100) / old_success_rate;
1875 }
1876
1877 /* If currently wielding the spellbook containing the spell that we're
1878 * trying to cast, reduce the Pw cost of casting by half rounded up.
1879 * But don't reduce further than the innate base amount of power the spell
1880 * normally takes to cast. */
1881 if (uwep && uwep->otyp == spellid(spell)) {
1882 int half_energy = (energy + 1) / 2;
1883 if (half_energy < base_energy)
1884 half_energy = base_energy;
1885 energy = half_energy;
1886 }
1887
1888 return energy;
1889 }
1890
1891 /* Learn a spell during creation of the initial inventory */
1892 void
initialspell(struct obj * obj)1893 initialspell(struct obj* obj)
1894 {
1895 int i, otyp = obj->otyp;
1896
1897 for (i = 0; i < MAXSPELL; i++)
1898 if (spellid(i) == NO_SPELL || spellid(i) == otyp)
1899 break;
1900
1901 if (i == MAXSPELL) {
1902 impossible("Too many spells memorized!");
1903 } else if (spellid(i) != NO_SPELL) {
1904 /* initial inventory shouldn't contain duplicate spellbooks */
1905 impossible("Spell %s already known.", OBJ_NAME(objects[otyp]));
1906 } else {
1907 g.spl_book[i].sp_id = otyp;
1908 g.spl_book[i].sp_lev = objects[otyp].oc_level;
1909 incrnknow(i, 0);
1910 }
1911 return;
1912 }
1913
1914 /*spell.c*/
1915