Updated AMS marker feats. Removed arcane & divine marker feats. Updated Dread Necromancer for epic progression. Updated weapon baseitem models. Updated new weapons for crafting & npc equip. Updated prefix. Updated release archive.
		
			
				
	
	
		
			623 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			623 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
//:://////////////////////////////////////////////
 | 
						|
//:: 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(!array_exists(oToken, 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 = array_get_size(oToken, sArrayIDX);
 | 
						|
            for(nSlot = 0; nSlot < nSlots; nSlot++)
 | 
						|
            {
 | 
						|
                nSpellbookID = array_get_int(oToken, sArrayIDX, nSlot);
 | 
						|
                int nCount = nSpellbookID ? array_get_int(oToken, 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. +1 to skip 0s
 | 
						|
          SQLocals_SetInt(oPC, sSpellsToBeMemorized, nSlots+1); 
 | 
						|
 | 
						|
          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 = GetHideToken(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 = 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 = 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
 | 
						|
      array_set_int(oToken, 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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 |