Wikia

Wikihack

Source:Spell.c

2,032pages on
this wiki
Talk0

In the vanilla 3.4.3 source code, the file spell.c contains code to handle spells and spellbooks.

TODO: check if the annotated values derived from "rnd" calls are correct.

Below is the full text to src/spell.c from NetHack 3.4.3. To link to a particular line, write [[spell.c#line123|spell.c#line123]], for example.

Top of file Edit

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.    
The NetHack General Public License applies to screenshots, source code and other content from NetHack.
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.    

TODO: check what is "xscribed" in this context.

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.   

This sets up a bunch of macros. Most of these are simply programming shortcuts for this file. When you successfully read a spellbook, your knowledge is set to KEEN. Here, KEEN is 20000. This means that your spell knowledge will last for 20000 turns.

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.   

Now are the function declarations.

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.   

These macros will appear in the code that controls how the wearing of metal armor impacts the ability to succeed in casting a spell.

90.   /* since the spellbook itself doesn't blow up, don't say just "explodes" */
91.   static const char explodes[] = "radiates explosive energy";
92.   

The variable explodes is a constant C string. Farther down in the code are printf-style constructs like "The book %s!", explodes to message the player that the book is exploding. However, as this comment reveals, the spellbooks do not actually explode, they only "radiate explosive energy".

spell_let_to_idx Edit

93.   /* convert a letter into a number in the range 0..51, or -1 if not a letter */
94.   STATIC_OVL int
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.  

cursed_book Edit

The cursed_book function randomly decides what bad things will happen when you try to read a spellbook that's either too hard or cursed.

The function receives an object pointer bp to a spellbook. It decides what harm the spellbook will cause, implements the harm, and decides whether the spellbook destroyed itself in the process. The function that called cursed_book must do the actual process of destroying the spellbook, if necessary.

cursed_book is called in two places: study_book, which handles a player who begins to read a book (which might be hard to read or cursed), and learn, which handles a player who is in the middle of reading a book (which might have become cursed after they began reading).

107.  /* TRUE: book should be destroyed by caller */
108.  STATIC_OVL boolean
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;

A common effect of reading a cursed spellbook is the "wrenching sensation", which causes the reader to teleport. When you need to escape, and you do not have a scroll of teleportation, wand of teleportation or other method to teleport, reading a low-level cursed spellbook might save you.

119.  	case 1:
120.  		You_feel("threatened.");
121.  		aggravate();
122.  		break;

This is one of several ways to aggravate the monsters around you. This has some effects, for example monsters which were peaceful will now attack you.

123.  	case 2:
124.  		make_blinded(Blinded + rn1(100,250),TRUE);
125.  		break;

No yellow light around? Then note that sometimes, a peek into a cursed spellbook will harm you so much as to blind you for 100 to 250 turns! Because of the Blinded + part, if you are already blind, then you will remain blind for 100 to 250 more turns. (Ignore that there seems to be no way to read a spellbook if you are already blind.)

126.  	case 3:
127.  		take_gold();
128.  		break;

This is annoying. The code is in another function somewhere, but when it runs, your purse empties completely. Remember this: reading a cursed spellbook of level 3 or greater could cost you all of your gold.

129.  	case 4:
130.  		pline("These runes were just too much to comprehend.");
131.  		make_confused(HConfusion + rn1(7,16),FALSE);
132.  		break;

Confusion for only 7 to 16 turns does not seem so bad, when compared to the blindness under cased 2.

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;

Sometimes, when reading a cursed spellbook, it will randomly seem to be poisoned. This poison has harmful effects in addition to the usual loss of strength and health. If you have poison resistance, eat as many poisonous corpses as you want, but beware of contact-poisoned spellbooks; poison resistance will reduce but not prevent harm.

  • uarmg refers to your gloves. Poison, it seems, does not only reduce strength and harm health, but it in this case it apparently also corrodes your gloves. (Note: in obj.h each object has two types of erosion, uarmg->oeroded and uarmg->oeroded2; this code uses the latter.)
  • In the call to losestr, you have the potential to lose up to 4 points of strength, or up to 2 points if you have poison resistance.
  • As with any call to losehp, this call might cause death. In this case, the cause is "contact-poisoned spellbook"; this is the only way in NetHack to have a contact-poisoned spellbook kill your player. You lose 10 hp (which is quite bad for low-level players), or 6 hp if you have poison resistance.
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;

It is a magical explosion! The book "explodes", but remember that explodes is actually defined at the top of this source file, so the book "radiates explosive energy".

The good news is that magic resistance is effective. The same thing that guards you against magic missiles and magic traps is also good for magic spellbooks. If you lack that resistance, then you can lose up to 105 hp. (Ouch!)

170.  	default:
171.  		rndcurse();
172.  		break;
173.  	}
174.  	return FALSE;
175.  }
176.  

rndcurse curses few items in inventory randomly.

confused_book Edit

177.  /* study while confused: returns TRUE if the book is destroyed */
178.  STATIC_OVL boolean
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.  

dead_book Edit

202.  /* special effects for The Book of the Dead */
203.  STATIC_OVL void
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.  

learn Edit

315.  STATIC_PTR int
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.  

study_book Edit

389.  int
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.  

book_disappears Edit

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
500.  book_disappears(obj)
501.  struct obj *obj;
502.  {
503.  	if (obj == book) book = (struct obj *)0;
504.  }
505.  

book_substitution Edit

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
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.  

age_spells Edit

516.  /* called from moveloop() */
517.  void
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.  

getspell Edit

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
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.  

docast Edit

579.  /* the 'Z' command -- cast a spell */
580.  int
581.  docast()
582.  {
583.  	int spell_no;
584.  
585.  	if (getspell(&spell_no))
586.  	    return spelleffects(spell_no, FALSE);
587.  	return 0;
588.  }
589.  

spelltypemnemonic Edit

590.  STATIC_OVL const char *
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.  

spell_skilltype Edit

615.  int
616.  spell_skilltype(booktype)
617.  int booktype;
618.  {
619.  	return (objects[booktype].oc_skill);
620.  }
621.  

cast_protection Edit

622.  STATIC_OVL void
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.  

spell_backfire Edit

683.  /* attempting to cast a forgotten spell will cause disorientation */
684.  STATIC_OVL void
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.  

spelleffects Edit

712.  int
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.  

throwspell Edit

966.  /* Choose location where spell takes effect. */
967.  STATIC_OVL int
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. 

losespells Edit

1003. void
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. 

dovspell Edit

1022. /* the '+' command -- view known spells */
1023. int
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. 

dospellmenu Edit

1047. STATIC_OVL boolean
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. 

isqrt Edit

1117. /* Integer square root function without using floating point. */
1118. STATIC_OVL int
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. 

percent_success Edit

1132. STATIC_OVL int
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. 

initialspell Edit

1240. /* Learn a spell during creation of the initial inventory */
1241. void
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*/

Around Wikia's network

Random Wiki