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