Wikihack
Advertisement

Below is the full text to spell.c from the source code of SLASH'EM 0.0.7E7F2. To link to a particular line, write [[SLASH'EM 0.0.7E7F2/spell.c#line123]], for example.

The latest source code for vanilla NetHack is at Source code.


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