Source:NetHack 3.6.0/src/spell.c

From NetHackWiki
(Redirected from Source:Spell.c)
Jump to: navigation, search

Below is the full text to spell.c from the source code of NetHack 3.6.0. To link to a particular line, write [[Source:NetHack 3.6.0/src/spell.c#line123]], for example.

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.

Top of file

  1.  /* NetHack 3.6	spell.c	$NHDT-Date: 1447653429 2015/11/16 05:57:09 $  $NHDT-Branch: master $:$NHDT-Revision: 1.72 $ */
  2.  /*      Copyright (c) M. Stephenson 1988                          */
  3.  /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5.  #include "hack.h"
  6.  
  7.  /* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */
  8.  #define SPELLMENU_CAST (-2)
  9.  #define SPELLMENU_VIEW (-1)
  10.  #define SPELLMENU_SORT (MAXSPELL) /* special menu entry */
  11.  
  12.  /* spell retention period, in turns; at 10% of this value, player becomes
  13.     eligible to reread the spellbook and regain 100% retention (the threshold
  14.     used to be 1000 turns, which was 10% of the original 10000 turn retention
  15.     period but didn't get adjusted when that period got doubled to 20000) */
  16.  #define KEEN 20000
  17.  /* x: need to add 1 when used for reading a spellbook rather than for hero
  18.     initialization; spell memory is decremented at the end of each turn,
  19.     including the turn on which the spellbook is read; without the extra
  20.     increment, the hero used to get cheated out of 1 turn of retention */
  21.  #define incrnknow(spell, x) (spl_book[spell].sp_know = KEEN + (x))
  22.  
  23.  #define spellev(spell) spl_book[spell].sp_lev
  24.  #define spellname(spell) OBJ_NAME(objects[spellid(spell)])
  25.  #define spellet(spell) \
  26.      ((char) ((spell < 26) ? ('a' + spell) : ('A' + spell - 26)))
  27.  
  28.  STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P));
  29.  STATIC_DCL boolean FDECL(cursed_book, (struct obj * bp));
  30.  STATIC_DCL boolean FDECL(confused_book, (struct obj *));
  31.  STATIC_DCL void FDECL(deadbook, (struct obj *));
  32.  STATIC_PTR int NDECL(learn);
  33.  STATIC_DCL boolean NDECL(rejectcasting);
  34.  STATIC_DCL boolean FDECL(getspell, (int *));
  35.  STATIC_PTR int FDECL(CFDECLSPEC spell_cmp, (const genericptr,
  36.                                              const genericptr));
  37.  STATIC_DCL void NDECL(sortspells);
  38.  STATIC_DCL boolean NDECL(spellsortmenu);
  39.  STATIC_DCL boolean FDECL(dospellmenu, (const char *, int, int *));
  40.  STATIC_DCL int FDECL(percent_success, (int));
  41.  STATIC_DCL char *FDECL(spellretention, (int, char *));
  42.  STATIC_DCL int NDECL(throwspell);
  43.  STATIC_DCL void NDECL(cast_protection);
  44.  STATIC_DCL void FDECL(spell_backfire, (int));
  45.  STATIC_DCL const char *FDECL(spelltypemnemonic, (int));
  46.  
  47.  /* The roles[] table lists the role-specific values for tuning
  48.   * percent_success().
  49.   *
  50.   * Reasoning:
  51.   *   spelbase, spelheal:
  52.   *      Arc are aware of magic through historical research
  53.   *      Bar abhor magic (Conan finds it "interferes with his animal instincts")
  54.   *      Cav are ignorant to magic
  55.   *      Hea are very aware of healing magic through medical research
  56.   *      Kni are moderately aware of healing from Paladin training
  57.   *      Mon use magic to attack and defend in lieu of weapons and armor
  58.   *      Pri are very aware of healing magic through theological research
  59.   *      Ran avoid magic, preferring to fight unseen and unheard
  60.   *      Rog are moderately aware of magic through trickery
  61.   *      Sam have limited magical awareness, preferring meditation to conjuring
  62.   *      Tou are aware of magic from all the great films they have seen
  63.   *      Val have limited magical awareness, preferring fighting
  64.   *      Wiz are trained mages
  65.   *
  66.   *      The arms penalty is lessened for trained fighters Bar, Kni, Ran,
  67.   *      Sam, Val -- the penalty is its metal interference, not encumbrance.
  68.   *      The `spelspec' is a single spell which is fundamentally easier
  69.   *      for that role to cast.
  70.   *
  71.   *  spelspec, spelsbon:
  72.   *      Arc map masters (SPE_MAGIC_MAPPING)
  73.   *      Bar fugue/berserker (SPE_HASTE_SELF)
  74.   *      Cav born to dig (SPE_DIG)
  75.   *      Hea to heal (SPE_CURE_SICKNESS)
  76.   *      Kni to turn back evil (SPE_TURN_UNDEAD)
  77.   *      Mon to preserve their abilities (SPE_RESTORE_ABILITY)
  78.   *      Pri to bless (SPE_REMOVE_CURSE)
  79.   *      Ran to hide (SPE_INVISIBILITY)
  80.   *      Rog to find loot (SPE_DETECT_TREASURE)
  81.   *      Sam to be At One (SPE_CLAIRVOYANCE)
  82.   *      Tou to smile (SPE_CHARM_MONSTER)
  83.   *      Val control the cold (SPE_CONE_OF_COLD)
  84.   *      Wiz all really, but SPE_MAGIC_MISSILE is their party trick
  85.   *
  86.   *      See percent_success() below for more comments.
  87.   *
  88.   *  uarmbon, uarmsbon, uarmhbon, uarmgbon, uarmfbon:
  89.   *      Fighters find body armour & shield a little less limiting.
  90.   *      Headgear, Gauntlets and Footwear are not role-specific (but
  91.   *      still have an effect, except helm of brilliance, which is designed
  92.   *      to permit magic-use).
  93.   */
  94.  
  95.  #define uarmhbon 4 /* Metal helmets interfere with the mind */
  96.  #define uarmgbon 6 /* Casting channels through the hands */
  97.  #define uarmfbon 2 /* All metal interferes to some degree */
  98.  
  99.  /* since the spellbook itself doesn't blow up, don't say just "explodes" */
  100.  static const char explodes[] = "radiates explosive energy";
  101.  

spell_let_to_idx

  1.  /* convert a letter into a number in the range 0..51, or -1 if not a letter */
  2.  STATIC_OVL int
  3.  spell_let_to_idx(ilet)
  4.  char ilet;
  5.  {
  6.      int indx;
  7.  
  8.      indx = ilet - 'a';
  9.      if (indx >= 0 && indx < 26)
  10.          return indx;
  11.      indx = ilet - 'A';
  12.      if (indx >= 0 && indx < 26)
  13.          return indx + 26;
  14.      return -1;
  15.  }
  16.  

cursed_book

  1.  /* TRUE: book should be destroyed by caller */
  2.  STATIC_OVL boolean
  3.  cursed_book(bp)
  4.  struct obj *bp;
  5.  {
  6.      int lev = objects[bp->otyp].oc_level;
  7.      int dmg = 0;
  8.  
  9.      switch (rn2(lev)) {
  10.      case 0:
  11.          You_feel("a wrenching sensation.");
  12.          tele(); /* teleport him */
  13.          break;
  14.      case 1:
  15.          You_feel("threatened.");
  16.          aggravate();
  17.          break;
  18.      case 2:
  19.          make_blinded(Blinded + rn1(100, 250), TRUE);
  20.          break;
  21.      case 3:
  22.          take_gold();
  23.          break;
  24.      case 4:
  25.          pline("These runes were just too much to comprehend.");
  26.          make_confused(HConfusion + rn1(7, 16), FALSE);
  27.          break;
  28.      case 5:
  29.          pline_The("book was coated with contact poison!");
  30.          if (uarmg) {
  31.              erode_obj(uarmg, "gloves", ERODE_CORRODE, EF_GREASE | EF_VERBOSE);
  32.              break;
  33.          }
  34.          /* temp disable in_use; death should not destroy the book */
  35.          bp->in_use = FALSE;
  36.          losestr(Poison_resistance ? rn1(2, 1) : rn1(4, 3));
  37.          losehp(rnd(Poison_resistance ? 6 : 10), "contact-poisoned spellbook",
  38.                 KILLED_BY_AN);
  39.          bp->in_use = TRUE;
  40.          break;
  41.      case 6:
  42.          if (Antimagic) {
  43.              shieldeff(u.ux, u.uy);
  44.              pline_The("book %s, but you are unharmed!", explodes);
  45.          } else {
  46.              pline("As you read the book, it %s in your %s!", explodes,
  47.                    body_part(FACE));
  48.              dmg = 2 * rnd(10) + 5;
  49.              losehp(Maybe_Half_Phys(dmg), "exploding rune", KILLED_BY_AN);
  50.          }
  51.          return TRUE;
  52.      default:
  53.          rndcurse();
  54.          break;
  55.      }
  56.      return FALSE;
  57.  }
  58.  

confused_book

  1.  /* study while confused: returns TRUE if the book is destroyed */
  2.  STATIC_OVL boolean
  3.  confused_book(spellbook)
  4.  struct obj *spellbook;
  5.  {
  6.      boolean gone = FALSE;
  7.  
  8.      if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
  9.          spellbook->in_use = TRUE; /* in case called from learn */
  10.          pline(
  11.           "Being confused you have difficulties in controlling your actions.");
  12.          display_nhwindow(WIN_MESSAGE, FALSE);
  13.          You("accidentally tear the spellbook to pieces.");
  14.          if (!objects[spellbook->otyp].oc_name_known
  15.              && !objects[spellbook->otyp].oc_uname)
  16.              docall(spellbook);
  17.          useup(spellbook);
  18.          gone = TRUE;
  19.      } else {
  20.          You("find yourself reading the %s line over and over again.",
  21.              spellbook == context.spbook.book ? "next" : "first");
  22.      }
  23.      return gone;
  24.  }
  25.  

deadbook

  1.  /* special effects for The Book of the Dead */
  2.  STATIC_OVL void
  3.  deadbook(book2)
  4.  struct obj *book2;
  5.  {
  6.      struct monst *mtmp, *mtmp2;
  7.      coord mm;
  8.  
  9.      You("turn the pages of the Book of the Dead...");
  10.      makeknown(SPE_BOOK_OF_THE_DEAD);
  11.      /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */
  12.      book2->known = 1;
  13.      if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
  14.          register struct obj *otmp;
  15.          register boolean arti1_primed = FALSE, arti2_primed = FALSE,
  16.                           arti_cursed = FALSE;
  17.  
  18.          if (book2->cursed) {
  19.              pline_The("runes appear scrambled.  You can't read them!");
  20.              return;
  21.          }
  22.  
  23.          if (!u.uhave.bell || !u.uhave.menorah) {
  24.              pline("A chill runs down your %s.", body_part(SPINE));
  25.              if (!u.uhave.bell)
  26.                  You_hear("a faint chime...");
  27.              if (!u.uhave.menorah)
  28.                  pline("Vlad's doppelganger is amused.");
  29.              return;
  30.          }
  31.  
  32.          for (otmp = invent; otmp; otmp = otmp->nobj) {
  33.              if (otmp->otyp == CANDELABRUM_OF_INVOCATION && otmp->spe == 7
  34.                  && otmp->lamplit) {
  35.                  if (!otmp->cursed)
  36.                      arti1_primed = TRUE;
  37.                  else
  38.                      arti_cursed = TRUE;
  39.              }
  40.              if (otmp->otyp == BELL_OF_OPENING
  41.                  && (moves - otmp->age) < 5L) { /* you rang it recently */
  42.                  if (!otmp->cursed)
  43.                      arti2_primed = TRUE;
  44.                  else
  45.                      arti_cursed = TRUE;
  46.              }
  47.          }
  48.  
  49.          if (arti_cursed) {
  50.              pline_The("invocation fails!");
  51.              pline("At least one of your artifacts is cursed...");
  52.          } else if (arti1_primed && arti2_primed) {
  53.              unsigned soon =
  54.                  (unsigned) d(2, 6); /* time til next intervene() */
  55.  
  56.              /* successful invocation */
  57.              mkinvokearea();
  58.              u.uevent.invoked = 1;
  59.              /* in case you haven't killed the Wizard yet, behave as if
  60.                 you just did */
  61.              u.uevent.udemigod = 1; /* wizdead() */
  62.              if (!u.udg_cnt || u.udg_cnt > soon)
  63.                  u.udg_cnt = soon;
  64.          } else { /* at least one artifact not prepared properly */
  65.              You("have a feeling that %s is amiss...", something);
  66.              goto raise_dead;
  67.          }
  68.          return;
  69.      }
  70.  
  71.      /* when not an invocation situation */
  72.      if (book2->cursed) {
  73.      raise_dead:
  74.  
  75.          You("raised the dead!");
  76.          /* first maybe place a dangerous adversary */
  77.          if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH], u.ux, u.uy,
  78.                                          NO_MINVENT)) != 0
  79.                          || (mtmp = makemon(&mons[PM_NALFESHNEE], u.ux, u.uy,
  80.                                             NO_MINVENT)) != 0)) {
  81.              mtmp->mpeaceful = 0;
  82.              set_malign(mtmp);
  83.          }
  84.          /* next handle the affect on things you're carrying */
  85.          (void) unturn_dead(&youmonst);
  86.          /* last place some monsters around you */
  87.          mm.x = u.ux;
  88.          mm.y = u.uy;
  89.          mkundead(&mm, TRUE, NO_MINVENT);
  90.      } else if (book2->blessed) {
  91.          for (mtmp = fmon; mtmp; mtmp = mtmp2) {
  92.              mtmp2 = mtmp->nmon; /* tamedog() changes chain */
  93.              if (DEADMONSTER(mtmp))
  94.                  continue;
  95.  
  96.              if ((is_undead(mtmp->data) || is_vampshifter(mtmp))
  97.                  && cansee(mtmp->mx, mtmp->my)) {
  98.                  mtmp->mpeaceful = TRUE;
  99.                  if (sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
  100.                      && distu(mtmp->mx, mtmp->my) < 4)
  101.                      if (mtmp->mtame) {
  102.                          if (mtmp->mtame < 20)
  103.                              mtmp->mtame++;
  104.                      } else
  105.                          (void) tamedog(mtmp, (struct obj *) 0);
  106.                  else
  107.                      monflee(mtmp, 0, FALSE, TRUE);
  108.              }
  109.          }
  110.      } else {
  111.          switch (rn2(3)) {
  112.          case 0:
  113.              Your("ancestors are annoyed with you!");
  114.              break;
  115.          case 1:
  116.              pline_The("headstones in the cemetery begin to move!");
  117.              break;
  118.          default:
  119.              pline("Oh my!  Your name appears in the book!");
  120.          }
  121.      }
  122.      return;
  123.  }
  124.  

learn

  1.  STATIC_PTR int
  2.  learn(VOID_ARGS)
  3.  {
  4.      int i;
  5.      short booktype;
  6.      char splname[BUFSZ];
  7.      boolean costly = TRUE;
  8.      struct obj *book = context.spbook.book;
  9.  
  10.      /* JDS: lenses give 50% faster reading; 33% smaller read time */
  11.      if (context.spbook.delay && ublindf && ublindf->otyp == LENSES && rn2(2))
  12.          context.spbook.delay++;
  13.      if (Confusion) { /* became confused while learning */
  14.          (void) confused_book(book);
  15.          context.spbook.book = 0; /* no longer studying */
  16.          context.spbook.o_id = 0;
  17.          nomul(context.spbook.delay); /* remaining delay is uninterrupted */
  18.          multi_reason = "reading a book";
  19.          nomovemsg = 0;
  20.          context.spbook.delay = 0;
  21.          return 0;
  22.      }
  23.      if (context
  24.              .spbook.delay) { /* not if (context.spbook.delay++), so at end
  25.                                  delay == 0 */
  26.          context.spbook.delay++;
  27.          return 1; /* still busy */
  28.      }
  29.      exercise(A_WIS, TRUE); /* you're studying. */
  30.      booktype = book->otyp;
  31.      if (booktype == SPE_BOOK_OF_THE_DEAD) {
  32.          deadbook(book);
  33.          return 0;
  34.      }
  35.  
  36.      Sprintf(splname,
  37.              objects[booktype].oc_name_known ? "\"%s\"" : "the \"%s\" spell",
  38.              OBJ_NAME(objects[booktype]));
  39.      for (i = 0; i < MAXSPELL; i++)
  40.          if (spellid(i) == booktype || spellid(i) == NO_SPELL)
  41.              break;
  42.  
  43.      if (i == MAXSPELL) {
  44.          impossible("Too many spells memorized!");
  45.      } else if (spellid(i) == booktype) {
  46.          /* normal book can be read and re-read a total of 4 times */
  47.          if (book->spestudied > MAX_SPELL_STUDY) {
  48.              pline("This spellbook is too faint to be read any more.");
  49.              book->otyp = booktype = SPE_BLANK_PAPER;
  50.              /* reset spestudied as if polymorph had taken place */
  51.              book->spestudied = rn2(book->spestudied);
  52.          } else if (spellknow(i) > KEEN / 10) {
  53.              You("know %s quite well already.", splname);
  54.              costly = FALSE;
  55.          } else { /* spellknow(i) <= KEEN/10 */
  56.              Your("knowledge of %s is %s.", splname,
  57.                   spellknow(i) ? "keener" : "restored");
  58.              incrnknow(i, 1);
  59.              book->spestudied++;
  60.              exercise(A_WIS, TRUE); /* extra study */
  61.          }
  62.          /* make book become known even when spell is already
  63.             known, in case amnesia made you forget the book */
  64.          makeknown((int) booktype);
  65.      } else { /* (spellid(i) == NO_SPELL) */
  66.          /* for a normal book, spestudied will be zero, but for
  67.             a polymorphed one, spestudied will be non-zero and
  68.             one less reading is available than when re-learning */
  69.          if (book->spestudied >= MAX_SPELL_STUDY) {
  70.              /* pre-used due to being the product of polymorph */
  71.              pline("This spellbook is too faint to read even once.");
  72.              book->otyp = booktype = SPE_BLANK_PAPER;
  73.              /* reset spestudied as if polymorph had taken place */
  74.              book->spestudied = rn2(book->spestudied);
  75.          } else {
  76.              spl_book[i].sp_id = booktype;
  77.              spl_book[i].sp_lev = objects[booktype].oc_level;
  78.              incrnknow(i, 1);
  79.              book->spestudied++;
  80.              You(i > 0 ? "add %s to your repertoire." : "learn %s.", splname);
  81.          }
  82.          makeknown((int) booktype);
  83.      }
  84.  
  85.      if (book->cursed) { /* maybe a demon cursed it */
  86.          if (cursed_book(book)) {
  87.              useup(book);
  88.              context.spbook.book = 0;
  89.              context.spbook.o_id = 0;
  90.              return 0;
  91.          }
  92.      }
  93.      if (costly)
  94.          check_unpaid(book);
  95.      context.spbook.book = 0;
  96.      context.spbook.o_id = 0;
  97.      return 0;
  98.  }
  99.  

study_book

  1.  int
  2.  study_book(spellbook)
  3.  register struct obj *spellbook;
  4.  {
  5.      int booktype = spellbook->otyp;
  6.      boolean confused = (Confusion != 0);
  7.      boolean too_hard = FALSE;
  8.  
  9.      /* attempting to read dull book may make hero fall asleep */
  10.      if (!confused && booktype != SPE_BLANK_PAPER
  11.          && !strcmp(OBJ_DESCR(objects[booktype]), "dull")) {
  12.          const char *eyes;
  13.          int dullbook = rnd(25) - ACURR(A_WIS);
  14.  
  15.          /* adjust chance if hero stayed awake, got interrupted, retries */
  16.          if (context.spbook.delay && spellbook == context.spbook.book)
  17.              dullbook -= rnd(objects[booktype].oc_level);
  18.  
  19.          if (dullbook > 0) {
  20.              eyes = body_part(EYE);
  21.              if (eyecount(youmonst.data) > 1)
  22.                  eyes = makeplural(eyes);
  23.              pline("This book is so dull that you can't keep your %s open.",
  24.                    eyes);
  25.              dullbook += rnd(2 * objects[booktype].oc_level);
  26.              fall_asleep(-dullbook, TRUE);
  27.              return 1;
  28.          }
  29.      }
  30.  
  31.      if (context.spbook.delay && !confused && spellbook == context.spbook.book
  32.          /* handle the sequence: start reading, get interrupted, have
  33.             context.spbook.book become erased somehow, resume reading it */
  34.          && booktype != SPE_BLANK_PAPER) {
  35.          You("continue your efforts to %s.",
  36.              (booktype == SPE_NOVEL) ? "read the novel" : "memorize the spell");
  37.      } else {
  38.          /* KMH -- Simplified this code */
  39.          if (booktype == SPE_BLANK_PAPER) {
  40.              pline("This spellbook is all blank.");
  41.              makeknown(booktype);
  42.              return 1;
  43.          }
  44.  
  45.          /* 3.6.0 tribute */
  46.          if (booktype == SPE_NOVEL) {
  47.              /* Obtain current Terry Pratchett book title */
  48.              const char *tribtitle = noveltitle(&spellbook->novelidx);
  49.  
  50.              if (read_tribute("books", tribtitle, 0, (char *) 0, 0,
  51.                               spellbook->o_id)) {
  52.                  u.uconduct.literate++;
  53.                  check_unpaid(spellbook);
  54.                  if (!u.uevent.read_tribute) {
  55.                      /* give bonus of 20 xp and 4*20+0 pts */
  56.                      more_experienced(20, 0);
  57.                      newexplevel();
  58.                      u.uevent.read_tribute = 1; /* only once */
  59.                  }
  60.              }
  61.              return 1;
  62.          }
  63.  
  64.          switch (objects[booktype].oc_level) {
  65.          case 1:
  66.          case 2:
  67.              context.spbook.delay = -objects[booktype].oc_delay;
  68.              break;
  69.          case 3:
  70.          case 4:
  71.              context.spbook.delay = -(objects[booktype].oc_level - 1)
  72.                                     * objects[booktype].oc_delay;
  73.              break;
  74.          case 5:
  75.          case 6:
  76.              context.spbook.delay =
  77.                  -objects[booktype].oc_level * objects[booktype].oc_delay;
  78.              break;
  79.          case 7:
  80.              context.spbook.delay = -8 * objects[booktype].oc_delay;
  81.              break;
  82.          default:
  83.              impossible("Unknown spellbook level %d, book %d;",
  84.                         objects[booktype].oc_level, booktype);
  85.              return 0;
  86.          }
  87.  
  88.          /* Books are often wiser than their readers (Rus.) */
  89.          spellbook->in_use = TRUE;
  90.          if (!spellbook->blessed && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
  91.              if (spellbook->cursed) {
  92.                  too_hard = TRUE;
  93.              } else {
  94.                  /* uncursed - chance to fail */
  95.                  int read_ability =
  96.                      ACURR(A_INT) + 4 + u.ulevel / 2
  97.                      - 2 * objects[booktype].oc_level
  98.                      + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);
  99.                  /* only wizards know if a spell is too difficult */
  100.                  if (Role_if(PM_WIZARD) && read_ability < 20 && !confused) {
  101.                      char qbuf[QBUFSZ];
  102.                      Sprintf(qbuf,
  103.                       "This spellbook is %sdifficult to comprehend. Continue?",
  104.                              (read_ability < 12 ? "very " : ""));
  105.                      if (yn(qbuf) != 'y') {
  106.                          spellbook->in_use = FALSE;
  107.                          return 1;
  108.                      }
  109.                  }
  110.                  /* its up to random luck now */
  111.                  if (rnd(20) > read_ability) {
  112.                      too_hard = TRUE;
  113.                  }
  114.              }
  115.          }
  116.  
  117.          if (too_hard) {
  118.              boolean gone = cursed_book(spellbook);
  119.  
  120.              nomul(context.spbook.delay); /* study time */
  121.              multi_reason = "reading a book";
  122.              nomovemsg = 0;
  123.              context.spbook.delay = 0;
  124.              if (gone || !rn2(3)) {
  125.                  if (!gone)
  126.                      pline_The("spellbook crumbles to dust!");
  127.                  if (!objects[spellbook->otyp].oc_name_known
  128.                      && !objects[spellbook->otyp].oc_uname)
  129.                      docall(spellbook);
  130.                  useup(spellbook);
  131.              } else
  132.                  spellbook->in_use = FALSE;
  133.              return 1;
  134.          } else if (confused) {
  135.              if (!confused_book(spellbook)) {
  136.                  spellbook->in_use = FALSE;
  137.              }
  138.              nomul(context.spbook.delay);
  139.              multi_reason = "reading a book";
  140.              nomovemsg = 0;
  141.              context.spbook.delay = 0;
  142.              return 1;
  143.          }
  144.          spellbook->in_use = FALSE;
  145.  
  146.          You("begin to %s the runes.",
  147.              spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : "memorize");
  148.      }
  149.  
  150.      context.spbook.book = spellbook;
  151.      if (context.spbook.book)
  152.          context.spbook.o_id = context.spbook.book->o_id;
  153.      set_occupation(learn, "studying", 0);
  154.      return 1;
  155.  }
  156.  

book_disappears

  1.  /* a spellbook has been destroyed or the character has changed levels;
  2.     the stored address for the current book is no longer valid */
  3.  void
  4.  book_disappears(obj)
  5.  struct obj *obj;
  6.  {
  7.      if (obj == context.spbook.book) {
  8.          context.spbook.book = (struct obj *) 0;
  9.          context.spbook.o_id = 0;
  10.      }
  11.  }
  12.  

book_substitution

  1.  /* renaming an object usually results in it having a different address;
  2.     so the sequence start reading, get interrupted, name the book, resume
  3.     reading would read the "new" book from scratch */
  4.  void
  5.  book_substitution(old_obj, new_obj)
  6.  struct obj *old_obj, *new_obj;
  7.  {
  8.      if (old_obj == context.spbook.book) {
  9.          context.spbook.book = new_obj;
  10.          if (context.spbook.book)
  11.              context.spbook.o_id = context.spbook.book->o_id;
  12.      }
  13.  }
  14.  

age_spells

  1.  /* called from moveloop() */
  2.  void
  3.  age_spells()
  4.  {
  5.      int i;
  6.      /*
  7.       * The time relative to the hero (a pass through move
  8.       * loop) causes all spell knowledge to be decremented.
  9.       * The hero's speed, rest status, conscious status etc.
  10.       * does not alter the loss of memory.
  11.       */
  12.      for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++)
  13.          if (spellknow(i))
  14.              decrnknow(i);
  15.      return;
  16.  }
  17.  

rejectcasting

  1.  /* return True if spellcasting is inhibited;
  2.     only covers a small subset of reasons why casting won't work */
  3.  STATIC_OVL boolean
  4.  rejectcasting()
  5.  {
  6.      /* rejections which take place before selecting a particular spell */
  7.      if (Stunned) {
  8.          You("are too impaired to cast a spell.");
  9.          return TRUE;
  10.      } else if (!freehand()) {
  11.          /* Note: !freehand() occurs when weapon and shield (or two-handed
  12.           * weapon) are welded to hands, so "arms" probably doesn't need
  13.           * to be makeplural(bodypart(ARM)).
  14.           *
  15.           * But why isn't lack of free arms (for gesturing) an issue when
  16.           * poly'd hero has no limbs?
  17.           */
  18.          Your("arms are not free to cast!");
  19.          return TRUE;
  20.      }
  21.      return FALSE;
  22.  }
  23.  

getspell

  1.  /*
  2.   * Return TRUE if a spell was picked, with the spell index in the return
  3.   * parameter.  Otherwise return FALSE.
  4.   */
  5.  STATIC_OVL boolean
  6.  getspell(spell_no)
  7.  int *spell_no;
  8.  {
  9.      int nspells, idx;
  10.      char ilet, lets[BUFSZ], qbuf[QBUFSZ];
  11.  
  12.      if (spellid(0) == NO_SPELL) {
  13.          You("don't know any spells right now.");
  14.          return FALSE;
  15.      }
  16.      if (rejectcasting())
  17.          return FALSE; /* no spell chosen */
  18.  
  19.      if (flags.menu_style == MENU_TRADITIONAL) {
  20.          /* we know there is at least 1 known spell */
  21.          for (nspells = 1; nspells < MAXSPELL && spellid(nspells) != NO_SPELL;
  22.               nspells++)
  23.              continue;
  24.  
  25.          if (nspells == 1)
  26.              Strcpy(lets, "a");
  27.          else if (nspells < 27)
  28.              Sprintf(lets, "a-%c", 'a' + nspells - 1);
  29.          else if (nspells == 27)
  30.              Sprintf(lets, "a-zA");
  31.          /* this assumes that there are at most 52 spells... */
  32.          else
  33.              Sprintf(lets, "a-zA-%c", 'A' + nspells - 27);
  34.  
  35.          for (;;) {
  36.              Sprintf(qbuf, "Cast which spell? [%s *?]", lets);
  37.              ilet = yn_function(qbuf, (char *) 0, '\0');
  38.              if (ilet == '*' || ilet == '?')
  39.                  break; /* use menu mode */
  40.              if (index(quitchars, ilet))
  41.                  return FALSE;
  42.  
  43.              idx = spell_let_to_idx(ilet);
  44.              if (idx < 0 || idx >= nspells) {
  45.                  You("don't know that spell.");
  46.                  continue; /* ask again */
  47.              }
  48.              *spell_no = idx;
  49.              return TRUE;
  50.          }
  51.      }
  52.      return dospellmenu("Choose which spell to cast", SPELLMENU_CAST,
  53.                         spell_no);
  54.  }
  55.  

docast

  1.  /* the 'Z' command -- cast a spell */
  2.  int
  3.  docast()
  4.  {
  5.      int spell_no;
  6.  
  7.      if (getspell(&spell_no))
  8.          return spelleffects(spell_no, FALSE);
  9.      return 0;
  10.  }
  11.  

spelltypemnemonic

  1.  STATIC_OVL const char *
  2.  spelltypemnemonic(skill)
  3.  int skill;
  4.  {
  5.      switch (skill) {
  6.      case P_ATTACK_SPELL:
  7.          return "attack";
  8.      case P_HEALING_SPELL:
  9.          return "healing";
  10.      case P_DIVINATION_SPELL:
  11.          return "divination";
  12.      case P_ENCHANTMENT_SPELL:
  13.          return "enchantment";
  14.      case P_CLERIC_SPELL:
  15.          return "clerical";
  16.      case P_ESCAPE_SPELL:
  17.          return "escape";
  18.      case P_MATTER_SPELL:
  19.          return "matter";
  20.      default:
  21.          impossible("Unknown spell skill, %d;", skill);
  22.          return "";
  23.      }
  24.  }
  25.  

spell_skilltype

  1.  int
  2.  spell_skilltype(booktype)
  3.  int booktype;
  4.  {
  5.      return objects[booktype].oc_skill;
  6.  }
  7.  

cast_protection

  1.  STATIC_OVL void
  2.  cast_protection()
  3.  {
  4.      int l = u.ulevel, loglev = 0,
  5.          gain, natac = u.uac + u.uspellprot;
  6.      /* note: u.uspellprot is subtracted when find_ac() factors it into u.uac,
  7.         so adding here factors it back out
  8.         (versions prior to 3.6 had this backwards) */
  9.  
  10.      /* loglev=log2(u.ulevel)+1 (1..5) */
  11.      while (l) {
  12.          loglev++;
  13.          l /= 2;
  14.      }
  15.  
  16.      /* The more u.uspellprot you already have, the less you get,
  17.       * and the better your natural ac, the less you get.
  18.       *
  19.       *  LEVEL AC    SPELLPROT from successive SPE_PROTECTION casts
  20.       *      1     10    0,  1,  2,  3,  4
  21.       *      1      0    0,  1,  2,  3
  22.       *      1    -10    0,  1,  2
  23.       *      2-3   10    0,  2,  4,  5,  6,  7,  8
  24.       *      2-3    0    0,  2,  4,  5,  6
  25.       *      2-3  -10    0,  2,  3,  4
  26.       *      4-7   10    0,  3,  6,  8,  9, 10, 11, 12
  27.       *      4-7    0    0,  3,  5,  7,  8,  9
  28.       *      4-7  -10    0,  3,  5,  6
  29.       *      7-15 -10    0,  3,  5,  6
  30.       *      8-15  10    0,  4,  7, 10, 12, 13, 14, 15, 16
  31.       *      8-15   0    0,  4,  7,  9, 10, 11, 12
  32.       *      8-15 -10    0,  4,  6,  7,  8
  33.       *     16-30  10    0,  5,  9, 12, 14, 16, 17, 18, 19, 20
  34.       *     16-30   0    0,  5,  9, 11, 13, 14, 15
  35.       *     16-30 -10    0,  5,  8,  9, 10
  36.       */
  37.      natac = (10 - natac) / 10; /* convert to positive and scale down */
  38.      gain = loglev - (int) u.uspellprot / (4 - min(3, natac));
  39.  
  40.      if (gain > 0) {
  41.          if (!Blind) {
  42.              int rmtyp;
  43.              const char *hgolden = hcolor(NH_GOLDEN), *atmosphere;
  44.  
  45.              if (u.uspellprot) {
  46.                  pline_The("%s haze around you becomes more dense.", hgolden);
  47.              } else {
  48.                  rmtyp = levl[u.ux][u.uy].typ;
  49.                  atmosphere = u.uswallow
  50.                                  ? ((u.ustuck->data == &mons[PM_FOG_CLOUD])
  51.                                     ? "mist"
  52.                                     : is_whirly(u.ustuck->data)
  53.                                        ? "maelstrom"
  54.                                        : is_animal(u.ustuck->data)
  55.                                           ? "maw"
  56.                                           : "ooze")
  57.                                  : (u.uinwater
  58.                                     ? "water"
  59.                                     : (rmtyp == CLOUD)
  60.                                        ? "cloud"
  61.                                        : IS_TREE(rmtyp)
  62.                                           ? "vegitation"
  63.                                           : IS_STWALL(rmtyp)
  64.                                              ? "stone"
  65.                                              : "air");
  66.                  pline_The("%s around you begins to shimmer with %s haze.",
  67.                            atmosphere, an(hgolden));
  68.              }
  69.          }
  70.          u.uspellprot += gain;
  71.          u.uspmtime = (P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT)
  72.                          ? 20 : 10;
  73.          if (!u.usptime)
  74.              u.usptime = u.uspmtime;
  75.          find_ac();
  76.      } else {
  77.          Your("skin feels warm for a moment.");
  78.      }
  79.  }
  80.  

spell_backfire

  1.  /* attempting to cast a forgotten spell will cause disorientation */
  2.  STATIC_OVL void
  3.  spell_backfire(spell)
  4.  int spell;
  5.  {
  6.      long duration = (long) ((spellev(spell) + 1) * 3), /* 6..24 */
  7.           old_stun = (HStun & TIMEOUT), old_conf = (HConfusion & TIMEOUT);
  8.  
  9.      /* Prior to 3.4.1, only effect was confusion; it still predominates.
  10.       *
  11.       * 3.6.0: this used to override pre-existing confusion duration
  12.       * (cases 0..8) and pre-existing stun duration (cases 4..9);
  13.       * increase them instead.   (Hero can no longer cast spells while
  14.       * Stunned, so the potential increment to stun duration here is
  15.       * just hypothetical.)
  16.       */
  17.      switch (rn2(10)) {
  18.      case 0:
  19.      case 1:
  20.      case 2:
  21.      case 3:
  22.          make_confused(old_conf + duration, FALSE); /* 40% */
  23.          break;
  24.      case 4:
  25.      case 5:
  26.      case 6:
  27.          make_confused(old_conf + 2L * duration / 3L, FALSE); /* 30% */
  28.          make_stunned(old_stun + duration / 3L, FALSE);
  29.          break;
  30.      case 7:
  31.      case 8:
  32.          make_stunned(old_conf + 2L * duration / 3L, FALSE); /* 20% */
  33.          make_confused(old_stun + duration / 3L, FALSE);
  34.          break;
  35.      case 9:
  36.          make_stunned(old_stun + duration, FALSE); /* 10% */
  37.          break;
  38.      }
  39.      return;
  40.  }
  41.  

spelleffects

  1.  int
  2.  spelleffects(spell, atme)
  3.  int spell;
  4.  boolean atme;
  5.  {
  6.      int energy, damage, chance, n, intell;
  7.      int skill, role_skill;
  8.      boolean confused = (Confusion != 0);
  9.      boolean physical_damage = FALSE;
  10.      struct obj *pseudo;
  11.      coord cc;
  12.  
  13.      /*
  14.       * Reject attempting to cast while stunned or with no free hands.
  15.       * Already done in getspell() to stop casting before choosing
  16.       * which spell, but duplicated here for cases where spelleffects()
  17.       * gets called directly for ^T without intrinsic teleport capability
  18.       * or #turn for non-priest/non-knight.
  19.       * (There's no duplication of messages; when the rejection takes
  20.       * place in getspell(), we don't get called.)
  21.       */
  22.      if (rejectcasting()) {
  23.          return 0; /* no time elapses */
  24.      }
  25.  
  26.      /*
  27.       * Spell casting no longer affects knowledge of the spell. A
  28.       * decrement of spell knowledge is done every turn.
  29.       */
  30.      if (spellknow(spell) <= 0) {
  31.          Your("knowledge of this spell is twisted.");
  32.          pline("It invokes nightmarish images in your mind...");
  33.          spell_backfire(spell);
  34.          return 1;
  35.      } else if (spellknow(spell) <= KEEN / 200) { /* 100 turns left */
  36.          You("strain to recall the spell.");
  37.      } else if (spellknow(spell) <= KEEN / 40) { /* 500 turns left */
  38.          You("have difficulty remembering the spell.");
  39.      } else if (spellknow(spell) <= KEEN / 20) { /* 1000 turns left */
  40.          Your("knowledge of this spell is growing faint.");
  41.      } else if (spellknow(spell) <= KEEN / 10) { /* 2000 turns left */
  42.          Your("recall of this spell is gradually fading.");
  43.      }
  44.      energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */
  45.  
  46.      if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) {
  47.          You("are too hungry to cast that spell.");
  48.          return 0;
  49.      } else if (ACURR(A_STR) < 4 && spellid(spell) != SPE_RESTORE_ABILITY) {
  50.          You("lack the strength to cast spells.");
  51.          return 0;
  52.      } else if (check_capacity(
  53.                  "Your concentration falters while carrying so much stuff.")) {
  54.          return 1;
  55.      }
  56.  
  57.      if (u.uhave.amulet) {
  58.          You_feel("the amulet draining your energy away.");
  59.          energy += rnd(2 * energy);
  60.      }
  61.      if (energy > u.uen) {
  62.          You("don't have enough energy to cast that spell.");
  63.          return 0;
  64.      } else {
  65.          if (spellid(spell) != SPE_DETECT_FOOD) {
  66.              int hungr = energy * 2;
  67.  
  68.              /* If hero is a wizard, their current intelligence
  69.               * (bonuses + temporary + current)
  70.               * affects hunger reduction in casting a spell.
  71.               * 1. int = 17-18 no reduction
  72.               * 2. int = 16    1/4 hungr
  73.               * 3. int = 15    1/2 hungr
  74.               * 4. int = 1-14  normal reduction
  75.               * The reason for this is:
  76.               * a) Intelligence affects the amount of exertion
  77.               * in thinking.
  78.               * b) Wizards have spent their life at magic and
  79.               * understand quite well how to cast spells.
  80.               */
  81.              intell = acurr(A_INT);
  82.              if (!Role_if(PM_WIZARD))
  83.                  intell = 10;
  84.              switch (intell) {
  85.              case 25:
  86.              case 24:
  87.              case 23:
  88.              case 22:
  89.              case 21:
  90.              case 20:
  91.              case 19:
  92.              case 18:
  93.              case 17:
  94.                  hungr = 0;
  95.                  break;
  96.              case 16:
  97.                  hungr /= 4;
  98.                  break;
  99.              case 15:
  100.                  hungr /= 2;
  101.                  break;
  102.              }
  103.              /* don't put player (quite) into fainting from
  104.               * casting a spell, particularly since they might
  105.               * not even be hungry at the beginning; however,
  106.               * this is low enough that they must eat before
  107.               * casting anything else except detect food
  108.               */
  109.              if (hungr > u.uhunger - 3)
  110.                  hungr = u.uhunger - 3;
  111.              morehungry(hungr);
  112.          }
  113.      }
  114.  
  115.      chance = percent_success(spell);
  116.      if (confused || (rnd(100) > chance)) {
  117.          You("fail to cast the spell correctly.");
  118.          u.uen -= energy / 2;
  119.          context.botl = 1;
  120.          return 1;
  121.      }
  122.  
  123.      u.uen -= energy;
  124.      context.botl = 1;
  125.      exercise(A_WIS, TRUE);
  126.      /* pseudo is a temporary "false" object containing the spell stats */
  127.      pseudo = mksobj(spellid(spell), FALSE, FALSE);
  128.      pseudo->blessed = pseudo->cursed = 0;
  129.      pseudo->quan = 20L; /* do not let useup get it */
  130.      /*
  131.       * Find the skill the hero has in a spell type category.
  132.       * See spell_skilltype for categories.
  133.       */
  134.      skill = spell_skilltype(pseudo->otyp);
  135.      role_skill = P_SKILL(skill);
  136.  
  137.      switch (pseudo->otyp) {
  138.      /*
  139.       * At first spells act as expected.  As the hero increases in skill
  140.       * with the appropriate spell type, some spells increase in their
  141.       * effects, e.g. more damage, further distance, and so on, without
  142.       * additional cost to the spellcaster.
  143.       */
  144.      case SPE_FIREBALL:
  145.      case SPE_CONE_OF_COLD:
  146.          if (role_skill >= P_SKILLED) {
  147.              if (throwspell()) {
  148.                  cc.x = u.dx;
  149.                  cc.y = u.dy;
  150.                  n = rnd(8) + 1;
  151.                  while (n--) {
  152.                      if (!u.dx && !u.dy && !u.dz) {
  153.                          if ((damage = zapyourself(pseudo, TRUE)) != 0) {
  154.                              char buf[BUFSZ];
  155.                              Sprintf(buf, "zapped %sself with a spell",
  156.                                      uhim());
  157.                              losehp(damage, buf, NO_KILLER_PREFIX);
  158.                          }
  159.                      } else {
  160.                          explode(u.dx, u.dy,
  161.                                  pseudo->otyp - SPE_MAGIC_MISSILE + 10,
  162.                                  spell_damage_bonus(u.ulevel / 2 + 1), 0,
  163.                                  (pseudo->otyp == SPE_CONE_OF_COLD)
  164.                                     ? EXPL_FROSTY
  165.                                     : EXPL_FIERY);
  166.                      }
  167.                      u.dx = cc.x + rnd(3) - 2;
  168.                      u.dy = cc.y + rnd(3) - 2;
  169.                      if (!isok(u.dx, u.dy) || !cansee(u.dx, u.dy)
  170.                          || IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) {
  171.                          /* Spell is reflected back to center */
  172.                          u.dx = cc.x;
  173.                          u.dy = cc.y;
  174.                      }
  175.                  }
  176.              }
  177.              break;
  178.          } /* else fall through... */
  179.  
  180.      /* these spells are all duplicates of wand effects */
  181.      case SPE_FORCE_BOLT:
  182.          physical_damage = TRUE;
  183.      /* fall through */
  184.      case SPE_SLEEP:
  185.      case SPE_MAGIC_MISSILE:
  186.      case SPE_KNOCK:
  187.      case SPE_SLOW_MONSTER:
  188.      case SPE_WIZARD_LOCK:
  189.      case SPE_DIG:
  190.      case SPE_TURN_UNDEAD:
  191.      case SPE_POLYMORPH:
  192.      case SPE_TELEPORT_AWAY:
  193.      case SPE_CANCELLATION:
  194.      case SPE_FINGER_OF_DEATH:
  195.      case SPE_LIGHT:
  196.      case SPE_DETECT_UNSEEN:
  197.      case SPE_HEALING:
  198.      case SPE_EXTRA_HEALING:
  199.      case SPE_DRAIN_LIFE:
  200.      case SPE_STONE_TO_FLESH:
  201.          if (!(objects[pseudo->otyp].oc_dir == NODIR)) {
  202.              if (atme) {
  203.                  u.dx = u.dy = u.dz = 0;
  204.              } else if (!getdir((char *) 0)) {
  205.                  /* getdir cancelled, re-use previous direction */
  206.                  /*
  207.                   * FIXME:  reusing previous direction only makes sense
  208.                   * if there is an actual previous direction.  When there
  209.                   * isn't one, the spell gets cast at self which is rarely
  210.                   * what the player intended.  Unfortunately, the way
  211.                   * spelleffects() is organized means that aborting with
  212.                   * "nevermind" is not an option.
  213.                   */
  214.                  pline_The("magical energy is released!");
  215.              }
  216.              if (!u.dx && !u.dy && !u.dz) {
  217.                  if ((damage = zapyourself(pseudo, TRUE)) != 0) {
  218.                      char buf[BUFSZ];
  219.  
  220.                      Sprintf(buf, "zapped %sself with a spell", uhim());
  221.                      if (physical_damage)
  222.                          damage = Maybe_Half_Phys(damage);
  223.                      losehp(damage, buf, NO_KILLER_PREFIX);
  224.                  }
  225.              } else
  226.                  weffects(pseudo);
  227.          } else
  228.              weffects(pseudo);
  229.          update_inventory(); /* spell may modify inventory */
  230.          break;
  231.  
  232.      /* these are all duplicates of scroll effects */
  233.      case SPE_REMOVE_CURSE:
  234.      case SPE_CONFUSE_MONSTER:
  235.      case SPE_DETECT_FOOD:
  236.      case SPE_CAUSE_FEAR:
  237.      case SPE_IDENTIFY:
  238.          /* high skill yields effect equivalent to blessed scroll */
  239.          if (role_skill >= P_SKILLED)
  240.              pseudo->blessed = 1;
  241.      /* fall through */
  242.      case SPE_CHARM_MONSTER:
  243.      case SPE_MAGIC_MAPPING:
  244.      case SPE_CREATE_MONSTER:
  245.          (void) seffects(pseudo);
  246.          break;
  247.  
  248.      /* these are all duplicates of potion effects */
  249.      case SPE_HASTE_SELF:
  250.      case SPE_DETECT_TREASURE:
  251.      case SPE_DETECT_MONSTERS:
  252.      case SPE_LEVITATION:
  253.      case SPE_RESTORE_ABILITY:
  254.          /* high skill yields effect equivalent to blessed potion */
  255.          if (role_skill >= P_SKILLED)
  256.              pseudo->blessed = 1;
  257.      /* fall through */
  258.      case SPE_INVISIBILITY:
  259.          (void) peffects(pseudo);
  260.          break;
  261.  
  262.      case SPE_CURE_BLINDNESS:
  263.          healup(0, 0, FALSE, TRUE);
  264.          break;
  265.      case SPE_CURE_SICKNESS:
  266.          if (Sick)
  267.              You("are no longer ill.");
  268.          if (Slimed)
  269.              make_slimed(0L, "The slime disappears!");
  270.          healup(0, 0, TRUE, FALSE);
  271.          break;
  272.      case SPE_CREATE_FAMILIAR:
  273.          (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE);
  274.          break;
  275.      case SPE_CLAIRVOYANCE:
  276.          if (!BClairvoyant)
  277.              do_vicinity_map();
  278.          /* at present, only one thing blocks clairvoyance */
  279.          else if (uarmh && uarmh->otyp == CORNUTHAUM)
  280.              You("sense a pointy hat on top of your %s.", body_part(HEAD));
  281.          break;
  282.      case SPE_PROTECTION:
  283.          cast_protection();
  284.          break;
  285.      case SPE_JUMPING:
  286.          if (!jump(max(role_skill, 1)))
  287.              pline1(nothing_happens);
  288.          break;
  289.      default:
  290.          impossible("Unknown spell %d attempted.", spell);
  291.          obfree(pseudo, (struct obj *) 0);
  292.          return 0;
  293.      }
  294.  
  295.      /* gain skill for successful cast */
  296.      use_skill(skill, spellev(spell));
  297.  
  298.      obfree(pseudo, (struct obj *) 0); /* now, get rid of it */
  299.      return 1;
  300.  }
  301.  

throwspell

  1.  /* Choose location where spell takes effect. */
  2.  STATIC_OVL int
  3.  throwspell()
  4.  {
  5.      coord cc;
  6.      struct monst *mtmp;
  7.  
  8.      if (u.uinwater) {
  9.          pline("You're joking! In this weather?");
  10.          return 0;
  11.      } else if (Is_waterlevel(&u.uz)) {
  12.          You("had better wait for the sun to come out.");
  13.          return 0;
  14.      }
  15.  
  16.      pline("Where do you want to cast the spell?");
  17.      cc.x = u.ux;
  18.      cc.y = u.uy;
  19.      if (getpos(&cc, TRUE, "the desired position") < 0)
  20.          return 0; /* user pressed ESC */
  21.      /* The number of moves from hero to where the spell drops.*/
  22.      if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) {
  23.          pline_The("spell dissipates over the distance!");
  24.          return 0;
  25.      } else if (u.uswallow) {
  26.          pline_The("spell is cut short!");
  27.          exercise(A_WIS, FALSE); /* What were you THINKING! */
  28.          u.dx = 0;
  29.          u.dy = 0;
  30.          return 1;
  31.      } else if ((!cansee(cc.x, cc.y)
  32.                  && (!(mtmp = m_at(cc.x, cc.y)) || !canspotmon(mtmp)))
  33.                 || IS_STWALL(levl[cc.x][cc.y].typ)) {
  34.          Your("mind fails to lock onto that location!");
  35.          return 0;
  36.      }
  37.  
  38.      u.dx = cc.x;
  39.      u.dy = cc.y;
  40.      return 1;
  41.  }
  42.  

losespells

  1.  /* forget a random selection of known spells due to amnesia;
  2.     they used to be lost entirely, as if never learned, but now we
  3.     just set the memory retention to zero so that they can't be cast */
  4.  void
  5.  losespells()
  6.  {
  7.      int n, nzap, i;
  8.  
  9.      /* in case reading has been interrupted earlier, discard context */
  10.      context.spbook.book = 0;
  11.      context.spbook.o_id = 0;
  12.      /* count the number of known spells */
  13.      for (n = 0; n < MAXSPELL; ++n)
  14.          if (spellid(n) == NO_SPELL)
  15.              break;
  16.  
  17.      /* lose anywhere from zero to all known spells;
  18.         if confused, use the worse of two die rolls */
  19.      nzap = rn2(n + 1);
  20.      if (Confusion) {
  21.          i = rn2(n + 1);
  22.          if (i > nzap)
  23.              nzap = i;
  24.      }
  25.      /* good Luck might ameliorate spell loss */
  26.      if (nzap > 1 && !rnl(7))
  27.          nzap = rnd(nzap);
  28.  
  29.      /*
  30.       * Forget 'nzap' out of 'n' known spells by setting their memory
  31.       * retention to zero.  Every spell has the same probability to be
  32.       * forgotten, even if its retention is already zero.
  33.       *
  34.       * Perhaps we should forget the corresponding book too?
  35.       *
  36.       * (3.4.3 removed spells entirely from the list, but always did
  37.       * so from its end, so the 'nzap' most recently learned spells
  38.       * were the ones lost by default.  Player had sort control over
  39.       * the list, so could move the most useful spells to front and
  40.       * only lose them if 'nzap' turned out to be a large value.
  41.       *
  42.       * Discarding from the end of the list had the virtue of making
  43.       * casting letters for lost spells become invalid and retaining
  44.       * the original letter for the ones which weren't lost, so there
  45.       * was no risk to the player of accidentally casting the wrong
  46.       * spell when using a letter that was in use prior to amnesia.
  47.       * That wouldn't be the case if we implemented spell loss spread
  48.       * throughout the list of known spells; every spell located past
  49.       * the first lost spell would end up with new letter assigned.)
  50.       */
  51.      for (i = 0; nzap > 0; ++i) {
  52.          /* when nzap is small relative to the number of spells left,
  53.             the chance to lose spell [i] is small; as the number of
  54.             remaining candidates shrinks, the chance per candidate
  55.             gets bigger; overall, exactly nzap entries are affected */
  56.          if (rn2(n - i) < nzap) {
  57.              /* lose access to spell [i] */
  58.              spellknow(i) = 0;
  59.  #if 0
  60.              /* also forget its book */
  61.              forget_single_object(spellid(i));
  62.  #endif
  63.              /* and abuse wisdom */
  64.              exercise(A_WIS, FALSE);
  65.              /* there's now one less spell slated to be forgotten */
  66.              --nzap;
  67.          }
  68.      }
  69.  }
  70.  

spell_cmp

  1.  /*
  2.   * Allow player to sort the list of known spells.  Manually swapping
  3.   * pairs of them becomes very tedious once the list reaches two pages.
  4.   *
  5.   * Possible extensions:
  6.   *      provide means for player to control ordering of skill classes;
  7.   *      provide means to supply value N such that first N entries stick
  8.   *      while rest of list is being sorted;
  9.   *      make chosen sort order be persistent such that when new spells
  10.   *      are learned, they get inserted into sorted order rather than be
  11.   *      appended to the end of the list?
  12.   */
  13.  static const char *spl_sortchoices[] = {
  14.      "by casting letter",
  15.  #define SORTBY_LETTER 0
  16.      "alphabetically",
  17.  #define SORTBY_ALPHA 1
  18.      "by level, low to high",
  19.  #define SORTBY_LVL_LO 2
  20.      "by level, high to low",
  21.  #define SORTBY_LVL_HI 3
  22.      "by skill group, alphabetized within each group",
  23.  #define SORTBY_SKL_AL 4
  24.      "by skill group, low to high level within group",
  25.  #define SORTBY_SKL_LO 5
  26.      "by skill group, high to low level within group",
  27.  #define SORTBY_SKL_HI 6
  28.      "maintain current ordering",
  29.  #define SORTBY_CURRENT 7
  30.      /* a menu choice rather than a sort choice */
  31.      "reassign casting letters to retain current order",
  32.  #define SORTRETAINORDER 8
  33.  };
  34.  static int spl_sortmode = 0;   /* index into spl_sortchoices[] */
  35.  static int *spl_orderindx = 0; /* array of spl_book[] indices */
  36.  
  37.  /* qsort callback routine */
  38.  STATIC_PTR int CFDECLSPEC
  39.  spell_cmp(vptr1, vptr2)
  40.  const genericptr vptr1;
  41.  const genericptr vptr2;
  42.  {
  43.      /*
  44.       * gather up all of the possible parameters except spell name
  45.       * in advance, even though some might not be needed:
  46.       *  indx. = spl_orderindx[] index into spl_book[];
  47.       *  otyp. = spl_book[] index into objects[];
  48.       *  levl. = spell level;
  49.       *  skil. = skill group aka spell class.
  50.       */
  51.      int indx1 = *(int *) vptr1, indx2 = *(int *) vptr2,
  52.          otyp1 = spl_book[indx1].sp_id, otyp2 = spl_book[indx2].sp_id,
  53.          levl1 = objects[otyp1].oc_level, levl2 = objects[otyp2].oc_level,
  54.          skil1 = objects[otyp1].oc_skill, skil2 = objects[otyp2].oc_skill;
  55.  
  56.      switch (spl_sortmode) {
  57.      case SORTBY_LETTER:
  58.          return indx1 - indx2;
  59.      case SORTBY_ALPHA:
  60.          break;
  61.      case SORTBY_LVL_LO:
  62.          if (levl1 != levl2)
  63.              return levl1 - levl2;
  64.          break;
  65.      case SORTBY_LVL_HI:
  66.          if (levl1 != levl2)
  67.              return levl2 - levl1;
  68.          break;
  69.      case SORTBY_SKL_AL:
  70.          if (skil1 != skil2)
  71.              return skil1 - skil2;
  72.          break;
  73.      case SORTBY_SKL_LO:
  74.          if (skil1 != skil2)
  75.              return skil1 - skil2;
  76.          if (levl1 != levl2)
  77.              return levl1 - levl2;
  78.          break;
  79.      case SORTBY_SKL_HI:
  80.          if (skil1 != skil2)
  81.              return skil1 - skil2;
  82.          if (levl1 != levl2)
  83.              return levl2 - levl1;
  84.          break;
  85.      case SORTBY_CURRENT:
  86.      default:
  87.          return (vptr1 < vptr2) ? -1
  88.                                 : (vptr1 > vptr2); /* keep current order */
  89.      }
  90.      /* tie-breaker for most sorts--alphabetical by spell name */
  91.      return strcmpi(OBJ_NAME(objects[otyp1]), OBJ_NAME(objects[otyp2]));
  92.  }
  93.  

sortspells

  1.  /* sort the index used for display order of the "view known spells"
  2.     list (sortmode == SORTBY_xxx), or sort the spellbook itself to make
  3.     the current display order stick (sortmode == SORTRETAINORDER) */
  4.  STATIC_OVL void
  5.  sortspells()
  6.  {
  7.      int i;
  8.  #if defined(SYSV) || defined(DGUX)
  9.      unsigned n;
  10.  #else
  11.      int n;
  12.  #endif
  13.  
  14.      if (spl_sortmode == SORTBY_CURRENT)
  15.          return;
  16.      for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; ++n)
  17.          continue;
  18.      if (n < 2)
  19.          return; /* not enough entries to need sorting */
  20.  
  21.      if (!spl_orderindx) {
  22.          /* we haven't done any sorting yet; list is in casting order */
  23.          if (spl_sortmode == SORTBY_LETTER /* default */
  24.              || spl_sortmode == SORTRETAINORDER)
  25.              return;
  26.          /* allocate enough for full spellbook rather than just N spells */
  27.          spl_orderindx = (int *) alloc(MAXSPELL * sizeof(int));
  28.          for (i = 0; i < MAXSPELL; i++)
  29.              spl_orderindx[i] = i;
  30.      }
  31.  
  32.      if (spl_sortmode == SORTRETAINORDER) {
  33.          struct spell tmp_book[MAXSPELL];
  34.  
  35.          /* sort spl_book[] rather than spl_orderindx[];
  36.             this also updates the index to reflect the new ordering (we
  37.             could just free it since that ordering becomes the default) */
  38.          for (i = 0; i < MAXSPELL; i++)
  39.              tmp_book[i] = spl_book[spl_orderindx[i]];
  40.          for (i = 0; i < MAXSPELL; i++)
  41.              spl_book[i] = tmp_book[i], spl_orderindx[i] = i;
  42.          spl_sortmode = SORTBY_LETTER; /* reset */
  43.          return;
  44.      }
  45.  
  46.      /* usual case, sort the index rather than the spells themselves */
  47.      qsort((genericptr_t) spl_orderindx, n, sizeof *spl_orderindx, spell_cmp);
  48.      return;
  49.  }
  50.  

spellsortmenu

  1.  /* called if the [sort spells] entry in the view spells menu gets chosen */
  2.  STATIC_OVL boolean
  3.  spellsortmenu()
  4.  {
  5.      winid tmpwin;
  6.      menu_item *selected;
  7.      anything any;
  8.      char let;
  9.      int i, n, choice;
  10.  
  11.      tmpwin = create_nhwindow(NHW_MENU);
  12.      start_menu(tmpwin);
  13.      any = zeroany; /* zero out all bits */
  14.  
  15.      for (i = 0; i < SIZE(spl_sortchoices); i++) {
  16.          if (i == SORTRETAINORDER) {
  17.              let = 'z'; /* assumes fewer than 26 sort choices... */
  18.              /* separate final choice from others with a blank line */
  19.              any.a_int = 0;
  20.              add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
  21.                       MENU_UNSELECTED);
  22.          } else {
  23.              let = 'a' + i;
  24.          }
  25.          any.a_int = i + 1;
  26.          add_menu(tmpwin, NO_GLYPH, &any, let, 0, ATR_NONE, spl_sortchoices[i],
  27.                   (i == spl_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
  28.      }
  29.      end_menu(tmpwin, "View known spells list sorted");
  30.  
  31.      n = select_menu(tmpwin, PICK_ONE, &selected);
  32.      destroy_nhwindow(tmpwin);
  33.      if (n > 0) {
  34.          choice = selected[0].item.a_int - 1;
  35.          /* skip preselected entry if we have more than one item chosen */
  36.          if (n > 1 && choice == spl_sortmode)
  37.              choice = selected[1].item.a_int - 1;
  38.          free((genericptr_t) selected);
  39.          spl_sortmode = choice;
  40.          return TRUE;
  41.      }
  42.      return FALSE;
  43.  }
  44.  

dovspell

  1.  /* the '+' command -- view known spells */
  2.  int
  3.  dovspell()
  4.  {
  5.      char qbuf[QBUFSZ];
  6.      int splnum, othnum;
  7.      struct spell spl_tmp;
  8.  
  9.      if (spellid(0) == NO_SPELL) {
  10.          You("don't know any spells right now.");
  11.      } else {
  12.          while (dospellmenu("Currently known spells",
  13.                             SPELLMENU_VIEW, &splnum)) {
  14.              if (splnum == SPELLMENU_SORT) {
  15.                  if (spellsortmenu())
  16.                      sortspells();
  17.              } else {
  18.                  Sprintf(qbuf, "Reordering spells; swap '%c' with",
  19.                          spellet(splnum));
  20.                  if (!dospellmenu(qbuf, splnum, &othnum))
  21.                      break;
  22.  
  23.                  spl_tmp = spl_book[splnum];
  24.                  spl_book[splnum] = spl_book[othnum];
  25.                  spl_book[othnum] = spl_tmp;
  26.              }
  27.          }
  28.      }
  29.      if (spl_orderindx) {
  30.          free((genericptr_t) spl_orderindx);
  31.          spl_orderindx = 0;
  32.      }
  33.      spl_sortmode = SORTBY_LETTER; /* 0 */
  34.      return 0;
  35.  }
  36.  

dospellmenu

  1.  STATIC_OVL boolean
  2.  dospellmenu(prompt, splaction, spell_no)
  3.  const char *prompt;
  4.  int splaction; /* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */
  5.  int *spell_no;
  6.  {
  7.      winid tmpwin;
  8.      int i, n, how, splnum;
  9.      char buf[BUFSZ], retentionbuf[24];
  10.      const char *fmt;
  11.      menu_item *selected;
  12.      anything any;
  13.  
  14.      tmpwin = create_nhwindow(NHW_MENU);
  15.      start_menu(tmpwin);
  16.      any = zeroany; /* zero out all bits */
  17.  
  18.      /*
  19.       * The correct spacing of the columns when not using
  20.       * tab separation depends on the following:
  21.       * (1) that the font is monospaced, and
  22.       * (2) that selection letters are pre-pended to the
  23.       * given string and are of the form "a - ".
  24.       */
  25.      if (!iflags.menu_tab_sep) {
  26.          Sprintf(buf, "%-20s     Level %-12s Fail Retention", "    Name",
  27.                  "Category");
  28.          fmt = "%-20s  %2d   %-12s %3d%% %9s";
  29.      } else {
  30.          Sprintf(buf, "Name\tLevel\tCategory\tFail\tRetention");
  31.          fmt = "%s\t%-d\t%s\t%-d%%\t%s";
  32.      }
  33.      add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, buf,
  34.               MENU_UNSELECTED);
  35.      for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) {
  36.          splnum = !spl_orderindx ? i : spl_orderindx[i];
  37.          Sprintf(buf, fmt, spellname(splnum), spellev(splnum),
  38.                  spelltypemnemonic(spell_skilltype(spellid(splnum))),
  39.                  100 - percent_success(splnum),
  40.                  spellretention(splnum, retentionbuf));
  41.  
  42.          any.a_int = splnum + 1; /* must be non-zero */
  43.          add_menu(tmpwin, NO_GLYPH, &any, spellet(splnum), 0, ATR_NONE, buf,
  44.                   (splnum == splaction) ? MENU_SELECTED : MENU_UNSELECTED);
  45.      }
  46.      how = PICK_ONE;
  47.      if (splaction == SPELLMENU_VIEW) {
  48.          if (spellid(1) == NO_SPELL) {
  49.              /* only one spell => nothing to swap with */
  50.              how = PICK_NONE;
  51.          } else {
  52.              /* more than 1 spell, add an extra menu entry */
  53.              any.a_int = SPELLMENU_SORT + 1;
  54.              add_menu(tmpwin, NO_GLYPH, &any, '+', 0, ATR_NONE,
  55.                       "[sort spells]", MENU_UNSELECTED);
  56.          }
  57.      }
  58.      end_menu(tmpwin, prompt);
  59.  
  60.      n = select_menu(tmpwin, how, &selected);
  61.      destroy_nhwindow(tmpwin);
  62.      if (n > 0) {
  63.          *spell_no = selected[0].item.a_int - 1;
  64.          /* menu selection for `PICK_ONE' does not
  65.             de-select any preselected entry */
  66.          if (n > 1 && *spell_no == splaction)
  67.              *spell_no = selected[1].item.a_int - 1;
  68.          free((genericptr_t) selected);
  69.          /* default selection of preselected spell means that
  70.             user chose not to swap it with anything */
  71.          if (*spell_no == splaction)
  72.              return FALSE;
  73.          return TRUE;
  74.      } else if (splaction >= 0) {
  75.          /* explicit de-selection of preselected spell means that
  76.             user is still swapping but not for the current spell */
  77.          *spell_no = splaction;
  78.          return TRUE;
  79.      }
  80.      return FALSE;
  81.  }
  82.  

percent_success

  1.  STATIC_OVL int
  2.  percent_success(spell)
  3.  int spell;
  4.  {
  5.      /* Intrinsic and learned ability are combined to calculate
  6.       * the probability of player's success at cast a given spell.
  7.       */
  8.      int chance, splcaster, special, statused;
  9.      int difficulty;
  10.      int skill;
  11.  
  12.      /* Calculate intrinsic ability (splcaster) */
  13.  
  14.      splcaster = urole.spelbase;
  15.      special = urole.spelheal;
  16.      statused = ACURR(urole.spelstat);
  17.  
  18.      if (uarm && is_metallic(uarm))
  19.          splcaster += (uarmc && uarmc->otyp == ROBE) ? urole.spelarmr / 2
  20.                                                      : urole.spelarmr;
  21.      else if (uarmc && uarmc->otyp == ROBE)
  22.          splcaster -= urole.spelarmr;
  23.      if (uarms)
  24.          splcaster += urole.spelshld;
  25.  
  26.      if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE)
  27.          splcaster += uarmhbon;
  28.      if (uarmg && is_metallic(uarmg))
  29.          splcaster += uarmgbon;
  30.      if (uarmf && is_metallic(uarmf))
  31.          splcaster += uarmfbon;
  32.  
  33.      if (spellid(spell) == urole.spelspec)
  34.          splcaster += urole.spelsbon;
  35.  
  36.      /* `healing spell' bonus */
  37.      if (spellid(spell) == SPE_HEALING || spellid(spell) == SPE_EXTRA_HEALING
  38.          || spellid(spell) == SPE_CURE_BLINDNESS
  39.          || spellid(spell) == SPE_CURE_SICKNESS
  40.          || spellid(spell) == SPE_RESTORE_ABILITY
  41.          || spellid(spell) == SPE_REMOVE_CURSE)
  42.          splcaster += special;
  43.  
  44.      if (splcaster > 20)
  45.          splcaster = 20;
  46.  
  47.      /* Calculate learned ability */
  48.  
  49.      /* Players basic likelihood of being able to cast any spell
  50.       * is based of their `magic' statistic. (Int or Wis)
  51.       */
  52.      chance = 11 * statused / 2;
  53.  
  54.      /*
  55.       * High level spells are harder.  Easier for higher level casters.
  56.       * The difficulty is based on the hero's level and their skill level
  57.       * in that spell type.
  58.       */
  59.      skill = P_SKILL(spell_skilltype(spellid(spell)));
  60.      skill = max(skill, P_UNSKILLED) - 1; /* unskilled => 0 */
  61.      difficulty =
  62.          (spellev(spell) - 1) * 4 - ((skill * 6) + (u.ulevel / 3) + 1);
  63.  
  64.      if (difficulty > 0) {
  65.          /* Player is too low level or unskilled. */
  66.          chance -= isqrt(900 * difficulty + 2000);
  67.      } else {
  68.          /* Player is above level.  Learning continues, but the
  69.           * law of diminishing returns sets in quickly for
  70.           * low-level spells.  That is, a player quickly gains
  71.           * no advantage for raising level.
  72.           */
  73.          int learning = 15 * -difficulty / spellev(spell);
  74.          chance += learning > 20 ? 20 : learning;
  75.      }
  76.  
  77.      /* Clamp the chance: >18 stat and advanced learning only help
  78.       * to a limit, while chances below "hopeless" only raise the
  79.       * specter of overflowing 16-bit ints (and permit wearing a
  80.       * shield to raise the chances :-).
  81.       */
  82.      if (chance < 0)
  83.          chance = 0;
  84.      if (chance > 120)
  85.          chance = 120;
  86.  
  87.      /* Wearing anything but a light shield makes it very awkward
  88.       * to cast a spell.  The penalty is not quite so bad for the
  89.       * player's role-specific spell.
  90.       */
  91.      if (uarms && weight(uarms) > (int) objects[SMALL_SHIELD].oc_weight) {
  92.          if (spellid(spell) == urole.spelspec) {
  93.              chance /= 2;
  94.          } else {
  95.              chance /= 4;
  96.          }
  97.      }
  98.  
  99.      /* Finally, chance (based on player intell/wisdom and level) is
  100.       * combined with ability (based on player intrinsics and
  101.       * encumbrances).  No matter how intelligent/wise and advanced
  102.       * a player is, intrinsics and encumbrance can prevent casting;
  103.       * and no matter how able, learning is always required.
  104.       */
  105.      chance = chance * (20 - splcaster) / 15 - splcaster;
  106.  
  107.      /* Clamp to percentile */
  108.      if (chance > 100)
  109.          chance = 100;
  110.      if (chance < 0)
  111.          chance = 0;
  112.  
  113.      return chance;
  114.  }
  115.  

spellretention

  1.  STATIC_OVL char *
  2.  spellretention(idx, outbuf)
  3.  int idx;
  4.  char *outbuf;
  5.  {
  6.      long turnsleft, percent, accuracy;
  7.      int skill;
  8.  
  9.      skill = P_SKILL(spell_skilltype(spellid(idx)));
  10.      skill = max(skill, P_UNSKILLED); /* restricted same as unskilled */
  11.      turnsleft = spellknow(idx);
  12.      *outbuf = '\0'; /* lint suppression */
  13.  
  14.      if (turnsleft < 1L) {
  15.          /* spell has expired; hero can't successfully cast it anymore */
  16.          Strcpy(outbuf, "(gone)");
  17.      } else if (turnsleft >= (long) KEEN) {
  18.          /* full retention, first turn or immediately after reading book */
  19.          Strcpy(outbuf, "100%");
  20.      } else {
  21.          /*
  22.           * Retention is displayed as a range of percentages of
  23.           * amount of time left until memory of the spell expires;
  24.           * the precision of the range depends upon hero's skill
  25.           * in this spell.
  26.           *    expert:  2% intervals; 1-2,   3-4,  ...,   99-100;
  27.           *   skilled:  5% intervals; 1-5,   6-10, ...,   95-100;
  28.           *     basic: 10% intervals; 1-10, 11-20, ...,   91-100;
  29.           * unskilled: 25% intervals; 1-25, 26-50, 51-75, 76-100.
  30.           *
  31.           * At the low end of each range, a value of N% really means
  32.           * (N-1)%+1 through N%; so 1% is "greater than 0, at most 200".
  33.           * KEEN is a multiple of 100; KEEN/100 loses no precision.
  34.           */
  35.          percent = (turnsleft - 1L) / ((long) KEEN / 100L) + 1L;
  36.          accuracy =
  37.              (skill == P_EXPERT) ? 2L : (skill == P_SKILLED)
  38.                                             ? 5L
  39.                                             : (skill == P_BASIC) ? 10L : 25L;
  40.          /* round up to the high end of this range */
  41.          percent = accuracy * ((percent - 1L) / accuracy + 1L);
  42.          Sprintf(outbuf, "%ld%%-%ld%%", percent - accuracy + 1L, percent);
  43.      }
  44.      return outbuf;
  45.  }
  46.  

initialspell

  1.  /* Learn a spell during creation of the initial inventory */
  2.  void
  3.  initialspell(obj)
  4.  struct obj *obj;
  5.  {
  6.      int i, otyp = obj->otyp;
  7.  
  8.      for (i = 0; i < MAXSPELL; i++)
  9.          if (spellid(i) == NO_SPELL || spellid(i) == otyp)
  10.              break;
  11.  
  12.      if (i == MAXSPELL) {
  13.          impossible("Too many spells memorized!");
  14.      } else if (spellid(i) != NO_SPELL) {
  15.          /* initial inventory shouldn't contain duplicate spellbooks */
  16.          impossible("Spell %s already known.", OBJ_NAME(objects[otyp]));
  17.      } else {
  18.          spl_book[i].sp_id = otyp;
  19.          spl_book[i].sp_lev = objects[otyp].oc_level;
  20.          incrnknow(i, 0);
  21.      }
  22.      return;
  23.  }
  24.  
  25.  /*spell.c*/