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