Files
PRC8/nwn/nwnprc/trunk/newspellbook/tob_jpm_spellcon.nss
Jaysyn904 ce7d550670 2025/12/06 Update
Added Inscribe Epic Runes.
Added Great Intelligence and Inscribe Epic Runes as Runecaster epic bons feats.
Changed Runecaster epic bonus feat progression to 1 every 4 levels past 10th.
Bardic PrCs should be able to take Lingering Song & Extra Music as general feats.
Forsakers can't use psionics, invocations, spellfire, shadowcasting, truenaming, binding, soulmelds or Supernatural Martial Maneuvers.
Fixed elven courtblade / ambidexterity bug.
Added more guardrails to prevent self-damage from onHit spells during PerformAttack().
Updated GetProperTarget()
Removed ableist slur.
RHD casters should work with JPM now.
Reworked Blood in the Water's effect icon.
Fixed Seize Item's VFX.
RHD casters should be able to enter Dragon Disciple.
Sharp Note doesn't step on Dragonfire Inspiration anymore.
2025-12-06 22:55:23 -05:00

355 lines
16 KiB
Plaintext

//:://////////////////////////////////////////////
//:: Spell selection conversation for the Jade Phoenix Mage's abilities
//:: tob_jpm_spellconv.nss
//:://////////////////////////////////////////////
/** @file
Spell selection for Jade Phoenix Mage's abilities
Handles the dynamic convo *and* the quickselects
@author Stratovaris
@date Created - yyyy.mm.dd
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////
#include "prc_spell_const"
#include "inc_dynconv"
#include "inc_newspellbook"
////#include "prc_alterations"
#include "prc_inc_castlvl"
/* Constant defintions */
const int STAGE_ENTRY = 0;
const int STAGE_SLOT = 1;
const int STAGE_LVL0 = 10;
const int STAGE_LVL9 = 20;
void PopulateList(object oPC, int nLevel, int iClass, int nChoice)
{
if(!GetLocalInt(oPC, "DynConv_Waiting"))
return;
int nRace = GetRacialType(oPC);
//SendMessageToPC(oPC, "*Tick* *" + IntToString(iClass) + "*");
int nClass = GetClassByPosition(iClass);
if(GetIsArcaneClass(nClass))
{
int i = 0, MaxValue = 0, nSpellID;
if(nClass == CLASS_TYPE_WIZARD
|| (nClass == CLASS_TYPE_SORCERER && GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK)))
{
string sFile = "cls_spell_sorc";
object oToken = GetObjectByTag("SpellLvl_9_Level_" + IntToString(nLevel));
MaxValue = array_get_size(oToken, "Lkup");
//DoDebug("JPM PopulateList: nClass = "+IntToString(nClass));
//DoDebug("JPM PopulateList: nLevel = "+IntToString(nLevel));
//DoDebug("JPM PopulateList: MaxValue = "+IntToString(MaxValue));
while(i < MaxValue)
{
nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", array_get_int(oToken, "Lkup", i)));
if(PRCGetHasSpell(nSpellID, oPC))
{
string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));
AddChoice(sName, nChoice, oPC);
SetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice), nSpellID);
SetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice), -1);
nChoice++;
}
i++;
}
}
else if(nClass == CLASS_TYPE_SORCERER && nRace == RACIAL_TYPE_DRIDER
|| nRace == RACIAL_TYPE_ARKAMOI
|| nRace == RACIAL_TYPE_MARRUTACT
|| nRace == RACIAL_TYPE_REDSPAWN_ARCANISS
|| nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL
|| nRace == RACIAL_TYPE_RAKSHASA
|| nRace == RACIAL_TYPE_ARANEA
&& !GetLevelByClass(CLASS_TYPE_SORCERER, oPC))
{
string sFile = "cls_spell_sorc";
object oToken = GetObjectByTag("SpellLvl_9_Level_" + IntToString(nLevel));
MaxValue = array_get_size(oToken, "Lkup");
//DoDebug("JPM PopulateList: nClass = "+IntToString(nClass));
//DoDebug("JPM PopulateList: nLevel = "+IntToString(nLevel));
//DoDebug("JPM PopulateList: MaxValue = "+IntToString(MaxValue));
while(i < MaxValue)
{
nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", array_get_int(oToken, "Lkup", i)));
if(PRCGetHasSpell(nSpellID, oPC))
{
string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));
AddChoice(sName, nChoice, oPC);
SetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice), nSpellID);
SetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice), -1);
nChoice++;
}
i++;
}
}
else if(nClass == CLASS_TYPE_BARD && GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK))
{
string sFile = "cls_spell_bard";
object oToken = GetObjectByTag("SpellLvl_1_Level_" + IntToString(nLevel));
MaxValue = array_get_size(oToken, "Lkup");
//DoDebug("JPM PopulateList: MaxValue = "+IntToString(MaxValue));
while(i < MaxValue)
{
nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", array_get_int(oToken, "Lkup", i)));
if(PRCGetHasSpell(nSpellID, oPC))
{
string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));
AddChoice(sName, nChoice, oPC);
SetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice), nSpellID);
SetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice), -1);
nChoice++;
}
i++;
}
}
else if(nClass == CLASS_TYPE_BARD && nRace == RACIAL_TYPE_GLOURA && !GetLevelByClass(CLASS_TYPE_BARD, oPC))
{
string sFile = "cls_spell_bard";
object oToken = GetObjectByTag("SpellLvl_1_Level_" + IntToString(nLevel));
MaxValue = array_get_size(oToken, "Lkup");
//DoDebug("JPM PopulateList: MaxValue = "+IntToString(MaxValue));
while(i < MaxValue)
{
nSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", array_get_int(oToken, "Lkup", i)));
if(PRCGetHasSpell(nSpellID, oPC))
{
string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)));
AddChoice(sName, nChoice, oPC);
SetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice), nSpellID);
SetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice), -1);
nChoice++;
}
i++;
}
}
else
{
string sFile = GetFileForClass(nClass);
string sArray = "NewSpellbookMem_" + IntToString(nClass);
// if we ever add another arcane caster with prepared spellbook
// uncomment all following lines
//int nSpellbookType = GetSpellbookTypeForClass(nClass);
//if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
{
int nCount = persistant_array_get_int(oPC, sArray, nLevel);
//DoDebug("JPM PopulateList: nCount = "+IntToString(nCount));
if(nCount)
{
MaxValue = persistant_array_get_size(oPC, "Spellbook"+IntToString(nClass));
while(i < MaxValue)
{
int nNewSpellbookID = persistant_array_get_int(oPC, "Spellbook"+IntToString(nClass), i);
if(nLevel == StringToInt(Get2DACache(sFile, "Level", nNewSpellbookID))
&& GetHasFeat(StringToInt(Get2DACache(sFile, "FeatID", nNewSpellbookID)), oPC))
{
int nRealSpell = StringToInt(Get2DACache(sFile, "SpellID", nNewSpellbookID));
string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nRealSpell)));
AddChoice(sName, nChoice, oPC);
SetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice), nLevel);
SetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice), nRealSpell);
SetLocalString(oPC, "JPM_CLASS_ARRAY_" + IntToString(nChoice), sArray);
nChoice++;
}
i++;
}
}
}
/*else if(nSpellbookType == SPELLBOOK_TYPE_PREPARED)
{
string sArrayIDX = "SpellbookIDX" + IntToString(nLevel) + "_" + IntToString(nClass);
MaxValue = persistant_array_get_size(oPC, sArrayIDX);
while(i < MaxValue)
{
int nNewSpellbookID = persistant_array_get_int(oPC, sArrayIDX, i);
int nCount = persistant_array_get_int(oPC, sArray, nNewSpellbookID);
if(nCount
&& GetHasFeat(StringToInt(Get2DACache(sFile, "FeatID", nNewSpellbookID)), oPC))
{
int nRealSpell = StringToInt(Get2DACache(sFile, "RealSpellID", nNewSpellbookID));
string sName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nRealSpell)));
AddChoice(sName, nChoice, oPC);
SetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice), nNewSpellbookID);
SetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice), nRealSpell);
SetLocalString(oPC, "JPM_CLASS_ARRAY_" + IntToString(nChoice), sArray);
nChoice++;
}
i++;
}
}*/
}
}
if(iClass == 8)
{
SetDefaultTokens();
DeleteLocalInt(oPC, "DynConv_Waiting");
FloatingTextStringOnCreature("*Done*", oPC, FALSE);
return;
}
DelayCommand(0.01, PopulateList(oPC, nLevel, iClass + 1, nChoice));
}
void main()
{
object oPC = GetPCSpeaker();
int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE);
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;
// Check if this stage is marked as already set up
// This stops list duplication when scrolling
//SendMessageToPC(OBJECT_SELF, "prc_jpm_spellconv:" + IntToString(nID) + " nVal:"+ IntToString(nValue));
/* 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
*/
// The stage is used to determine the active conversation node.
// 0 is the entry node.
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))
{
// variable named nStage determines the current conversation node
// Function SetHeader to set the text displayed to the PC
// Function AddChoice to add a response option for the PC. The responses are show in order added
if(nStage == STAGE_ENTRY)
{
SetHeader("Select Spell Level:");
AddChoice(GetStringByStrRef(690), 1, oPC);//"Level 1"
AddChoice(GetStringByStrRef(725), 2, oPC);//"Level 2"
AddChoice(GetStringByStrRef(687), 3, oPC);//"Level 3"
AddChoice(GetStringByStrRef(684), 4, oPC);//"Level 4"
AddChoice(GetStringByStrRef(1026), 5, oPC);//"Level 5"
AddChoice(GetStringByStrRef(1014), 6, oPC);//"Level 6"
AddChoice(GetStringByStrRef(2214), 7, oPC);//"Level 7"
AddChoice(GetStringByStrRef(2215), 8, oPC);//"Level 8"
AddChoice(GetStringByStrRef(2216), 9, oPC);//"Level 9"
MarkStageSetUp(nStage, oPC); // This prevents the setup being run for this stage again until MarkStageNotSetUp is called for it
SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
}
else if (nStage >= STAGE_LVL0 && nStage <= STAGE_LVL9)
{
// Set the header
SetHeader("Select Spell:");
int nLevel = nStage - STAGE_LVL0;
SetLocalInt(oPC, "DynConv_Waiting", TRUE);
PopulateList(oPC, nLevel, 1, 1);
MarkStageSetUp(nStage, oPC);
}
else if (nStage = STAGE_SLOT)
{
SetHeader("Select QuickSlot:");
AddChoice("Slot 1", 1, oPC);
AddChoice("Slot 2", 2, oPC);
AddChoice("Slot 3", 3, oPC);
AddChoice("Slot 4", 4, oPC);
MarkStageSetUp(nStage, oPC); // This prevents the setup being run for this stage again until MarkStageNotSetUp is called for it
SetDefaultTokens(); // Set the next, previous, exit and wait tokens to default values
}
//add more stages for more nodes with Else If clauses
}
// Do token setup
SetupTokens();
}
// End of conversation cleanup
else if(nValue == DYNCONV_EXITED)
{
int nChoice = 1;
while(GetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice)))
{
DeleteLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice));
DeleteLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice));
DeleteLocalString(oPC, "JPM_CLASS_ARRAY_" + IntToString(nChoice));
nChoice++;
}
DeleteLocalInt(oPC, "JPM_SPELL_ID");
DeleteLocalInt(oPC, "JPM_REAL_SPELL_ID");
DeleteLocalString(oPC, "JPM_CLASS_ARRAY_ID");
DeleteLocalInt(oPC, "JPM_SPELL_LEVEL_CHOICE");
}
// Abort conversation cleanup.
// NOTE: This section is only run when the conversation is aborted
// while aborting is allowed. When it isn't, the dynconvo infrastructure
// handles restoring the conversation in a transparent manner
else if(nValue == DYNCONV_ABORTED)
{
int nChoice = 1;
while (GetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice)))
{
DeleteLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice));
DeleteLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice));
DeleteLocalString(oPC, "JPM_CLASS_ARRAY_" + IntToString(nChoice));
nChoice++;
}
DeleteLocalInt(oPC, "JPM_SPELL_ID");
DeleteLocalInt(oPC, "JPM_REAL_SPELL_ID");
DeleteLocalString(oPC, "JPM_CLASS_ARRAY_ID");
DeleteLocalInt(oPC, "JPM_SPELL_LEVEL_CHOICE");
}
// Handle PC responses
else
{
// variable named nChoice is the value of the player's choice as stored when building the choice list
// variable named nStage determines the current conversation node
int nChoice = GetChoice(oPC);
if(nStage == STAGE_ENTRY)
{
int nLevel = nChoice;
SetLocalInt(oPC, "JPM_SPELL_LEVEL_CHOICE", nLevel);
nStage = STAGE_LVL0 + nLevel;
// Move to another stage based on response, for example
//nStage = STAGE_QUUX;
}
else if (nStage >= STAGE_LVL0 && nStage <= STAGE_LVL9)
{
MarkStageNotSetUp(nStage, oPC);
int nSpell = GetLocalInt(oPC, "JPM_SPELL_CHOICE_" + IntToString(nChoice));
int nRealSpell = GetLocalInt(oPC, "JPM_REAL_SPELL_CHOICE_" + IntToString(nChoice));
string sArray = GetLocalString(oPC, "JPM_CLASS_ARRAY_" + IntToString(nChoice));
SetLocalInt(oPC, "JPM_SPELL_ID", nSpell);
SetLocalInt(oPC, "JPM_REAL_SPELL_ID", nRealSpell);
SetLocalString(oPC, "JPM_CLASS_ARRAY_ID", sArray);
nStage = STAGE_SLOT;
}
else if (nStage = STAGE_SLOT)
{
int nSpell = GetLocalInt(oPC, "JPM_SPELL_ID");
int nRealSpell = GetLocalInt(oPC, "JPM_REAL_SPELL_ID");
string sArray = GetLocalString(oPC, "JPM_CLASS_ARRAY_ID");
int nLevel = GetLocalInt(oPC, "JPM_SPELL_LEVEL_CHOICE");
if(DEBUG) DoDebug("tob_jpm_spellconv: nSpell value = " + IntToString(nSpell));
SetLocalInt(oPC, "JPM_SPELL_QUICK" + IntToString(nChoice), nSpell);
SetLocalInt(oPC, "JPM_REAL_SPELL_QUICK" + IntToString(nChoice), nRealSpell);
SetLocalString(oPC, "JPM_SPELL_QUICK" + IntToString(nChoice), sArray);
SetLocalInt(oPC, "JPM_SPELL_QUICK" + IntToString(nChoice) + "LVL", nLevel);
nStage = STAGE_ENTRY;
}
// Store the stage value. If it has been changed, this clears out the choices
SetStage(nStage, oPC);
}
}