//:://////////////////////////////////////////////
//:: PRC New Spellbooks use conversation
//:: prc_s_spellb
//:://////////////////////////////////////////////
/** @file
    @todo Primo: Could you write a blurb on what
                 this does and TLKify it?


    @author Primogenitor
    @date   Created  - yyyy.mm.dd
  
last changed by motu99, April 29, 2008:

Conversation script for setting up spells to be memorized by prepared casters

This conversation script sets up a persistent array of the spells to be memorized
(at the end of the next rest) for any newspellbook prepared caster class.

It uses the persistent array name prefix "Spellbook", then appends the spell level
(converted to a string) to the prefix and lastly appends the class-nr (converted to a string)

The thus appended prefix is a persistent array name, in which the nSpellbookIDs of the
spells to be memorized at the end of the next rest are stored

the conversation is called by activating the prc_spellbook feat (#1999 in feats.2da)
which fires the spellscript prc_spellbook (#1792 in spells.2da), which then calls this
conversation script
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////


// persistant storage format on hide
/**
MEMORIZED SPELLS FOR PREP CASTERS:
All spell levels are all stored in one single array (motu99: very unfortunate, should be changed):
  sArrayName = "NewSpellbookMem_"+IntToString(nClass)
The array is indexed by the spellbookID; the value is the number of spells with that spellbookID still in memory:
  nNrOfSpellsStillInMemory = sArrayName[nSpellbookID]

SPELLS TO BE MEMORIZED BY PREP CASTERS
They are stored in up to ten arrays, one array for each spell level (or rather spell slot level)
  sArrayName = "Spellbook"+IntToString(nSpellLevel)+"_"+IntToString(nClass)
The array is indexed by the slot number, starting at #0; the value contains the nSpelllbookID of the spell to be memorized
  nSpellbookID = sArrayName[nSlotNr]

SPELLS MEMORIZED BY PREP CASTERS - INDEX
Array created from "spells to be memorized" in OnRest event. Only unique spellids are stored.
  sArrayName = "SpellbookIDX"+IntToString(nSpellLevel)+"_"+IntToString(nClass)
Serves as "NewSpellbookMem_" index for fast search and delete - allows looping only through memorized SpellbookIDs
(archivist - max 56 + bonus slots from high WIS) instead of all SpellbookIDs (archivist - 2400+)
Should help reduce instruction count sagnificantly.

SPELLS KNOWN BY PREP CASTERS:
so far prep NSB casters know all spells in their class spellbook; they need not learn spells
motu99: This might change, if for instance wizards use the NSB system to gain higher spell slot levels (10+)
//for archivist:
They are stored in up to ten arrays, one array for each spell level (or rather spell slot level)
  sArrayName = "Spellbook_Known_"+IntToString(nClass)+"_"+IntToString(nSpellLevel)

SPELLS KNOWN BY SPONT CASTERS:
The spells known are stored in one single array (the array contains only the non-metamagic versions and only master spells)
  sArrayName = "Spellbook" + IntToString(nClass);
The array is indexed by a counter (the i-th spell learned); the value contains the nSpellbookID of the (non-metamagic master) spell known
  nSpellbookID = sArrayName[i]

AVAILABLE SPELL SLOTS FOR SPONT CASTERS:
The nr of still available spell slots for a prep caster are all stored in one single array
  sArrayName =  "NewSpellbookMem_" + IntToString(nClass)
The array is indexed by the spell (slot) level, the value contains the nr of still available slots at that spell (slot) level
  nNrOfSpellSlotsAvailable = sArrayName[nSpellSlotLevel]
*/

// spells in the class spellbook of nClass (a spont caster generally will not know all of these spells)
/**
SPELLS IN THE CLASS SPELLBOOK OF PREP OR SPONT CASTERS:
The spells that are potentially learnable by nClass are stored on the prc cache object in up to 10 different tokens.
The class spell book ONLY stores the masterspells and ONLY the non-metamagicked version!

There is one storage token for every spell level (and class); it has the tag name:
  sTag = "SpellLvl_"+IntToString(nClass)+"_Level_"+IntToString(nSpellLevel);
The spells are stored on the token object oToken (defined by the unique sTag) in an array with name sArrayName
  oToken = GetObjectByTag(sTag)
  sArrayName = "Lkup"
The array is indexed by a counter (the i-th spell of a given level in the class spellbook); the value is the spellbookID
  nSpellbookID = sArrayName[i]
*/

#include "x2_inc_spellhook"
#include "inc_dynconv"
#include "inc_sp_gain_mem"

//////////////////////////////////////////////////
/* Constant defintions                          */
//////////////////////////////////////////////////

const int STAGE_SELECT_CLASS       = 0;
const int STAGE_SELECT_SPELL_LEVEL = 1;
const int STAGE_SELECT_SPELL_SLOT  = 2;
const int STAGE_SELECT_METAMAGIC   = 3;
const int STAGE_SELECT_SPELL       = 4;

const int CHOICE_RETURN_TO_PREVIOUS = 0xEFFFFFFF;


const string CONV_SPELLB_CLASS = "SpellClass";
const string CONV_SPELLB_LEVEL = "SpellLevel";
const string CONV_SPELLB_META = "MetaMagic";
const string CONV_SPELLB_SLOT = "SpellSlot";

const int DYNCONV_NEXT_STAGE = -4;


//////////////////////////////////////////////////
/* Aid functions                                */
//////////////////////////////////////////////////

const string SPELLS_MEMORIZED_CACHE = "SMCCCache";

void DeleteSpellsMemorizedCache(object oPC)
{
    int i;
    for (i=1; i <= MAX_CLASSES; i++)
    {
        int nClass = GetClassByPosition(i, oPC);
        if (nClass == CLASS_TYPE_INVALID) break;

        if(GetLocalInt(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass)))
        {
            DeleteLocalInt(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass));
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "0");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "1");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "2");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "3");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "4");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "5");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "6");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "7");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "8");
            DeleteLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + "9");
        }
    }
}

// now build the cache
void GenerateSpellsMemorizedCache(int nClass, object oPC)
{
    // get the object on the hide of oPC where the persistant data are stored
    //object oToken = GetHideToken(oPC);
    string sSpellsMemorized = GetSpellsMemorized_Array(nClass);

    // if the persistant array with the remaining memorized spells does not exist, abort
    if(!persistant_array_exists(oPC, sSpellsMemorized))
    {
        if(DEBUG) DoDebug("Error: " +sSpellsMemorized+ " array does not exist");
    }
    else
    {
        string sFile = GetNSBDefinitionFileName(nClass);
        string sArrayIDX, sSpellbookID, sMessage, sMess, sClass = IntToString(nClass);

        // remember the class (because this might change during the conversation)
        SetLocalInt(oPC, SPELLS_MEMORIZED_CACHE + sClass, TRUE);

        int nSpellLevel, nSlot, nSlots, nSpellbookID;
        for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++)
        {
            sArrayIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + sClass;
            sMessage = "";
            nSlots = persistant_array_get_size(oPC, sArrayIDX);
            for(nSlot = 0; nSlot < nSlots; nSlot++)
            {
                nSpellbookID = persistant_array_get_int(oPC, sArrayIDX, nSlot);
                int nCount = nSpellbookID ? persistant_array_get_int(oPC, sSpellsMemorized, nSpellbookID) : 0;
                if(nCount)
                {
                    // determine spell name from spellID by reference
                    int nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", nSpellbookID));
                    sMess = PRC_TEXT_WHITE + GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));
                    // add the metamagic [ext emp]
                    int nMetaMagicFeat = StringToInt(Get2DACache(sFile, "ReqFeat", nSpellbookID));
                    if(nMetaMagicFeat)
                    {
                        int nMetaMagic = GetMetaMagicFromFeat(nMetaMagicFeat);
                        sMess += " - " +GetMetaMagicString(nMetaMagic);
                    }
                    // add the nr of spells in memory
                    sMess += PRC_TEXT_BLUE + " [" +IntToString(nCount)+ "]\n";
                    sMessage += sMess;
                }
            }
            // now store the values for later retrieval
            if (sMessage != "") SetLocalString(oPC, SPELLS_MEMORIZED_CACHE + sClass + "_" + IntToString(nSpellLevel), sMessage);
        }
    }

    // we delete the cached values on exit from the conversation, so no need to do it now
    // DelayCommand(6.0, DeleteSpellsMemorizedCache(oPC));
}

// creates a string with a list of memorized spells of the given nClass and nSpellSlotLevel
// each spell has an extra line, which denotes the spell's name 
string ListMemorizedSpells(int nClass, int nSpellSlotLevel, object oPC)
{
  // try to get the list from cache; but only if cache is for the correct nClass
  //if (GetLocalInt(oPC, SPELLS_MEMORIZED_CACHE) == nClass)
  //{
    return GetLocalString(oPC, SPELLS_MEMORIZED_CACHE + IntToString(nClass) + "_" + IntToString(nSpellSlotLevel));
  //}
}


//////////////////////////////////////////////////
/* Main function                                */
//////////////////////////////////////////////////

void main()
{
  object oPC = GetPCSpeaker();
  /* Get the value of the local variable set by the conversation script calling
   * this script. Values:
   * DYNCONV_ABORTED     Conversation aborted
   * DYNCONV_EXITED      Conversation exited via the exit node
   * DYNCONV_SETUP_STAGE System's reply turn
   * 0                   Error - something else called the script
   * Other               The user made a choice
   */
  int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE);
  // The stage is used to determine the active conversation node.
  // 0 is the entry node.
  int nStage = GetStage(oPC);

  // Check which of the conversation scripts called the scripts
  if(nValue == 0) // All of them set the DynConv_Var to non-zero value, so something is wrong -> abort
    return;

  if(nValue == DYNCONV_SETUP_STAGE)
  {
    // Check if this stage is marked as already set up
    // This stops list duplication when scrolling
    if(!GetIsStageSetUp(nStage, oPC))
    {
      if(nStage == STAGE_SELECT_CLASS)
      {
        //select spell class
        SetHeader("Select a spell book:");
        int i;
        for (i=1; i <= MAX_CLASSES; i++)
        {
          int nClass = GetClassByPosition(i, oPC);
          if (nClass == CLASS_TYPE_INVALID) break;

          if(GetIsNSBClass(nClass) && // must be a new spellbook class
             GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED) // must be a prepared caster
          {
            // must have levels in the prepared class and at least level 1 spell slots
            int nAbilityScore = GetAbilityScoreForClass(nClass, oPC);
            int nClassLevel = GetLevelByPosition(i, oPC);
            if(nClassLevel
              && (GetSlotCount(nClassLevel, 0, nAbilityScore, nClass)
               || GetSlotCount(nClassLevel, 1, nAbilityScore, nClass)))
            {
              string sClassName = GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", nClass)));
              GenerateSpellsMemorizedCache(nClass, oPC);
              AddChoice(sClassName, nClass, oPC);
            }
          }
        }
        SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
        MarkStageSetUp(nStage, oPC);
      }
      else if(nStage == STAGE_SELECT_SPELL_LEVEL)
      {
        int nClass = GetLocalInt(oPC, CONV_SPELLB_CLASS);
        int nCasterLevel = GetCasterLevelByClass(nClass, oPC);

        int nMaxSpellSlotLevel = GetMaxSpellLevelForCasterLevel(nClass, nCasterLevel);
        int nMinSpellSlotLevel = GetMinSpellLevelForCasterLevel(nClass, nCasterLevel);

        int nChoiceAdded = FALSE;

        if(nMaxSpellSlotLevel >= nMinSpellSlotLevel)
        {
          string sChoiceSpellLevel = "Spell slot level ";
          int nAbilityScore = GetAbilityScoreForClass(nClass, oPC);

          // List all spell slot levels available to the caster for this class
          int nSpellSlotLevel;
          for(nSpellSlotLevel = nMinSpellSlotLevel; nSpellSlotLevel <= nMaxSpellSlotLevel; nSpellSlotLevel++)
          {
            // for every spell level, determine the slot count, and if it is non-zero add a choice
            // we do not break out of the loop on an empty slot count, because of bonus slot counts from items there might be gaps
            if(GetSlotCount(nCasterLevel, nSpellSlotLevel, nAbilityScore, nClass))
            {
              AddChoice(sChoiceSpellLevel +IntToString(nSpellSlotLevel), nSpellSlotLevel, oPC);
              nChoiceAdded = TRUE;
            }
          }
        }

        if (nChoiceAdded)
          SetHeader("Select a spell slot level:");
        else
          SetHeader("You cannot memorize any spells at the moment - check your ability score");

        SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
        MarkStageSetUp(nStage, oPC);
      }
      else if(nStage == STAGE_SELECT_SPELL_SLOT)
      {
        int nClass = GetLocalInt(oPC, CONV_SPELLB_CLASS);
        int nSpellSlotLevel = GetLocalInt(oPC, CONV_SPELLB_LEVEL);
        int nCasterLevel = GetCasterLevelByClass(nClass, oPC);

        int nAbilityScore = GetAbilityScoreForClass(nClass, oPC);

        // get the object on the hide of oPC where the persistant data are stored
        //object oToken = GetHideToken(oPC);

        // determine the name of the persistant array that holds the spells to be memorized for the given nClass and nSpellLevel
        // (the index to the array is the nr of the slot of the given nClass and nSpellLevel)
        string sSpellsToBeMemorized = GetSpellsToBeMemorized_Array(nClass, nSpellSlotLevel);
        // unfortunatly, the spellsMemorized list has a different format (all spell levels in one huge sparse array, indexed by nSpellbookID)
        string sSpellsMemorized = GetSpellsMemorized_Array(nClass);

        // now check if the arrays "spells to be memorized" and "spells memorized" exist at the given spell slot level and create them, if not
        if (persistant_array_get_size(oPC, sSpellsToBeMemorized) < 0) persistant_array_create(oPC, sSpellsToBeMemorized);
        if (persistant_array_get_size(oPC, sSpellsMemorized) < 0)   persistant_array_create(oPC, sSpellsMemorized);

                string sHeader = "You have remaining:\n";
        sHeader += ListMemorizedSpells(nClass, nSpellSlotLevel, oPC) + "\n";

        // get the nr of spell slots for the given nClass and nSpellLevel
        // (should be non-zero, because we only allow the PC to select spell slot levels with non-zero slot count)
        int nSlots = GetSlotCount(nCasterLevel, nSpellSlotLevel, nAbilityScore, nClass, oPC);
        if (nSlots > 0)
        {
          sHeader += "Select a spell slot:\n"+ PRC_TEXT_WHITE + "spell to be memorized " + PRC_TEXT_BLUE + "[# still in memory]";
            
          // set the array size of "spells to be memorized" and "spells memorized" to the nr of slots
          array_set_size(oPC, sSpellsToBeMemorized, nSlots);

          string sFile = GetNSBDefinitionFileName(nClass);
          string sChoice;
          string sNameToBeMemorized;
          // add a choice for every slot; show what is currently in the slot (if nothing, show "empty")
          int nSlotNr;
          for(nSlotNr = 0; nSlotNr < nSlots; nSlotNr++)
          {
            // get the spell associated with the i-th slot
            int nSpellbookID = persistant_array_get_int(oPC, sSpellsToBeMemorized, nSlotNr);
            int nMetaMagic = 0;
            // nothing "to be memorized" for this slot?
            if (nSpellbookID == 0)
            {
              sNameToBeMemorized = "Empty";
            }
            else
            {
              // get the spell name "to be memorized", including metamagic
              // int nIPFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID));
              // sNameToBeMemorized = GetStringByStrRef(StringToInt(Get2DACache("iprp_feats", "Name", nIPFeatID)));
              int nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", nSpellbookID));
              sNameToBeMemorized = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));

              nMetaMagic = GetMetaMagicFromFeat(StringToInt(Get2DACache(sFile, "ReqFeat", nSpellbookID)));
              if (nMetaMagic) sNameToBeMemorized += " - " + GetMetaMagicString(nMetaMagic);
            }

            //first we show what spell will "be memorized" at next rest from the given slot (this is in white)
            sChoice = PRC_TEXT_WHITE + sNameToBeMemorized;

            // now check if there are spells still in memory that are equal to the spell "to be memorized" at the given slot
            if (nSpellbookID)
            {
              int nNrOfSpells_Mem = persistant_array_get_int(oPC, sSpellsMemorized, nSpellbookID);
              // show in blue and in brackets
              sChoice += PRC_TEXT_BLUE + " [" +IntToString(nNrOfSpells_Mem)+ "]";
            }
            
            // add the slot nr as choice 
            AddChoice(sChoice, nSlotNr, oPC);
          }
        }
        else
        {
          sHeader += PRC_TEXT_WHITE + "there aren't any slots available at the chosen level - check your ability score";
        }
        SetHeader(sHeader);
        AddChoice("Back", CHOICE_RETURN_TO_PREVIOUS);
        SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
        MarkStageSetUp(nStage, oPC);
      }
      else if (nStage == STAGE_SELECT_METAMAGIC)
      {
        // get the metamagic feats oPC possesses
        int nMetaMagicCaster = GetMetaMagicOfCaster(oPC);
        int bChoiceAdded;
        // only need to do this, if the caster has at least one metamagic feat
        if (nMetaMagicCaster)
        {
          // get the currently selected spell slot level
          int nSpellSlotLevel = GetLocalInt(oPC, CONV_SPELLB_LEVEL);
          int nClass = GetLocalInt(oPC, CONV_SPELLB_CLASS);
          int nCasterLevel = GetCasterLevelByClass(nClass, oPC);
          int nMinSpellSlotLevel = GetMinSpellLevelForCasterLevel(nClass, nCasterLevel);

          // metamagics only for slot levels higher than the lowest slot level
          if (nSpellSlotLevel > nMinSpellSlotLevel)
          {
            // calculate the maximum metamagic adjustment that is possible at the given spell slot level
            // note that the metamagic adjustment will generally result in spells to choose from, that are
            // lower in level than the spell slot level. But we cannot reduce the level below the minimum spell level of the class
            int nMaxMetaMagicAdj = nSpellSlotLevel - nMinSpellSlotLevel;

            // go through all possible metamagics by shifting 1 to the left
            // this will result in checking for some metamagics that are not implemented
            // but the check against the metamagic feats possessed be oPC will get rid of all non-implemented
            int nMetaMagic;
            for (nMetaMagic = 1; nMetaMagic < 0x40; nMetaMagic <<= 1)
            {
              if ((nMetaMagicCaster & nMetaMagic) // caster must have the metamagic feat
                // and the combined levels of the already chosen metamagic with the metamagic to choose must be
                // less or equal than the max metamagic adjustment allowed for nClass at the given nSpellSlotLevel
                && GetMetaMagicSpellLevelAdjustment(nMetaMagic) <= nMaxMetaMagicAdj)
              {
                AddChoice(GetMetaMagicString(nMetaMagic), nMetaMagic, oPC);
                bChoiceAdded = TRUE;
              }
            }

            if (bChoiceAdded)
            {
              SetHeader("Select a metamagic adjustment:");
              AddChoice("No metamagic", 0, oPC);
              AddChoice("Back", CHOICE_RETURN_TO_PREVIOUS);
              SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
            }
          }
        }
        
        // no metamagics available at the spell slot level?
        if (!bChoiceAdded)
        {
          // then advance to next stage and clear metamagic
          SetStage(++nStage, oPC);
          DeleteLocalInt(oPC, CONV_SPELLB_META);
        }

        MarkStageSetUp(STAGE_SELECT_METAMAGIC, oPC);
      }
      
      // if-clause is intentional; DONT change to else-if
      if(nStage == STAGE_SELECT_SPELL)
      {
        int nClass = GetLocalInt(oPC, CONV_SPELLB_CLASS);
        int nMetaMagic = GetLocalInt(oPC, CONV_SPELLB_META);
        int nSpellSlotLevel = GetLocalInt(oPC, CONV_SPELLB_LEVEL);
        int nSpellLevel = nSpellSlotLevel - GetMetaMagicSpellLevelAdjustment(nMetaMagic);

        // determine from where to get the spells known (for nClass at the given level)
        // so far this is the class spellbook, eg. all spells are available (divine casters)
        string sFile = GetNSBDefinitionFileName(nClass);
        object oToken;
        string sSpellBook;
        if(bKnowsAllClassSpells(nClass))
        {
            oToken = GetSpellsOfClass_Token(nClass, nSpellLevel);
            sSpellBook = GetSpellsOfClass_Array();
        }
        else
        {
            oToken = oPC;
            sSpellBook = GetSpellsKnown_Array(nClass, nSpellLevel);
        }

        // go through all spells that oPC has in his spellbook at the given nSpellLevel ( for divine casters this might be all) 
        // motu99: This array does NOT include the metamagicked versions of the spells or subradial versions!
        int nSpellsKnown = persistant_array_get_size(oToken, sSpellBook);
        int bSpellSelected = FALSE;
        int i;
        for(i = 0; i < nSpellsKnown; i++)
        {
          // this is the cls_spell_* row nr for the UNMETAMAGICKED version of the spell
          int nSpellbookID = persistant_array_get_int(oToken, sSpellBook, i);
          
          // get the real spellID
          int nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", nSpellbookID));
          
          // if we choose a metamagic, find the nSpellbookID for the metamagic version of the spell
          // all metamagic versions of the master spell lie in a consecutive block after the non-metamagic version
          if (nMetaMagic)
          {
            // get the next row in cls_spell_* and test if it belongs to the same real spellID
            while (StringToInt(Get2DACache(sFile, "RealSpellID", ++nSpellbookID)) == nSpellID)
            {
              // do the metamagics match?
              if (nMetaMagic == GetMetaMagicFromFeat(StringToInt(Get2DACache(sFile, "ReqFeat", nSpellbookID))))
              {
                // indicate success by negative nr
                nSpellbookID = -nSpellbookID;
                break;
              }
            }
            
            // success? then redo the negation
            if (nSpellbookID < 0)
              nSpellbookID = -nSpellbookID;
            // otherwise indicate failure by setting nSpellbookID to zero
            else
              nSpellbookID = 0;
          }

          // did we find an appropriate spellbook ID for the given spell slot evel and metamagic?
          // then add it to the list
          if (nSpellbookID)
          {
            // int nIPFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID));
            // string sName = GetStringByStrRef(StringToInt(Get2DACache("iprp_feats", "Name", nIPFeatID)));
            string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));
            if (nMetaMagic) sName + " - " +GetMetaMagicString(nMetaMagic);

            AddChoice(sName, nSpellbookID, oPC);
            bSpellSelected = TRUE;
          }
        }
        
        if (bSpellSelected)
          SetHeader("Select a spell:");
        else
          SetHeader("No spells to select at spell level " + IntToString (nSpellLevel));

        SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
        MarkStageSetUp(nStage, oPC);
      }
    }

    // Do token setup
    SetupTokens();
  }
  else if(nValue == DYNCONV_EXITED ||
      nValue == DYNCONV_ABORTED )
  {
    //end of conversation cleanup
    DeleteLocalInt(oPC, CONV_SPELLB_CLASS);
    DeleteLocalInt(oPC, CONV_SPELLB_LEVEL);
    DeleteLocalInt(oPC, CONV_SPELLB_SLOT);
    DeleteLocalInt(oPC, CONV_SPELLB_META);

    DeleteSpellsMemorizedCache(oPC);
  }
  else
  {
    int nChoice = GetChoice(oPC);
    if(nStage == STAGE_SELECT_CLASS)
    {
       //store nClass and proceed to slot level selection
      SetLocalInt(oPC, CONV_SPELLB_CLASS, nChoice);
      nStage = STAGE_SELECT_SPELL_LEVEL;
      MarkStageNotSetUp(nStage, oPC);
    }
    else if(nStage == STAGE_SELECT_SPELL_LEVEL)
    {
      //store slot level and proceed to spell slot selection
      SetLocalInt(oPC, CONV_SPELLB_LEVEL, nChoice);
      nStage = STAGE_SELECT_SPELL_SLOT;
      MarkStageNotSetUp(nStage, oPC);
    }
    else if(nStage == STAGE_SELECT_SPELL_SLOT)
    {
      if(nChoice == CHOICE_RETURN_TO_PREVIOUS)
          nStage = STAGE_SELECT_SPELL_LEVEL;
      else
      {
        // store the spell slot nr and go to metamagic selection phase
        SetLocalInt(oPC, CONV_SPELLB_SLOT, nChoice);
        nStage = STAGE_SELECT_METAMAGIC;
      }
      MarkStageNotSetUp(nStage, oPC);
    }
    else if(nStage == STAGE_SELECT_METAMAGIC)
    {
      if(nChoice == CHOICE_RETURN_TO_PREVIOUS)
          nStage = STAGE_SELECT_SPELL_SLOT;
      else
      {
        // store the metamagic and proceed to spell selection phase
        SetLocalInt(oPC, CONV_SPELLB_META, nChoice);
        nStage = STAGE_SELECT_SPELL;
      }
      MarkStageNotSetUp(nStage, oPC);
    }
    else if(nStage == STAGE_SELECT_SPELL)
    {
      // our choice is the nSpellbookID
      
      // get the other vital information
      int nSpellSlot = GetLocalInt(oPC, CONV_SPELLB_SLOT);
      int nSpellSlotLevel = GetLocalInt(oPC, CONV_SPELLB_LEVEL);
      int nClass = GetLocalInt(oPC, CONV_SPELLB_CLASS);
      int nMetaMagic = GetLocalInt(oPC, CONV_SPELLB_META);
      
      // get the object on the hide of oPC where the persistant data are stored
      //object oToken = GetHideToken(oPC);
      // determine the name of the persistant array that holds the spells to be memorized for the given nClass and nSpellLevel
      // (the index to the array is the nr of the slot of the given nClass and nSpellLevel)
      string sSpellsToBeMemorized = GetSpellsToBeMemorized_Array(nClass, nSpellSlotLevel);

      // store the chosen nSpellbookID (row nr in the newspellbook file cls_spells_*) in the spells to be memorized array
      persistant_array_set_int(oPC, sSpellsToBeMemorized, nSpellSlot, nChoice);

      // let oPC select a new spell (starting with the spell level)
      nStage = STAGE_SELECT_SPELL_LEVEL;
      MarkStageNotSetUp(nStage, oPC);
    }

    // Store the stage value. If it has been changed, this clears out the choices
    SetStage(nStage, oPC);
  }
}