Difference between revisions of "Source:NetHack 3.4.3/src/spell.c"

From NetHackWiki
Jump to navigation Jump to search
(Upload some annotation (mostly for cursed_book, other sections are empty).)
 
(fix some numbers)
 
(15 intermediate revisions by 9 users not shown)
Line 1: Line 1:
The file '''spell.c''' contains code to handle [[spell]]s and [[spellbooks]].
+
__MIXEDSYNTAXHIGHLIGHT__
 +
In the [[vanilla]] 3.4.3 [[source code]], the file '''spell.c''' contains code to handle [[spell]]s and [[spellbooks]].
  
 
TODO: check if the annotated values derived from "rnd" calls are correct.
 
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 ===
 
=== Top of file ===
<pre>
+
<span id="line1">1.    /* SCCS Id: @(#)spell.c 3.4 2003/01/17 */</span>
/* SCCS Id: @(#)spell.c 3.4 2003/01/17 */
+
<span id="line2">2.    /* Copyright (c) M. Stephenson 1988   */</span>
/* Copyright (c) M. Stephenson 1988   */
+
<span id="line3">3.    /* NetHack may be freely redistributed.  See license for details. */</span>
/* NetHack may be freely redistributed.  See license for details. */
+
<span id="line4">4.    </span>
</pre>
 
  
 
{{NGPL}}
 
{{NGPL}}
  
  #include "[[hack.h]]"
+
  <span id="line5">5.    #include "hack.h"</span>
   
+
  <span id="line6">6.    </span>
  static NEARDATA schar delay; /* moves left for this spell */
+
  <span id="line7">7.    static NEARDATA schar delay; /* moves left for this spell */</span>
  static NEARDATA struct obj *book; /* last/current book being xscribed */
+
  <span id="line8">8.    static NEARDATA struct obj *book; /* last/current book being xscribed */</span>
 +
<span id="line9">9.    </span>
  
 
TODO: check what is "xscribed" in this context.
 
TODO: check what is "xscribed" in this context.
  
<pre>
+
<span id="line10">10.  /* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */</span>
/* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */
+
<span id="line11">11.  #define SPELLMENU_CAST (-2)</span>
#define SPELLMENU_CAST (-2)
+
<span id="line12">12.  #define SPELLMENU_VIEW (-1)</span>
#define SPELLMENU_VIEW (-1)
+
<span id="line13">13.  </span>
 +
<span id="line14">14.  #define KEEN 20000</span>
 +
<span id="line15">15.  #define MAX_SPELL_STUDY 3</span>
 +
<span id="line16">16.  #define incrnknow(spell)        spl_book[spell].sp_know = KEEN</span>
 +
<span id="line17">17.  </span>
 +
<span id="line18">18.  #define spellev(spell) spl_book[spell].sp_lev</span>
 +
<span id="line19">19.  #define spellname(spell) OBJ_NAME(objects[spellid(spell)])</span>
 +
<span id="line20">20.  #define spellet(spell) \</span>
 +
<span id="line21">21.  ((char)((spell < 26) ? ('a' + spell) : ('A' + spell - 26)))</span>
 +
<span id="line22">22.  </span>
  
#define KEEN 20000
+
This sets up a bunch of [[macro]]s. 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.
#define MAX_SPELL_STUDY 3
 
#define incrnknow(spell)        spl_book[spell].sp_know = KEEN
 
  
#define spellev(spell) spl_book[spell].sp_lev
+
<span id="line23">23.   STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P));</span>
#define spellname(spell) OBJ_NAME(objects[spellid(spell)])
+
  <span id="line24">24.  STATIC_DCL boolean FDECL(cursed_book, (struct obj *bp));</span>
#define spellet(spell) \
+
  <span id="line25">25.  STATIC_DCL boolean FDECL(confused_book, (struct obj *));</span>
((char)((spell < 26) ? ('a' + spell) : ('A' + spell - 26)))
+
  <span id="line26">26.  STATIC_DCL void FDECL(deadbook, (struct obj *));</span>
</pre>
+
  <span id="line27">27.  STATIC_PTR int NDECL(learn);</span>
 
+
  <span id="line28">28.  STATIC_DCL boolean FDECL(getspell, (int *));</span>
This sets up a bunch of [[macro]]s. Most of these are simply programming shortcuts for this file. When [[you]] successfully [[read]] a [[spellbook]], your knowledge is set to KEEN. Here, KEENn is 20000. This means that your spell knowledge will last for 20000 turns.
+
  <span id="line29">29.  STATIC_DCL boolean FDECL(dospellmenu, (const char *,int,int *));</span>
 
+
  <span id="line30">30.  STATIC_DCL int FDECL(percent_success, (int));</span>
STATIC_DCL int FDECL([[#spell_let_to_idx|spell_let_to_idx]], (CHAR_P));
+
  <span id="line31">31.  STATIC_DCL int NDECL(throwspell);</span>
  STATIC_DCL boolean FDECL([[#cursed_book|cursed_book]], (struct obj *bp));
+
  <span id="line32">32.  STATIC_DCL void NDECL(cast_protection);</span>
  STATIC_DCL boolean FDECL([[#confused_book|confused_book]], (struct obj *));
+
  <span id="line33">33.  STATIC_DCL void FDECL(spell_backfire, (int));</span>
  STATIC_DCL void FDECL([[#deadbook|deadbook]], (struct obj *));
+
  <span id="line34">34.  STATIC_DCL const char *FDECL(spelltypemnemonic, (int));</span>
  STATIC_PTR int NDECL([[#learn|learn]]);
+
  <span id="line35">35.  STATIC_DCL int FDECL(isqrt, (int));</span>
  STATIC_DCL boolean FDECL([[#getspell|getspell]], (int *));
+
<span id="line36">36.  </span>
  STATIC_DCL boolean FDECL([[#dospellmenu|dospellmenu]], (const char *,int,int *));
 
  STATIC_DCL int FDECL([[#percent_success|percent_success]], (int));
 
  STATIC_DCL int NDECL([[#throwspell|throwspell]]);
 
  STATIC_DCL void NDECL([[#cast_protection|cast_protection]]);
 
  STATIC_DCL void FDECL([[#spell_backfire|spell_backfire]], (int));
 
  STATIC_DCL const char *FDECL([[#spelltypemnemonic|spelltypemnemonic]], (int));
 
  STATIC_DCL int FDECL([[#isqrt|isqrt]], (int));
 
  
 
Now are the [[function]] declarations.
 
Now are the [[function]] declarations.
  
: ''[After the function declarations is a long spoiler comment about the ability of different [[role]]s to cast spells. It reveals a spell that each [[role]] can cast well.]''
+
<span id="line37">37.  /* The roles[] table lists the role-specific values for tuning</span>
 
+
<span id="line38">38.    * percent_success().</span>
<pre>
+
<span id="line39">39.    *</span>
#define uarmhbon 4 /* Metal helmets interfere with the mind */
+
<span id="line40">40.    * Reasoning:</span>
#define uarmgbon 6 /* Casting channels through the hands */
+
<span id="line41">41.    *  spelbase, spelheal:</span>
#define uarmfbon 2 /* All metal interferes to some degree */
+
<span id="line42">42.    * Arc are aware of magic through historical research</span>
</pre>
+
<span id="line43">43.    * Bar abhor magic (Conan finds it "interferes with his animal instincts")</span>
 +
<span id="line44">44.    * Cav are ignorant to magic</span>
 +
<span id="line45">45.    * Hea are very aware of healing magic through medical research</span>
 +
<span id="line46">46.    * Kni are moderately aware of healing from Paladin training</span>
 +
<span id="line47">47.    * Mon use magic to attack and defend in lieu of weapons and armor</span>
 +
<span id="line48">48.    * Pri are very aware of healing magic through theological research</span>
 +
<span id="line49">49.    * Ran avoid magic, preferring to fight unseen and unheard</span>
 +
<span id="line50">50.    * Rog are moderately aware of magic through trickery</span>
 +
<span id="line51">51.    * Sam have limited magical awareness, prefering meditation to conjuring</span>
 +
<span id="line52">52.    * Tou are aware of magic from all the great films they have seen</span>
 +
<span id="line53">53.    * Val have limited magical awareness, prefering fighting</span>
 +
<span id="line54">54.    * Wiz are trained mages</span>
 +
<span id="line55">55.    *</span>
 +
<span id="line56">56.    * The arms penalty is lessened for trained fighters Bar, Kni, Ran,</span>
 +
<span id="line57">57.    * Sam, Val -</span>
 +
<span id="line58">58.    * the penalty is its metal interference, not encumbrance.</span>
 +
<span id="line59">59.    * The `spelspec' is a single spell which is fundamentally easier</span>
 +
<span id="line60">60.    * for that role to cast.</span>
 +
<span id="line61">61.    *</span>
 +
<span id="line62">62.    *  spelspec, spelsbon:</span>
 +
<span id="line63">63.    * Arc map masters (SPE_MAGIC_MAPPING)</span>
 +
<span id="line64">64.    * Bar fugue/berserker (SPE_HASTE_SELF)</span>
 +
<span id="line65">65.    * Cav born to dig (SPE_DIG)</span>
 +
<span id="line66">66.    * Hea to heal (SPE_CURE_SICKNESS)</span>
 +
<span id="line67">67.    * Kni to turn back evil (SPE_TURN_UNDEAD)</span>
 +
<span id="line68">68.    * Mon to preserve their abilities (SPE_RESTORE_ABILITY)</span>
 +
<span id="line69">69.    * Pri to bless (SPE_REMOVE_CURSE)</span>
 +
<span id="line70">70.    * Ran to hide (SPE_INVISIBILITY)</span>
 +
<span id="line71">71.    * Rog to find loot (SPE_DETECT_TREASURE)</span>
 +
<span id="line72">72.    * Sam to be At One (SPE_CLAIRVOYANCE)</span>
 +
<span id="line73">73.    * Tou to smile (SPE_CHARM_MONSTER)</span>
 +
<span id="line74">74.    * Val control the cold (SPE_CONE_OF_COLD)</span>
 +
<span id="line75">75.    * Wiz all really, but SPE_MAGIC_MISSILE is their party trick</span>
 +
<span id="line76">76.    *</span>
 +
<span id="line77">77.    * See percent_success() below for more comments.</span>
 +
<span id="line78">78.    *</span>
 +
<span id="line79">79.    *  uarmbon, uarmsbon, uarmhbon, uarmgbon, uarmfbon:</span>
 +
<span id="line80">80.    * Fighters find body armour & shield a little less limiting.</span>
 +
<span id="line81">81.    * Headgear, Gauntlets and Footwear are not role-specific (but</span>
 +
<span id="line82">82.    * still have an effect, except helm of brilliance, which is designed</span>
 +
<span id="line83">83.    * to permit magic-use).</span>
 +
<span id="line84">84.    */</span>
 +
<span id="line85">85.  </span>
 +
<span id="line86">86.  #define uarmhbon 4 /* Metal helmets interfere with the mind */</span>
 +
<span id="line87">87.  #define uarmgbon 6 /* Casting channels through the hands */</span>
 +
<span id="line88">88.  #define uarmfbon 2 /* All metal interferes to some degree */</span>
 +
<span id="line89">89.  </span>
  
 
These [[macro]]s will appear in the code that controls how the wearing of metal [[armor]] impacts the ability to succeed in casting a spell.
 
These [[macro]]s will appear in the code that controls how the wearing of metal [[armor]] impacts the ability to succeed in casting a spell.
  
<pre>
+
<span id="line90">90.  /* since the spellbook itself doesn't blow up, don't say just "explodes" */</span>
/* since the spellbook itself doesn't blow up, don't say just "explodes" */
+
<span id="line91">91.  static const char explodes[] = "radiates explosive energy";</span>
static const char explodes[] = "radiates explosive energy";
+
<span id="line92">92.  </span>
</pre>
 
  
 
The variable <code>explodes</code> is a constant C string. Farther down in the code are printf-style constructs like <code>"The book %s!", explodes</code> 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".
 
The variable <code>explodes</code> is a constant C string. Farther down in the code are printf-style constructs like <code>"The book %s!", explodes</code> 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 ===
 
=== spell_let_to_idx ===
 +
 +
<span id="line93">93.  /* convert a letter into a number in the range 0..51, or -1 if not a letter */</span>
 +
<span id="line94">94.  STATIC_OVL int</span>
 +
<span id="line95">95.  spell_let_to_idx(ilet)</span>
 +
<span id="line96">96.  char ilet;</span>
 +
<span id="line97">97.  {</span>
 +
<span id="line98">98.      int indx;</span>
 +
<span id="line99">99.  </span>
 +
<span id="line100">100.      indx = ilet - 'a';</span>
 +
<span id="line101">101.      if (indx >= 0 && indx < 26) return indx;</span>
 +
<span id="line102">102.      indx = ilet - 'A';</span>
 +
<span id="line103">103.      if (indx >= 0 && indx < 26) return indx + 26;</span>
 +
<span id="line104">104.      return -1;</span>
 +
<span id="line105">105.  }</span>
 +
<span id="line106">106.  </span>
  
 
=== cursed_book ===
 
=== cursed_book ===
The ''cursed_book'' function randomly decides what bad will happen when you try to read a [[cursed]] [[spellbook]]. In general, never read a cursed spellbook, or always uncurse it before reading.
 
  
The function receives an [[object]] pointer <code>bp</code>, presumably a cursed 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 <code>cursed_book</code> must do the actual process of destroying the spellbook, if necessary.
+
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]].
  
<pre>
+
The function receives an [[object]] pointer <code>bp</code> 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 <code>cursed_book</code> must do the actual process of destroying the spellbook, if necessary.
/* TRUE: book should be destroyed by caller */
 
STATIC_OVL boolean
 
cursed_book(bp)
 
struct obj *bp;
 
{
 
int lev = objects[bp->otyp].oc_level;
 
  
switch(rn2(lev)) {
+
<code>cursed_book</code> is called in two places: <code>[[#study_book|study_book]]</code>, which handles a player who begins to read a book (which might be hard to read or cursed), and <code>[[#learn|learn]]</code>, which handles a player who is in the middle of reading a book (which might have become cursed after they began reading).
case 0:
 
You_feel("a wrenching sensation.");
 
tele(); /* teleport him */
 
break;
 
</pre>
 
  
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 teleport]], [[wand of teleport]] or other method to teleport, reading a low-level cursed spellbook might save you.
+
<span id="line107">107.  /* TRUE: book should be destroyed by caller */</span>
 +
<span id="line108">108.  STATIC_OVL boolean</span>
 +
<span id="line109">109.  cursed_book(bp)</span>
 +
<span id="line110">110.  struct obj *bp;</span>
 +
<span id="line111">111.  {</span>
 +
<span id="line112">112.  int lev = objects[bp->otyp].oc_level;</span>
 +
<span id="line113">113.  </span>
 +
<span id="line114">114.  switch(rn2(lev)) {</span>
 +
<span id="line115">115.  case 0:</span>
 +
<span id="line116">116.  You_feel("a wrenching sensation.");</span>
 +
<span id="line117">117.  tele(); /* teleport him */</span>
 +
<span id="line118">118. break;</span>
  
<pre>
+
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.
case 1:
 
You_feel("threatened.");
 
aggravate();
 
break;
 
</pre>
 
  
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.
+
<span id="line119">119.  case 1:</span>
 +
<span id="line120">120.  You_feel("threatened.");</span>
 +
<span id="line121">121.  aggravate();</span>
 +
<span id="line122">122. break;</span>
  
<pre>
+
This is one of several ways to [[aggravate]] the monsters around you. This has some effects, for example monsters which were sleeping or paralyzed will now attack you.
case 2:
 
make_blinded(Blinded + rn1(100,250),TRUE);
 
break;
 
</pre>
 
  
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 [[turn]]s! The <code>Blinded +</code> part is needed to add on the preexisting number of turns left if you are already blind.
+
<span id="line123">123.  case 2:</span>
 +
<span id="line124">124.  make_blinded(Blinded + rn1(100,250),TRUE);</span>
 +
<span id="line125">125. break;</span>
  
<pre>
+
No [[yellow light]] around? Then note that sometimes, a peek into a cursed spellbook will harm you so much as to [[blind]] you for 250 to 349 [[turn]]s! Because of the <code>Blinded +</code> part, if you are already blind, then you will remain blind for 250 to 349 more turns. (Ignore that there seems to be no way to read a spellbook if you are already blind.)
case 3:
+
 
take_gold();
+
<span id="line126">126.  case 3:</span>
break;
+
<span id="line127">127.  take_gold();</span>
</pre>
+
<span id="line128">128.  break;</span>
  
 
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]].
 
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]].
  
<pre>
+
<span id="line129">129.  case 4:</span>
case 4:
+
<span id="line130">130.  pline("These runes were just too much to comprehend.");</span>
pline("These runes were just too much to comprehend.");
+
<span id="line131">131.  make_confused(HConfusion + rn1(7,16),FALSE);</span>
make_confused(HConfusion + rn1(7,16),FALSE);
+
<span id="line132">132.  break;</span>
break;
 
</pre>
 
  
[[Confusion]] for only 7 to 16 turns does not seem so bad, when compared to the blindness under cased 2.
+
[[Confusion]] for only 16 to 22 turns does not seem so bad, when compared to the blindness under cased 2.
  
<pre>
+
<span id="line133">133.  case 5:</span>
case 5:
+
<span id="line134">134.  pline_The("book was coated with contact poison!");</span>
pline_The("book was coated with contact poison!");
+
<span id="line135">135.  if (uarmg) {</span>
if (uarmg) {
+
<span id="line136">136.      if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {</span>
    if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {
+
<span id="line137">137.  Your("gloves seem unaffected.");</span>
Your("gloves seem unaffected.");
+
<span id="line138">138.      } else if (uarmg->oeroded2 < MAX_ERODE) {</span>
    } else if (uarmg->oeroded2 < MAX_ERODE) {
+
<span id="line139">139.  if (uarmg->greased) {</span>
if (uarmg->greased) {
+
<span id="line140">140.      grease_protect(uarmg, "gloves", &youmonst);</span>
    grease_protect(uarmg, "gloves", &youmonst);
+
<span id="line141">141.  } else {</span>
} else {
+
<span id="line142">142.      Your("gloves corrode%s!",</span>
    Your("gloves corrode%s!",
+
<span id="line143">143.  uarmg->oeroded2+1 == MAX_ERODE ?</span>
uarmg->oeroded2+1 == MAX_ERODE ?
+
<span id="line144">144.  " completely" : uarmg->oeroded2 ?</span>
" completely" : uarmg->oeroded2 ?
+
<span id="line145">145.  " further" : "");</span>
" further" : "");
+
<span id="line146">146.      uarmg->oeroded2++;</span>
    uarmg->oeroded2++;
+
<span id="line147">147.  }</span>
}
+
<span id="line148">148.      } else</span>
    } else
+
<span id="line149">149.  Your("gloves %s completely corroded.",</span>
Your("gloves %s completely corroded.",
+
<span id="line150">150.      Blind ? "feel" : "look");</span>
    Blind ? "feel" : "look");
+
<span id="line151">151.      break;</span>
    break;
+
<span id="line152">152.  }</span>
}
+
<span id="line153">153.  /* temp disable in_use; death should not destroy the book */</span>
/* temp disable in_use; death should not destroy the book */
+
<span id="line154">154.  bp->in_use = FALSE;</span>
bp->in_use = FALSE;
+
<span id="line155">155.  losestr(Poison_resistance ? rn1(2,1) : rn1(4,3));</span>
losestr(Poison_resistance ? rn1(2,1) : rn1(4,3));
+
<span id="line156">156.  losehp(rnd(Poison_resistance ? 6 : 10),</span>
losehp(rnd(Poison_resistance ? 6 : 10),
+
<span id="line157">157.        "contact-poisoned spellbook", KILLED_BY_AN);</span>
      "contact-poisoned spellbook", KILLED_BY_AN);
+
<span id="line158">158.  bp->in_use = TRUE;</span>
bp->in_use = TRUE;
+
<span id="line159">159.  break;</span>
break;
 
</pre>
 
  
Sometimes, when reading a cursed spellbook, it will randomly seem to be poisoned. Further, in addition to the usual loss of [[strength]] and [[health]], this poison has additional harmful effects. If you have [[poison resistance]], eat as many poisonous [[corpse]]s as you want, but beware of contact-poisoned spellbooks; poison resistance will reduce but not prevent harm.
+
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 [[corpse]]s as you want, but beware of contact-poisoned spellbooks; poison resistance will reduce but not prevent harm.
 
* <code>uarmg</code> refers to [[you]]r [[glove]]s. 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]], <code>uarmg->oeroded</code> and <code>uarmg->oeroded2</code>; this code uses the latter.)
 
* <code>uarmg</code> refers to [[you]]r [[glove]]s. 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]], <code>uarmg->oeroded</code> and <code>uarmg->oeroded2</code>; this code uses the latter.)
* In the call to <code>losestr</code>, you have the potential to lose up to 4 points of [[strength]], or up to 2 points if you have poison resistance.
+
* In the call to <code>losestr</code>, you have the potential to lose up to 6 points of [[strength]], or up to 2 points if you have poison resistance.
* As with any call to <code>losehp</code>, this call might cause [[death]]. In this case, the cause is "[[contact-poisoned spellbook]]"; this appears to be 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.
+
* As with any call to <code>losehp</code>, this call might cause [[death (character)|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.
  
<pre>
+
<span id="line160">160.  case 6:</span>
case 6:
+
<span id="line161">161.  if(Antimagic) {</span>
if(Antimagic) {
+
<span id="line162">162.      shieldeff(u.ux, u.uy);</span>
    shieldeff(u.ux, u.uy);
+
<span id="line163">163.      pline_The("book %s, but you are unharmed!", explodes);</span>
    pline_The("book %s, but you are unharmed!", explodes);
+
<span id="line164">164.  } else {</span>
} else {
+
<span id="line165">165.      pline("As you read the book, it %s in your %s!",</span>
    pline("As you read the book, it %s in your %s!",
+
<span id="line166">166.    explodes, body_part(FACE));</span>
  explodes, body_part(FACE));
+
<span id="line167">167.      losehp(2*rnd(10)+5, "exploding rune", KILLED_BY_AN);</span>
    losehp(2*rnd(10)+5, "exploding rune", KILLED_BY_AN);
+
<span id="line168">168.  }</span>
}
+
<span id="line169">169.  return TRUE;</span>
return TRUE;
 
</pre>
 
  
 
It is a [[magic]]al explosion! The book "explodes", but remember that <tt>explodes</tt> is actually defined at the top of this source file, so the book "radiates explosive energy".
 
It is a [[magic]]al explosion! The book "explodes", but remember that <tt>explodes</tt> 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 that guards you against [[magic missile]]s and [[magic trap]]s is also good for magic spellbooks. If you lack that resistance, then you can lose up to 105 hp. (Ouch!)
+
The good news is that [[magic resistance]] is effective. The same thing that guards you against [[magic missile]]s and [[magic trap]]s is also good for magic spellbooks. If you lack that resistance, then you can lose up to 25 hp.
  
<pre>
+
<span id="line170">170.  default:</span>
default:
+
<span id="line171">171.  rndcurse();</span>
rndcurse();
+
<span id="line172">172.  break;</span>
break;
+
<span id="line173">173.  }</span>
}
+
<span id="line174">174.  return FALSE;</span>
return FALSE;
+
<span id="line175">175.  }</span>
}
+
<span id="line176">176.  </span>
</pre>
 
  
TODO: check what <code>rndcurse</code> does. Find the code, or maybe play [[wizard mode]] and order several cursed [[spellbook of identify|spellbooks of identify]].
+
<code>rndcurse</code> curses few items in inventory randomly.
  
 
=== confused_book ===
 
=== confused_book ===
  
=== deadbook ===
+
<span id="line177">177.  /* study while confused: returns TRUE if the book is destroyed */</span>
 +
<span id="line178">178.  STATIC_OVL boolean</span>
 +
<span id="line179">179.  confused_book(spellbook)</span>
 +
<span id="line180">180.  struct obj *spellbook;</span>
 +
<span id="line181">181.  {</span>
 +
<span id="line182">182.  boolean gone = FALSE;</span>
 +
<span id="line183">183.  </span>
 +
<span id="line184">184.  if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {</span>
 +
<span id="line185">185.      spellbook->in_use = TRUE; /* in case called from learn */</span>
 +
<span id="line186">186.      pline(</span>
 +
<span id="line187">187.  "Being confused you have difficulties in controlling your actions.");</span>
 +
<span id="line188">188.      display_nhwindow(WIN_MESSAGE, FALSE);</span>
 +
<span id="line189">189.      You("accidentally tear the spellbook to pieces.");</span>
 +
<span id="line190">190.      if (!objects[spellbook->otyp].oc_name_known &&</span>
 +
<span id="line191">191.  !objects[spellbook->otyp].oc_uname)</span>
 +
<span id="line192">192.  docall(spellbook);</span>
 +
<span id="line193">193.      useup(spellbook);</span>
 +
<span id="line194">194.      gone = TRUE;</span>
 +
<span id="line195">195.  } else {</span>
 +
<span id="line196">196.      You("find yourself reading the %s line over and over again.",</span>
 +
<span id="line197">197.  spellbook == book ? "next" : "first");</span>
 +
<span id="line198">198.  }</span>
 +
<span id="line199">199.  return gone;</span>
 +
<span id="line200">200.  }</span>
 +
<span id="line201">201.  </span>
 +
 
 +
== deadbook ==
 +
 
 +
<span id="line202">202.  /* special effects for The Book of the Dead */</span>
 +
<span id="line203">203.  STATIC_OVL void</span>
 +
<span id="line204">204.  deadbook(book2)</span>
 +
<span id="line205">205.  struct obj *book2;</span>
 +
<span id="line206">206.  {</span>
 +
<span id="line207">207.      struct monst *mtmp, *mtmp2;</span>
 +
<span id="line208">208.      coord mm;</span>
 +
<span id="line209">209.  </span>
 +
<span id="line210">210.      You("turn the pages of the Book of the Dead...");</span>
 +
<span id="line211">211.      makeknown(SPE_BOOK_OF_THE_DEAD);</span>
 +
<span id="line212">212.      /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */</span>
 +
<span id="line213">213.      book2->known = 1;</span>
 +
<span id="line214">214.      if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {</span>
 +
<span id="line215">215.  register struct obj *otmp;</span>
 +
<span id="line216">216.  register boolean arti1_primed = FALSE, arti2_primed = FALSE,</span>
 +
<span id="line217">217.  arti_cursed = FALSE;</span>
 +
<span id="line218">218.  </span>
 +
<span id="line219">219.  if(book2->cursed) {</span>
 +
<span id="line220">220.      pline_The("runes appear scrambled.  You can't read them!");</span>
 +
<span id="line221">221.      return;</span>
 +
<span id="line222">222.  }</span>
 +
<span id="line223">223.  </span>
 +
<span id="line224">224.  if(!u.uhave.bell || !u.uhave.menorah) {</span>
 +
<span id="line225">225.      pline("A chill runs down your %s.", body_part(SPINE));</span>
 +
<span id="line226">226.      if(!u.uhave.bell) You_hear("a faint chime...");</span>
 +
<span id="line227">227.      if(!u.uhave.menorah) pline("Vlad's doppelganger is amused.");</span>
 +
<span id="line228">228.      return;</span>
 +
<span id="line229">229.  }</span>
 +
<span id="line230">230.  </span>
 +
<span id="line231">231.  for(otmp = invent; otmp; otmp = otmp->nobj) {</span>
 +
<span id="line232">232.      if(otmp->otyp == CANDELABRUM_OF_INVOCATION &&</span>
 +
<span id="line233">233.        otmp->spe == 7 && otmp->lamplit) {</span>
 +
<span id="line234">234.  if(!otmp->cursed) arti1_primed = TRUE;</span>
 +
<span id="line235">235.  else arti_cursed = TRUE;</span>
 +
<span id="line236">236.      }</span>
 +
<span id="line237">237.      if(otmp->otyp == BELL_OF_OPENING &&</span>
 +
<span id="line238">238.        (moves - otmp->age) < 5L) { /* you rang it recently */</span>
 +
<span id="line239">239.  if(!otmp->cursed) arti2_primed = TRUE;</span>
 +
<span id="line240">240.  else arti_cursed = TRUE;</span>
 +
<span id="line241">241.      }</span>
 +
<span id="line242">242.  }</span>
 +
<span id="line243">243.  </span>
 +
<span id="line244">244.  if(arti_cursed) {</span>
 +
<span id="line245">245.      pline_The("invocation fails!");</span>
 +
<span id="line246">246.      pline("At least one of your artifacts is cursed...");</span>
 +
<span id="line247">247.  } else if(arti1_primed && arti2_primed) {</span>
 +
<span id="line248">248.      unsigned soon = (unsigned) d(2,6); /* time til next intervene() */</span>
 +
<span id="line249">249.  </span>
 +
<span id="line250">250.      /* successful invocation */</span>
 +
<span id="line251">251.      mkinvokearea();</span>
 +
<span id="line252">252.      u.uevent.invoked = 1;</span>
 +
<span id="line253">253.      /* in case you haven't killed the Wizard yet, behave as if</span>
 +
<span id="line254">254.        you just did */</span>
 +
<span id="line255">255.      u.uevent.udemigod = 1; /* wizdead() */</span>
 +
<span id="line256">256.      if (!u.udg_cnt || u.udg_cnt > soon) u.udg_cnt = soon;</span>
 +
<span id="line257">257.  } else { /* at least one artifact not prepared properly */</span>
 +
<span id="line258">258.      You("have a feeling that %s is amiss...", something);</span>
 +
<span id="line259">259.      goto raise_dead;</span>
 +
<span id="line260">260.  }</span>
 +
<span id="line261">261.  return;</span>
 +
<span id="line262">262.      }</span>
 +
<span id="line263">263.  </span>
 +
<span id="line264">264.      /* when not an invocation situation */</span>
 +
<span id="line265">265.      if (book2->cursed) {</span>
 +
<span id="line266">266.  raise_dead:</span>
 +
<span id="line267">267.  </span>
 +
<span id="line268">268.  You("raised the dead!");</span>
 +
<span id="line269">269.  /* first maybe place a dangerous adversary */</span>
 +
<span id="line270">270.  if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH],</span>
 +
<span id="line271">271.  u.ux, u.uy, NO_MINVENT)) != 0 ||</span>
 +
<span id="line272">272.  (mtmp = makemon(&mons[PM_NALFESHNEE],</span>
 +
<span id="line273">273.  u.ux, u.uy, NO_MINVENT)) != 0)) {</span>
 +
<span id="line274">274.      mtmp->mpeaceful = 0;</span>
 +
<span id="line275">275.      set_malign(mtmp);</span>
 +
<span id="line276">276.  }</span>
 +
<span id="line277">277.  /* next handle the affect on things you're carrying */</span>
 +
<span id="line278">278.  (void) unturn_dead(&youmonst);</span>
 +
<span id="line279">279.  /* last place some monsters around you */</span>
 +
<span id="line280">280.  mm.x = u.ux;</span>
 +
<span id="line281">281.  mm.y = u.uy;</span>
 +
<span id="line282">282.  mkundead(&mm, TRUE, NO_MINVENT);</span>
 +
<span id="line283">283.      } else if(book2->blessed) {</span>
 +
<span id="line284">284.  for(mtmp = fmon; mtmp; mtmp = mtmp2) {</span>
 +
<span id="line285">285.      mtmp2 = mtmp->nmon; /* tamedog() changes chain */</span>
 +
<span id="line286">286.      if (DEADMONSTER(mtmp)) continue;</span>
 +
<span id="line287">287.  </span>
 +
<span id="line288">288.      if (is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {</span>
 +
<span id="line289">289.  mtmp->mpeaceful = TRUE;</span>
 +
<span id="line290">290.  if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)</span>
 +
<span id="line291">291.    && distu(mtmp->mx, mtmp->my) < 4)</span>
 +
<span id="line292">292.      if (mtmp->mtame) {</span>
 +
<span id="line293">293.  if (mtmp->mtame < 20)</span>
 +
<span id="line294">294.      mtmp->mtame++;</span>
 +
<span id="line295">295.      } else</span>
 +
<span id="line296">296.  (void) tamedog(mtmp, (struct obj *)0);</span>
 +
<span id="line297">297.  else monflee(mtmp, 0, FALSE, TRUE);</span>
 +
<span id="line298">298.      }</span>
 +
<span id="line299">299.  }</span>
 +
<span id="line300">300.      } else {</span>
 +
<span id="line301">301.  switch(rn2(3)) {</span>
 +
<span id="line302">302.  case 0:</span>
 +
<span id="line303">303.      Your("ancestors are annoyed with you!");</span>
 +
<span id="line304">304.      break;</span>
 +
<span id="line305">305.  case 1:</span>
 +
<span id="line306">306.      pline_The("headstones in the cemetery begin to move!");</span>
 +
<span id="line307">307.      break;</span>
 +
<span id="line308">308.  default:</span>
 +
<span id="line309">309.      pline("Oh my!  Your name appears in the book!");</span>
 +
<span id="line310">310.  }</span>
 +
<span id="line311">311.      }</span>
 +
<span id="line312">312.      return;</span>
 +
<span id="line313">313.  }</span>
 +
<span id="line314">314.  </span>
 +
 
 +
== learn ==
 +
 
 +
<span id="line315">315.  STATIC_PTR int</span>
 +
<span id="line316">316.  learn()</span>
 +
<span id="line317">317.  {</span>
 +
<span id="line318">318.  int i;</span>
 +
<span id="line319">319.  short booktype;</span>
 +
<span id="line320">320.  char splname[BUFSZ];</span>
 +
<span id="line321">321.  boolean costly = TRUE;</span>
 +
<span id="line322">322.  </span>
 +
<span id="line323">323.  /* JDS: lenses give 50% faster reading; 33% smaller read time */</span>
 +
<span id="line324">324.  if (delay && ublindf && ublindf->otyp == LENSES && rn2(2)) delay++;</span>
 +
<span id="line325">325.  if (Confusion) { /* became confused while learning */</span>
 +
<span id="line326">326.      (void) confused_book(book);</span>
 +
<span id="line327">327.      book = 0; /* no longer studying */</span>
 +
<span id="line328">328.      nomul(delay); /* remaining delay is uninterrupted */</span>
 +
<span id="line329">329.      delay = 0;</span>
 +
<span id="line330">330.      return(0);</span>
 +
<span id="line331">331.  }</span>
 +
<span id="line332">332.  if (delay) { /* not if (delay++), so at end delay == 0 */</span>
 +
<span id="line333">333.      delay++;</span>
 +
<span id="line334">334.      return(1); /* still busy */</span>
 +
<span id="line335">335.  }</span>
 +
<span id="line336">336.  exercise(A_WIS, TRUE); /* you're studying. */</span>
 +
<span id="line337">337.  booktype = book->otyp;</span>
 +
<span id="line338">338.  if(booktype == SPE_BOOK_OF_THE_DEAD) {</span>
 +
<span id="line339">339.      deadbook(book);</span>
 +
<span id="line340">340.      return(0);</span>
 +
<span id="line341">341.  }</span>
 +
<span id="line342">342.  </span>
 +
<span id="line343">343.  Sprintf(splname, objects[booktype].oc_name_known ?</span>
 +
<span id="line344">344.  "\"%s\"" : "the \"%s\" spell",</span>
 +
<span id="line345">345.  OBJ_NAME(objects[booktype]));</span>
 +
<span id="line346">346.  for (i = 0; i < MAXSPELL; i++)  {</span>
 +
<span id="line347">347.  if (spellid(i) == booktype)  {</span>
 +
<span id="line348">348.  if (book->spestudied > MAX_SPELL_STUDY) {</span>
 +
<span id="line349">349.      pline("This spellbook is too faint to be read any more.");</span>
 +
<span id="line350">350.      book->otyp = booktype = SPE_BLANK_PAPER;</span>
 +
<span id="line351">351.  } else if (spellknow(i) <= 1000) {</span>
 +
<span id="line352">352.      Your("knowledge of %s is keener.", splname);</span>
 +
<span id="line353">353.      incrnknow(i);</span>
 +
<span id="line354">354.      book->spestudied++;</span>
 +
<span id="line355">355.      exercise(A_WIS,TRUE);      /* extra study */</span>
 +
<span id="line356">356.  } else { /* 1000 < spellknow(i) <= MAX_SPELL_STUDY */</span>
 +
<span id="line357">357.      You("know %s quite well already.", splname);</span>
 +
<span id="line358">358.      costly = FALSE;</span>
 +
<span id="line359">359.  }</span>
 +
<span id="line360">360.  /* make book become known even when spell is already</span>
 +
<span id="line361">361.    known, in case amnesia made you forget the book */</span>
 +
<span id="line362">362.  makeknown((int)booktype);</span>
 +
<span id="line363">363.  break;</span>
 +
<span id="line364">364.  } else if (spellid(i) == NO_SPELL)  {</span>
 +
<span id="line365">365.  spl_book[i].sp_id = booktype;</span>
 +
<span id="line366">366.  spl_book[i].sp_lev = objects[booktype].oc_level;</span>
 +
<span id="line367">367.  incrnknow(i);</span>
 +
<span id="line368">368.  book->spestudied++;</span>
 +
<span id="line369">369.  You(i > 0 ? "add %s to your repertoire." : "learn %s.",</span>
 +
<span id="line370">370.      splname);</span>
 +
<span id="line371">371.  makeknown((int)booktype);</span>
 +
<span id="line372">372.  break;</span>
 +
<span id="line373">373.  }</span>
 +
<span id="line374">374.  }</span>
 +
<span id="line375">375.  if (i == MAXSPELL) impossible("Too many spells memorized!");</span>
 +
<span id="line376">376.  </span>
 +
<span id="line377">377.  if (book->cursed) { /* maybe a demon cursed it */</span>
 +
<span id="line378">378.      if (cursed_book(book)) {</span>
 +
<span id="line379">379.  useup(book);</span>
 +
<span id="line380">380.  book = 0;</span>
 +
<span id="line381">381.  return 0;</span>
 +
<span id="line382">382.      }</span>
 +
<span id="line383">383.  }</span>
 +
<span id="line384">384.  if (costly) check_unpaid(book);</span>
 +
<span id="line385">385.  book = 0;</span>
 +
<span id="line386">386.  return(0);</span>
 +
<span id="line387">387.  }</span>
 +
<span id="line388">388.  </span>
 +
 
 +
== study_book ==
 +
 
 +
<span id="line389">389.  int</span>
 +
<span id="line390">390.  study_book(spellbook)</span>
 +
<span id="line391">391.  register struct obj *spellbook;</span>
 +
<span id="line392">392.  {</span>
 +
<span id="line393">393.  register int booktype = spellbook->otyp;</span>
 +
<span id="line394">394.  register boolean confused = (Confusion != 0);</span>
 +
<span id="line395">395.  boolean too_hard = FALSE;</span>
 +
<span id="line396">396.  </span>
 +
<span id="line397">397.  if (delay && !confused && spellbook == book &&</span>
 +
<span id="line398">398.      /* handle the sequence: start reading, get interrupted,</span>
 +
<span id="line399">399.        have book become erased somehow, resume reading it */</span>
 +
<span id="line400">400.      booktype != SPE_BLANK_PAPER) {</span>
 +
<span id="line401">401.  You("continue your efforts to memorize the spell.");</span>
 +
<span id="line402">402.  } else {</span>
 +
<span id="line403">403.  /* KMH -- Simplified this code */</span>
 +
<span id="line404">404.  if (booktype == SPE_BLANK_PAPER) {</span>
 +
<span id="line405">405.  pline("This spellbook is all blank.");</span>
 +
<span id="line406">406.  makeknown(booktype);</span>
 +
<span id="line407">407.  return(1);</span>
 +
<span id="line408">408.  }</span>
 +
<span id="line409">409.  switch (objects[booktype].oc_level) {</span>
 +
<span id="line410">410.  case 1:</span>
 +
<span id="line411">411.  case 2:</span>
 +
<span id="line412">412.  delay = -objects[booktype].oc_delay;</span>
 +
<span id="line413">413.  break;</span>
 +
<span id="line414">414.  case 3:</span>
 +
<span id="line415">415.  case 4:</span>
 +
<span id="line416">416.  delay = -(objects[booktype].oc_level - 1) *</span>
 +
<span id="line417">417.  objects[booktype].oc_delay;</span>
 +
<span id="line418">418.  break;</span>
 +
<span id="line419">419.  case 5:</span>
 +
<span id="line420">420.  case 6:</span>
 +
<span id="line421">421.  delay = -objects[booktype].oc_level *</span>
 +
<span id="line422">422.  objects[booktype].oc_delay;</span>
 +
<span id="line423">423.  break;</span>
 +
<span id="line424">424.  case 7:</span>
 +
<span id="line425">425.  delay = -8 * objects[booktype].oc_delay;</span>
 +
<span id="line426">426.  break;</span>
 +
<span id="line427">427.  default:</span>
 +
<span id="line428">428.  impossible("Unknown spellbook level %d, book %d;",</span>
 +
<span id="line429">429.  objects[booktype].oc_level, booktype);</span>
 +
<span id="line430">430.  return 0;</span>
 +
<span id="line431">431.  }</span>
 +
<span id="line432">432.  </span>
 +
<span id="line433">433.  /* Books are often wiser than their readers (Rus.) */</span>
 +
<span id="line434">434.  spellbook->in_use = TRUE;</span>
 +
<span id="line435">435.  if (!spellbook->blessed &&</span>
 +
<span id="line436">436.      spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {</span>
 +
<span id="line437">437.      if (spellbook->cursed) {</span>
 +
<span id="line438">438.  too_hard = TRUE;</span>
 +
<span id="line439">439.      } else {</span>
 +
<span id="line440">440.  /* uncursed - chance to fail */</span>
 +
<span id="line441">441.  int read_ability = ACURR(A_INT) + 4 + u.ulevel/2</span>
 +
<span id="line442">442.      - 2*objects[booktype].oc_level</span>
 +
<span id="line443">443.      + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);</span>
 +
<span id="line444">444.  /* only wizards know if a spell is too difficult */</span>
 +
<span id="line445">445.  if (Role_if(PM_WIZARD) && read_ability < 20 &&</span>
 +
<span id="line446">446.      !confused) {</span>
 +
<span id="line447">447.      char qbuf[QBUFSZ];</span>
 +
<span id="line448">448.      Sprintf(qbuf,</span>
 +
<span id="line449">449.        "This spellbook is %sdifficult to comprehend. Continue?",</span>
 +
<span id="line450">450.      (read_ability < 12 ? "very " : ""));</span>
 +
<span id="line451">451.      if (yn(qbuf) != 'y') {</span>
 +
<span id="line452">452.  spellbook->in_use = FALSE;</span>
 +
<span id="line453">453.  return(1);</span>
 +
<span id="line454">454.      }</span>
 +
<span id="line455">455.  }</span>
 +
<span id="line456">456.  /* its up to random luck now */</span>
 +
<span id="line457">457.  if (rnd(20) > read_ability) {</span>
 +
<span id="line458">458.      too_hard = TRUE;</span>
 +
<span id="line459">459.  }</span>
 +
<span id="line460">460.      }</span>
 +
<span id="line461">461.  }</span>
 +
<span id="line462">462.  </span>
 +
<span id="line463">463.  if (too_hard) {</span>
 +
<span id="line464">464.      boolean gone = cursed_book(spellbook);</span>
 +
<span id="line465">465.  </span>
 +
<span id="line466">466.      nomul(delay); /* study time */</span>
 +
<span id="line467">467.      delay = 0;</span>
 +
<span id="line468">468.      if(gone || !rn2(3)) {</span>
 +
<span id="line469">469.  if (!gone) pline_The("spellbook crumbles to dust!");</span>
 +
<span id="line470">470.  if (!objects[spellbook->otyp].oc_name_known &&</span>
 +
<span id="line471">471.  !objects[spellbook->otyp].oc_uname)</span>
 +
<span id="line472">472.      docall(spellbook);</span>
 +
<span id="line473">473.  useup(spellbook);</span>
 +
<span id="line474">474.      } else</span>
 +
<span id="line475">475.  spellbook->in_use = FALSE;</span>
 +
<span id="line476">476.      return(1);</span>
 +
<span id="line477">477.  } else if (confused) {</span>
 +
<span id="line478">478.      if (!confused_book(spellbook)) {</span>
 +
<span id="line479">479.  spellbook->in_use = FALSE;</span>
 +
<span id="line480">480.      }</span>
 +
<span id="line481">481.      nomul(delay);</span>
 +
<span id="line482">482.      delay = 0;</span>
 +
<span id="line483">483.      return(1);</span>
 +
<span id="line484">484.  }</span>
 +
<span id="line485">485.  spellbook->in_use = FALSE;</span>
 +
<span id="line486">486.  </span>
 +
<span id="line487">487.  You("begin to %s the runes.",</span>
 +
<span id="line488">488.      spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" :</span>
 +
<span id="line489">489.      "memorize");</span>
 +
<span id="line490">490.  }</span>
 +
<span id="line491">491.  </span>
 +
<span id="line492">492.  book = spellbook;</span>
 +
<span id="line493">493.  set_occupation(learn, "studying", 0);</span>
 +
<span id="line494">494.  return(1);</span>
 +
<span id="line495">495.  }</span>
 +
<span id="line496">496.  </span>
 +
 
 +
== book_disappears ==
 +
 
 +
<span id="line497">497.  /* a spellbook has been destroyed or the character has changed levels;</span>
 +
<span id="line498">498.    the stored address for the current book is no longer valid */</span>
 +
<span id="line499">499.  void</span>
 +
<span id="line500">500.  book_disappears(obj)</span>
 +
<span id="line501">501.  struct obj *obj;</span>
 +
<span id="line502">502.  {</span>
 +
<span id="line503">503.  if (obj == book) book = (struct obj *)0;</span>
 +
<span id="line504">504.  }</span>
 +
<span id="line505">505.  </span>
 +
 
 +
== book_substitution ==
 +
 
 +
<span id="line506">506.  /* renaming an object usually results in it having a different address;</span>
 +
<span id="line507">507.    so the sequence start reading, get interrupted, name the book, resume</span>
 +
<span id="line508">508.    reading would read the "new" book from scratch */</span>
 +
<span id="line509">509.  void</span>
 +
<span id="line510">510.  book_substitution(old_obj, new_obj)</span>
 +
<span id="line511">511.  struct obj *old_obj, *new_obj;</span>
 +
<span id="line512">512.  {</span>
 +
<span id="line513">513.  if (old_obj == book) book = new_obj;</span>
 +
<span id="line514">514.  }</span>
 +
<span id="line515">515.  </span>
 +
 
 +
== age_spells ==
 +
 
 +
<span id="line516">516.  /* called from moveloop() */</span>
 +
<span id="line517">517.  void</span>
 +
<span id="line518">518.  age_spells()</span>
 +
<span id="line519">519.  {</span>
 +
<span id="line520">520.  int i;</span>
 +
<span id="line521">521.  /*</span>
 +
<span id="line522">522.  * The time relative to the hero (a pass through move</span>
 +
<span id="line523">523.  * loop) causes all spell knowledge to be decremented.</span>
 +
<span id="line524">524.  * The hero's speed, rest status, conscious status etc.</span>
 +
<span id="line525">525.  * does not alter the loss of memory.</span>
 +
<span id="line526">526.  */</span>
 +
<span id="line527">527.  for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++)</span>
 +
<span id="line528">528.      if (spellknow(i))</span>
 +
<span id="line529">529.  decrnknow(i);</span>
 +
<span id="line530">530.  return;</span>
 +
<span id="line531">531.  }</span>
 +
<span id="line532">532.  </span>
 +
 
 +
== getspell ==
 +
 
 +
<span id="line533">533.  /*</span>
 +
<span id="line534">534.  * Return TRUE if a spell was picked, with the spell index in the return</span>
 +
<span id="line535">535.  * parameter.  Otherwise return FALSE.</span>
 +
<span id="line536">536.  */</span>
 +
<span id="line537">537.  STATIC_OVL boolean</span>
 +
<span id="line538">538.  getspell(spell_no)</span>
 +
<span id="line539">539.  int *spell_no;</span>
 +
<span id="line540">540.  {</span>
 +
<span id="line541">541.  int nspells, idx;</span>
 +
<span id="line542">542.  char ilet, lets[BUFSZ], qbuf[QBUFSZ];</span>
 +
<span id="line543">543.  </span>
 +
<span id="line544">544.  if (spellid(0) == NO_SPELL)  {</span>
 +
<span id="line545">545.      You("don't know any spells right now.");</span>
 +
<span id="line546">546.      return FALSE;</span>
 +
<span id="line547">547.  }</span>
 +
<span id="line548">548.  if (flags.menu_style == MENU_TRADITIONAL) {</span>
 +
<span id="line549">549.      /* we know there is at least 1 known spell */</span>
 +
<span id="line550">550.      for (nspells = 1; nspells < MAXSPELL</span>
 +
<span id="line551">551.      && spellid(nspells) != NO_SPELL; nspells++)</span>
 +
<span id="line552">552.  continue;</span>
 +
<span id="line553">553.  </span>
 +
<span id="line554">554.      if (nspells == 1)  Strcpy(lets, "a");</span>
 +
<span id="line555">555.      else if (nspells < 27)  Sprintf(lets, "a-%c", 'a' + nspells - 1);</span>
 +
<span id="line556">556.      else if (nspells == 27)  Sprintf(lets, "a-zA");</span>
 +
<span id="line557">557.      else Sprintf(lets, "a-zA-%c", 'A' + nspells - 27);</span>
 +
<span id="line558">558.  </span>
 +
<span id="line559">559.      for(;;)  {</span>
 +
<span id="line560">560.  Sprintf(qbuf, "Cast which spell? [%s ?]", lets);</span>
 +
<span id="line561">561.  if ((ilet = yn_function(qbuf, (char *)0, '\0')) == '?')</span>
 +
<span id="line562">562.      break;</span>
 +
<span id="line563">563.  </span>
 +
<span id="line564">564.  if (index(quitchars, ilet))</span>
 +
<span id="line565">565.      return FALSE;</span>
 +
<span id="line566">566.  </span>
 +
<span id="line567">567.  idx = spell_let_to_idx(ilet);</span>
 +
<span id="line568">568.  if (idx >= 0 && idx < nspells) {</span>
 +
<span id="line569">569.      *spell_no = idx;</span>
 +
<span id="line570">570.      return TRUE;</span>
 +
<span id="line571">571.  } else</span>
 +
<span id="line572">572.      You("don't know that spell.");</span>
 +
<span id="line573">573.      }</span>
 +
<span id="line574">574.  }</span>
 +
<span id="line575">575.  return dospellmenu("Choose which spell to cast",</span>
 +
<span id="line576">576.    SPELLMENU_CAST, spell_no);</span>
 +
<span id="line577">577.  }</span>
 +
<span id="line578">578.  </span>
 +
 
 +
== docast ==
 +
 
 +
<span id="line579">579.  /* the 'Z' command -- cast a spell */</span>
 +
<span id="line580">580.  int</span>
 +
<span id="line581">581.  docast()</span>
 +
<span id="line582">582.  {</span>
 +
<span id="line583">583.  int spell_no;</span>
 +
<span id="line584">584.  </span>
 +
<span id="line585">585.  if (getspell(&spell_no))</span>
 +
<span id="line586">586.      return spelleffects(spell_no, FALSE);</span>
 +
<span id="line587">587.  return 0;</span>
 +
<span id="line588">588.  }</span>
 +
<span id="line589">589.  </span>
 +
 
 +
== spelltypemnemonic ==
 +
 
 +
<span id="line590">590.  STATIC_OVL const char *</span>
 +
<span id="line591">591.  spelltypemnemonic(skill)</span>
 +
<span id="line592">592.  int skill;</span>
 +
<span id="line593">593.  {</span>
 +
<span id="line594">594.  switch (skill) {</span>
 +
<span id="line595">595.      case P_ATTACK_SPELL:</span>
 +
<span id="line596">596.  return "attack";</span>
 +
<span id="line597">597.      case P_HEALING_SPELL:</span>
 +
<span id="line598">598.  return "healing";</span>
 +
<span id="line599">599.      case P_DIVINATION_SPELL:</span>
 +
<span id="line600">600.  return "divination";</span>
 +
<span id="line601">601.      case P_ENCHANTMENT_SPELL:</span>
 +
<span id="line602">602.  return "enchantment";</span>
 +
<span id="line603">603.      case P_CLERIC_SPELL:</span>
 +
<span id="line604">604.  return "clerical";</span>
 +
<span id="line605">605.      case P_ESCAPE_SPELL:</span>
 +
<span id="line606">606.  return "escape";</span>
 +
<span id="line607">607.      case P_MATTER_SPELL:</span>
 +
<span id="line608">608.  return "matter";</span>
 +
<span id="line609">609.      default:</span>
 +
<span id="line610">610.  impossible("Unknown spell skill, %d;", skill);</span>
 +
<span id="line611">611.  return "";</span>
 +
<span id="line612">612.  }</span>
 +
<span id="line613">613.  }</span>
 +
<span id="line614">614.  </span>
 +
 
 +
== spell_skilltype ==
 +
 
 +
<span id="line615">615.  int</span>
 +
<span id="line616">616.  spell_skilltype(booktype)</span>
 +
<span id="line617">617.  int booktype;</span>
 +
<span id="line618">618.  {</span>
 +
<span id="line619">619.  return (objects[booktype].oc_skill);</span>
 +
<span id="line620">620.  }</span>
 +
<span id="line621">621.  </span>
 +
 
 +
== cast_protection ==
 +
 
 +
<span id="line622">622.  STATIC_OVL void</span>
 +
<span id="line623">623.  cast_protection()</span>
 +
<span id="line624">624.  {</span>
 +
<span id="line625">625.  int loglev = 0;</span>
 +
<span id="line626">626.  int l = u.ulevel;</span>
 +
<span id="line627">627.  int natac = u.uac - u.uspellprot;</span>
 +
<span id="line628">628.  int gain;</span>
 +
<span id="line629">629.  </span>
 +
<span id="line630">630.  /* loglev=log2(u.ulevel)+1 (1..5) */</span>
 +
<span id="line631">631.  while (l) {</span>
 +
<span id="line632">632.      loglev++;</span>
 +
<span id="line633">633.      l /= 2;</span>
 +
<span id="line634">634.  }</span>
 +
<span id="line635">635.  </span>
 +
<span id="line636">636.  /* The more u.uspellprot you already have, the less you get,</span>
 +
<span id="line637">637.  * and the better your natural ac, the less you get.</span>
 +
<span id="line638">638.  *</span>
 +
<span id="line639">639.  * LEVEL AC    SPELLPROT from sucessive SPE_PROTECTION casts</span>
 +
<span id="line640">640.  *      1    10    0,  1,  2,  3,  4</span>
 +
<span id="line641">641.  *      1      0    0,  1,  2,  3</span>
 +
<span id="line642">642.  *      1    -10    0,  1,  2</span>
 +
<span id="line643">643.  *      2-3  10    0,  2,  4,  5,  6,  7,  8</span>
 +
<span id="line644">644.  *      2-3    0    0,  2,  4,  5,  6</span>
 +
<span id="line645">645.  *      2-3  -10    0,  2,  3,  4</span>
 +
<span id="line646">646.  *      4-7  10    0,  3,  6,  8,  9, 10, 11, 12</span>
 +
<span id="line647">647.  *      4-7    0    0,  3,  5,  7,  8,  9</span>
 +
<span id="line648">648.  *      4-7  -10    0,  3,  5,  6</span>
 +
<span id="line649">649.  *      7-15 -10    0,  3,  5,  6</span>
 +
<span id="line650">650.  *      8-15  10    0,  4,  7, 10, 12, 13, 14, 15, 16</span>
 +
<span id="line651">651.  *      8-15  0    0,  4,  7,  9, 10, 11, 12</span>
 +
<span id="line652">652.  *      8-15 -10    0,  4,  6,  7,  8</span>
 +
<span id="line653">653.  *    16-30  10    0,  5,  9, 12, 14, 16, 17, 18, 19, 20</span>
 +
<span id="line654">654.  *    16-30  0    0,  5,  9, 11, 13, 14, 15</span>
 +
<span id="line655">655.  *    16-30 -10    0,  5,  8,  9, 10</span>
 +
<span id="line656">656.  */</span>
 +
<span id="line657">657.  gain = loglev - (int)u.uspellprot / (4 - min(3,(10 - natac)/10));</span>
 +
<span id="line658">658.  </span>
 +
<span id="line659">659.  if (gain > 0) {</span>
 +
<span id="line660">660.      if (!Blind) {</span>
 +
<span id="line661">661.  const char *hgolden = hcolor(NH_GOLDEN);</span>
 +
<span id="line662">662.  </span>
 +
<span id="line663">663.  if (u.uspellprot)</span>
 +
<span id="line664">664.      pline_The("%s haze around you becomes more dense.",</span>
 +
<span id="line665">665.        hgolden);</span>
 +
<span id="line666">666.  else</span>
 +
<span id="line667">667.      pline_The("%s around you begins to shimmer with %s haze.",</span>
 +
<span id="line668">668.  /*[ what about being inside solid rock while polyd? ]*/</span>
 +
<span id="line669">669.  (Underwater || Is_waterlevel(&u.uz)) ? "water" : "air",</span>
 +
<span id="line670">670.        an(hgolden));</span>
 +
<span id="line671">671.      }</span>
 +
<span id="line672">672.      u.uspellprot += gain;</span>
 +
<span id="line673">673.      u.uspmtime =</span>
 +
<span id="line674">674.  P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT ? 20 : 10;</span>
 +
<span id="line675">675.      if (!u.usptime)</span>
 +
<span id="line676">676.  u.usptime = u.uspmtime;</span>
 +
<span id="line677">677.      find_ac();</span>
 +
<span id="line678">678.  } else {</span>
 +
<span id="line679">679.      Your("skin feels warm for a moment.");</span>
 +
<span id="line680">680.  }</span>
 +
<span id="line681">681.  }</span>
 +
<span id="line682">682.  </span>
 +
 
 +
== spell_backfire ==
 +
 
 +
<span id="line683">683.  /* attempting to cast a forgotten spell will cause disorientation */</span>
 +
<span id="line684">684.  STATIC_OVL void</span>
 +
<span id="line685">685.  spell_backfire(spell)</span>
 +
<span id="line686">686.  int spell;</span>
 +
<span id="line687">687.  {</span>
 +
<span id="line688">688.      long duration = (long)((spellev(spell) + 1) * 3); /* 6..24 */</span>
 +
<span id="line689">689.  </span>
 +
<span id="line690">690.      /* prior to 3.4.1, the only effect was confusion; it still predominates */</span>
 +
<span id="line691">691.      switch (rn2(10)) {</span>
 +
<span id="line692">692.      case 0:</span>
 +
<span id="line693">693.      case 1:</span>
 +
<span id="line694">694.      case 2:</span>
 +
<span id="line695">695.      case 3: make_confused(duration, FALSE); /* 40% */</span>
 +
<span id="line696">696.      break;</span>
 +
<span id="line697">697.      case 4:</span>
 +
<span id="line698">698.      case 5:</span>
 +
<span id="line699">699.      case 6: make_confused(2L * duration / 3L, FALSE); /* 30% */</span>
 +
<span id="line700">700.      make_stunned(duration / 3L, FALSE);</span>
 +
<span id="line701">701.      break;</span>
 +
<span id="line702">702.      case 7:</span>
 +
<span id="line703">703.      case 8: make_stunned(2L * duration / 3L, FALSE); /* 20% */</span>
 +
<span id="line704">704.      make_confused(duration / 3L, FALSE);</span>
 +
<span id="line705">705.      break;</span>
 +
<span id="line706">706.      case 9: make_stunned(duration, FALSE); /* 10% */</span>
 +
<span id="line707">707.      break;</span>
 +
<span id="line708">708.      }</span>
 +
<span id="line709">709.      return;</span>
 +
<span id="line710">710.  }</span>
 +
<span id="line711">711.  </span>
 +
 
 +
== spelleffects ==
 +
 
 +
<span id="line712">712.  int</span>
 +
<span id="line713">713.  spelleffects(spell, atme)</span>
 +
<span id="line714">714.  int spell;</span>
 +
<span id="line715">715.  boolean atme;</span>
 +
<span id="line716">716.  {</span>
 +
<span id="line717">717.  int energy, damage, chance, n, intell;</span>
 +
<span id="line718">718.  int skill, role_skill;</span>
 +
<span id="line719">719.  boolean confused = (Confusion != 0);</span>
 +
<span id="line720">720.  struct obj *pseudo;</span>
 +
<span id="line721">721.  coord cc;</span>
 +
<span id="line722">722.  </span>
 +
<span id="line723">723.  /*</span>
 +
<span id="line724">724.  * Spell casting no longer affects knowledge of the spell. A</span>
 +
<span id="line725">725.  * decrement of spell knowledge is done every turn.</span>
 +
<span id="line726">726.  */</span>
 +
<span id="line727">727.  if (spellknow(spell) <= 0) {</span>
 +
<span id="line728">728.      Your("knowledge of this spell is twisted.");</span>
 +
<span id="line729">729.      pline("It invokes nightmarish images in your mind...");</span>
 +
<span id="line730">730.      spell_backfire(spell);</span>
 +
<span id="line731">731.      return(0);</span>
 +
<span id="line732">732.  } else if (spellknow(spell) <= 100) {</span>
 +
<span id="line733">733.      You("strain to recall the spell.");</span>
 +
<span id="line734">734.  } else if (spellknow(spell) <= 1000) {</span>
 +
<span id="line735">735.      Your("knowledge of this spell is growing faint.");</span>
 +
<span id="line736">736.  }</span>
 +
<span id="line737">737.  energy = (spellev(spell) * 5);    /* 5 <= energy <= 35 */</span>
 +
<span id="line738">738.  </span>
 +
<span id="line739">739.  if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) {</span>
 +
<span id="line740">740.  You("are too hungry to cast that spell.");</span>
 +
<span id="line741">741.  return(0);</span>
 +
<span id="line742">742.  } else if (ACURR(A_STR) < 4)  {</span>
 +
<span id="line743">743.  You("lack the strength to cast spells.");</span>
 +
<span id="line744">744.  return(0);</span>
 +
<span id="line745">745.  } else if(check_capacity(</span>
 +
<span id="line746">746.  "Your concentration falters while carrying so much stuff.")) {</span>
 +
<span id="line747">747.      return (1);</span>
 +
<span id="line748">748.  } else if (!freehand()) {</span>
 +
<span id="line749">749.  Your("arms are not free to cast!");</span>
 +
<span id="line750">750.  return (0);</span>
 +
<span id="line751">751.  }</span>
 +
<span id="line752">752.  </span>
 +
<span id="line753">753.  if (u.uhave.amulet) {</span>
 +
<span id="line754">754.  You_feel("the amulet draining your energy away.");</span>
 +
<span id="line755">755.  energy += rnd(2*energy);</span>
 +
<span id="line756">756.  }</span>
 +
<span id="line757">757.  if(energy > u.uen)  {</span>
 +
<span id="line758">758.  You("don't have enough energy to cast that spell.");</span>
 +
<span id="line759">759.  return(0);</span>
 +
<span id="line760">760.  } else {</span>
 +
<span id="line761">761.  if (spellid(spell) != SPE_DETECT_FOOD) {</span>
 +
<span id="line762">762.  int hungr = energy * 2;</span>
 +
<span id="line763">763.  </span>
 +
<span id="line764">764.  /* If hero is a wizard, their current intelligence</span>
 +
<span id="line765">765.  * (bonuses + temporary + current)</span>
 +
<span id="line766">766.  * affects hunger reduction in casting a spell.</span>
 +
<span id="line767">767.  * 1. int = 17-18 no reduction</span>
 +
<span id="line768">768.  * 2. int = 16    1/4 hungr</span>
 +
<span id="line769">769.  * 3. int = 15    1/2 hungr</span>
 +
<span id="line770">770.  * 4. int = 1-14  normal reduction</span>
 +
<span id="line771">771.  * The reason for this is:</span>
 +
<span id="line772">772.  * a) Intelligence affects the amount of exertion</span>
 +
<span id="line773">773.  * in thinking.</span>
 +
<span id="line774">774.  * b) Wizards have spent their life at magic and</span>
 +
<span id="line775">775.  * understand quite well how to cast spells.</span>
 +
<span id="line776">776.  */</span>
 +
<span id="line777">777.  intell = acurr(A_INT);</span>
 +
<span id="line778">778.  if (!Role_if(PM_WIZARD)) intell = 10;</span>
 +
<span id="line779">779.  switch (intell) {</span>
 +
<span id="line780">780.  case 25: case 24: case 23: case 22:</span>
 +
<span id="line781">781.  case 21: case 20: case 19: case 18:</span>
 +
<span id="line782">782.  case 17: hungr = 0; break;</span>
 +
<span id="line783">783.  case 16: hungr /= 4; break;</span>
 +
<span id="line784">784.  case 15: hungr /= 2; break;</span>
 +
<span id="line785">785.  }</span>
 +
<span id="line786">786.  /* don't put player (quite) into fainting from</span>
 +
<span id="line787">787.  * casting a spell, particularly since they might</span>
 +
<span id="line788">788.  * not even be hungry at the beginning; however,</span>
 +
<span id="line789">789.  * this is low enough that they must eat before</span>
 +
<span id="line790">790.  * casting anything else except detect food</span>
 +
<span id="line791">791.  */</span>
 +
<span id="line792">792.  if (hungr > u.uhunger-3)</span>
 +
<span id="line793">793.  hungr = u.uhunger-3;</span>
 +
<span id="line794">794.  morehungry(hungr);</span>
 +
<span id="line795">795.  }</span>
 +
<span id="line796">796.  }</span>
 +
<span id="line797">797.  </span>
 +
<span id="line798">798.  chance = percent_success(spell);</span>
 +
<span id="line799">799.  if (confused || (rnd(100) > chance)) {</span>
 +
<span id="line800">800.  You("fail to cast the spell correctly.");</span>
 +
<span id="line801">801.  u.uen -= energy / 2;</span>
 +
<span id="line802">802.  flags.botl = 1;</span>
 +
<span id="line803">803.  return(1);</span>
 +
<span id="line804">804.  }</span>
 +
<span id="line805">805.  </span>
 +
<span id="line806">806.  u.uen -= energy;</span>
 +
<span id="line807">807.  flags.botl = 1;</span>
 +
<span id="line808">808.  exercise(A_WIS, TRUE);</span>
 +
<span id="line809">809.  /* pseudo is a temporary "false" object containing the spell stats */</span>
 +
<span id="line810">810.  pseudo = mksobj(spellid(spell), FALSE, FALSE);</span>
 +
<span id="line811">811.  pseudo->blessed = pseudo->cursed = 0;</span>
 +
<span id="line812">812.  pseudo->quan = 20L; /* do not let useup get it */</span>
 +
<span id="line813">813.  /*</span>
 +
<span id="line814">814.  * Find the skill the hero has in a spell type category.</span>
 +
<span id="line815">815.  * See spell_skilltype for categories.</span>
 +
<span id="line816">816.  */</span>
 +
<span id="line817">817.  skill = spell_skilltype(pseudo->otyp);</span>
 +
<span id="line818">818.  role_skill = P_SKILL(skill);</span>
 +
<span id="line819">819.  </span>
 +
<span id="line820">820.  switch(pseudo->otyp)  {</span>
 +
<span id="line821">821.  /*</span>
 +
<span id="line822">822.  * At first spells act as expected.  As the hero increases in skill</span>
 +
<span id="line823">823.  * with the appropriate spell type, some spells increase in their</span>
 +
<span id="line824">824.  * effects, e.g. more damage, further distance, and so on, without</span>
 +
<span id="line825">825.  * additional cost to the spellcaster.</span>
 +
<span id="line826">826.  */</span>
 +
<span id="line827">827.  case SPE_CONE_OF_COLD:</span>
 +
<span id="line828">828.  case SPE_FIREBALL:</span>
 +
<span id="line829">829.      if (role_skill >= P_SKILLED) {</span>
 +
<span id="line830">830.          if (throwspell()) {</span>
 +
<span id="line831">831.      cc.x=u.dx;cc.y=u.dy;</span>
 +
<span id="line832">832.      n=rnd(8)+1;</span>
 +
<span id="line833">833.      while(n--) {</span>
 +
<span id="line834">834.  if(!u.dx && !u.dy && !u.dz) {</span>
 +
<span id="line835">835.      if ((damage = zapyourself(pseudo, TRUE)) != 0) {</span>
 +
<span id="line836">836.  char buf[BUFSZ];</span>
 +
<span id="line837">837.  Sprintf(buf, "zapped %sself with a spell", uhim());</span>
 +
<span id="line838">838.  losehp(damage, buf, NO_KILLER_PREFIX);</span>
 +
<span id="line839">839.      }</span>
 +
<span id="line840">840.  } else {</span>
 +
<span id="line841">841.      explode(u.dx, u.dy,</span>
 +
<span id="line842">842.      pseudo->otyp - SPE_MAGIC_MISSILE + 10,</span>
 +
<span id="line843">843.      u.ulevel/2 + 1 + spell_damage_bonus(), 0,</span>
 +
<span id="line844">844.  (pseudo->otyp == SPE_CONE_OF_COLD) ?</span>
 +
<span id="line845">845.  EXPL_FROSTY : EXPL_FIERY);</span>
 +
<span id="line846">846.  }</span>
 +
<span id="line847">847.  u.dx = cc.x+rnd(3)-2; u.dy = cc.y+rnd(3)-2;</span>
 +
<span id="line848">848.  if (!isok(u.dx,u.dy) || !cansee(u.dx,u.dy) ||</span>
 +
<span id="line849">849.      IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) {</span>
 +
<span id="line850">850.      /* Spell is reflected back to center */</span>
 +
<span id="line851">851.      u.dx = cc.x;</span>
 +
<span id="line852">852.      u.dy = cc.y;</span>
 +
<span id="line853">853.          }</span>
 +
<span id="line854">854.      }</span>
 +
<span id="line855">855.  }</span>
 +
<span id="line856">856.  break;</span>
 +
<span id="line857">857.      } /* else fall through... */</span>
 +
<span id="line858">858.  </span>
 +
<span id="line859">859.  /* these spells are all duplicates of wand effects */</span>
 +
<span id="line860">860.  case SPE_FORCE_BOLT:</span>
 +
<span id="line861">861.  case SPE_SLEEP:</span>
 +
<span id="line862">862.  case SPE_MAGIC_MISSILE:</span>
 +
<span id="line863">863.  case SPE_KNOCK:</span>
 +
<span id="line864">864.  case SPE_SLOW_MONSTER:</span>
 +
<span id="line865">865.  case SPE_WIZARD_LOCK:</span>
 +
<span id="line866">866.  case SPE_DIG:</span>
 +
<span id="line867">867.  case SPE_TURN_UNDEAD:</span>
 +
<span id="line868">868.  case SPE_POLYMORPH:</span>
 +
<span id="line869">869.  case SPE_TELEPORT_AWAY:</span>
 +
<span id="line870">870.  case SPE_CANCELLATION:</span>
 +
<span id="line871">871.  case SPE_FINGER_OF_DEATH:</span>
 +
<span id="line872">872.  case SPE_LIGHT:</span>
 +
<span id="line873">873.  case SPE_DETECT_UNSEEN:</span>
 +
<span id="line874">874.  case SPE_HEALING:</span>
 +
<span id="line875">875.  case SPE_EXTRA_HEALING:</span>
 +
<span id="line876">876.  case SPE_DRAIN_LIFE:</span>
 +
<span id="line877">877.  case SPE_STONE_TO_FLESH:</span>
 +
<span id="line878">878.  if (!(objects[pseudo->otyp].oc_dir == NODIR)) {</span>
 +
<span id="line879">879.  if (atme) u.dx = u.dy = u.dz = 0;</span>
 +
<span id="line880">880.  else if (!getdir((char *)0)) {</span>
 +
<span id="line881">881.      /* getdir cancelled, re-use previous direction */</span>
 +
<span id="line882">882.      pline_The("magical energy is released!");</span>
 +
<span id="line883">883.  }</span>
 +
<span id="line884">884.  if(!u.dx && !u.dy && !u.dz) {</span>
 +
<span id="line885">885.      if ((damage = zapyourself(pseudo, TRUE)) != 0) {</span>
 +
<span id="line886">886.  char buf[BUFSZ];</span>
 +
<span id="line887">887.  Sprintf(buf, "zapped %sself with a spell", uhim());</span>
 +
<span id="line888">888.  losehp(damage, buf, NO_KILLER_PREFIX);</span>
 +
<span id="line889">889.      }</span>
 +
<span id="line890">890.  } else weffects(pseudo);</span>
 +
<span id="line891">891.  } else weffects(pseudo);</span>
 +
<span id="line892">892.  update_inventory(); /* spell may modify inventory */</span>
 +
<span id="line893">893.  break;</span>
 +
<span id="line894">894.  </span>
 +
<span id="line895">895.  /* these are all duplicates of scroll effects */</span>
 +
<span id="line896">896.  case SPE_REMOVE_CURSE:</span>
 +
<span id="line897">897.  case SPE_CONFUSE_MONSTER:</span>
 +
<span id="line898">898.  case SPE_DETECT_FOOD:</span>
 +
<span id="line899">899.  case SPE_CAUSE_FEAR:</span>
 +
<span id="line900">900.  /* high skill yields effect equivalent to blessed scroll */</span>
 +
<span id="line901">901.  if (role_skill >= P_SKILLED) pseudo->blessed = 1;</span>
 +
<span id="line902">902.  /* fall through */</span>
 +
<span id="line903">903.  case SPE_CHARM_MONSTER:</span>
 +
<span id="line904">904.  case SPE_MAGIC_MAPPING:</span>
 +
<span id="line905">905.  case SPE_CREATE_MONSTER:</span>
 +
<span id="line906">906.  case SPE_IDENTIFY:</span>
 +
<span id="line907">907.  (void) seffects(pseudo);</span>
 +
<span id="line908">908.  break;</span>
 +
<span id="line909">909.  </span>
 +
<span id="line910">910.  /* these are all duplicates of potion effects */</span>
 +
<span id="line911">911.  case SPE_HASTE_SELF:</span>
 +
<span id="line912">912.  case SPE_DETECT_TREASURE:</span>
 +
<span id="line913">913.  case SPE_DETECT_MONSTERS:</span>
 +
<span id="line914">914.  case SPE_LEVITATION:</span>
 +
<span id="line915">915.  case SPE_RESTORE_ABILITY:</span>
 +
<span id="line916">916.  /* high skill yields effect equivalent to blessed potion */</span>
 +
<span id="line917">917.  if (role_skill >= P_SKILLED) pseudo->blessed = 1;</span>
 +
<span id="line918">918.  /* fall through */</span>
 +
<span id="line919">919.  case SPE_INVISIBILITY:</span>
 +
<span id="line920">920.  (void) peffects(pseudo);</span>
 +
<span id="line921">921.  break;</span>
 +
<span id="line922">922.  </span>
 +
<span id="line923">923.  case SPE_CURE_BLINDNESS:</span>
 +
<span id="line924">924.  healup(0, 0, FALSE, TRUE);</span>
 +
<span id="line925">925.  break;</span>
 +
<span id="line926">926.  case SPE_CURE_SICKNESS:</span>
 +
<span id="line927">927.  if (Sick) You("are no longer ill.");</span>
 +
<span id="line928">928.  if (Slimed) {</span>
 +
<span id="line929">929.      pline_The("slime disappears!");</span>
 +
<span id="line930">930.      Slimed = 0;</span>
 +
<span id="line931">931.  /* flags.botl = 1; -- healup() handles this */</span>
 +
<span id="line932">932.  }</span>
 +
<span id="line933">933.  healup(0, 0, TRUE, FALSE);</span>
 +
<span id="line934">934.  break;</span>
 +
<span id="line935">935.  case SPE_CREATE_FAMILIAR:</span>
 +
<span id="line936">936.  (void) make_familiar((struct obj *)0, u.ux, u.uy, FALSE);</span>
 +
<span id="line937">937.  break;</span>
 +
<span id="line938">938.  case SPE_CLAIRVOYANCE:</span>
 +
<span id="line939">939.  if (!BClairvoyant)</span>
 +
<span id="line940">940.      do_vicinity_map();</span>
 +
<span id="line941">941.  /* at present, only one thing blocks clairvoyance */</span>
 +
<span id="line942">942.  else if (uarmh && uarmh->otyp == CORNUTHAUM)</span>
 +
<span id="line943">943.      You("sense a pointy hat on top of your %s.",</span>
 +
<span id="line944">944.  body_part(HEAD));</span>
 +
<span id="line945">945.  break;</span>
 +
<span id="line946">946.  case SPE_PROTECTION:</span>
 +
<span id="line947">947.  cast_protection();</span>
 +
<span id="line948">948.  break;</span>
 +
<span id="line949">949.  case SPE_JUMPING:</span>
 +
<span id="line950">950.  if (!jump(max(role_skill,1)))</span>
 +
<span id="line951">951.  pline(nothing_happens);</span>
 +
<span id="line952">952.  break;</span>
 +
<span id="line953">953.  default:</span>
 +
<span id="line954">954.  impossible("Unknown spell %d attempted.", spell);</span>
 +
<span id="line955">955.  obfree(pseudo, (struct obj *)0);</span>
 +
<span id="line956">956.  return(0);</span>
 +
<span id="line957">957.  }</span>
 +
<span id="line958">958.  </span>
 +
<span id="line959">959.  /* gain skill for successful cast */</span>
 +
<span id="line960">960.  use_skill(skill, spellev(spell));</span>
 +
<span id="line961">961.  </span>
 +
<span id="line962">962.  obfree(pseudo, (struct obj *)0); /* now, get rid of it */</span>
 +
<span id="line963">963.  return(1);</span>
 +
<span id="line964">964.  }</span>
 +
<span id="line965">965.  </span>
 +
 
 +
== throwspell ==
 +
 
 +
<span id="line966">966.  /* Choose location where spell takes effect. */</span>
 +
<span id="line967">967.  STATIC_OVL int</span>
 +
<span id="line968">968.  throwspell()</span>
 +
<span id="line969">969.  {</span>
 +
<span id="line970">970.  coord cc;</span>
 +
<span id="line971">971.  </span>
 +
<span id="line972">972.  if (u.uinwater) {</span>
 +
<span id="line973">973.      pline("You're joking! In this weather?"); return 0;</span>
 +
<span id="line974">974.  } else if (Is_waterlevel(&u.uz)) {</span>
 +
<span id="line975">975.      You("had better wait for the sun to come out."); return 0;</span>
 +
<span id="line976">976.  }</span>
 +
<span id="line977">977.  </span>
 +
<span id="line978">978.  pline("Where do you want to cast the spell?");</span>
 +
<span id="line979">979.  cc.x = u.ux;</span>
 +
<span id="line980">980.  cc.y = u.uy;</span>
 +
<span id="line981">981.  if (getpos(&cc, TRUE, "the desired position") < 0)</span>
 +
<span id="line982">982.      return 0; /* user pressed ESC */</span>
 +
<span id="line983">983.  /* The number of moves from hero to where the spell drops.*/</span>
 +
<span id="line984">984.  if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) {</span>
 +
<span id="line985">985.      pline_The("spell dissipates over the distance!");</span>
 +
<span id="line986">986.      return 0;</span>
 +
<span id="line987">987.  } else if (u.uswallow) {</span>
 +
<span id="line988">988.      pline_The("spell is cut short!");</span>
 +
<span id="line989">989.      exercise(A_WIS, FALSE); /* What were you THINKING! */</span>
 +
<span id="line990">990.      u.dx = 0;</span>
 +
<span id="line991">991.      u.dy = 0;</span>
 +
<span id="line992">992.      return 1;</span>
 +
<span id="line993">993.  } else if (!cansee(cc.x, cc.y) || IS_STWALL(levl[cc.x][cc.y].typ)) {</span>
 +
<span id="line994">994.      Your("mind fails to lock onto that location!");</span>
 +
<span id="line995">995.      return 0;</span>
 +
<span id="line996">996.  } else {</span>
 +
<span id="line997">997.      u.dx=cc.x;</span>
 +
<span id="line998">998.      u.dy=cc.y;</span>
 +
<span id="line999">999.      return 1;</span>
 +
<span id="line1000">1000. }</span>
 +
<span id="line1001">1001. }</span>
 +
<span id="line1002">1002. </span>
 +
 
 +
== losespells ==
 +
 
 +
<span id="line1003">1003. void</span>
 +
<span id="line1004">1004. losespells()</span>
 +
<span id="line1005">1005. {</span>
 +
<span id="line1006">1006. boolean confused = (Confusion != 0);</span>
 +
<span id="line1007">1007. int  n, nzap, i;</span>
 +
<span id="line1008">1008. </span>
 +
<span id="line1009">1009. book = 0;</span>
 +
<span id="line1010">1010. for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; n++)</span>
 +
<span id="line1011">1011. continue;</span>
 +
<span id="line1012">1012. if (n) {</span>
 +
<span id="line1013">1013. nzap = rnd(n) + confused ? 1 : 0;</span>
 +
<span id="line1014">1014. if (nzap > n) nzap = n;</span>
 +
<span id="line1015">1015. for (i = n - nzap; i < n; i++) {</span>
 +
<span id="line1016">1016.     spellid(i) = NO_SPELL;</span>
 +
<span id="line1017">1017.     exercise(A_WIS, FALSE); /* ouch! */</span>
 +
<span id="line1018">1018. }</span>
 +
<span id="line1019">1019. }</span>
 +
<span id="line1020">1020. }</span>
 +
<span id="line1021">1021. </span>
  
=== learn ===
+
== dovspell ==
  
=== getspell ===
+
<span id="line1022">1022. /* the '+' command -- view known spells */</span>
 +
<span id="line1023">1023. int</span>
 +
<span id="line1024">1024. dovspell()</span>
 +
<span id="line1025">1025. {</span>
 +
<span id="line1026">1026. char qbuf[QBUFSZ];</span>
 +
<span id="line1027">1027. int splnum, othnum;</span>
 +
<span id="line1028">1028. struct spell spl_tmp;</span>
 +
<span id="line1029">1029. </span>
 +
<span id="line1030">1030. if (spellid(0) == NO_SPELL)</span>
 +
<span id="line1031">1031.     You("don't know any spells right now.");</span>
 +
<span id="line1032">1032. else {</span>
 +
<span id="line1033">1033.     while (dospellmenu("Currently known spells",</span>
 +
<span id="line1034">1034.       SPELLMENU_VIEW, &splnum)) {</span>
 +
<span id="line1035">1035. Sprintf(qbuf, "Reordering spells; swap '%c' with",</span>
 +
<span id="line1036">1036. spellet(splnum));</span>
 +
<span id="line1037">1037. if (!dospellmenu(qbuf, splnum, &othnum)) break;</span>
 +
<span id="line1038">1038. </span>
 +
<span id="line1039">1039. spl_tmp = spl_book[splnum];</span>
 +
<span id="line1040">1040. spl_book[splnum] = spl_book[othnum];</span>
 +
<span id="line1041">1041. spl_book[othnum] = spl_tmp;</span>
 +
<span id="line1042">1042.     }</span>
 +
<span id="line1043">1043. }</span>
 +
<span id="line1044">1044. return 0;</span>
 +
<span id="line1045">1045. }</span>
 +
<span id="line1046">1046. </span>
  
=== dospellmenu ===
+
== dospellmenu ==
  
=== percent_success ===
+
<span id="line1047">1047. STATIC_OVL boolean</span>
 +
<span id="line1048">1048. dospellmenu(prompt, splaction, spell_no)</span>
 +
<span id="line1049">1049. const char *prompt;</span>
 +
<span id="line1050">1050. int splaction; /* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */</span>
 +
<span id="line1051">1051. int *spell_no;</span>
 +
<span id="line1052">1052. {</span>
 +
<span id="line1053">1053. winid tmpwin;</span>
 +
<span id="line1054">1054. int i, n, how;</span>
 +
<span id="line1055">1055. char buf[BUFSZ];</span>
 +
<span id="line1056">1056. menu_item *selected;</span>
 +
<span id="line1057">1057. anything any;</span>
 +
<span id="line1058">1058. </span>
 +
<span id="line1059">1059. tmpwin = create_nhwindow(NHW_MENU);</span>
 +
<span id="line1060">1060. start_menu(tmpwin);</span>
 +
<span id="line1061">1061. any.a_void = 0; /* zero out all bits */</span>
 +
<span id="line1062">1062. </span>
 +
<span id="line1063">1063. /*</span>
 +
<span id="line1064">1064. * The correct spacing of the columns depends on the</span>
 +
<span id="line1065">1065. * following that (1) the font is monospaced and (2)</span>
 +
<span id="line1066">1066. * that selection letters are pre-pended to the given</span>
 +
<span id="line1067">1067. * string and are of the form "a - ".</span>
 +
<span id="line1068">1068. *</span>
 +
<span id="line1069">1069. * To do it right would require that we implement columns</span>
 +
<span id="line1070">1070. * in the window-ports (say via a tab character).</span>
 +
<span id="line1071">1071. */</span>
 +
<span id="line1072">1072. if (!iflags.menu_tab_sep)</span>
 +
<span id="line1073">1073. Sprintf(buf, "%-20s    Level  %-12s Fail", "    Name", "Category");</span>
 +
<span id="line1074">1074. else</span>
 +
<span id="line1075">1075. Sprintf(buf, "Name\tLevel\tCategory\tFail");</span>
 +
<span id="line1076">1076. add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, buf, MENU_UNSELECTED);</span>
 +
<span id="line1077">1077. for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) {</span>
 +
<span id="line1078">1078. Sprintf(buf, iflags.menu_tab_sep ?</span>
 +
<span id="line1079">1079. "%s\t%-d%s\t%s\t%-d%%" : "%-20s  %2d%s  %-12s %3d%%",</span>
 +
<span id="line1080">1080. spellname(i), spellev(i),</span>
 +
<span id="line1081">1081. spellknow(i) ? " " : "*",</span>
 +
<span id="line1082">1082. spelltypemnemonic(spell_skilltype(spellid(i))),</span>
 +
<span id="line1083">1083. 100 - percent_success(i));</span>
 +
<span id="line1084">1084. </span>
 +
<span id="line1085">1085. any.a_int = i+1; /* must be non-zero */</span>
 +
<span id="line1086">1086. add_menu(tmpwin, NO_GLYPH, &any,</span>
 +
<span id="line1087">1087. spellet(i), 0, ATR_NONE, buf,</span>
 +
<span id="line1088">1088. (i == splaction) ? MENU_SELECTED : MENU_UNSELECTED);</span>
 +
<span id="line1089">1089.       }</span>
 +
<span id="line1090">1090. end_menu(tmpwin, prompt);</span>
 +
<span id="line1091">1091. </span>
 +
<span id="line1092">1092. how = PICK_ONE;</span>
 +
<span id="line1093">1093. if (splaction == SPELLMENU_VIEW && spellid(1) == NO_SPELL)</span>
 +
<span id="line1094">1094.     how = PICK_NONE; /* only one spell => nothing to swap with */</span>
 +
<span id="line1095">1095. n = select_menu(tmpwin, how, &selected);</span>
 +
<span id="line1096">1096. destroy_nhwindow(tmpwin);</span>
 +
<span id="line1097">1097. if (n > 0) {</span>
 +
<span id="line1098">1098. *spell_no = selected[0].item.a_int - 1;</span>
 +
<span id="line1099">1099. /* menu selection for `PICK_ONE' does not</span>
 +
<span id="line1100">1100.   de-select any preselected entry */</span>
 +
<span id="line1101">1101. if (n > 1 && *spell_no == splaction)</span>
 +
<span id="line1102">1102.     *spell_no = selected[1].item.a_int - 1;</span>
 +
<span id="line1103">1103. free((genericptr_t)selected);</span>
 +
<span id="line1104">1104. /* default selection of preselected spell means that</span>
 +
<span id="line1105">1105.   user chose not to swap it with anything */</span>
 +
<span id="line1106">1106. if (*spell_no == splaction) return FALSE;</span>
 +
<span id="line1107">1107. return TRUE;</span>
 +
<span id="line1108">1108. } else if (splaction >= 0) {</span>
 +
<span id="line1109">1109.     /* explicit de-selection of preselected spell means that</span>
 +
<span id="line1110">1110.       user is still swapping but not for the current spell */</span>
 +
<span id="line1111">1111.     *spell_no = splaction;</span>
 +
<span id="line1112">1112.     return TRUE;</span>
 +
<span id="line1113">1113. }</span>
 +
<span id="line1114">1114. return FALSE;</span>
 +
<span id="line1115">1115. }</span>
 +
<span id="line1116">1116. </span>
  
=== throwspell ===
+
== isqrt ==
  
=== cast_protection ===
+
<span id="line1117">1117. /* Integer square root function without using floating point. */</span>
 +
<span id="line1118">1118. STATIC_OVL int</span>
 +
<span id="line1119">1119. isqrt(val)</span>
 +
<span id="line1120">1120. int val;</span>
 +
<span id="line1121">1121. {</span>
 +
<span id="line1122">1122.    int rt = 0;</span>
 +
<span id="line1123">1123.    int odd = 1;</span>
 +
<span id="line1124">1124.    while(val >= odd) {</span>
 +
<span id="line1125">1125. val = val-odd;</span>
 +
<span id="line1126">1126. odd = odd+2;</span>
 +
<span id="line1127">1127. rt = rt + 1;</span>
 +
<span id="line1128">1128.    }</span>
 +
<span id="line1129">1129.    return rt;</span>
 +
<span id="line1130">1130. }</span>
 +
<span id="line1131">1131. </span>
  
=== spell_backfire ===
+
== percent_success ==
  
=== spelltypemnemonic ===
+
<span id="line1132">1132. STATIC_OVL int</span>
 +
<span id="line1133">1133. percent_success(spell)</span>
 +
<span id="line1134">1134. int spell;</span>
 +
<span id="line1135">1135. {</span>
 +
<span id="line1136">1136. /* Intrinsic and learned ability are combined to calculate</span>
 +
<span id="line1137">1137. * the probability of player's success at cast a given spell.</span>
 +
<span id="line1138">1138. */</span>
 +
<span id="line1139">1139. int chance, splcaster, special, statused;</span>
 +
<span id="line1140">1140. int difficulty;</span>
 +
<span id="line1141">1141. int skill;</span>
 +
<span id="line1142">1142. </span>
 +
<span id="line1143">1143. /* Calculate intrinsic ability (splcaster) */</span>
 +
<span id="line1144">1144. </span>
 +
<span id="line1145">1145. splcaster = urole.spelbase;</span>
 +
<span id="line1146">1146. special = urole.spelheal;</span>
 +
<span id="line1147">1147. statused = ACURR(urole.spelstat);</span>
 +
<span id="line1148">1148. </span>
 +
<span id="line1149">1149. if (uarm && is_metallic(uarm))</span>
 +
<span id="line1150">1150.     splcaster += (uarmc && uarmc->otyp == ROBE) ?</span>
 +
<span id="line1151">1151. urole.spelarmr/2 : urole.spelarmr;</span>
 +
<span id="line1152">1152. else if (uarmc && uarmc->otyp == ROBE)</span>
 +
<span id="line1153">1153.     splcaster -= urole.spelarmr;</span>
 +
<span id="line1154">1154. if (uarms) splcaster += urole.spelshld;</span>
 +
<span id="line1155">1155. </span>
 +
<span id="line1156">1156. if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE)</span>
 +
<span id="line1157">1157. splcaster += uarmhbon;</span>
 +
<span id="line1158">1158. if (uarmg && is_metallic(uarmg)) splcaster += uarmgbon;</span>
 +
<span id="line1159">1159. if (uarmf && is_metallic(uarmf)) splcaster += uarmfbon;</span>
 +
<span id="line1160">1160. </span>
 +
<span id="line1161">1161. if (spellid(spell) == urole.spelspec)</span>
 +
<span id="line1162">1162. splcaster += urole.spelsbon;</span>
 +
<span id="line1163">1163. </span>
 +
<span id="line1164">1164. </span>
 +
<span id="line1165">1165. /* `healing spell' bonus */</span>
 +
<span id="line1166">1166. if (spellid(spell) == SPE_HEALING ||</span>
 +
<span id="line1167">1167.     spellid(spell) == SPE_EXTRA_HEALING ||</span>
 +
<span id="line1168">1168.     spellid(spell) == SPE_CURE_BLINDNESS ||</span>
 +
<span id="line1169">1169.     spellid(spell) == SPE_CURE_SICKNESS ||</span>
 +
<span id="line1170">1170.     spellid(spell) == SPE_RESTORE_ABILITY ||</span>
 +
<span id="line1171">1171.     spellid(spell) == SPE_REMOVE_CURSE) splcaster += special;</span>
 +
<span id="line1172">1172. </span>
 +
<span id="line1173">1173. if (splcaster > 20) splcaster = 20;</span>
 +
<span id="line1174">1174. </span>
 +
<span id="line1175">1175. /* Calculate learned ability */</span>
 +
<span id="line1176">1176. </span>
 +
<span id="line1177">1177. /* Players basic likelihood of being able to cast any spell</span>
 +
<span id="line1178">1178. * is based of their `magic' statistic. (Int or Wis)</span>
 +
<span id="line1179">1179. */</span>
 +
<span id="line1180">1180. chance = 11 * statused / 2;</span>
 +
<span id="line1181">1181. </span>
 +
<span id="line1182">1182. /*</span>
 +
<span id="line1183">1183. * High level spells are harder.  Easier for higher level casters.</span>
 +
<span id="line1184">1184. * The difficulty is based on the hero's level and their skill level</span>
 +
<span id="line1185">1185. * in that spell type.</span>
 +
<span id="line1186">1186. */</span>
 +
<span id="line1187">1187. skill = P_SKILL(spell_skilltype(spellid(spell)));</span>
 +
<span id="line1188">1188. skill = max(skill,P_UNSKILLED) - 1; /* unskilled => 0 */</span>
 +
<span id="line1189">1189. difficulty= (spellev(spell)-1) * 4 - ((skill * 6) + (u.ulevel/3) + 1);</span>
 +
<span id="line1190">1190. </span>
 +
<span id="line1191">1191. if (difficulty > 0) {</span>
 +
<span id="line1192">1192. /* Player is too low level or unskilled. */</span>
 +
<span id="line1193">1193. chance -= isqrt(900 * difficulty + 2000);</span>
 +
<span id="line1194">1194. } else {</span>
 +
<span id="line1195">1195. /* Player is above level.  Learning continues, but the</span>
 +
<span id="line1196">1196. * law of diminishing returns sets in quickly for</span>
 +
<span id="line1197">1197. * low-level spells.  That is, a player quickly gains</span>
 +
<span id="line1198">1198. * no advantage for raising level.</span>
 +
<span id="line1199">1199. */</span>
 +
<span id="line1200">1200. int learning = 15 * -difficulty / spellev(spell);</span>
 +
<span id="line1201">1201. chance += learning > 20 ? 20 : learning;</span>
 +
<span id="line1202">1202. }</span>
 +
<span id="line1203">1203. </span>
 +
<span id="line1204">1204. /* Clamp the chance: >18 stat and advanced learning only help</span>
 +
<span id="line1205">1205. * to a limit, while chances below "hopeless" only raise the</span>
 +
<span id="line1206">1206. * specter of overflowing 16-bit ints (and permit wearing a</span>
 +
<span id="line1207">1207. * shield to raise the chances :-).</span>
 +
<span id="line1208">1208. */</span>
 +
<span id="line1209">1209. if (chance < 0) chance = 0;</span>
 +
<span id="line1210">1210. if (chance > 120) chance = 120;</span>
 +
<span id="line1211">1211. </span>
 +
<span id="line1212">1212. /* Wearing anything but a light shield makes it very awkward</span>
 +
<span id="line1213">1213. * to cast a spell.  The penalty is not quite so bad for the</span>
 +
<span id="line1214">1214. * player's role-specific spell.</span>
 +
<span id="line1215">1215. */</span>
 +
<span id="line1216">1216. if (uarms && weight(uarms) > (int) objects[SMALL_SHIELD].oc_weight) {</span>
 +
<span id="line1217">1217. if (spellid(spell) == urole.spelspec) {</span>
 +
<span id="line1218">1218. chance /= 2;</span>
 +
<span id="line1219">1219. } else {</span>
 +
<span id="line1220">1220. chance /= 4;</span>
 +
<span id="line1221">1221. }</span>
 +
<span id="line1222">1222. }</span>
 +
<span id="line1223">1223. </span>
 +
<span id="line1224">1224. /* Finally, chance (based on player intell/wisdom and level) is</span>
 +
<span id="line1225">1225. * combined with ability (based on player intrinsics and</span>
 +
<span id="line1226">1226. * encumbrances).  No matter how intelligent/wise and advanced</span>
 +
<span id="line1227">1227. * a player is, intrinsics and encumbrance can prevent casting;</span>
 +
<span id="line1228">1228. * and no matter how able, learning is always required.</span>
 +
<span id="line1229">1229. */</span>
 +
<span id="line1230">1230. chance = chance * (20-splcaster) / 15 - splcaster;</span>
 +
<span id="line1231">1231. </span>
 +
<span id="line1232">1232. /* Clamp to percentile */</span>
 +
<span id="line1233">1233. if (chance > 100) chance = 100;</span>
 +
<span id="line1234">1234. if (chance < 0) chance = 0;</span>
 +
<span id="line1235">1235. </span>
 +
<span id="line1236">1236. return chance;</span>
 +
<span id="line1237">1237. }</span>
 +
<span id="line1238">1238. </span>
 +
<span id="line1239">1239. </span>
  
=== isqrt ===
+
== initialspell ==
  
{{stub|Does anyone want to document the other functions?}}
+
<span id="line1240">1240. /* Learn a spell during creation of the initial inventory */</span>
 +
<span id="line1241">1241. void</span>
 +
<span id="line1242">1242. initialspell(obj)</span>
 +
<span id="line1243">1243. struct obj *obj;</span>
 +
<span id="line1244">1244. {</span>
 +
<span id="line1245">1245. int i;</span>
 +
<span id="line1246">1246. </span>
 +
<span id="line1247">1247. for (i = 0; i < MAXSPELL; i++) {</span>
 +
<span id="line1248">1248.     if (spellid(i) == obj->otyp) {</span>
 +
<span id="line1249">1249.         pline("Error: Spell %s already known.",</span>
 +
<span id="line1250">1250.         OBJ_NAME(objects[obj->otyp]));</span>
 +
<span id="line1251">1251.         return;</span>
 +
<span id="line1252">1252.     }</span>
 +
<span id="line1253">1253.     if (spellid(i) == NO_SPELL)  {</span>
 +
<span id="line1254">1254.         spl_book[i].sp_id = obj->otyp;</span>
 +
<span id="line1255">1255.         spl_book[i].sp_lev = objects[obj->otyp].oc_level;</span>
 +
<span id="line1256">1256.         incrnknow(i);</span>
 +
<span id="line1257">1257.         return;</span>
 +
<span id="line1258">1258.     }</span>
 +
<span id="line1259">1259. }</span>
 +
<span id="line1260">1260. impossible("Too many spells memorized!");</span>
 +
<span id="line1261">1261. return;</span>
 +
<span id="line1262">1262. }</span>
 +
<span id="line1263">1263. </span>
 +
<span id="line1264">1264. /*spell.c*/</span>
  
[[Category:Source code]]
+
[[Category:source code]]

Latest revision as of 03:42, 27 October 2011

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

/*	SCCS Id: @(#)spell.c	3.4	2003/01/17	*/
/*	Copyright (c) M. Stephenson 1988			  */
/* NetHack may be freely redistributed.  See license for details. */

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

#include "hack.h"

static NEARDATA schar delay;		/* moves left for this spell */
static NEARDATA struct obj *book;	/* last/current book being xscribed */

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

/* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */
#define SPELLMENU_CAST (-2)
#define SPELLMENU_VIEW (-1)

#define KEEN 20000
#define MAX_SPELL_STUDY 3
#define incrnknow(spell)        spl_book[spell].sp_know = KEEN

#define spellev(spell)		spl_book[spell].sp_lev
#define spellname(spell)	OBJ_NAME(objects[spellid(spell)])
#define spellet(spell)	\
	((char)((spell < 26) ? ('a' + spell) : ('A' + spell - 26)))

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.

STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P));
STATIC_DCL boolean FDECL(cursed_book, (struct obj *bp));
STATIC_DCL boolean FDECL(confused_book, (struct obj *));
STATIC_DCL void FDECL(deadbook, (struct obj *));
STATIC_PTR int NDECL(learn);
STATIC_DCL boolean FDECL(getspell, (int *));
STATIC_DCL boolean FDECL(dospellmenu, (const char *,int,int *));
STATIC_DCL int FDECL(percent_success, (int));
STATIC_DCL int NDECL(throwspell);
STATIC_DCL void NDECL(cast_protection);
STATIC_DCL void FDECL(spell_backfire, (int));
STATIC_DCL const char *FDECL(spelltypemnemonic, (int));
STATIC_DCL int FDECL(isqrt, (int));

Now are the function declarations.

/* The roles[] table lists the role-specific values for tuning
* percent_success().
*
* Reasoning:
*   spelbase, spelheal:
*	Arc are aware of magic through historical research
*	Bar abhor magic (Conan finds it "interferes with his animal instincts")
*	Cav are ignorant to magic
*	Hea are very aware of healing magic through medical research
*	Kni are moderately aware of healing from Paladin training
*	Mon use magic to attack and defend in lieu of weapons and armor
*	Pri are very aware of healing magic through theological research
*	Ran avoid magic, preferring to fight unseen and unheard
*	Rog are moderately aware of magic through trickery
*	Sam have limited magical awareness, prefering meditation to conjuring
*	Tou are aware of magic from all the great films they have seen
*	Val have limited magical awareness, prefering fighting
*	Wiz are trained mages
*
*	The arms penalty is lessened for trained fighters Bar, Kni, Ran,
*	Sam, Val -
*	the penalty is its metal interference, not encumbrance.
*	The `spelspec' is a single spell which is fundamentally easier
*	 for that role to cast.
*
*  spelspec, spelsbon:
*	Arc map masters (SPE_MAGIC_MAPPING)
*	Bar fugue/berserker (SPE_HASTE_SELF)
*	Cav born to dig (SPE_DIG)
*	Hea to heal (SPE_CURE_SICKNESS)
*	Kni to turn back evil (SPE_TURN_UNDEAD)
*	Mon to preserve their abilities (SPE_RESTORE_ABILITY)
*	Pri to bless (SPE_REMOVE_CURSE)
*	Ran to hide (SPE_INVISIBILITY)
*	Rog to find loot (SPE_DETECT_TREASURE)
*	Sam to be At One (SPE_CLAIRVOYANCE)
*	Tou to smile (SPE_CHARM_MONSTER)
*	Val control the cold (SPE_CONE_OF_COLD)
*	Wiz all really, but SPE_MAGIC_MISSILE is their party trick
*
*	See percent_success() below for more comments.
*
*  uarmbon, uarmsbon, uarmhbon, uarmgbon, uarmfbon:
*	Fighters find body armour & shield a little less limiting.
*	Headgear, Gauntlets and Footwear are not role-specific (but
*	still have an effect, except helm of brilliance, which is designed
*	to permit magic-use).
*/

#define uarmhbon 4 /* Metal helmets interfere with the mind */
#define uarmgbon 6 /* Casting channels through the hands */
#define uarmfbon 2 /* All metal interferes to some degree */

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

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

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

/* convert a letter into a number in the range 0..51, or -1 if not a letter */
STATIC_OVL int
spell_let_to_idx(ilet)
char ilet;
{
int indx;

indx = ilet - 'a';
if (indx >= 0 && indx < 26) return indx;
indx = ilet - 'A';
if (indx >= 0 && indx < 26) return indx + 26;
return -1;
}

cursed_book

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

/* TRUE: book should be destroyed by caller */
STATIC_OVL boolean
cursed_book(bp)
	struct obj *bp;
{
	int lev = objects[bp->otyp].oc_level;

	switch(rn2(lev)) {
	case 0:
		You_feel("a wrenching sensation.");
		tele();		/* teleport him */
		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.

	case 1:
		You_feel("threatened.");
		aggravate();
		break;

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

	case 2:
		make_blinded(Blinded + rn1(100,250),TRUE);
		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 250 to 349 turns! Because of the Blinded + part, if you are already blind, then you will remain blind for 250 to 349 more turns. (Ignore that there seems to be no way to read a spellbook if you are already blind.)

	case 3:
		take_gold();
		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.

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

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

	case 5:
		pline_The("book was coated with contact poison!");
		if (uarmg) {
		    if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {
			Your("gloves seem unaffected.");
		    } else if (uarmg->oeroded2 < MAX_ERODE) {
			if (uarmg->greased) {
			    grease_protect(uarmg, "gloves", &youmonst);
			} else {
			    Your("gloves corrode%s!",
				 uarmg->oeroded2+1 == MAX_ERODE ?
				 " completely" : uarmg->oeroded2 ?
				 " further" : "");
			    uarmg->oeroded2++;
			}
		    } else
			Your("gloves %s completely corroded.",
			     Blind ? "feel" : "look");
		    break;
		}
		/* temp disable in_use; death should not destroy the book */
		bp->in_use = FALSE;
		losestr(Poison_resistance ? rn1(2,1) : rn1(4,3));
		losehp(rnd(Poison_resistance ? 6 : 10),
		       "contact-poisoned spellbook", KILLED_BY_AN);
		bp->in_use = TRUE;
		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 6 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.
	case 6:
		if(Antimagic) {
		    shieldeff(u.ux, u.uy);
		    pline_The("book %s, but you are unharmed!", explodes);
		} else {
		    pline("As you read the book, it %s in your %s!",
			  explodes, body_part(FACE));
		    losehp(2*rnd(10)+5, "exploding rune", KILLED_BY_AN);
		}
		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 25 hp.

	default:
		rndcurse();
		break;
	}
	return FALSE;
}

rndcurse curses few items in inventory randomly.

confused_book

/* study while confused: returns TRUE if the book is destroyed */
STATIC_OVL boolean
confused_book(spellbook)
struct obj *spellbook;
{
	boolean gone = FALSE;

	if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
	    spellbook->in_use = TRUE;	/* in case called from learn */
	    pline(
	"Being confused you have difficulties in controlling your actions.");
	    display_nhwindow(WIN_MESSAGE, FALSE);
	    You("accidentally tear the spellbook to pieces.");
	    if (!objects[spellbook->otyp].oc_name_known &&
		!objects[spellbook->otyp].oc_uname)
		docall(spellbook);
	    useup(spellbook);
	    gone = TRUE;
	} else {
	    You("find yourself reading the %s line over and over again.",
		spellbook == book ? "next" : "first");
	}
	return gone;
}

deadbook

/* special effects for The Book of the Dead */
STATIC_OVL void
deadbook(book2)
struct obj *book2;
{
struct monst *mtmp, *mtmp2;
coord mm;

You("turn the pages of the Book of the Dead...");
makeknown(SPE_BOOK_OF_THE_DEAD);
/* KMH -- Need ->known to avoid "_a_ Book of the Dead" */
book2->known = 1;
if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
	register struct obj *otmp;
	register boolean arti1_primed = FALSE, arti2_primed = FALSE,
			 arti_cursed = FALSE;

	if(book2->cursed) {
	    pline_The("runes appear scrambled.  You can't read them!");
	    return;
	}

	if(!u.uhave.bell || !u.uhave.menorah) {
	    pline("A chill runs down your %s.", body_part(SPINE));
	    if(!u.uhave.bell) You_hear("a faint chime...");
	    if(!u.uhave.menorah) pline("Vlad's doppelganger is amused.");
	    return;
	}

	for(otmp = invent; otmp; otmp = otmp->nobj) {
	    if(otmp->otyp == CANDELABRUM_OF_INVOCATION &&
	       otmp->spe == 7 && otmp->lamplit) {
		if(!otmp->cursed) arti1_primed = TRUE;
		else arti_cursed = TRUE;
	    }
	    if(otmp->otyp == BELL_OF_OPENING &&
	       (moves - otmp->age) < 5L) { /* you rang it recently */
		if(!otmp->cursed) arti2_primed = TRUE;
		else arti_cursed = TRUE;
	    }
	}

	if(arti_cursed) {
	    pline_The("invocation fails!");
	    pline("At least one of your artifacts is cursed...");
	} else if(arti1_primed && arti2_primed) {
	    unsigned soon = (unsigned) d(2,6);	/* time til next intervene() */

	    /* successful invocation */
	    mkinvokearea();
	    u.uevent.invoked = 1;
	    /* in case you haven't killed the Wizard yet, behave as if
	       you just did */
	    u.uevent.udemigod = 1;	/* wizdead() */
	    if (!u.udg_cnt || u.udg_cnt > soon) u.udg_cnt = soon;
	} else {	/* at least one artifact not prepared properly */
	    You("have a feeling that %s is amiss...", something);
	    goto raise_dead;
	}
	return;
}

/* when not an invocation situation */
if (book2->cursed) {
raise_dead:

	You("raised the dead!");
	/* first maybe place a dangerous adversary */
	if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH],
					u.ux, u.uy, NO_MINVENT)) != 0 ||
			(mtmp = makemon(&mons[PM_NALFESHNEE],
					u.ux, u.uy, NO_MINVENT)) != 0)) {
	    mtmp->mpeaceful = 0;
	    set_malign(mtmp);
	}
	/* next handle the affect on things you're carrying */
	(void) unturn_dead(&youmonst);
	/* last place some monsters around you */
	mm.x = u.ux;
	mm.y = u.uy;
	mkundead(&mm, TRUE, NO_MINVENT);
} else if(book2->blessed) {
	for(mtmp = fmon; mtmp; mtmp = mtmp2) {
	    mtmp2 = mtmp->nmon;		/* tamedog() changes chain */
	    if (DEADMONSTER(mtmp)) continue;

	    if (is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {
		mtmp->mpeaceful = TRUE;
		if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
		   && distu(mtmp->mx, mtmp->my) < 4)
		    if (mtmp->mtame) {
			if (mtmp->mtame < 20)
			    mtmp->mtame++;
		    } else
			(void) tamedog(mtmp, (struct obj *)0);
		else monflee(mtmp, 0, FALSE, TRUE);
	    }
	}
} else {
	switch(rn2(3)) {
	case 0:
	    Your("ancestors are annoyed with you!");
	    break;
	case 1:
	    pline_The("headstones in the cemetery begin to move!");
	    break;
	default:
	    pline("Oh my!  Your name appears in the book!");
	}
}
return;
}

learn

STATIC_PTR int
learn()
{
	int i;
	short booktype;
	char splname[BUFSZ];
	boolean costly = TRUE;

	/* JDS: lenses give 50% faster reading; 33% smaller read time */
	if (delay && ublindf && ublindf->otyp == LENSES && rn2(2)) delay++;
	if (Confusion) {		/* became confused while learning */
	    (void) confused_book(book);
	    book = 0;			/* no longer studying */
	    nomul(delay);		/* remaining delay is uninterrupted */
	    delay = 0;
	    return(0);
	}
	if (delay) {	/* not if (delay++), so at end delay == 0 */
	    delay++;
	    return(1); /* still busy */
	}
	exercise(A_WIS, TRUE);		/* you're studying. */
	booktype = book->otyp;
	if(booktype == SPE_BOOK_OF_THE_DEAD) {
	    deadbook(book);
	    return(0);
	}

	Sprintf(splname, objects[booktype].oc_name_known ?
			"\"%s\"" : "the \"%s\" spell",
		OBJ_NAME(objects[booktype]));
	for (i = 0; i < MAXSPELL; i++)  {
		if (spellid(i) == booktype)  {
			if (book->spestudied > MAX_SPELL_STUDY) {
			    pline("This spellbook is too faint to be read any more.");
			    book->otyp = booktype = SPE_BLANK_PAPER;
			} else if (spellknow(i) <= 1000) {
			    Your("knowledge of %s is keener.", splname);
			    incrnknow(i);
			    book->spestudied++;
			    exercise(A_WIS,TRUE);       /* extra study */
			} else { /* 1000 < spellknow(i) <= MAX_SPELL_STUDY */
			    You("know %s quite well already.", splname);
			    costly = FALSE;
			}
			/* make book become known even when spell is already
			   known, in case amnesia made you forget the book */
			makeknown((int)booktype);
			break;
		} else if (spellid(i) == NO_SPELL)  {
			spl_book[i].sp_id = booktype;
			spl_book[i].sp_lev = objects[booktype].oc_level;
			incrnknow(i);
			book->spestudied++;
			You(i > 0 ? "add %s to your repertoire." : "learn %s.",
			    splname);
			makeknown((int)booktype);
			break;
		}
	}
	if (i == MAXSPELL) impossible("Too many spells memorized!");

	if (book->cursed) {	/* maybe a demon cursed it */
	    if (cursed_book(book)) {
		useup(book);
		book = 0;
		return 0;
	    }
	}
	if (costly) check_unpaid(book);
	book = 0;
	return(0);
}

study_book

int
study_book(spellbook)
register struct obj *spellbook;
{
	register int	 booktype = spellbook->otyp;
	register boolean confused = (Confusion != 0);
	boolean too_hard = FALSE;

	if (delay && !confused && spellbook == book &&
		    /* handle the sequence: start reading, get interrupted,
		       have book become erased somehow, resume reading it */
		    booktype != SPE_BLANK_PAPER) {
		You("continue your efforts to memorize the spell.");
	} else {
		/* KMH -- Simplified this code */
		if (booktype == SPE_BLANK_PAPER) {
			pline("This spellbook is all blank.");
			makeknown(booktype);
			return(1);
		}
		switch (objects[booktype].oc_level) {
		 case 1:
		 case 2:
			delay = -objects[booktype].oc_delay;
			break;
		 case 3:
		 case 4:
			delay = -(objects[booktype].oc_level - 1) *
				objects[booktype].oc_delay;
			break;
		 case 5:
		 case 6:
			delay = -objects[booktype].oc_level *
				objects[booktype].oc_delay;
			break;
		 case 7:
			delay = -8 * objects[booktype].oc_delay;
			break;
		 default:
			impossible("Unknown spellbook level %d, book %d;",
				objects[booktype].oc_level, booktype);
			return 0;
		}

		/* Books are often wiser than their readers (Rus.) */
		spellbook->in_use = TRUE;
		if (!spellbook->blessed &&
		    spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
		    if (spellbook->cursed) {
			too_hard = TRUE;
		    } else {
			/* uncursed - chance to fail */
			int read_ability = ACURR(A_INT) + 4 + u.ulevel/2
			    - 2*objects[booktype].oc_level
			    + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);
			/* only wizards know if a spell is too difficult */
			if (Role_if(PM_WIZARD) && read_ability < 20 &&
			    !confused) {
			    char qbuf[QBUFSZ];
			    Sprintf(qbuf,
		      "This spellbook is %sdifficult to comprehend. Continue?",
				    (read_ability < 12 ? "very " : ""));
			    if (yn(qbuf) != 'y') {
				spellbook->in_use = FALSE;
				return(1);
			    }
			}
			/* its up to random luck now */
			if (rnd(20) > read_ability) {
			    too_hard = TRUE;
			}
		    }
		}

		if (too_hard) {
		    boolean gone = cursed_book(spellbook);

		    nomul(delay);			/* study time */
		    delay = 0;
		    if(gone || !rn2(3)) {
			if (!gone) pline_The("spellbook crumbles to dust!");
			if (!objects[spellbook->otyp].oc_name_known &&
				!objects[spellbook->otyp].oc_uname)
			    docall(spellbook);
			useup(spellbook);
		    } else
			spellbook->in_use = FALSE;
		    return(1);
		} else if (confused) {
		    if (!confused_book(spellbook)) {
			spellbook->in_use = FALSE;
		    }
		    nomul(delay);
		    delay = 0;
		    return(1);
		}
		spellbook->in_use = FALSE;

		You("begin to %s the runes.",
		    spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" :
		    "memorize");
	}

	book = spellbook;
	set_occupation(learn, "studying", 0);
	return(1);
}

book_disappears

/* a spellbook has been destroyed or the character has changed levels;
the stored address for the current book is no longer valid */
void
book_disappears(obj)
struct obj *obj;
{
	if (obj == book) book = (struct obj *)0;
}

book_substitution

/* renaming an object usually results in it having a different address;
so the sequence start reading, get interrupted, name the book, resume
reading would read the "new" book from scratch */
void
book_substitution(old_obj, new_obj)
struct obj *old_obj, *new_obj;
{
	if (old_obj == book) book = new_obj;
}

age_spells

/* called from moveloop() */
void
age_spells()
{
	int i;
	/*
	 * The time relative to the hero (a pass through move
	 * loop) causes all spell knowledge to be decremented.
	 * The hero's speed, rest status, conscious status etc.
	 * does not alter the loss of memory.
	 */
	for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++)
	    if (spellknow(i))
		decrnknow(i);
	return;
}

getspell

/*
* Return TRUE if a spell was picked, with the spell index in the return
* parameter.  Otherwise return FALSE.
*/
STATIC_OVL boolean
getspell(spell_no)
	int *spell_no;
{
	int nspells, idx;
	char ilet, lets[BUFSZ], qbuf[QBUFSZ];

	if (spellid(0) == NO_SPELL)  {
	    You("don't know any spells right now.");
	    return FALSE;
	}
	if (flags.menu_style == MENU_TRADITIONAL) {
	    /* we know there is at least 1 known spell */
	    for (nspells = 1; nspells < MAXSPELL
			    && spellid(nspells) != NO_SPELL; nspells++)
		continue;

	    if (nspells == 1)  Strcpy(lets, "a");
	    else if (nspells < 27)  Sprintf(lets, "a-%c", 'a' + nspells - 1);
	    else if (nspells == 27)  Sprintf(lets, "a-zA");
	    else Sprintf(lets, "a-zA-%c", 'A' + nspells - 27);

	    for(;;)  {
		Sprintf(qbuf, "Cast which spell? [%s ?]", lets);
		if ((ilet = yn_function(qbuf, (char *)0, '\0')) == '?')
		    break;

		if (index(quitchars, ilet))
		    return FALSE;

		idx = spell_let_to_idx(ilet);
		if (idx >= 0 && idx < nspells) {
		    *spell_no = idx;
		    return TRUE;
		} else
		    You("don't know that spell.");
	    }
	}
	return dospellmenu("Choose which spell to cast",
			   SPELLMENU_CAST, spell_no);
}

docast

/* the 'Z' command -- cast a spell */
int
docast()
{
	int spell_no;

	if (getspell(&spell_no))
	    return spelleffects(spell_no, FALSE);
	return 0;
}

spelltypemnemonic

STATIC_OVL const char *
spelltypemnemonic(skill)
int skill;
{
	switch (skill) {
	    case P_ATTACK_SPELL:
		return "attack";
	    case P_HEALING_SPELL:
		return "healing";
	    case P_DIVINATION_SPELL:
		return "divination";
	    case P_ENCHANTMENT_SPELL:
		return "enchantment";
	    case P_CLERIC_SPELL:
		return "clerical";
	    case P_ESCAPE_SPELL:
		return "escape";
	    case P_MATTER_SPELL:
		return "matter";
	    default:
		impossible("Unknown spell skill, %d;", skill);
		return "";
	}
}

spell_skilltype

int
spell_skilltype(booktype)
int booktype;
{
	return (objects[booktype].oc_skill);
}

cast_protection

STATIC_OVL void
cast_protection()
{
	int loglev = 0;
	int l = u.ulevel;
	int natac = u.uac - u.uspellprot;
	int gain;

	/* loglev=log2(u.ulevel)+1 (1..5) */
	while (l) {
	    loglev++;
	    l /= 2;
	}

	/* The more u.uspellprot you already have, the less you get,
	 * and the better your natural ac, the less you get.
	 *
	 *	LEVEL AC    SPELLPROT from sucessive SPE_PROTECTION casts
	 *      1     10    0,  1,  2,  3,  4
	 *      1      0    0,  1,  2,  3
	 *      1    -10    0,  1,  2
	 *      2-3   10    0,  2,  4,  5,  6,  7,  8
	 *      2-3    0    0,  2,  4,  5,  6
	 *      2-3  -10    0,  2,  3,  4
	 *      4-7   10    0,  3,  6,  8,  9, 10, 11, 12
	 *      4-7    0    0,  3,  5,  7,  8,  9
	 *      4-7  -10    0,  3,  5,  6
	 *      7-15 -10    0,  3,  5,  6
	 *      8-15  10    0,  4,  7, 10, 12, 13, 14, 15, 16
	 *      8-15   0    0,  4,  7,  9, 10, 11, 12
	 *      8-15 -10    0,  4,  6,  7,  8
	 *     16-30  10    0,  5,  9, 12, 14, 16, 17, 18, 19, 20
	 *     16-30   0    0,  5,  9, 11, 13, 14, 15
	 *     16-30 -10    0,  5,  8,  9, 10
	 */
	gain = loglev - (int)u.uspellprot / (4 - min(3,(10 - natac)/10));

	if (gain > 0) {
	    if (!Blind) {
		const char *hgolden = hcolor(NH_GOLDEN);

		if (u.uspellprot)
		    pline_The("%s haze around you becomes more dense.",
			      hgolden);
		else
		    pline_The("%s around you begins to shimmer with %s haze.",
			/*[ what about being inside solid rock while polyd? ]*/
			(Underwater || Is_waterlevel(&u.uz)) ? "water" : "air",
			      an(hgolden));
	    }
	    u.uspellprot += gain;
	    u.uspmtime =
		P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT ? 20 : 10;
	    if (!u.usptime)
		u.usptime = u.uspmtime;
	    find_ac();
	} else {
	    Your("skin feels warm for a moment.");
	}
}

spell_backfire

/* attempting to cast a forgotten spell will cause disorientation */
STATIC_OVL void
spell_backfire(spell)
int spell;
{
long duration = (long)((spellev(spell) + 1) * 3);	 /* 6..24 */

/* prior to 3.4.1, the only effect was confusion; it still predominates */
switch (rn2(10)) {
case 0:
case 1:
case 2:
case 3: make_confused(duration, FALSE);			/* 40% */
	    break;
case 4:
case 5:
case 6: make_confused(2L * duration / 3L, FALSE);		/* 30% */
	    make_stunned(duration / 3L, FALSE);
	    break;
case 7:
case 8: make_stunned(2L * duration / 3L, FALSE);		/* 20% */
	    make_confused(duration / 3L, FALSE);
	    break;
case 9: make_stunned(duration, FALSE);			/* 10% */
	    break;
}
return;
}

spelleffects

int
spelleffects(spell, atme)
int spell;
boolean atme;
{
	int energy, damage, chance, n, intell;
	int skill, role_skill;
	boolean confused = (Confusion != 0);
	struct obj *pseudo;
	coord cc;

	/*
	 * Spell casting no longer affects knowledge of the spell. A
	 * decrement of spell knowledge is done every turn.
	 */
	if (spellknow(spell) <= 0) {
	    Your("knowledge of this spell is twisted.");
	    pline("It invokes nightmarish images in your mind...");
	    spell_backfire(spell);
	    return(0);
	} else if (spellknow(spell) <= 100) {
	    You("strain to recall the spell.");
	} else if (spellknow(spell) <= 1000) {
	    Your("knowledge of this spell is growing faint.");
	}
	energy = (spellev(spell) * 5);    /* 5 <= energy <= 35 */

	if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) {
		You("are too hungry to cast that spell.");
		return(0);
	} else if (ACURR(A_STR) < 4)  {
		You("lack the strength to cast spells.");
		return(0);
	} else if(check_capacity(
		"Your concentration falters while carrying so much stuff.")) {
	    return (1);
	} else if (!freehand()) {
		Your("arms are not free to cast!");
		return (0);
	}

	if (u.uhave.amulet) {
		You_feel("the amulet draining your energy away.");
		energy += rnd(2*energy);
	}
	if(energy > u.uen)  {
		You("don't have enough energy to cast that spell.");
		return(0);
	} else {
		if (spellid(spell) != SPE_DETECT_FOOD) {
			int hungr = energy * 2;

			/* If hero is a wizard, their current intelligence
			 * (bonuses + temporary + current)
			 * affects hunger reduction in casting a spell.
			 * 1. int = 17-18 no reduction
			 * 2. int = 16    1/4 hungr
			 * 3. int = 15    1/2 hungr
			 * 4. int = 1-14  normal reduction
			 * The reason for this is:
			 * a) Intelligence affects the amount of exertion
			 * in thinking.
			 * b) Wizards have spent their life at magic and
			 * understand quite well how to cast spells.
			 */
			intell = acurr(A_INT);
			if (!Role_if(PM_WIZARD)) intell = 10;
			switch (intell) {
				case 25: case 24: case 23: case 22:
				case 21: case 20: case 19: case 18:
				case 17: hungr = 0; break;
				case 16: hungr /= 4; break;
				case 15: hungr /= 2; break;
			}
			/* don't put player (quite) into fainting from
			 * casting a spell, particularly since they might
			 * not even be hungry at the beginning; however,
			 * this is low enough that they must eat before
			 * casting anything else except detect food
			 */
			if (hungr > u.uhunger-3)
				hungr = u.uhunger-3;
			morehungry(hungr);
		}
	}

	chance = percent_success(spell);
	if (confused || (rnd(100) > chance)) {
		You("fail to cast the spell correctly.");
		u.uen -= energy / 2;
		flags.botl = 1;
		return(1);
	}

	u.uen -= energy;
	flags.botl = 1;
	exercise(A_WIS, TRUE);
	/* pseudo is a temporary "false" object containing the spell stats */
	pseudo = mksobj(spellid(spell), FALSE, FALSE);
	pseudo->blessed = pseudo->cursed = 0;
	pseudo->quan = 20L;			/* do not let useup get it */
	/*
	 * Find the skill the hero has in a spell type category.
	 * See spell_skilltype for categories.
	 */
	skill = spell_skilltype(pseudo->otyp);
	role_skill = P_SKILL(skill);

	switch(pseudo->otyp)  {
	/*
	 * At first spells act as expected.  As the hero increases in skill
	 * with the appropriate spell type, some spells increase in their
	 * effects, e.g. more damage, further distance, and so on, without
	 * additional cost to the spellcaster.
	 */
	case SPE_CONE_OF_COLD:
	case SPE_FIREBALL:
	    if (role_skill >= P_SKILLED) {
	        if (throwspell()) {
		    cc.x=u.dx;cc.y=u.dy;
		    n=rnd(8)+1;
		    while(n--) {
			if(!u.dx && !u.dy && !u.dz) {
			    if ((damage = zapyourself(pseudo, TRUE)) != 0) {
				char buf[BUFSZ];
				Sprintf(buf, "zapped %sself with a spell", uhim());
				losehp(damage, buf, NO_KILLER_PREFIX);
			    }
			} else {
			    explode(u.dx, u.dy,
				    pseudo->otyp - SPE_MAGIC_MISSILE + 10,
				    u.ulevel/2 + 1 + spell_damage_bonus(), 0,
					(pseudo->otyp == SPE_CONE_OF_COLD) ?
						EXPL_FROSTY : EXPL_FIERY);
			}
			u.dx = cc.x+rnd(3)-2; u.dy = cc.y+rnd(3)-2;
			if (!isok(u.dx,u.dy) || !cansee(u.dx,u.dy) ||
			    IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) {
			    /* Spell is reflected back to center */
			    u.dx = cc.x;
			    u.dy = cc.y;
		        }
		    }
		}
		break;
	    } /* else fall through... */

	/* these spells are all duplicates of wand effects */
	case SPE_FORCE_BOLT:
	case SPE_SLEEP:
	case SPE_MAGIC_MISSILE:
	case SPE_KNOCK:
	case SPE_SLOW_MONSTER:
	case SPE_WIZARD_LOCK:
	case SPE_DIG:
	case SPE_TURN_UNDEAD:
	case SPE_POLYMORPH:
	case SPE_TELEPORT_AWAY:
	case SPE_CANCELLATION:
	case SPE_FINGER_OF_DEATH:
	case SPE_LIGHT:
	case SPE_DETECT_UNSEEN:
	case SPE_HEALING:
	case SPE_EXTRA_HEALING:
	case SPE_DRAIN_LIFE:
	case SPE_STONE_TO_FLESH:
		if (!(objects[pseudo->otyp].oc_dir == NODIR)) {
			if (atme) u.dx = u.dy = u.dz = 0;
			else if (!getdir((char *)0)) {
			    /* getdir cancelled, re-use previous direction */
			    pline_The("magical energy is released!");
			}
			if(!u.dx && !u.dy && !u.dz) {
			    if ((damage = zapyourself(pseudo, TRUE)) != 0) {
				char buf[BUFSZ];
				Sprintf(buf, "zapped %sself with a spell", uhim());
				losehp(damage, buf, NO_KILLER_PREFIX);
			    }
			} else weffects(pseudo);
		} else weffects(pseudo);
		update_inventory();	/* spell may modify inventory */
		break;

	/* these are all duplicates of scroll effects */
	case SPE_REMOVE_CURSE:
	case SPE_CONFUSE_MONSTER:
	case SPE_DETECT_FOOD:
	case SPE_CAUSE_FEAR:
		/* high skill yields effect equivalent to blessed scroll */
		if (role_skill >= P_SKILLED) pseudo->blessed = 1;
		/* fall through */
	case SPE_CHARM_MONSTER:
	case SPE_MAGIC_MAPPING:
	case SPE_CREATE_MONSTER:
	case SPE_IDENTIFY:
		(void) seffects(pseudo);
		break;

	/* these are all duplicates of potion effects */
	case SPE_HASTE_SELF:
	case SPE_DETECT_TREASURE:
	case SPE_DETECT_MONSTERS:
	case SPE_LEVITATION:
	case SPE_RESTORE_ABILITY:
		/* high skill yields effect equivalent to blessed potion */
		if (role_skill >= P_SKILLED) pseudo->blessed = 1;
		/* fall through */
	case SPE_INVISIBILITY:
		(void) peffects(pseudo);
		break;

	case SPE_CURE_BLINDNESS:
		healup(0, 0, FALSE, TRUE);
		break;
	case SPE_CURE_SICKNESS:
		if (Sick) You("are no longer ill.");
		if (Slimed) {
		    pline_The("slime disappears!");
		    Slimed = 0;
		 /* flags.botl = 1; -- healup() handles this */
		}
		healup(0, 0, TRUE, FALSE);
		break;
	case SPE_CREATE_FAMILIAR:
		(void) make_familiar((struct obj *)0, u.ux, u.uy, FALSE);
		break;
	case SPE_CLAIRVOYANCE:
		if (!BClairvoyant)
		    do_vicinity_map();
		/* at present, only one thing blocks clairvoyance */
		else if (uarmh && uarmh->otyp == CORNUTHAUM)
		    You("sense a pointy hat on top of your %s.",
			body_part(HEAD));
		break;
	case SPE_PROTECTION:
		cast_protection();
		break;
	case SPE_JUMPING:
		if (!jump(max(role_skill,1)))
			pline(nothing_happens);
		break;
	default:
		impossible("Unknown spell %d attempted.", spell);
		obfree(pseudo, (struct obj *)0);
		return(0);
	}

	/* gain skill for successful cast */
	use_skill(skill, spellev(spell));

	obfree(pseudo, (struct obj *)0);	/* now, get rid of it */
	return(1);
}

throwspell

/* Choose location where spell takes effect. */
STATIC_OVL int
throwspell()
{
	coord cc;

	if (u.uinwater) {
	    pline("You're joking! In this weather?"); return 0;
	} else if (Is_waterlevel(&u.uz)) {
	    You("had better wait for the sun to come out."); return 0;
	}

	pline("Where do you want to cast the spell?");
	cc.x = u.ux;
	cc.y = u.uy;
	if (getpos(&cc, TRUE, "the desired position") < 0)
	    return 0;	/* user pressed ESC */
	/* The number of moves from hero to where the spell drops.*/
	if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) {
	    pline_The("spell dissipates over the distance!");
	    return 0;
	} else if (u.uswallow) {
	    pline_The("spell is cut short!");
	    exercise(A_WIS, FALSE); /* What were you THINKING! */
	    u.dx = 0;
	    u.dy = 0;
	    return 1;
	} else if (!cansee(cc.x, cc.y) || IS_STWALL(levl[cc.x][cc.y].typ)) {
	    Your("mind fails to lock onto that location!");
	    return 0;
	} else {
	    u.dx=cc.x;
	    u.dy=cc.y;
	    return 1;
	}
}

losespells

void
losespells()
{
	boolean confused = (Confusion != 0);
	int  n, nzap, i;

	book = 0;
	for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; n++)
		continue;
	if (n) {
		nzap = rnd(n) + confused ? 1 : 0;
		if (nzap > n) nzap = n;
		for (i = n - nzap; i < n; i++) {
		    spellid(i) = NO_SPELL;
		    exercise(A_WIS, FALSE);	/* ouch! */
		}
	}
}

dovspell

/* the '+' command -- view known spells */
int
dovspell()
{
	char qbuf[QBUFSZ];
	int splnum, othnum;
	struct spell spl_tmp;

	if (spellid(0) == NO_SPELL)
	    You("don't know any spells right now.");
	else {
	    while (dospellmenu("Currently known spells",
			       SPELLMENU_VIEW, &splnum)) {
		Sprintf(qbuf, "Reordering spells; swap '%c' with",
			spellet(splnum));
		if (!dospellmenu(qbuf, splnum, &othnum)) break;

		spl_tmp = spl_book[splnum];
		spl_book[splnum] = spl_book[othnum];
		spl_book[othnum] = spl_tmp;
	    }
	}
	return 0;
}

dospellmenu

STATIC_OVL boolean
dospellmenu(prompt, splaction, spell_no)
const char *prompt;
int splaction;	/* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */
int *spell_no;
{
	winid tmpwin;
	int i, n, how;
	char buf[BUFSZ];
	menu_item *selected;
	anything any;

	tmpwin = create_nhwindow(NHW_MENU);
	start_menu(tmpwin);
	any.a_void = 0;		/* zero out all bits */

	/*
	 * The correct spacing of the columns depends on the
	 * following that (1) the font is monospaced and (2)
	 * that selection letters are pre-pended to the given
	 * string and are of the form "a - ".
	 *
	 * To do it right would require that we implement columns
	 * in the window-ports (say via a tab character).
	 */
	if (!iflags.menu_tab_sep)
		Sprintf(buf, "%-20s     Level  %-12s Fail", "    Name", "Category");
	else
		Sprintf(buf, "Name\tLevel\tCategory\tFail");
	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, buf, MENU_UNSELECTED);
	for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) {
		Sprintf(buf, iflags.menu_tab_sep ?
			"%s\t%-d%s\t%s\t%-d%%" : "%-20s  %2d%s   %-12s %3d%%",
			spellname(i), spellev(i),
			spellknow(i) ? " " : "*",
			spelltypemnemonic(spell_skilltype(spellid(i))),
			100 - percent_success(i));

		any.a_int = i+1;	/* must be non-zero */
		add_menu(tmpwin, NO_GLYPH, &any,
			 spellet(i), 0, ATR_NONE, buf,
			 (i == splaction) ? MENU_SELECTED : MENU_UNSELECTED);
	      }
	end_menu(tmpwin, prompt);

	how = PICK_ONE;
	if (splaction == SPELLMENU_VIEW && spellid(1) == NO_SPELL)
	    how = PICK_NONE;	/* only one spell => nothing to swap with */
	n = select_menu(tmpwin, how, &selected);
	destroy_nhwindow(tmpwin);
	if (n > 0) {
		*spell_no = selected[0].item.a_int - 1;
		/* menu selection for `PICK_ONE' does not
		   de-select any preselected entry */
		if (n > 1 && *spell_no == splaction)
		    *spell_no = selected[1].item.a_int - 1;
		free((genericptr_t)selected);
		/* default selection of preselected spell means that
		   user chose not to swap it with anything */
		if (*spell_no == splaction) return FALSE;
		return TRUE;
	} else if (splaction >= 0) {
	    /* explicit de-selection of preselected spell means that
	       user is still swapping but not for the current spell */
	    *spell_no = splaction;
	    return TRUE;
	}
	return FALSE;
}

isqrt

/* Integer square root function without using floating point. */
STATIC_OVL int
isqrt(val)
int val;
{
int rt = 0;
int odd = 1;
while(val >= odd) {
	val = val-odd;
	odd = odd+2;
	rt = rt + 1;
}
return rt;
}

percent_success

STATIC_OVL int
percent_success(spell)
int spell;
{
	/* Intrinsic and learned ability are combined to calculate
	 * the probability of player's success at cast a given spell.
	 */
	int chance, splcaster, special, statused;
	int difficulty;
	int skill;

	/* Calculate intrinsic ability (splcaster) */

	splcaster = urole.spelbase;
	special = urole.spelheal;
	statused = ACURR(urole.spelstat);

	if (uarm && is_metallic(uarm))
	    splcaster += (uarmc && uarmc->otyp == ROBE) ?
		urole.spelarmr/2 : urole.spelarmr;
	else if (uarmc && uarmc->otyp == ROBE)
	    splcaster -= urole.spelarmr;
	if (uarms) splcaster += urole.spelshld;

	if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE)
		splcaster += uarmhbon;
	if (uarmg && is_metallic(uarmg)) splcaster += uarmgbon;
	if (uarmf && is_metallic(uarmf)) splcaster += uarmfbon;

	if (spellid(spell) == urole.spelspec)
		splcaster += urole.spelsbon;


	/* `healing spell' bonus */
	if (spellid(spell) == SPE_HEALING ||
	    spellid(spell) == SPE_EXTRA_HEALING ||
	    spellid(spell) == SPE_CURE_BLINDNESS ||
	    spellid(spell) == SPE_CURE_SICKNESS ||
	    spellid(spell) == SPE_RESTORE_ABILITY ||
	    spellid(spell) == SPE_REMOVE_CURSE) splcaster += special;

	if (splcaster > 20) splcaster = 20;

	/* Calculate learned ability */

	/* Players basic likelihood of being able to cast any spell
	 * is based of their `magic' statistic. (Int or Wis)
	 */
	chance = 11 * statused / 2;

	/*
	 * High level spells are harder.  Easier for higher level casters.
	 * The difficulty is based on the hero's level and their skill level
	 * in that spell type.
	 */
	skill = P_SKILL(spell_skilltype(spellid(spell)));
	skill = max(skill,P_UNSKILLED) - 1;	/* unskilled => 0 */
	difficulty= (spellev(spell)-1) * 4 - ((skill * 6) + (u.ulevel/3) + 1);

	if (difficulty > 0) {
		/* Player is too low level or unskilled. */
		chance -= isqrt(900 * difficulty + 2000);
	} else {
		/* Player is above level.  Learning continues, but the
		 * law of diminishing returns sets in quickly for
		 * low-level spells.  That is, a player quickly gains
		 * no advantage for raising level.
		 */
		int learning = 15 * -difficulty / spellev(spell);
		chance += learning > 20 ? 20 : learning;
	}

	/* Clamp the chance: >18 stat and advanced learning only help
	 * to a limit, while chances below "hopeless" only raise the
	 * specter of overflowing 16-bit ints (and permit wearing a
	 * shield to raise the chances :-).
	 */
	if (chance < 0) chance = 0;
	if (chance > 120) chance = 120;

	/* Wearing anything but a light shield makes it very awkward
	 * to cast a spell.  The penalty is not quite so bad for the
	 * player's role-specific spell.
	 */
	if (uarms && weight(uarms) > (int) objects[SMALL_SHIELD].oc_weight) {
		if (spellid(spell) == urole.spelspec) {
			chance /= 2;
		} else {
			chance /= 4;
		}
	}

	/* Finally, chance (based on player intell/wisdom and level) is
	 * combined with ability (based on player intrinsics and
	 * encumbrances).  No matter how intelligent/wise and advanced
	 * a player is, intrinsics and encumbrance can prevent casting;
	 * and no matter how able, learning is always required.
	 */
	chance = chance * (20-splcaster) / 15 - splcaster;

	/* Clamp to percentile */
	if (chance > 100) chance = 100;
	if (chance < 0) chance = 0;

	return chance;
}

initialspell

/* Learn a spell during creation of the initial inventory */
void
initialspell(obj)
struct obj *obj;
{
	int i;

	for (i = 0; i < MAXSPELL; i++) {
	    if (spellid(i) == obj->otyp) {
	         pline("Error: Spell %s already known.",
	         		OBJ_NAME(objects[obj->otyp]));
	         return;
	    }
	    if (spellid(i) == NO_SPELL)  {
	        spl_book[i].sp_id = obj->otyp;
	        spl_book[i].sp_lev = objects[obj->otyp].oc_level;
	        incrnknow(i);
	        return;
	    }
	}
	impossible("Too many spells memorized!");
	return;
}

/*spell.c*/