Fixed Vow of Poverty. Fixed Vow of Poverty bonus feats not being restored onClientEnter. Added PRC option for spontaneous casters unlearning spells (@Rakiov) Gloura was double dipping caster level. Added NUI levelup spellpicker for most AMS classes (@Rakiov) Tweaked Tactical Insight. Updated ToB maneuver TLK entries to display correct prerequisites.
3337 lines
130 KiB
Plaintext
3337 lines
130 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: PRC Level Up NUI
|
|
//:: prc_nui_lv_inc
|
|
//:://////////////////////////////////////////////
|
|
/*
|
|
This is the logic for the Level Up NUI, holding all the functions needed for
|
|
the NUI to operate properly and allow leveling up in different classes.
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:: Created By: Rakiov
|
|
//:: Created On: 20.06.2005
|
|
//:://////////////////////////////////////////////
|
|
|
|
#include "prc_nui_com_inc"
|
|
#include "tob_inc_tobfunc"
|
|
#include "tob_inc_moveknwn"
|
|
#include "inv_inc_invfunc"
|
|
#include "shd_inc_mystknwn"
|
|
#include "shd_inc_shdfunc"
|
|
#include "true_inc_truknwn"
|
|
#include "true_inc_trufunc"
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Spont Casters / Base ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// GetSpellListObject
|
|
// Gets the JSON Object representation of a class's spellbook 2da. This function
|
|
// will cache it's result to the object given to it to avoid further calculations
|
|
// and will not clear itself since it does not change.
|
|
//
|
|
// Arguments:
|
|
// nClass:int the ClassID
|
|
// oPC:Object the player
|
|
//
|
|
// Returns:
|
|
// Json:Dictionary<String, List[]> a dictionary of each circle's spellbook Ids.
|
|
//
|
|
json GetSpellListObject(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetKnownSpellListObject
|
|
// Gets the JSON Object representation of a player's known spell list. This function
|
|
// will temporarily cache it's result to the object given to avoid further calculations.
|
|
// However this should be cleared after done using the level up screen or reset.
|
|
//
|
|
// Arguments:
|
|
// nClass:int the ClassID
|
|
// oPC:Object the player
|
|
//
|
|
// Returns:
|
|
// Json:Dictionary<String, List[]> a dictionary of each circle's known spellbook Ids.
|
|
//
|
|
json GetKnownSpellListObject(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetKnownSpellListObject
|
|
// Gets the JSON Object representation of a player's chosen spell list. This function
|
|
// will temporarily cache it's result to the object given to avoid further calculations.
|
|
// However this should be cleared after done using the level up screen or reset.
|
|
//
|
|
// Arguments:
|
|
// nClass:int the ClassID
|
|
// oPC:Object the player
|
|
//
|
|
// Returns:
|
|
// Json:Dictionary<String, List[]> a dictionary of each circle's chosen spellbook Ids.
|
|
//
|
|
json GetChosenSpellListObject(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// ShouldAddSpellToSpellButtons
|
|
// Given a classId and a spellbookId, if the player knows the spell already we
|
|
// should not add the spell, otherwise we should
|
|
//
|
|
// Arguments:
|
|
// nClass:int Class ID
|
|
// spellbookId:int the spell book ID
|
|
// oPC:object the player
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if spell should be added, FALSE otherwise
|
|
//
|
|
int ShouldAddSpellToSpellButtons(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// OpenNUILevelUpWindow
|
|
// Opens the Level Up NUI window for the provided class
|
|
//
|
|
// Arguments:
|
|
// nClass:int the ClassID
|
|
//
|
|
void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// CloseNUILevelUpWindow
|
|
// Closes the NUI Level Up Window if its open
|
|
//
|
|
void CloseNUILevelUpWindow(object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetTrueClassType
|
|
// Gets the true class Id for a provided class Id, mostly for RHD and for
|
|
// ToB prestige classes
|
|
//
|
|
// Arguments:
|
|
// nClass:int classId
|
|
//
|
|
// Returns:
|
|
// int the true classId based off nClass
|
|
//
|
|
int GetTrueClassType(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetRemainingSpellChoices
|
|
// Gets the remaining spell choices for a class at the given circle by checking its
|
|
// chosen spells and comparing it against the total spells allowed. This value
|
|
// is cached on the player and cleared everytime the window is refreshed/closed
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// circleLevel:int the circle being checked
|
|
//
|
|
// Returns:
|
|
// int the amount of choices left at the circle
|
|
//
|
|
int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// ShouldSpellButtonBeEnabled
|
|
// Checks whether a spell button should be enabled either because all choices have
|
|
// been made, replacing spells isn't allowed, or for various other reasons
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// circleLevel:int the chosen circle
|
|
// spellbookId:int the chosen spell
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if spell button should be enabled, FALSE otherwise
|
|
//
|
|
int ShouldSpellButtonBeEnabled(int nClass, int circleLevel, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// AddSpellToChosenList
|
|
// Adds spell to the chosen spells list
|
|
//
|
|
// Arguments:
|
|
// nClass:int the classId
|
|
// spellbookId:int the spellbook Id
|
|
// spellCircle:int the current circle of the spell
|
|
//
|
|
void AddSpellToChosenList(int nClass, int spellbookId, int spellCircle, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// RemoveSpellFromChosenList
|
|
// Removes a spell from the chosen spell list
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId:int the spellbook Id
|
|
// spellCircle:int the circle of the spell
|
|
//
|
|
void RemoveSpellFromChosenList(int nClass, int spellbookId, int spellCircle, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// LearnSpells
|
|
// gives the player the spells they want to learn based off of the chosen spell
|
|
// list in a stored variable
|
|
//
|
|
// Arguments:
|
|
// nClass:int the classId
|
|
//
|
|
void LearnSpells(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// RemoveSpells
|
|
// removes spells from the player that they may know currently but aren't selected
|
|
// based off lists in stored variables
|
|
//
|
|
// Arguments:
|
|
// nClass:int the classId
|
|
//
|
|
void RemoveSpells(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// FinishLevelUp
|
|
// Finishes level up NUI by removing spells, learning spells, clearing cache, then closing the NUI
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
//
|
|
void FinishLevelUp(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// ClearLevelUpNUICaches
|
|
// Clears the cache (stored local variables) for the level up NUI so it is
|
|
// ready to be used for a new level up
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// oPC:object the player object this is stored under
|
|
//
|
|
void ClearLevelUpNUICaches(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// SpellIsWithinObject
|
|
// checks whether a spell is within a JSON Object structure used by the remaining
|
|
// spells object and known spells object, following this structure
|
|
// {
|
|
// "circleLevel:int": [ 1,2,3...,spellId],
|
|
// ...
|
|
// }
|
|
//
|
|
// Arguments
|
|
// nClass:int classId
|
|
// spellbookId:int the spellbook Id
|
|
// circleLevel:int the chosen circle of the spell
|
|
// spellList;JsonObject the spell list object being checked
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if it is in the object, FALSE otherwise
|
|
//
|
|
int SpellIsWithinObject(int nClass, int spellbookId, int circleLevel, json spellList, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// IsLevelUpNUIOpen
|
|
// Checks if the Level Up NUI is open for the player or not
|
|
//
|
|
// Arguments:
|
|
// oPC:object the player object
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if it is, FALSE otherwise
|
|
//
|
|
int IsLevelUpNUIOpen(object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// IsClassAllowedToUseLevelUpNUI
|
|
// Is the provided class allowed to use the level up NUI
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if it can, FALSE otherwise
|
|
//
|
|
int IsClassAllowedToUseLevelUpNUI(int nClass);
|
|
|
|
//
|
|
// EnabledChosenButton
|
|
// determines if a chosen spell button should be enabled or not. It may not due to
|
|
// class restrictions, replacing is not enabled, or other reason
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId: the spellbook Id
|
|
// circleLevel: the spell's circle
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if it should be enabled, FALSE otherwise
|
|
//
|
|
int EnableChosenButton(int nClass, int spellbookId, int circleLevel, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// ResetChoices
|
|
// Action for the Level Up NUI's 'Reset' button, resets choices by clearing the cache of
|
|
// the user so their choices are forgotten and they can start over.
|
|
//
|
|
// Arguments:
|
|
// oPC:object the player object
|
|
//
|
|
void ResetChoices(object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// RemoveSpellKnown
|
|
// Removes a spell from a player based off class id. This is for classes that
|
|
// aren't spont casters where we have to go in and adjust persistant arrays
|
|
// to say if a spell is known or not.
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// spellbookId:int the spellbook Id
|
|
// oPC:object the player object
|
|
// nList:int the list we are removing the spell from (extra invocations or expanded knowledge)
|
|
//
|
|
void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int nList=0);
|
|
|
|
//
|
|
// GetSpellIDsKnown
|
|
// Gets the SpellIDs list of the given class and list and returns it as a JsonObject following this structure
|
|
// {
|
|
// "spellId:int": TRUE,
|
|
// ...
|
|
// }
|
|
//
|
|
// This is to keep lookups at O(1) processing time. This value is cached and is
|
|
// cleared when the player finishes level up
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// oPC:object the player object
|
|
// nList:int the list we are checking if provided (extra invocations or expanded knowledge)
|
|
//
|
|
// Returns:
|
|
// JsonObject the list of spell ids the class knows in JsonObject format
|
|
//
|
|
json GetSpellIDsKnown(int nClass, object oPC=OBJECT_SELF, int nList=0);
|
|
|
|
//
|
|
// ReasonForDisabledSpell
|
|
// Provides the reason for why a spell choice is disabled
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId:int the spellbook Id
|
|
//
|
|
// Returns:
|
|
// string the reason for the disabled button, empty string otherwise
|
|
//
|
|
string ReasonForDisabledSpell(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// ReasonForDisabledChosen
|
|
// Provides the reason for why a chosen spell button is disabled
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId:int the spellbook Id
|
|
//
|
|
// Returns:
|
|
// string the reason for the disabled button, empty string otherwise
|
|
//
|
|
string ReasonForDisabledChosen(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetExpandedChoicesList
|
|
// Gets the expanded choices list for a class (the list of expanded knowledge or
|
|
// extra invocations). It follows this structure
|
|
//
|
|
// {
|
|
// "spellId:int": TRUE,
|
|
// ...
|
|
// }
|
|
// This is cached to reduce process times and is cleared everytime the window is refreshed/closed
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
//
|
|
// Returns:
|
|
// JsonObject the object representation of the expanded choices
|
|
//
|
|
json GetExpandedChoicesList(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetExpandedChoicesList
|
|
// Gets the epic expanded choices list for a class (the list of expanded knowledge or
|
|
// extra invocations). It follows this structure
|
|
//
|
|
// {
|
|
// "spellId:int": TRUE,
|
|
// ...
|
|
// }
|
|
// This is cached to reduce process times and is cleared everytime the window is refreshed/closed
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
//
|
|
// Returns:
|
|
// JsonObject the object representation of the expanded choices
|
|
//
|
|
json GetEpicExpandedChoicesList(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetRemainingExpandedChoices
|
|
// Gets the remaining expanded choices for a class based off list, comparing the
|
|
// total number of choices allowed and the total number chosen
|
|
//
|
|
// Arguments:
|
|
// nClass: class id
|
|
// nList: the list we are checking (extra invocations/expanded knowledge)
|
|
//
|
|
// Returns:
|
|
// int the amount of choices left
|
|
//
|
|
int GetRemainingExpandedChoices(int nClass, int nList, object oPC=OBJECT_SELF);
|
|
//
|
|
// IsSpellInExpandedChoices
|
|
// tells if a spell is in the expanded choices list or not
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// nList:int the list we are checking (extra invocations/expanded knowledge)
|
|
// spellId:int the spell id (not the spellbook id)
|
|
//
|
|
// Returns
|
|
// int:Boolean TRUE if it is a expanded choice, FALSE otherwise
|
|
//
|
|
int IsSpellInExpandedChoices(int nClass, int nList, int spellId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetChosenReplaceListObject
|
|
// The chosen list of spells we wish to replace for PnP replacing if Bioware replacing
|
|
// is disabled. This is cached and is cleared when the player is finished leveling
|
|
// or resets their choices
|
|
//
|
|
// Arguments:
|
|
// oPC:object the player
|
|
//
|
|
// Returns:
|
|
// json the list of spells chosen to replace
|
|
//
|
|
json GetChosenReplaceListObject(object oPC=OBJECT_SELF);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Psionics ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// IsExpKnowledgePower
|
|
// checks if a spell is a expanded knowledge spell
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// spellbookId:int the spellbook Id
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if the spell is a expanded knowledge spell, FALSE otherwise
|
|
//
|
|
int IsExpKnowledgePower(int nClass, int spellbookId);
|
|
|
|
//
|
|
// GetExpKnowledgePowerListRequired
|
|
// Tells what list the spell should be added to based on if it was added to the
|
|
// expanded choices or epic expanded choices list
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId:int the spellbook Id
|
|
//
|
|
// Returns:
|
|
// int -1 for the expanded knowledge list, -2 for the epic expanded knowledge
|
|
// list, 0 if just add it to the normal class list
|
|
//
|
|
int GetExpKnowledgePowerListRequired(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetCurrentPowerList
|
|
// Gets the current chosen powers list. This is cached and is cleared when the
|
|
// player either finishs leveling up or resets.
|
|
//
|
|
// Arguments:
|
|
// oPC:object the player object
|
|
//
|
|
// Returns:
|
|
// JsonArray the list of chosen powers wanting to learn
|
|
//
|
|
json GetCurrentPowerList(object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// ShouldAddPower
|
|
// Tells if the power should be added to the list of choices or not. It may not
|
|
// be added because its an expanded knowledge choice and you have no more expanded
|
|
// knowledge slots, or it may be a restricted spell like psions list
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId:int the spellbook id
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if it should be added, FALSE otherwise
|
|
//
|
|
int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// LearnPowers
|
|
// learns the list of chosen powers for the player based off their chosen power list
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// oPC:object the player object where stored variables are
|
|
//
|
|
void LearnPowers(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetMaxPowerLevelForClass
|
|
// gets the max power level for the player based off their level and the class's
|
|
// known 2da
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// oPC:object the player
|
|
//
|
|
// Returns:
|
|
// int the max power level (circle) the player can achieve on that class
|
|
//
|
|
int GetMaxPowerLevelForClass(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetRemainingPowerChoices
|
|
// Gets the remaining power choices the character has at the given chosen circle/power level
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// chosenCircle:int the chosen circle/power level
|
|
// oPC:object the player
|
|
// extra:int should we add the expanded knowledge choices or not
|
|
//
|
|
// Returns:
|
|
// int the number of choices left at the given circle
|
|
//
|
|
int GetRemainingPowerChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Initiators ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// GetDisciplineInfoObject
|
|
// Gets the disciplien info for the given class, telling what the chosen spells
|
|
// disicpline is, what type of maneuever it is, the different totals, and prerequisites.
|
|
// This is cached and is cleared when the window is refreshed/closed
|
|
//
|
|
// Argument:
|
|
// nClass:int class id
|
|
//
|
|
// Returns:
|
|
// JsonObject the object representation of the chosen spells discipline info
|
|
//
|
|
json GetDisciplineInfoObject(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// HasPreRequisitesForManeuver
|
|
// Does the player have the prerequisites for the given spell based off their chosen
|
|
// spell list
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// spellbookId:int the spellbook id
|
|
// oPC:object the player object with stored variables
|
|
//
|
|
// Returns:
|
|
// int:Boolean, TRUE if you have the prerequisites, FALSE otherwise
|
|
//
|
|
int HasPreRequisitesForManeuver(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetMaxInitiatorCircle
|
|
// gets the max circle/level a player can obtain with the given class
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// oPC:object the player
|
|
//
|
|
// Returns:
|
|
// int the highest circle the player can achieve with the class
|
|
//
|
|
int GetMaxInitiatorCircle(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetRemainingManeuverChoices
|
|
// Gets remaining maneuever choices for the player
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// oPC:object the player
|
|
//
|
|
// Returns:
|
|
// int the remaining maneuevers choices
|
|
//
|
|
int GetRemainingManeuverChoices(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetRemainingStanceChoices
|
|
// Gets remaining stance choices for the player
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// oPC:object the player
|
|
//
|
|
// Returns:
|
|
// int the remaining stance choices
|
|
//
|
|
int GetRemainingStanceChoices(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// IsRequiredForOtherManeuvers
|
|
// Checks the given prerequisite number and the chosen spells to see if removing it
|
|
// will cause it to fail the requirement for other maneuevers
|
|
//
|
|
// Arguments:
|
|
// nClass:int the class id
|
|
// prereq:int the chosen spells prerequisite number of maneuevers needed
|
|
// discipline:string the chosen spells discipline
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if it is required, FALSE otherwise
|
|
//
|
|
int IsRequiredForOtherManeuvers(int nClass, int prereq, string discipline, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// IsAllowedDiscipline
|
|
// checks to see if the given spell is a allowed discipline for a class
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// spellbookId:int the spellbook id
|
|
//
|
|
// Returns:
|
|
// int:boolean TRUE if it is allowed, FALSE otherwise
|
|
//
|
|
int IsAllowedDiscipline(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// AddSpellDisciplineInfo
|
|
// Adds the maneuver's discipline info to the class's discpline object
|
|
//
|
|
// Arguments:
|
|
// sFile:string the class's spell 2da
|
|
// spellbookId:int the spellbook Id
|
|
// classDisc:JsonObject the class discipline object we are adding to
|
|
//
|
|
// Returns:
|
|
// json:Object the classDisc with the given spells information added
|
|
//
|
|
json AddSpellDisciplineInfo(string sFile, int spellbookId, json classDisc);
|
|
|
|
//
|
|
// IsRequiredForToBPRCClass
|
|
// tells if a given maneuver is needed to satisfy a PRC's prerequsitie
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// spellbookId:int the spellbook id
|
|
//
|
|
// Returns:
|
|
// int:Boolean TRUE if the maneuver is required for a PRC, FALSE otherwise.
|
|
//
|
|
int IsRequiredForToBPRCClass(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Invokers ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// GetInvokerKnownListObject
|
|
// gets the invokers known invocations list in object format, needed to tell how many
|
|
// of each invocation level does a person know at a given level. This is cached on the
|
|
// player and not cleared since it never changes.
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
//
|
|
// Returns:
|
|
// json:Object the list of invocations known in json format
|
|
//
|
|
json GetInvokerKnownListObject(int nClass, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetRemainingInvocationChoices
|
|
// Gets the remaining invocation choices left
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// chosenCircle:int the chosen circle we are checking
|
|
// oPC:Object the player
|
|
// extra:int should we count the number of extra invocations we have left
|
|
//
|
|
// Returns:
|
|
// int the amount of choices left at the given circle
|
|
//
|
|
int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE);
|
|
|
|
//
|
|
// IsExtraChoiceInvocation
|
|
// tells if a given spell is a extra invocation choice
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// spellbookId:int the spellbook id
|
|
//
|
|
// Returns:
|
|
// int;Boolean TRUE if it is a extra choice, FALSE otherwise
|
|
//
|
|
int IsExtraChoiceInvocation(int nClass, int spellbookId, object oPC=OBJECT_SELF);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Truenamer ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// GetRemainingTruenameChoices
|
|
// gets the remaining truename choices left at the given lexicon type
|
|
//
|
|
// Arguments:
|
|
// nClass:int class id
|
|
// nType:int the lexicon
|
|
//
|
|
// Returns:
|
|
// int the amount of truename choices left for the given lexicon
|
|
//
|
|
int GetRemainingTruenameChoices(int nClass, int nType, object oPC=OBJECT_SELF);
|
|
|
|
//
|
|
// GetLexiconCircleKnownAtLevel
|
|
// gets the known circle level for a given lexicon
|
|
//
|
|
// Arguments:
|
|
// nLevel:int the level to check
|
|
// nType:int the lexicon we are checking
|
|
//
|
|
// Returns:
|
|
// int the highest circle we can achieve
|
|
//
|
|
int GetLexiconCircleKnownAtLevel(int nLevel, int nType);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Archivist ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
json GetArchivistNewSpellsList(object oPC=OBJECT_SELF);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Spont Casters / Base ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int IsLevelUpNUIOpen(object oPC=OBJECT_SELF)
|
|
{
|
|
int nPreviousToken = NuiFindWindow(oPC, NUI_LEVEL_UP_WINDOW_ID);
|
|
if (nPreviousToken != 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int IsClassAllowedToUseLevelUpNUI(int nClass)
|
|
{
|
|
|
|
if (GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
return TRUE;
|
|
|
|
if (nClass == CLASS_TYPE_PSYWAR
|
|
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|
|
|| nClass == CLASS_TYPE_PSION
|
|
|| nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|
|
|| nClass == CLASS_TYPE_WILDER
|
|
|| nClass == CLASS_TYPE_WARMIND)
|
|
return TRUE;
|
|
|
|
if (nClass == CLASS_TYPE_WARBLADE
|
|
|| nClass == CLASS_TYPE_SWORDSAGE
|
|
|| nClass == CLASS_TYPE_CRUSADER)
|
|
return TRUE;
|
|
|
|
if (nClass == CLASS_TYPE_WARLOCK
|
|
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT
|
|
|| nClass == CLASS_TYPE_DRAGON_SHAMAN)
|
|
return TRUE;
|
|
|
|
if (nClass == CLASS_TYPE_SHADOWCASTER
|
|
|| nClass == CLASS_TYPE_SHADOWSMITH)
|
|
return TRUE;
|
|
|
|
if (nClass == CLASS_TYPE_TRUENAMER)
|
|
return TRUE;
|
|
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int SpellIsWithinObject(int nClass, int spellbookId, int circleLevel, json spellList, object oPC=OBJECT_SELF)
|
|
{
|
|
// check to see if the spell circle isn't empty
|
|
json currentList = JsonObjectGet(spellList, IntToString(circleLevel));
|
|
if (currentList == JsonNull())
|
|
return FALSE;
|
|
|
|
int totalSpells = JsonGetLength(currentList);
|
|
|
|
// then loop through the spell list and find the spell.
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
int currentSpell = JsonGetInt(JsonArrayGet(currentList, i));
|
|
if (currentSpell == spellbookId)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int AllSpellsAreChosen(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
// we need the max number of circles a class has.
|
|
json spellList = GetSpellListObject(nClass, oPC);
|
|
json spellCircles = JsonObjectKeys(spellList);
|
|
int totalCircles = JsonGetLength(spellCircles);
|
|
|
|
int i;
|
|
for (i = 0; i < totalCircles; i++)
|
|
{
|
|
// loop through each circle and check if you have any remaining choices left
|
|
// if you do or you have a deficit then you need to remove or add something
|
|
// until you get 0
|
|
int spellCircle = StringToInt(JsonGetString(JsonArrayGet(spellCircles, i)));
|
|
int remainingChoices = GetRemainingSpellChoices(nClass, spellCircle, oPC);
|
|
if (remainingChoices < 0 || remainingChoices > 0)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void AddSpellToChosenList(int nClass, int spellbookId, int spellCircle, object oPC=OBJECT_SELF)
|
|
{
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
// get the remaining invocation choices left without extra feats
|
|
// if it is 0 then we are adding the chosen invocation to the extra lists
|
|
int totalInvocations = GetRemainingInvocationChoices(nClass, spellCircle, oPC, FALSE);
|
|
if (totalInvocations == 0)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
if (GetRemainingExpandedChoices(nClass, INVOCATION_LIST_EXTRA, oPC))
|
|
{
|
|
json expList = GetExpandedChoicesList(nClass, oPC);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
expList = JsonObjectSet(expList, spellId, JsonBool(TRUE));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR, expList);
|
|
}
|
|
else if (GetRemainingExpandedChoices(nClass, INVOCATION_LIST_EXTRA_EPIC, oPC))
|
|
{
|
|
json expList = GetEpicExpandedChoicesList(nClass, oPC);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
expList = JsonObjectSet(expList, spellId, JsonBool(TRUE));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR, expList);
|
|
}
|
|
}
|
|
}
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
// if the power is a expanded knowledge than we immediatly add it to the
|
|
// extra list, otherwise check to make sure we have made all choices in our
|
|
// base list first before adding it to the extra list.
|
|
if (IsExpKnowledgePower(nClass, spellbookId)
|
|
|| GetRemainingPowerChoices(nClass, spellCircle, oPC, FALSE) == 0)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
if (GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC))
|
|
{
|
|
json expList = GetExpandedChoicesList(nClass, oPC);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
expList = JsonObjectSet(expList, spellId, JsonBool(TRUE));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR, expList);
|
|
}
|
|
else if (GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC))
|
|
{
|
|
json expList = GetEpicExpandedChoicesList(nClass, oPC);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
expList = JsonObjectSet(expList, spellId, JsonBool(TRUE));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR, expList);
|
|
}
|
|
}
|
|
|
|
// add the power to the current power list.
|
|
json currPowerList = GetCurrentPowerList(oPC);
|
|
currPowerList = JsonArrayInsert(currPowerList, JsonInt(spellbookId));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_POWER_LIST_VAR, currPowerList);
|
|
}
|
|
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
json newSpells = GetArchivistNewSpellsList(oPC);
|
|
newSpells = JsonArrayInsert(newSpells, JsonInt(spellbookId));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR, newSpells);
|
|
}
|
|
|
|
// base logic for spont casters, add the spell to the ChosenSpells JSON object
|
|
// by adding it to it's appropriate circle.
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
json spellsAtCircle = JsonObjectGet(chosenSpells, IntToString(spellCircle));
|
|
if (spellsAtCircle == JsonNull())
|
|
spellsAtCircle = JsonArray();
|
|
spellsAtCircle = JsonArrayInsert(spellsAtCircle, JsonInt(spellbookId));
|
|
chosenSpells = JsonObjectSet(chosenSpells, IntToString(spellCircle), spellsAtCircle);
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_CHOSEN_SPELLS_VAR, chosenSpells);
|
|
|
|
// if we are not using bioware unlearning logic, then we need to limit the
|
|
// amount of spells we can replace.
|
|
if (!GetPRCSwitch(PRC_BIO_UNLEARN))
|
|
{
|
|
json unlearnList = GetChosenReplaceListObject(oPC);
|
|
// if the spell belongs to the unlearn list, then remove it to make room
|
|
// for a new spell.
|
|
if (JsonObjectGet(unlearnList, IntToString(spellbookId)) != JsonNull())
|
|
{
|
|
unlearnList = JsonObjectDel(unlearnList, IntToString(spellbookId));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_RELEARN_LIST_VAR, unlearnList);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemoveSpellFromChosenList(int nClass, int spellbookId, int spellCircle, object oPC=OBJECT_SELF)
|
|
{
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
json spellsAtCircle = JsonObjectGet(chosenSpells, IntToString(spellCircle));
|
|
if (spellsAtCircle == JsonNull())
|
|
spellsAtCircle = JsonArray();
|
|
|
|
int totalSpells = JsonGetLength(spellsAtCircle);
|
|
|
|
// find the spell at the circle in the chosen list and remove it.
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
if (spellbookId == JsonGetInt(JsonArrayGet(spellsAtCircle, i)))
|
|
{
|
|
spellsAtCircle = JsonArrayDel(spellsAtCircle, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
chosenSpells = JsonObjectSet(chosenSpells, IntToString(spellCircle), spellsAtCircle);
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_CHOSEN_SPELLS_VAR, chosenSpells);
|
|
|
|
// if we re not using bioware unlearn logic we need to limit how many spells
|
|
// can be replaced
|
|
if (!GetPRCSwitch(PRC_BIO_UNLEARN))
|
|
{
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json spellListAtCircle = JsonObjectGet(knownSpells, IntToString(spellCircle));
|
|
int totalSpells = JsonGetLength(spellListAtCircle);
|
|
|
|
// with the list of known spells, check the selected circle and see if the
|
|
// current spell belongs in the already known spell list.
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
int chosenSpell = JsonGetInt(JsonArrayGet(spellListAtCircle, i));
|
|
if (chosenSpell == spellbookId)
|
|
{
|
|
// if it does we need to add the spell to the unlearn JSON object to track what spells
|
|
// are being replaced.
|
|
json unlearnList = GetChosenReplaceListObject(oPC);
|
|
unlearnList = JsonObjectSet(unlearnList, IntToString(spellbookId), JsonBool(TRUE));
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_RELEARN_LIST_VAR, unlearnList);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
|
|
// for psionics we need to check if the removed spell was a expanded knowledge choice
|
|
// or not. The id of the list is -1 or -2.
|
|
int i;
|
|
for (i == -1; i >= -2; i--)
|
|
{
|
|
json expList = (i == -1) ? GetExpandedChoicesList(nClass, oPC) :
|
|
GetEpicExpandedChoicesList(nClass, oPC);
|
|
|
|
//if the spell belongs in the expanded knowledge list, then we need
|
|
// to remove it.
|
|
if (JsonObjectGet(expList, spellId) != JsonNull())
|
|
{
|
|
expList = JsonObjectDel(expList, spellId);
|
|
if (i == POWER_LIST_EXP_KNOWLEDGE)
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR, expList);
|
|
else
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR, expList);
|
|
}
|
|
}
|
|
|
|
// then we need to remove the power from the selected powers list.
|
|
json currPowerChoices = GetCurrentPowerList(oPC);
|
|
int totalPowers = JsonGetLength(currPowerChoices);
|
|
|
|
for (i = 0; i < totalPowers; i++)
|
|
{
|
|
if (spellbookId == JsonGetInt(JsonArrayGet(currPowerChoices, i)))
|
|
{
|
|
currPowerChoices = JsonArrayDel(currPowerChoices, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_POWER_LIST_VAR, currPowerChoices);
|
|
}
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
|
|
// for invocations we need to check if the spell was added to the extra
|
|
// invocations list, the list ids are the invalid class id, and -2
|
|
int i;
|
|
for (i = 0; i <= 1; i++)
|
|
{
|
|
json expList = (i == 0) ? GetExpandedChoicesList(nClass, oPC) :
|
|
GetEpicExpandedChoicesList(nClass, oPC);
|
|
|
|
// if the spell was found, remove it.
|
|
if (JsonObjectGet(expList, spellId) != JsonNull())
|
|
{
|
|
expList = JsonObjectDel(expList, spellId);
|
|
if (i == 0)
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR, expList);
|
|
else
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR, expList);
|
|
}
|
|
}
|
|
}
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
json newSpells = GetArchivistNewSpellsList(oPC);
|
|
int totalNew = JsonGetLength(newSpells);
|
|
|
|
int i;
|
|
for (i = 0; i < totalNew; i++)
|
|
{
|
|
int newSpellbookId = JsonGetInt(JsonArrayGet(newSpells, i));
|
|
if (newSpellbookId == spellbookId)
|
|
{
|
|
newSpells = JsonArrayDel(newSpells, i);
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR, newSpells);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
CloseNUILevelUpWindow(oPC);
|
|
// set the NUI to the given classId
|
|
int currentClass = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR);
|
|
// we need to clear the cache if it was used before to avoid weird behaviors
|
|
ClearLevelUpNUICaches(currentClass, oPC);
|
|
// sometimes we are given a different classId instead of the base, we need to
|
|
// figure out what the true base class is (mostly true for RHD)
|
|
int chosenClass = GetTrueClassType(nClass, oPC);
|
|
SetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR, chosenClass);
|
|
|
|
ExecuteScript("prc_nui_lv_view", oPC);
|
|
}
|
|
|
|
int GetTrueClassType(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
if (nClass == CLASS_TYPE_JADE_PHOENIX_MAGE
|
|
|| nClass == CLASS_TYPE_MASTER_OF_NINE
|
|
|| nClass == CLASS_TYPE_DEEPSTONE_SENTINEL
|
|
|| nClass == CLASS_TYPE_BLOODCLAW_MASTER
|
|
|| nClass == CLASS_TYPE_RUBY_VINDICATOR
|
|
|| nClass == CLASS_TYPE_ETERNAL_BLADE
|
|
|| nClass == CLASS_TYPE_SHADOW_SUN_NINJA)
|
|
{
|
|
int trueClass = GetPrimaryBladeMagicClass(oPC);
|
|
return trueClass;
|
|
}
|
|
|
|
if ((nClass == CLASS_TYPE_SHAPECHANGER
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_ARANEA)
|
|
|| (nClass == CLASS_TYPE_OUTSIDER
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA)
|
|
|| (nClass == CLASS_TYPE_ABERRATION
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_DRIDER)
|
|
|| (nClass == CLASS_TYPE_MONSTROUS
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI)
|
|
|| (nClass == CLASS_TYPE_MONSTROUS
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL)
|
|
|| (nClass == CLASS_TYPE_MONSTROUS
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS)
|
|
|| (nClass == CLASS_TYPE_MONSTROUS
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT))
|
|
return CLASS_TYPE_SORCERER;
|
|
if (nClass == CLASS_TYPE_FEY
|
|
&& GetRacialType(oPC) == RACIAL_TYPE_GLOURA)
|
|
return CLASS_TYPE_BARD;
|
|
|
|
return nClass;
|
|
}
|
|
|
|
void CloseNUILevelUpWindow(object oPC=OBJECT_SELF)
|
|
{
|
|
int currentClass = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR);
|
|
// if we are refreshing the NUI but not finished we need to clear some caching done
|
|
// to save computation time as they will need to be reprocessed.
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(currentClass));
|
|
SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, -20);
|
|
int nPreviousToken = NuiFindWindow(oPC, NUI_LEVEL_UP_WINDOW_ID);
|
|
if (nPreviousToken != 0)
|
|
{
|
|
NuiDestroy(oPC, nPreviousToken);
|
|
}
|
|
}
|
|
|
|
int ShouldSpellButtonBeEnabled(int nClass, int circleLevel, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
// logic for psionics
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
int maxLevel = GetMaxPowerLevelForClass(nClass, oPC);
|
|
if (circleLevel > maxLevel)
|
|
return FALSE;
|
|
|
|
// if its an expanded knowledge choice and we have already made all our
|
|
// exp knowledge choices then it needs to be disabled.
|
|
if (IsExpKnowledgePower(nClass, spellbookId))
|
|
{
|
|
int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC)
|
|
+ GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC);
|
|
if (!remainingExp)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (GetIsShadowMagicClass(nClass))
|
|
{
|
|
// mysteries are weird, the circles are sectioned by 1-3, 4-6, 7-9
|
|
// if you do not have at least 2 choices from a circle you can't progress up
|
|
// so you can have access to circles 1,2,4,7,8
|
|
int nType = 1;
|
|
if (circleLevel >= 4 && circleLevel <= 6)
|
|
nType = 2;
|
|
if (circleLevel >= 7 && circleLevel <= 9)
|
|
nType = 3;
|
|
int maxPossibleCircle = GetMaxMysteryLevelLearnable(oPC, nClass, nType);
|
|
if (circleLevel > maxPossibleCircle)
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetIsTruenamingClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int lexicon = StringToInt(Get2DACache(sFile, "Lexicon", spellbookId));
|
|
// each lexicon learns at different rates
|
|
int maxCircle = GetLexiconCircleKnownAtLevel(GetLevelByClass(nClass, oPC), lexicon);
|
|
if (circleLevel > maxCircle)
|
|
return FALSE;
|
|
|
|
if (GetRemainingTruenameChoices(nClass, lexicon, oPC))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
// logic for ToB
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
if (circleLevel > GetMaxInitiatorCircle(nClass, oPC))
|
|
return FALSE;
|
|
|
|
// if you do not have the prerequisite amount of maneuevers to learn
|
|
// the maneuever, then you can't learn it.
|
|
if (!HasPreRequisitesForManeuver(nClass, spellbookId, oPC))
|
|
return FALSE;
|
|
|
|
// maneuvers and stances have their own seperate limits
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int type = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
if (type == MANEUVER_TYPE_BOOST
|
|
|| type == MANEUVER_TYPE_COUNTER
|
|
|| type == MANEUVER_TYPE_STRIKE
|
|
|| type == MANEUVER_TYPE_MANEUVER)
|
|
{
|
|
int remainingMan = GetRemainingManeuverChoices(nClass, oPC);
|
|
if (remainingMan)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
if (type == MANEUVER_TYPE_STANCE)
|
|
{
|
|
int remainingStance = GetRemainingStanceChoices(nClass, oPC);
|
|
if (remainingStance)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
int maxLevel = GetMaxSpellLevelForCasterLevel(nClass, GetCasterLevelByClass(nClass, oPC));
|
|
if (circleLevel > maxLevel)
|
|
return FALSE;
|
|
}
|
|
|
|
// default logic
|
|
// determine remaining Spell/Power choices left for player, if there is any
|
|
// remaining, enable the buttons.
|
|
if (GetRemainingSpellChoices(nClass, circleLevel, oPC))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int ShouldAddSpellToSpellButtons(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
|
|
string spellLevel = Get2DACache(sFile, "Level", spellbookId);
|
|
json chosenSpellsAtCircle = JsonObjectGet(chosenSpells, spellLevel);
|
|
|
|
int chosenSpellCount = JsonGetLength(chosenSpellsAtCircle);
|
|
|
|
// if the spell is in the chosen list, then don't add it to the available list
|
|
int i;
|
|
for (i = 0; i < chosenSpellCount; i++)
|
|
{
|
|
int chosenSpellId = JsonGetInt(JsonArrayGet(chosenSpellsAtCircle, i));
|
|
if (chosenSpellId == spellbookId)
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetIsBladeMagicClass(nClass))
|
|
return IsAllowedDiscipline(nClass, spellbookId, oPC);
|
|
|
|
// if a psionic class we need to see if the power is a expanded knowledge
|
|
// choice and if we should show it or not
|
|
if (GetIsPsionicClass(nClass))
|
|
return ShouldAddPower(nClass, spellbookId, oPC);
|
|
|
|
// for these set of classes we need to only allow 'advanced learning'
|
|
// spells to be added
|
|
if (nClass == CLASS_TYPE_BEGUILER
|
|
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|
|
|| nClass == CLASS_TYPE_WARMAGE)
|
|
{
|
|
int advancedLearning = StringToInt(Get2DACache(sFile, "AL", spellbookId));
|
|
if (advancedLearning)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
int nLevel = GetLevelByClass(nClass, oPC);
|
|
if ((StringToInt(spellLevel) == 0) && (nLevel == 1))
|
|
return FALSE;
|
|
int advancedLearning = StringToInt(Get2DACache(sFile, "AL", spellbookId));
|
|
if (advancedLearning)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
json GetChosenSpellListObject(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json retValue = GetLocalJson(oPC, NUI_LEVEL_UP_CHOSEN_SPELLS_VAR);
|
|
// if this isn't set yet then we the chosen currently is the known spells
|
|
if (retValue == JsonNull())
|
|
{
|
|
retValue = GetKnownSpellListObject(nClass, oPC);
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_CHOSEN_SPELLS_VAR, retValue);
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
json GetKnownSpellListObject(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json retValue = GetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_SPELLS_VAR);
|
|
if (retValue == JsonNull())
|
|
retValue = JsonObject();
|
|
else
|
|
return retValue;
|
|
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int totalSpells = Get2DARowCount(sFile);
|
|
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
string sSpellbook = GetSpellsKnown_Array(nClass, i);
|
|
|
|
int nSize = persistant_array_get_size(oPC, sSpellbook);
|
|
|
|
int j;
|
|
for (j = 0; j < nSize; j++)
|
|
{
|
|
int knownSpellbookID = persistant_array_get_int(oPC, sSpellbook, j);
|
|
// we store things in a JSON Object where the spell circle
|
|
// is the key to a JsonArray of spellbookIds.
|
|
json spellList = JsonObjectGet(retValue, IntToString(i));
|
|
if (spellList == JsonNull())
|
|
spellList = JsonArray();
|
|
spellList = JsonArrayInsert(spellList, JsonInt(knownSpellbookID));
|
|
retValue = JsonObjectSet(retValue, IntToString(i), spellList);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// loop through all the spells in the class's 2da
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
int featId = StringToInt(Get2DACache(sFile, "FeatID", i));
|
|
// if you have the feat, you know the spell
|
|
if (featId && GetHasFeat(featId, oPC, TRUE))
|
|
{
|
|
string spellLevel = Get2DACache(sFile, "Level", i);
|
|
int nSpellLevel = StringToInt(spellLevel);
|
|
// some spells have **** as their level, so make sure we have
|
|
// parsed it correctly
|
|
if (IntToString(nSpellLevel) == spellLevel)
|
|
{
|
|
// we store things in a JSON Object where the spell circle
|
|
// is the key to a JsonArray of spellbookIds.
|
|
json spellList = JsonObjectGet(retValue, spellLevel);
|
|
if (spellList == JsonNull())
|
|
spellList = JsonArray();
|
|
spellList = JsonArrayInsert(spellList, JsonInt(i));
|
|
retValue = JsonObjectSet(retValue, spellLevel, spellList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_SPELLS_VAR, retValue);
|
|
return retValue;
|
|
}
|
|
|
|
json GetSpellListObject(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json retValue = GetLocalJson(oPC, NUI_LEVEL_UP_SPELLBOOK_OBJECT_CACHE_VAR + IntToString(nClass));
|
|
if (retValue == JsonNull())
|
|
retValue = JsonObject();
|
|
else
|
|
return retValue;
|
|
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int totalSpells = Get2DARowCount(sFile);
|
|
|
|
// loop through all the spells in the 2da and convert it to a JSON Object representation
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
string spellLevel = Get2DACache(sFile, "Level", i);
|
|
int nSpellLevel = StringToInt(spellLevel);
|
|
// some spells in the list have **** as spell level. We need to ignore them
|
|
if (IntToString(nSpellLevel) == spellLevel)
|
|
{
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
int reqFeat = StringToInt(Get2DACache(sFile, "ReqFeat", i));
|
|
if (!reqFeat)
|
|
{
|
|
json spellList = JsonObjectGet(retValue, spellLevel);
|
|
if (spellList == JsonNull())
|
|
spellList = JsonArray();
|
|
spellList = JsonArrayInsert(spellList, JsonInt(i));
|
|
retValue = JsonObjectSet(retValue, spellLevel, spellList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
json spellList = JsonObjectGet(retValue, spellLevel);
|
|
if (spellList == JsonNull())
|
|
spellList = JsonArray();
|
|
spellList = JsonArrayInsert(spellList, JsonInt(i));
|
|
retValue = JsonObjectSet(retValue, spellLevel, spellList);
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_SPELLBOOK_OBJECT_CACHE_VAR + IntToString(nClass), retValue);
|
|
return retValue;
|
|
}
|
|
|
|
int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF)
|
|
{
|
|
int chosenCircle = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CIRCLE_VAR);
|
|
int remainingChoices = 0;
|
|
|
|
// we only want to cache on the current circle.
|
|
if (chosenCircle == circleLevel)
|
|
{
|
|
remainingChoices = GetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR);
|
|
// -20 is the chosen number to say there is no cache set since 0 is
|
|
// a valid option
|
|
if (remainingChoices != -20)
|
|
return remainingChoices;
|
|
}
|
|
|
|
// logic for psionics
|
|
if (GetIsPsionicClass(nClass))
|
|
remainingChoices = GetRemainingPowerChoices(nClass, circleLevel, oPC);
|
|
|
|
// logic for ToB
|
|
if (GetIsBladeMagicClass(nClass))
|
|
remainingChoices = (GetRemainingManeuverChoices(nClass, oPC) +
|
|
GetRemainingStanceChoices(nClass, oPC));
|
|
|
|
// logic for Invokers
|
|
if (GetIsInvocationClass(nClass))
|
|
remainingChoices = GetRemainingInvocationChoices(nClass, circleLevel, oPC);
|
|
|
|
// logic for mysteries
|
|
if (GetIsShadowMagicClass(nClass))
|
|
{
|
|
int totalChosen = 0;
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
json circles = JsonObjectKeys(chosenSpells);
|
|
int totalCircles = JsonGetLength(circles);
|
|
|
|
int i;
|
|
for (i = 0; i < totalCircles; i++)
|
|
{
|
|
// loop through each circle and add its total spells together since
|
|
// we don't care about where you spend your spells, only the amount
|
|
string currentCircle = JsonGetString(JsonArrayGet(circles, i));
|
|
json spellList = JsonObjectGet(chosenSpells, currentCircle);
|
|
if (spellList != JsonNull())
|
|
totalChosen += JsonGetLength(spellList);
|
|
}
|
|
|
|
int maxKnown = GetMaxMysteryCount(oPC, nClass);
|
|
remainingChoices = (maxKnown - totalChosen);
|
|
}
|
|
|
|
if (GetIsTruenamingClass(nClass))
|
|
remainingChoices = GetRemainingTruenameChoices(nClass, -1, oPC);
|
|
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
int nLevel = GetLevelByClass(CLASS_TYPE_ARCHIVIST, oPC);
|
|
int spellsAvailable;
|
|
if (nLevel == 1)
|
|
spellsAvailable = (3 + GetAbilityModifier(ABILITY_INTELLIGENCE, oPC));
|
|
else
|
|
spellsAvailable = 2;
|
|
|
|
json newSpells = GetArchivistNewSpellsList(oPC);
|
|
int totalNewSpells = JsonGetLength(newSpells);
|
|
remainingChoices = (spellsAvailable - totalNewSpells);
|
|
}
|
|
if (GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
{
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
int totalSpellsKnown = 0;
|
|
int casterLevel = GetCasterLevelByClass(nClass, oPC);
|
|
|
|
// these specific classes only learn at specific rates
|
|
int advancedLearning = 0;
|
|
// beguiler learns every 4th level starting on 3
|
|
if (nClass == CLASS_TYPE_BEGUILER)
|
|
advancedLearning = ((casterLevel+1)/4);
|
|
// dread learns every 4th level
|
|
if (nClass == CLASS_TYPE_DREAD_NECROMANCER)
|
|
advancedLearning = (casterLevel/4);
|
|
// warmage is a bastard child that choses when it learns a spell whenever
|
|
// it decides it feels like it wants to
|
|
if (nClass == CLASS_TYPE_WARMAGE)
|
|
{
|
|
if (casterLevel >= 3) // 1 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 6) // 2 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 11) // 3 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 16) // 4 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 24) // 5 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 28) // 6 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 32) // 7 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 36) // 8 choice
|
|
advancedLearning++;
|
|
if (casterLevel >= 40) // 9 choice
|
|
advancedLearning++;
|
|
}
|
|
|
|
if (advancedLearning)
|
|
{
|
|
int maxSpellLevel = GetMaxSpellLevelForCasterLevel(nClass, casterLevel);
|
|
// can't learn what you can't achieve
|
|
if (circleLevel > maxSpellLevel)
|
|
remainingChoices = 0;
|
|
else
|
|
{
|
|
int chosenSpellsAmount = 0;
|
|
|
|
json circles = JsonObjectKeys(chosenSpells);
|
|
int totalCircles = JsonGetLength(circles);
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
|
|
int i;
|
|
for (i = 0; i <= totalCircles; i++)
|
|
{
|
|
string currentCircle = JsonGetString(JsonArrayGet(circles, i));
|
|
json spellList = JsonObjectGet(chosenSpells, currentCircle);
|
|
if ((spellList != JsonNull()))
|
|
{
|
|
// loop through the spells of a given circle and count how
|
|
// many advanced learning spells you know
|
|
int numOfSpells = JsonGetLength(spellList);
|
|
int j;
|
|
for (j = 0; j < numOfSpells; j++)
|
|
{
|
|
int nSpellbookID = JsonGetInt(JsonArrayGet(spellList, j));
|
|
int isAL = StringToInt(Get2DACache(sFile, "AL", nSpellbookID));
|
|
if (isAL)
|
|
chosenSpellsAmount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
remainingChoices = (advancedLearning - chosenSpellsAmount);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// default logic for spont casters
|
|
totalSpellsKnown = GetSpellKnownMaxCount(casterLevel, circleLevel, nClass, oPC);
|
|
// Favoured Soul has more 0 choices than there are spells for some reason
|
|
if (nClass == CLASS_TYPE_FAVOURED_SOUL && circleLevel == 0 && totalSpellsKnown > 6)
|
|
totalSpellsKnown = 6;
|
|
|
|
// logic for spont casters
|
|
json selectedCircle = JsonObjectGet(chosenSpells, IntToString(circleLevel));
|
|
if (selectedCircle == JsonNull())
|
|
return totalSpellsKnown;
|
|
|
|
int selectedSpellCount = JsonGetLength(selectedCircle);
|
|
remainingChoices = (totalSpellsKnown - selectedSpellCount);
|
|
}
|
|
}
|
|
|
|
if (chosenCircle == circleLevel)
|
|
SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, remainingChoices);
|
|
return remainingChoices;
|
|
}
|
|
|
|
void FinishLevelUp(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
RemoveSpells(nClass, oPC);
|
|
LearnSpells(nClass, oPC);
|
|
if (nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
int nLevel = GetLevelByClass(nClass, oPC);
|
|
SetPersistantLocalInt(oPC, "LastSpellGainLevel", nLevel);
|
|
}
|
|
ClearLevelUpNUICaches(nClass, oPC);
|
|
}
|
|
|
|
void ClearLevelUpNUICaches(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
// clear the chosen spells you made
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_CHOSEN_SPELLS_VAR);
|
|
// clear the known spells you have
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_KNOWN_SPELLS_VAR);
|
|
SetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CIRCLE_VAR, -1);
|
|
DeleteLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR);
|
|
// clear the psionics selected choices
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_POWER_LIST_VAR);
|
|
// clear the expanded choices for psionics and invokers
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR);
|
|
// clear the PnP replace list
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_RELEARN_LIST_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR);
|
|
// for invocation and psionics we grab the list of known extra spells and cache it
|
|
// so we need to clear those caches
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_0");
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_" + IntToString(INVOCATION_LIST_EXTRA));
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_" + IntToString(INVOCATION_LIST_EXTRA_EPIC));
|
|
}
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_0");
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_" + IntToString(POWER_LIST_EXP_KNOWLEDGE));
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_" + IntToString(POWER_LIST_EPIC_EXP_KNOWLEDGE));
|
|
}
|
|
// for ToB we need to clear all the discipline info for determining PrC choice validity
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(CLASS_TYPE_SWORDSAGE));
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(CLASS_TYPE_WARBLADE));
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(CLASS_TYPE_CRUSADER));
|
|
}
|
|
}
|
|
|
|
void RemoveSpells(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
// we don't remove on psionic classes and archivist
|
|
if (GetIsPsionicClass(nClass) || nClass == CLASS_TYPE_ARCHIVIST)
|
|
return;
|
|
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
json spellCircles = JsonObjectKeys(knownSpells);
|
|
int totalCircles = JsonGetLength(spellCircles);
|
|
|
|
// loop through all the known spells circles
|
|
int i;
|
|
for (i = 0; i < totalCircles; i++)
|
|
{
|
|
string sSpellLevel = JsonGetString(JsonArrayGet(spellCircles, i));
|
|
int nSpellLevel = StringToInt(sSpellLevel);
|
|
|
|
json chosenCircle = JsonObjectGet(knownSpells, sSpellLevel);
|
|
int totalSpells = JsonGetLength(chosenCircle);
|
|
|
|
// loop through the spell list at the given circle
|
|
int y;
|
|
for (y = 0; y < totalSpells; y++)
|
|
{
|
|
int nSpellbookID = JsonGetInt(JsonArrayGet(chosenCircle, y));
|
|
// if the spell is not a chosen spell, then it was removed
|
|
if (!SpellIsWithinObject(nClass, nSpellbookID, nSpellLevel, chosenSpells, oPC))
|
|
{
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string spellId = Get2DACache(sFile, "SpellID", nSpellbookID);
|
|
int chosenList = 0;
|
|
// check to see if its a extra invocation choice and set it's chosen list
|
|
if (GetHasFeat(FEAT_EXTRA_INVOCATION_I, oPC))
|
|
{
|
|
json expList = GetSpellIDsKnown(nClass, oPC, INVOCATION_LIST_EXTRA);
|
|
if (JsonObjectGet(expList, spellId) != JsonNull())
|
|
chosenList = INVOCATION_LIST_EXTRA;
|
|
}
|
|
if (GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_I, oPC))
|
|
{
|
|
json expList = GetSpellIDsKnown(nClass, oPC, INVOCATION_LIST_EXTRA_EPIC);
|
|
if (JsonObjectGet(expList, spellId) != JsonNull())
|
|
chosenList = INVOCATION_LIST_EXTRA_EPIC;
|
|
}
|
|
RemoveSpellKnown(nClass, nSpellbookID, oPC, chosenList);
|
|
}
|
|
if (GetIsBladeMagicClass(nClass) || GetIsShadowMagicClass(nClass))
|
|
RemoveSpellKnown(nClass, nSpellbookID, oPC);
|
|
|
|
if (GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string sSpellBook = GetSpellsKnown_Array(nClass);
|
|
// remove the spell from the spellbook
|
|
array_extract_int(oPC, sSpellBook, nSpellbookID);
|
|
// wipe the spell from the player
|
|
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID));
|
|
WipeSpellFromHide(ipFeatID, oPC);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LearnSpells(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
LearnPowers(nClass, oPC);
|
|
return;
|
|
}
|
|
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json spellCircles = JsonObjectKeys(chosenSpells);
|
|
int totalCircles = JsonGetLength(spellCircles);
|
|
|
|
// loop through chosen spells circles
|
|
int i;
|
|
for (i = 0; i < totalCircles; i++)
|
|
{
|
|
string sSpellLevel = JsonGetString(JsonArrayGet(spellCircles, i));
|
|
int nSpellLevel = StringToInt(sSpellLevel);
|
|
|
|
json chosenCircle = JsonObjectGet(chosenSpells, sSpellLevel);
|
|
int totalSpells = JsonGetLength(chosenCircle);
|
|
|
|
// loop through the spell list at the circle
|
|
int y;
|
|
for (y = 0; y < totalSpells; y++)
|
|
{
|
|
int nSpellbookID = JsonGetInt(JsonArrayGet(chosenCircle, y));
|
|
// if the spell is not in the known spell list then it was newly added
|
|
if (!SpellIsWithinObject(nClass, nSpellbookID, nSpellLevel, knownSpells, oPC))
|
|
{
|
|
if (GetIsTruenamingClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
// find out what lexicon it belongs to and add it to that
|
|
int lexicon = StringToInt(Get2DACache(sFile, "Lexicon", nSpellbookID));
|
|
AddUtteranceKnown(oPC, nClass, nSpellbookID, lexicon, TRUE, GetHitDice(oPC));
|
|
}
|
|
|
|
if (GetIsShadowMagicClass(nClass))
|
|
AddMysteryKnown(oPC, nClass, nSpellbookID, TRUE, GetHitDice(oPC));
|
|
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string spellId = Get2DACache(sFile, "SpellID", nSpellbookID);
|
|
int chosenList = nClass;
|
|
json expList = GetExpandedChoicesList(nClass, oPC);
|
|
// if the invocation belongs to the extra or epic extra list
|
|
// then we need to provide those list ids instead.
|
|
if (JsonObjectGet(expList, spellId) != JsonNull())
|
|
chosenList = INVOCATION_LIST_EXTRA;
|
|
expList = GetEpicExpandedChoicesList(nClass, oPC);
|
|
if (JsonObjectGet(expList, spellId) != JsonNull())
|
|
chosenList = INVOCATION_LIST_EXTRA_EPIC;
|
|
AddInvocationKnown(oPC, chosenList, nSpellbookID, TRUE, GetHitDice(oPC));
|
|
}
|
|
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int maneuverType = StringToInt(Get2DACache(sFile, "Type", nSpellbookID));
|
|
// we save our moves either to stance or maneuever
|
|
if (maneuverType != MANEUVER_TYPE_STANCE)
|
|
maneuverType = MANEUVER_TYPE_MANEUVER;
|
|
|
|
AddManeuverKnown(oPC, nClass, nSpellbookID, maneuverType, TRUE, GetHitDice(oPC));
|
|
}
|
|
int nSpellbookType = GetSpellbookTypeForClass(nClass);
|
|
if (nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS
|
|
|| nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
// these classes have their own syste,
|
|
if (nClass == CLASS_TYPE_BEGUILER
|
|
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|
|
|| nClass == CLASS_TYPE_WARMAGE)
|
|
{
|
|
int casterLevel = GetCasterLevelByClass(nClass, oPC);
|
|
// this is taken from prc_s_spellgain as it is coupled with the
|
|
// dynamic dialogue system
|
|
int advancedLearning = 0;
|
|
// beguilers learn every 4th level starting on 3rd
|
|
if (nClass == CLASS_TYPE_BEGUILER)
|
|
advancedLearning = ((casterLevel+1)/4);
|
|
// dread learns every 4th level
|
|
if (nClass == CLASS_TYPE_DREAD_NECROMANCER)
|
|
advancedLearning = (casterLevel/4);
|
|
if (nClass == CLASS_TYPE_WARMAGE)
|
|
{
|
|
if (casterLevel >= 3)
|
|
advancedLearning++;
|
|
}
|
|
|
|
if (advancedLearning)
|
|
{
|
|
// incremenet the total advanced learning known
|
|
int nAdvLearn = GetPersistantLocalInt(oPC, "AdvancedLearning_"+IntToString(nClass));
|
|
nAdvLearn++;
|
|
SetPersistantLocalInt(oPC, "AdvancedLearning_"+IntToString(nClass), nAdvLearn);
|
|
}
|
|
}
|
|
|
|
// get location of persistant storage on the hide
|
|
string sSpellbook = GetSpellsKnown_Array(nClass, nSpellLevel);
|
|
//object oToken = GetHideToken(oPC);
|
|
|
|
// Create spells known persistant array if it is missing
|
|
int nSize = persistant_array_get_size(oPC, sSpellbook);
|
|
if (nSize < 0)
|
|
{
|
|
persistant_array_create(oPC, sSpellbook);
|
|
nSize = 0;
|
|
}
|
|
|
|
// Mark the spell as known (e.g. add it to the end of oPCs spellbook)
|
|
persistant_array_set_int(oPC, sSpellbook, nSize, nSpellbookID);
|
|
|
|
if (nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
{
|
|
// add spell
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string sArrayName = "NewSpellbookMem_" + IntToString(nClass);
|
|
int featId = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID));
|
|
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID));
|
|
AddSpellUse(oPC, nSpellbookID, nClass, sFile, sArrayName, nSpellbookType, GetPCSkin(oPC), featId, ipFeatID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int EnableChosenButton(int nClass, int spellbookId, int circleLevel, object oPC=OBJECT_SELF)
|
|
{
|
|
if (GetIsPsionicClass(nClass)
|
|
|| GetIsShadowMagicClass(nClass)
|
|
|| GetIsTruenamingClass(nClass)
|
|
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|
|
|| nClass == CLASS_TYPE_BEGUILER
|
|
|| nClass == CLASS_TYPE_WARMAGE
|
|
|| nClass == CLASS_TYPE_ARCHIVIST)
|
|
{
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json currentCircle = JsonObjectGet(knownSpells, IntToString(circleLevel));
|
|
int totalSpells = JsonGetLength(currentCircle);
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
// if spell belongs to known spells, then disable, we don't allow
|
|
// replacing for these classes.
|
|
int currentSpellbookId = JsonGetInt(JsonArrayGet(currentCircle, i));
|
|
if (currentSpellbookId == spellbookId)
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int prereqs = StringToInt(Get2DACache(sFile, "Prereqs", spellbookId));
|
|
string discipline = Get2DACache(sFile, "Discipline", spellbookId);
|
|
// if the maneuver is required for others to exist, t hen disable it
|
|
if (IsRequiredForOtherManeuvers(nClass, prereqs, discipline, oPC))
|
|
return FALSE;
|
|
// if it is required for a PRC to exist, then disable it.
|
|
if (IsRequiredForToBPRCClass(nClass, spellbookId, oPC))
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
// dragon Shamans can't replace
|
|
if (nClass == CLASS_TYPE_DRAGON_SHAMAN)
|
|
{
|
|
json invokKnown = GetSpellIDsKnown(nClass, oPC);
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
json chosenSpell = JsonObjectGet(invokKnown, spellId);
|
|
if (chosenSpell != JsonNull())
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// If we do not use the bioware unlearn system, we follow PnP
|
|
if (!GetPRCSwitch(PRC_BIO_UNLEARN))
|
|
{
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json currentCircle = JsonObjectGet(knownSpells, IntToString(circleLevel));
|
|
int totalSpells = JsonGetLength(currentCircle);
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
int currentSpellbookId = JsonGetInt(JsonArrayGet(currentCircle, i));
|
|
// if the spell belongs to the known spell list, then we need to determine
|
|
if (currentSpellbookId == spellbookId)
|
|
{
|
|
json unlearnList = GetChosenReplaceListObject(oPC);
|
|
int totalUnlearned = JsonGetLength(unlearnList);
|
|
int totalAllowed = GetPRCSwitch(PRC_UNLEARN_SPELL_MAXNR);
|
|
// we default to 1 if no max number of unlearns is set
|
|
if (!totalAllowed)
|
|
totalAllowed = 1;
|
|
// we cannot replace a spell if we have more than or equal to the
|
|
// amount of relearns allowed, therefore disable.
|
|
return totalUnlearned < totalAllowed;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void ResetChoices(object oPC=OBJECT_SELF)
|
|
{
|
|
// reset choices made so far
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_CHOSEN_SPELLS_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_POWER_LIST_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_RELEARN_LIST_VAR);
|
|
DeleteLocalJson(oPC, NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR);
|
|
}
|
|
|
|
void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int nList=0)
|
|
{
|
|
string sBase;
|
|
string levelArrayBaseId;
|
|
string generalArrayBaseId;
|
|
string totalCountId;
|
|
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int chosenList = (nList != 0) ? nList : nClass;
|
|
int spellID = StringToInt(Get2DACache(sFile, "SpellID", spellbookId));
|
|
|
|
// if statements are to change the location of the spellbook we are grabbing
|
|
if (GetIsShadowMagicClass(nClass))
|
|
{
|
|
sBase = _MYSTERY_LIST_NAME_BASE + IntToString(chosenList);
|
|
levelArrayBaseId = _MYSTERY_LIST_LEVEL_ARRAY;
|
|
generalArrayBaseId = _MYSTERY_LIST_GENERAL_ARRAY;
|
|
totalCountId = _MYSTERY_LIST_TOTAL_KNOWN;
|
|
}
|
|
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
sBase = _INVOCATION_LIST_NAME_BASE + IntToString(chosenList);
|
|
levelArrayBaseId = _INVOCATION_LIST_LEVEL_ARRAY;
|
|
generalArrayBaseId = _INVOCATION_LIST_GENERAL_ARRAY;
|
|
totalCountId = _INVOCATION_LIST_TOTAL_KNOWN;
|
|
}
|
|
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
int maneuverType = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
if (maneuverType != MANEUVER_TYPE_STANCE) maneuverType = MANEUVER_TYPE_MANEUVER;
|
|
sBase = _MANEUVER_LIST_NAME_BASE + IntToString(chosenList) + IntToString(maneuverType);
|
|
levelArrayBaseId = _MANEUVER_LIST_LEVEL_ARRAY;
|
|
generalArrayBaseId = _MANEUVER_LIST_GENERAL_ARRAY;
|
|
totalCountId = _MANEUVER_LIST_TOTAL_KNOWN;
|
|
}
|
|
|
|
if (GetIsTruenamingClass(nClass))
|
|
{
|
|
string lexicon = Get2DACache(sFile, "Lexicon", spellbookId);
|
|
sBase = _UTTERANCE_LIST_NAME_BASE + IntToString(chosenList) + lexicon;
|
|
levelArrayBaseId = _UTTERANCE_LIST_LEVEL_ARRAY;
|
|
generalArrayBaseId = _UTTERANCE_LIST_GENERAL_ARRAY;
|
|
totalCountId = _UTTERANCE_LIST_TOTAL_KNOWN;
|
|
}
|
|
|
|
string sTestArray;
|
|
|
|
int found = FALSE;
|
|
|
|
int i;
|
|
for (i = 1; i <= GetHitDice(oPC); i++)
|
|
{
|
|
sTestArray = sBase + levelArrayBaseId + IntToString(i);
|
|
if (persistant_array_exists(oPC, sTestArray))
|
|
{
|
|
// if we found the spell, then we remove it.
|
|
if (persistant_array_extract_int(oPC, sTestArray, spellID) >= 0)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
// if not found we check the general list where spells aren't set to a level.
|
|
sTestArray = sBase + generalArrayBaseId;
|
|
if (persistant_array_exists(oPC, sTestArray))
|
|
{
|
|
//if we could not find the spell here, something went wrong
|
|
if (persistant_array_extract_int(oPC, sTestArray, spellID) < 0)
|
|
{
|
|
SendMessageToPC(oPC, "Could not find spellID " + IntToString(spellID) + " in the class's spellbook!");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// decrement the amount of spells known.
|
|
SetPersistantLocalInt(oPC, sBase + totalCountId,
|
|
GetPersistantLocalInt(oPC, sBase + totalCountId) - 1
|
|
);
|
|
|
|
// if ToB we need to decrement the specific discipline as well.
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
int maneuverType = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
if (maneuverType == MANEUVER_TYPE_BOOST
|
|
|| maneuverType == MANEUVER_TYPE_COUNTER
|
|
|| maneuverType == MANEUVER_TYPE_STRIKE
|
|
|| maneuverType == MANEUVER_TYPE_MANEUVER)
|
|
maneuverType = MANEUVER_TYPE_MANEUVER;
|
|
string sDisciplineArray = _MANEUVER_LIST_DISCIPLINE + IntToString(maneuverType) + "_" + Get2DACache(sFile, "Discipline", spellbookId);
|
|
SetPersistantLocalInt(oPC, sDisciplineArray,
|
|
GetPersistantLocalInt(oPC, sDisciplineArray) - 1);
|
|
}
|
|
|
|
// remove spell from player
|
|
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", spellbookId));
|
|
itemproperty ipFeat = PRCItemPropertyBonusFeat(ipFeatID);
|
|
object oSkin = GetPCSkin(oPC);
|
|
RemoveItemProperty(oSkin, ipFeat);
|
|
CheckAndRemoveFeat(oSkin, ipFeat);
|
|
}
|
|
|
|
json GetSpellIDsKnown(int nClass, object oPC=OBJECT_SELF, int nList=0)
|
|
{
|
|
json spellIds = GetLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_" + IntToString(nList));
|
|
if (spellIds == JsonNull())
|
|
spellIds = JsonObject();
|
|
else
|
|
return spellIds;
|
|
|
|
string sBase;
|
|
string levelArrayBaseId;
|
|
string generalArrayBaseId;
|
|
// if we are given a listId then use that instead, used for extra choices and
|
|
// expanded knowledge
|
|
int chosenList = (nList != 0) ? nList : nClass;
|
|
// these if checks are for setting class specific ids
|
|
if (nClass == CLASS_TYPE_DRAGON_SHAMAN
|
|
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT
|
|
|| nClass == CLASS_TYPE_WARLOCK)
|
|
{
|
|
sBase = _INVOCATION_LIST_NAME_BASE + IntToString(chosenList);
|
|
levelArrayBaseId = _INVOCATION_LIST_LEVEL_ARRAY;
|
|
generalArrayBaseId = _INVOCATION_LIST_GENERAL_ARRAY;
|
|
}
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
sBase = _POWER_LIST_NAME_BASE + IntToString(chosenList);
|
|
levelArrayBaseId = _POWER_LIST_LEVEL_ARRAY;
|
|
generalArrayBaseId = _POWER_LIST_GENERAL_ARRAY;
|
|
}
|
|
|
|
// go through the level list and translate the spellIds into a JSON Object
|
|
// structure for easier access.
|
|
int i;
|
|
for (i = 1; i <= GetHitDice(oPC); i++)
|
|
{
|
|
string sTestArray = sBase + levelArrayBaseId + IntToString(i);
|
|
if (persistant_array_exists(oPC, sTestArray))
|
|
{
|
|
int nSize = persistant_array_get_size(oPC, sTestArray);
|
|
|
|
int j;
|
|
for (j = 0; j < nSize; j++)
|
|
{
|
|
spellIds = JsonObjectSet(spellIds, IntToString(persistant_array_get_int(oPC, sTestArray, j)), JsonBool(TRUE));
|
|
}
|
|
}
|
|
}
|
|
|
|
// go through the general list and translate the spellIds into a JSON Object
|
|
// structure for easier access.
|
|
string sTestArray = sBase + generalArrayBaseId;
|
|
if (persistant_array_exists(oPC, sTestArray))
|
|
{
|
|
int nSize = persistant_array_get_size(oPC, sTestArray);
|
|
|
|
int j;
|
|
for (j = 0; j < nSize; j++)
|
|
{
|
|
spellIds = JsonObjectSet(spellIds, IntToString(persistant_array_get_int(oPC, sTestArray, j)), JsonBool(TRUE));
|
|
}
|
|
}
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_SPELLID_LIST_VAR + IntToString(nClass) + "_" + IntToString(nList), spellIds);
|
|
return spellIds;
|
|
}
|
|
|
|
string ReasonForDisabledSpell(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int circleLevel = StringToInt(Get2DACache(sFile, "Level", spellbookId));
|
|
|
|
// logic for psionics
|
|
if (GetIsPsionicClass(nClass))
|
|
{
|
|
int maxLevel = GetMaxPowerLevelForClass(nClass, oPC);
|
|
if (circleLevel > maxLevel)
|
|
return "You are unable to learn at this level currently.";
|
|
|
|
// if its an expanded knowledge choice and we have already made all our
|
|
// exp knowledge choices then it needs to be disabled.
|
|
if (IsExpKnowledgePower(nClass, spellbookId))
|
|
{
|
|
int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC)
|
|
+ GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC);
|
|
if (!remainingExp)
|
|
return "You have no more expanded knowledge choices left.";
|
|
}
|
|
}
|
|
|
|
if (GetIsShadowMagicClass(nClass))
|
|
{
|
|
// mysteries are weird, the circles are sectioned by 1-3, 4-6, 7-9
|
|
// if you do not have at least 2 choices from a circle you can't progress up
|
|
// so you can have access to circles 1,2,4,7,8
|
|
int nType = 1;
|
|
if (circleLevel >= 4 && circleLevel <= 6)
|
|
nType = 2;
|
|
if (circleLevel >= 7 && circleLevel <= 9)
|
|
nType = 3;
|
|
int maxPossibleCircle = GetMaxMysteryLevelLearnable(oPC, nClass, nType);
|
|
if (circleLevel > maxPossibleCircle)
|
|
return "You are unable to learn at this level currently.";
|
|
}
|
|
|
|
if (GetIsTruenamingClass(nClass))
|
|
{
|
|
int lexicon = StringToInt(Get2DACache(sFile, "Lexicon", spellbookId));
|
|
// each lexicon learns at different rates
|
|
int maxCircle = GetLexiconCircleKnownAtLevel(GetLevelByClass(nClass, oPC), lexicon);
|
|
if (circleLevel > maxCircle)
|
|
return "You are unable to learn at this level currently.";
|
|
|
|
if (GetRemainingTruenameChoices(nClass, lexicon, oPC))
|
|
return "";
|
|
return "You have made all your truenaming choices.";
|
|
}
|
|
|
|
// logic for ToB
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
if (circleLevel > GetMaxInitiatorCircle(nClass, oPC))
|
|
return "You are unable to learn at this level currently.";
|
|
|
|
// if you do not have the prerequisite amount of maneuevers to learn
|
|
// the maneuever, then you can't learn it.
|
|
if (!HasPreRequisitesForManeuver(nClass, spellbookId, oPC))
|
|
return "You do not have the prerequisites for this maneuver.";
|
|
|
|
// maneuvers and stances have their own seperate limits
|
|
int type = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
if (type == MANEUVER_TYPE_BOOST
|
|
|| type == MANEUVER_TYPE_COUNTER
|
|
|| type == MANEUVER_TYPE_STRIKE
|
|
|| type == MANEUVER_TYPE_MANEUVER)
|
|
{
|
|
int remainingMan = GetRemainingManeuverChoices(nClass, oPC);
|
|
if (remainingMan)
|
|
return "";
|
|
return "You have made all your maneuver choices.";
|
|
}
|
|
if (type == MANEUVER_TYPE_STANCE)
|
|
{
|
|
int remainingStance = GetRemainingStanceChoices(nClass, oPC);
|
|
if (remainingStance)
|
|
return "";
|
|
return "You have made all your stance choices.";
|
|
}
|
|
}
|
|
|
|
// default logic
|
|
// determine remaining Spell/Power choices left for player, if there is any
|
|
// remaining, enable the buttons.
|
|
if (GetRemainingSpellChoices(nClass, circleLevel, oPC))
|
|
return "";
|
|
|
|
return "You have made all your spell choices.";
|
|
}
|
|
|
|
string ReasonForDisabledChosen(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int circleLevel = StringToInt(Get2DACache(sFile, "Level", spellbookId));
|
|
|
|
|
|
if (GetIsPsionicClass(nClass)
|
|
|| GetIsShadowMagicClass(nClass)
|
|
|| GetIsTruenamingClass(nClass)
|
|
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|
|
|| nClass == CLASS_TYPE_BEGUILER
|
|
|| nClass == CLASS_TYPE_WARMAGE)
|
|
{
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json currentCircle = JsonObjectGet(knownSpells, IntToString(circleLevel));
|
|
int totalSpells = JsonGetLength(currentCircle);
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
// if spell belongs to known spells, then disable, we don't allow
|
|
// replacing for these classes.
|
|
int currentSpellbookId = JsonGetInt(JsonArrayGet(currentCircle, i));
|
|
if (currentSpellbookId == spellbookId)
|
|
return "You cannot replace spells as this class.";
|
|
}
|
|
|
|
}
|
|
|
|
if (GetIsBladeMagicClass(nClass))
|
|
{
|
|
int prereqs = StringToInt(Get2DACache(sFile, "Prereqs", spellbookId));
|
|
string discipline = Get2DACache(sFile, "Discipline", spellbookId);
|
|
// if the maneuver is required for others to exist, t hen disable it
|
|
if (IsRequiredForOtherManeuvers(nClass, prereqs, discipline, oPC))
|
|
return "This maneuver is required for another maneuver.";
|
|
// if it is required for a PRC to exist, then disable it.
|
|
if (IsRequiredForToBPRCClass(nClass, spellbookId, oPC))
|
|
return "This maneuver is reuquired for a PRC class.";
|
|
}
|
|
|
|
if (GetIsInvocationClass(nClass))
|
|
{
|
|
// dragon Shamans can't replace
|
|
if (nClass == CLASS_TYPE_DRAGON_SHAMAN)
|
|
{
|
|
json invokKnown = GetSpellIDsKnown(nClass, oPC);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
json chosenSpell = JsonObjectGet(invokKnown, spellId);
|
|
if (chosenSpell != JsonNull())
|
|
return "You cannot replace invocations as this class.";
|
|
}
|
|
}
|
|
|
|
// If we do not use the bioware unlearn system, we follow PnP
|
|
if (!GetPRCSwitch(PRC_BIO_UNLEARN))
|
|
{
|
|
json knownSpells = GetKnownSpellListObject(nClass, oPC);
|
|
json currentCircle = JsonObjectGet(knownSpells, IntToString(circleLevel));
|
|
int totalSpells = JsonGetLength(currentCircle);
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
int currentSpellbookId = JsonGetInt(JsonArrayGet(currentCircle, i));
|
|
// if the spell belongs to the known spell list, then we need to determine
|
|
if (currentSpellbookId == spellbookId)
|
|
{
|
|
json unlearnList = GetChosenReplaceListObject(oPC);
|
|
int totalUnlearned = JsonGetLength(unlearnList);
|
|
int totalAllowed = GetPRCSwitch(PRC_UNLEARN_SPELL_MAXNR);
|
|
// we default to 1 if no max number of unlearns is set
|
|
if (!totalAllowed)
|
|
totalAllowed = 1;
|
|
// we cannot replace a spell if we have more than or equal to the
|
|
// amount of relearns allowed, therefore disable.
|
|
if (totalUnlearned < totalAllowed)
|
|
return "";
|
|
return "You can only replace " + IntToString(totalAllowed) + " spells during level up.";
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
json GetExpandedChoicesList(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json expandedChoices = GetLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR);
|
|
if (expandedChoices != JsonNull())
|
|
return expandedChoices;
|
|
|
|
if (GetIsPsionicClass(nClass))
|
|
expandedChoices = GetSpellIDsKnown(nClass, oPC, POWER_LIST_EXP_KNOWLEDGE);
|
|
else
|
|
expandedChoices = GetSpellIDsKnown(nClass, oPC, INVOCATION_LIST_EXTRA);
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EXPANDED_CHOICES_VAR, expandedChoices);
|
|
return expandedChoices;
|
|
}
|
|
|
|
int GetRemainingExpandedChoices(int nClass, int nList, object oPC=OBJECT_SELF)
|
|
{
|
|
int remainingChoices = 0;
|
|
|
|
json expandedList = (nList == INVOCATION_LIST_EXTRA || nList == POWER_LIST_EXP_KNOWLEDGE ) ? GetExpandedChoicesList(nClass, oPC)
|
|
: GetEpicExpandedChoicesList(nClass, oPC);
|
|
int expChoicesCount = JsonGetLength(JsonObjectKeys(expandedList));
|
|
int maxExpChoices = (GetIsPsionicClass(nClass)) ? GetMaxPowerCount(oPC, nList)
|
|
: GetMaxInvocationCount(oPC, nList);
|
|
remainingChoices += (maxExpChoices - expChoicesCount);
|
|
|
|
return remainingChoices;
|
|
}
|
|
|
|
json GetEpicExpandedChoicesList(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json expandedChoices = GetLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR);
|
|
if (expandedChoices != JsonNull())
|
|
return expandedChoices;
|
|
|
|
if (GetIsPsionicClass(nClass))
|
|
expandedChoices = GetSpellIDsKnown(nClass, oPC, POWER_LIST_EPIC_EXP_KNOWLEDGE);
|
|
else
|
|
expandedChoices = GetSpellIDsKnown(nClass, oPC, INVOCATION_LIST_EXTRA_EPIC);
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR, expandedChoices);
|
|
return expandedChoices;
|
|
}
|
|
|
|
int IsSpellInExpandedChoices(int nClass, int nList, int spellId, object oPC=OBJECT_SELF)
|
|
{
|
|
json expList = (nList == POWER_LIST_EXP_KNOWLEDGE || nList == INVOCATION_LIST_EXTRA) ? GetExpandedChoicesList(nClass, oPC)
|
|
: GetEpicExpandedChoicesList(nClass, oPC);
|
|
|
|
return (JsonObjectGet(expList, IntToString(spellId)) != JsonNull());
|
|
}
|
|
|
|
json GetChosenReplaceListObject(object oPC=OBJECT_SELF)
|
|
{
|
|
json replaceList = GetLocalJson(oPC, NUI_LEVEL_UP_RELEARN_LIST_VAR);
|
|
if (replaceList == JsonNull())
|
|
replaceList = JsonObject();
|
|
else
|
|
return replaceList;
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_RELEARN_LIST_VAR, replaceList);
|
|
return replaceList;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Psionics ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
int IsExpKnowledgePower(int nClass, int spellbookId)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId));
|
|
return isExp;
|
|
}
|
|
|
|
json GetCurrentPowerList(object oPC=OBJECT_SELF)
|
|
{
|
|
json retValue = GetLocalJson(oPC, NUI_LEVEL_UP_POWER_LIST_VAR);
|
|
if (retValue == JsonNull())
|
|
retValue = JsonArray();
|
|
else
|
|
return retValue;
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_POWER_LIST_VAR, retValue);
|
|
return retValue;
|
|
}
|
|
|
|
int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int featId = StringToInt(Get2DACache(sFile, "FeatID", spellbookId));
|
|
int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId));
|
|
// if you don't have the prereqs for a power then don't add it. Specific for
|
|
// psions
|
|
if (!CheckPowerPrereqs(featId, oPC))
|
|
return FALSE;
|
|
// if the power is a expanded knowledge power
|
|
if (isExp)
|
|
{
|
|
// and we have a expanded knowledge choice left to make then show
|
|
// the button
|
|
int addPower = FALSE;
|
|
int maxLevel = GetMaxPowerLevelForClass(nClass, oPC);
|
|
int currentCircle = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CIRCLE_VAR);
|
|
|
|
int choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC);
|
|
if (choicesLeft && (currentCircle <= (maxLevel-1)))
|
|
addPower = TRUE;
|
|
choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC);
|
|
if (choicesLeft)
|
|
addPower = TRUE;
|
|
// otherwise don't show the button.
|
|
return addPower;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LearnPowers(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
// add normal powers
|
|
json powerList = GetCurrentPowerList(oPC);
|
|
int totalPowers = JsonGetLength(powerList);
|
|
int i;
|
|
for (i = 0; i < totalPowers; i++)
|
|
{
|
|
int nSpellbookID = JsonGetInt(JsonArrayGet(powerList, i));
|
|
// get the expanded knowledge list we are adding to if any
|
|
int expKnow = GetExpKnowledgePowerListRequired(nClass, nSpellbookID, oPC);
|
|
AddPowerKnown(oPC, nClass, nSpellbookID, TRUE, GetManifesterLevel(oPC, nClass, TRUE), expKnow);
|
|
}
|
|
}
|
|
|
|
int GetExpKnowledgePowerListRequired(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
|
|
int i;
|
|
// expanded knowledge is -1, epic epxanded knowledge is -2
|
|
for (i = -1; i >= -2; i--)
|
|
{
|
|
int spellId = StringToInt(Get2DACache(sFile, "SpellID", spellbookId));
|
|
if (IsSpellInExpandedChoices(nClass, i, spellId, oPC))
|
|
return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int GetMaxPowerLevelForClass(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetAMSKnownFileName(nClass);
|
|
int nLevel = GetManifesterLevel(oPC, nClass, TRUE);
|
|
// index is level - 1 since it starts at 0.
|
|
int maxLevel = StringToInt(Get2DACache(sFile, "MaxPowerLevel", nLevel-1));
|
|
return maxLevel;
|
|
}
|
|
|
|
int GetRemainingPowerChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE)
|
|
{
|
|
int remaining = 0;
|
|
|
|
int maxLevel = GetMaxPowerLevelForClass(nClass, oPC);
|
|
if (chosenCircle > maxLevel)
|
|
return 0;
|
|
|
|
json choices = GetCurrentPowerList(oPC);
|
|
int totalChoices = JsonGetLength(choices);
|
|
int allowedChoices = GetMaxPowerCount(oPC, nClass);
|
|
int alreadyChosen = GetPowerCount(oPC, nClass);
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
|
|
int i = 0;
|
|
for (i = 0; i < totalChoices; i++)
|
|
{
|
|
int spellbookId = JsonGetInt(JsonArrayGet(choices, i));
|
|
int spellId = StringToInt(Get2DACache(sFile, "SpellID", spellbookId));
|
|
//if the power is a expanded knowledge choice, don't count it
|
|
if (!IsSpellInExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, spellId, oPC)
|
|
&& !IsSpellInExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, spellId, oPC))
|
|
remaining++;
|
|
}
|
|
remaining = (allowedChoices - remaining - alreadyChosen);
|
|
|
|
// if this is true we count expanded knowledge choices
|
|
if (extra)
|
|
{
|
|
if (GetHasFeat(FEAT_EXPANDED_KNOWLEDGE_1, oPC) && (chosenCircle <= (maxLevel-1)))
|
|
{
|
|
int totalExp = GetMaxPowerCount(oPC, POWER_LIST_EXP_KNOWLEDGE);
|
|
json expChoices = GetExpandedChoicesList(nClass, oPC);
|
|
int choicesCount = JsonGetLength(JsonObjectKeys(expChoices));
|
|
remaining += (totalExp - choicesCount);
|
|
}
|
|
if (GetHasFeat(FEAT_EPIC_EXPANDED_KNOWLEDGE_1, oPC))
|
|
{
|
|
int totalExp = GetMaxPowerCount(oPC, POWER_LIST_EPIC_EXP_KNOWLEDGE);
|
|
json expChoices = GetEpicExpandedChoicesList(nClass, oPC);
|
|
int choicesCount = JsonGetLength(JsonObjectKeys(expChoices));
|
|
remaining += (totalExp - choicesCount);
|
|
}
|
|
}
|
|
|
|
return remaining;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Initiators ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
json GetDisciplineInfoObject(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json disciplineInfo = GetLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(nClass));
|
|
if (disciplineInfo == JsonNull())
|
|
disciplineInfo = JsonObject();
|
|
else
|
|
return disciplineInfo;
|
|
|
|
int chosenClass = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR);
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
|
|
//if this is not the chosen class then we do not have a chosen spell list
|
|
// need to go through the class's 2da and check if you know the spell or not.
|
|
if (nClass != chosenClass)
|
|
{
|
|
int totalSpells = Get2DARowCount(sFile);
|
|
|
|
int i;
|
|
for (i = 0; i < totalSpells; i++)
|
|
{
|
|
int featId = StringToInt(Get2DACache(sFile, "FeatID", i));
|
|
if (featId && GetHasFeat(featId, oPC, TRUE))
|
|
disciplineInfo = AddSpellDisciplineInfo(sFile, i, disciplineInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
json chosenMans = GetChosenSpellListObject(nClass, oPC);
|
|
|
|
json circles = JsonObjectKeys(chosenMans);
|
|
int totalCircles = JsonGetLength(circles);
|
|
|
|
int i;
|
|
for (i = 0; i < totalCircles; i++)
|
|
{
|
|
string currentCircle = JsonGetString(JsonArrayGet(circles, i));
|
|
json currentList = JsonObjectGet(chosenMans, currentCircle);
|
|
int totalSpells = JsonGetLength(currentList);
|
|
|
|
int y;
|
|
for (y = 0; y < totalSpells; y++)
|
|
{
|
|
int spellbookId = JsonGetInt(JsonArrayGet(currentList, y));
|
|
disciplineInfo = AddSpellDisciplineInfo(sFile, spellbookId, disciplineInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(nClass), disciplineInfo);
|
|
return disciplineInfo;
|
|
}
|
|
|
|
int IsRequiredForOtherManeuvers(int nClass, int prereq, string discipline, object oPC=OBJECT_SELF)
|
|
{
|
|
json discInfo = GetDisciplineInfoObject(nClass, oPC);
|
|
|
|
int total = 0;
|
|
|
|
// loop through each prereq level and add up it's totals (ie how many maneuevrs
|
|
// do we know with 0,1,2...,n prereqs.
|
|
int i;
|
|
for (i = 0; i <= prereq; i++)
|
|
{
|
|
|
|
json currDisc = JsonObjectGet(discInfo, discipline);
|
|
string discKey = ("Prereq_" + IntToString(i));
|
|
int currentDiscPrereq = JsonGetInt(JsonObjectGet(currDisc, discKey));
|
|
total += currentDiscPrereq;
|
|
}
|
|
|
|
// then from above the given prereq check if we have any prereq maneuevers taken
|
|
for (i = (prereq+1); i < NUI_LEVEL_UP_MANEUVER_PREREQ_LIMIT; i++)
|
|
{
|
|
json currDisc = JsonObjectGet(discInfo, discipline);
|
|
string discKey = ("Prereq_" + IntToString(i));
|
|
json discPrereq = JsonObjectGet(currDisc, discKey);
|
|
if (discPrereq != JsonNull())
|
|
{
|
|
// if we do take the total and subtract by one, if it is lower than
|
|
// than the prereq needed, it is required
|
|
if (total - 1 < i)
|
|
return TRUE;
|
|
|
|
// otherwise add how many we have and move up and keep trying.
|
|
int currentDiscPrereq = JsonGetInt(discPrereq);
|
|
total += currentDiscPrereq;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int HasPreRequisitesForManeuver(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int prereqs = StringToInt(Get2DACache(sFile, "Prereqs", spellbookId));
|
|
if (!prereqs)
|
|
return TRUE;
|
|
string discipline = Get2DACache(sFile, "Discipline", spellbookId);
|
|
json discInfo = GetDisciplineInfoObject(nClass, oPC);
|
|
json chosenDisc = JsonObjectGet(discInfo, discipline);
|
|
if (chosenDisc != JsonNull())
|
|
{
|
|
int nManCount = JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
if (nManCount >= prereqs)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int GetMaxInitiatorCircle(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
int initiatorLevel = GetInitiatorLevel(oPC, nClass);
|
|
// initiators learn by ceiling(classLevel)
|
|
int highestCircle = (initiatorLevel + 1) / 2;
|
|
if (highestCircle > 9)
|
|
return 9;
|
|
return highestCircle;
|
|
}
|
|
|
|
int GetRemainingManeuverChoices(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json discInfo = GetDisciplineInfoObject(nClass, oPC);
|
|
|
|
json jManAmount = JsonObjectGet(discInfo, NUI_LEVEL_UP_MANEUVER_TOTAL);
|
|
int nManAmount = 0;
|
|
if (jManAmount != JsonNull())
|
|
nManAmount = JsonGetInt(jManAmount);
|
|
|
|
int maxAmount = GetMaxManeuverCount(oPC, nClass, MANEUVER_TYPE_MANEUVER);
|
|
|
|
return maxAmount - nManAmount;
|
|
}
|
|
|
|
int GetRemainingStanceChoices(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json discInfo = GetDisciplineInfoObject(nClass, oPC);
|
|
|
|
json jStanceAmount = JsonObjectGet(discInfo, NUI_LEVEL_UP_STANCE_TOTAL);
|
|
int nStanceAmount = 0;
|
|
if (jStanceAmount != JsonNull())
|
|
nStanceAmount = JsonGetInt(jStanceAmount);
|
|
|
|
int maxAmount = GetMaxManeuverCount(oPC, nClass, MANEUVER_TYPE_STANCE);
|
|
|
|
return maxAmount - nStanceAmount;
|
|
}
|
|
|
|
int IsAllowedDiscipline(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
// logic carried over from private function in discipline inc functions
|
|
// uses bitwise matching to tell if the discipline is allowed or not
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int discipline = StringToInt(Get2DACache(sFile, "Discipline", spellbookId));
|
|
|
|
int nOverride = GetPersistantLocalInt(oPC, "AllowedDisciplines");
|
|
if(nOverride == 0)
|
|
{
|
|
switch(nClass)
|
|
{
|
|
case CLASS_TYPE_CRUSADER: nOverride = 322; break;//DISCIPLINE_DEVOTED_SPIRIT + DISCIPLINE_STONE_DRAGON + DISCIPLINE_WHITE_RAVEN
|
|
case CLASS_TYPE_SWORDSAGE: nOverride = 245; break;//DISCIPLINE_DESERT_WIND + DISCIPLINE_DIAMOND_MIND + DISCIPLINE_SETTING_SUN + DISCIPLINE_SHADOW_HAND + DISCIPLINE_STONE_DRAGON + DISCIPLINE_TIGER_CLAW
|
|
case CLASS_TYPE_WARBLADE: nOverride = 460; break;//DISCIPLINE_DIAMOND_MIND + DISCIPLINE_IRON_HEART + DISCIPLINE_STONE_DRAGON + DISCIPLINE_TIGER_CLAW + DISCIPLINE_WHITE_RAVEN
|
|
}
|
|
}
|
|
return nOverride & discipline;
|
|
}
|
|
|
|
int IsRequiredForToBPRCClass(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
int currentClassPos = 1;
|
|
// loop through all the classes and look for a PRC
|
|
while (currentClassPos)
|
|
{
|
|
int currentClass = GetClassByPosition(currentClassPos, oPC);
|
|
// if we reached a non existant class, we reached the end.
|
|
if (currentClass != CLASS_TYPE_INVALID)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
int discipline = StringToInt(Get2DACache(sFile, "Discipline", spellbookId));
|
|
|
|
// check if the class is a ToB PRC Class and if the current spell's
|
|
// discipline is used for it.
|
|
int isUsed = FALSE;
|
|
if (currentClass == CLASS_TYPE_DEEPSTONE_SENTINEL
|
|
&& (discipline == DISCIPLINE_STONE_DRAGON))
|
|
isUsed = TRUE;
|
|
if (currentClass == CLASS_TYPE_BLOODCLAW_MASTER
|
|
&& (discipline == DISCIPLINE_TIGER_CLAW))
|
|
isUsed = TRUE;
|
|
if (currentClass == CLASS_TYPE_RUBY_VINDICATOR
|
|
&& (discipline == DISCIPLINE_DEVOTED_SPIRIT))
|
|
isUsed = TRUE;
|
|
if (currentClass == CLASS_TYPE_JADE_PHOENIX_MAGE)
|
|
isUsed = TRUE;
|
|
if (currentClass == CLASS_TYPE_MASTER_OF_NINE)
|
|
isUsed = TRUE;
|
|
if (currentClass == CLASS_TYPE_ETERNAL_BLADE
|
|
&& (discipline == DISCIPLINE_DEVOTED_SPIRIT
|
|
|| discipline == DISCIPLINE_DIAMOND_MIND))
|
|
isUsed = TRUE;
|
|
if (currentClass == CLASS_TYPE_SHADOW_SUN_NINJA
|
|
&& (discipline == DISCIPLINE_SETTING_SUN
|
|
|| discipline == DISCIPLINE_SHADOW_HAND))
|
|
isUsed = TRUE;
|
|
|
|
// if any of the above was true than we need to check if this spell
|
|
// is required for a PRC
|
|
if (isUsed)
|
|
{
|
|
// get the discipline info for all BladeMagic classes if we have them.
|
|
json discInfo = GetDisciplineInfoObject(nClass, oPC);
|
|
json classDisc2Info = JsonObject();
|
|
json classDisc3Info = JsonObject();
|
|
if (nClass == CLASS_TYPE_SWORDSAGE)
|
|
{
|
|
if (GetLevelByClass(CLASS_TYPE_WARBLADE, oPC))
|
|
classDisc2Info = GetDisciplineInfoObject(CLASS_TYPE_WARBLADE, oPC);
|
|
if (GetLevelByClass(CLASS_TYPE_CRUSADER, oPC))
|
|
classDisc3Info = GetDisciplineInfoObject(CLASS_TYPE_CRUSADER, oPC);
|
|
}
|
|
if (nClass == CLASS_TYPE_CRUSADER)
|
|
{
|
|
if (GetLevelByClass(CLASS_TYPE_WARBLADE, oPC))
|
|
classDisc2Info = GetDisciplineInfoObject(CLASS_TYPE_WARBLADE, oPC);
|
|
if (GetLevelByClass(CLASS_TYPE_SWORDSAGE, oPC))
|
|
classDisc3Info = GetDisciplineInfoObject(CLASS_TYPE_SWORDSAGE, oPC);
|
|
}
|
|
if (nClass == CLASS_TYPE_WARBLADE)
|
|
{
|
|
if (GetLevelByClass(CLASS_TYPE_CRUSADER, oPC))
|
|
classDisc2Info = GetDisciplineInfoObject(CLASS_TYPE_CRUSADER, oPC);
|
|
if (GetLevelByClass(CLASS_TYPE_SWORDSAGE, oPC))
|
|
classDisc3Info = GetDisciplineInfoObject(CLASS_TYPE_SWORDSAGE, oPC);
|
|
}
|
|
|
|
// Time to begin checking the PRCs
|
|
// this should follow the same logic as here
|
|
// https://gitea.raptio.us/Jaysyn/PRC8/src/commit/797442d3da7c9c8e1fcf585b97e2ff1cbe56045b/nwn/nwnprc/trunk/scripts/prc_prereq.nss#L991
|
|
|
|
// Check Deepstone Sentinel
|
|
if (currentClass == CLASS_TYPE_DEEPSTONE_SENTINEL)
|
|
{
|
|
// we need to look for 2 Stone Dragon Maneuvers and 1 Stone Dragon
|
|
// Stance. So add up the other MagicBlade classes and see if it is satisfied.
|
|
int stoneDMan, stoneDStance = 0;
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stoneDMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
stoneDStance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
if (stoneDMan >= 2 && stoneDStance >= 1)
|
|
return FALSE;
|
|
}
|
|
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stoneDMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
stoneDStance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
if (stoneDMan >= 2 && stoneDStance >= 1)
|
|
return FALSE;
|
|
}
|
|
// if it still isn't satisfied than the current class is required
|
|
// for it to exist. Check to see if it is safe to remove the maneuever
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stoneDMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
stoneDStance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
int type = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
//if the current maneuver is a stance, check to see if it is safe to remove
|
|
if (type == MANEUVER_TYPE_STANCE)
|
|
{
|
|
if (stoneDStance - 1 >= 1)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// if it is not a stance we can just check the maneuevers in general
|
|
if (stoneDMan - 1 >= 2)
|
|
return FALSE;
|
|
}
|
|
|
|
// this maneuver is required and should not be removed.
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// Check Bloodclaw Master
|
|
if (currentClass == CLASS_TYPE_BLOODCLAW_MASTER)
|
|
{
|
|
// bloodclaw needs 3 Tiger Claw maneuevers
|
|
int tigerCMan = 0;
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
tigerCMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
tigerCMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
if (tigerCMan >= 3)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
tigerCMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
tigerCMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
if (tigerCMan >= 3)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
tigerCMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
tigerCMan += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
if (tigerCMan-1 >= 3)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (currentClass == CLASS_TYPE_RUBY_VINDICATOR)
|
|
{
|
|
// Ruby Vindicator needs 1 stance and 1 maneuever from Devoted Spirit
|
|
int stance = 0;
|
|
int maneuver = 0;
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
maneuver += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
if (stance >= 1 && maneuver >= 1)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
maneuver += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
if (stance >= 1 && maneuver >= 1)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
maneuver += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
int type = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
if (type == MANEUVER_TYPE_STANCE)
|
|
{
|
|
if (stance - 1 >= 1)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
if (maneuver - 1 >= 1)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (currentClass == CLASS_TYPE_JADE_PHOENIX_MAGE)
|
|
{
|
|
// Jade Phoenix needs 1 stance and 2 maneuvers of any type
|
|
int stance = 0;
|
|
int maneuver = 0;
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
maneuver += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
if (stance >= 1 && maneuver >= 2)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
maneuver += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
if (stance >= 1 && maneuver >= 2)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
stance += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
maneuver += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
int type = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
if (type == MANEUVER_TYPE_STANCE)
|
|
{
|
|
if ((stance - 1 >= 1) && (maneuver -1 >= 2))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
if (maneuver - 1 >= 2)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (currentClass == CLASS_TYPE_MASTER_OF_NINE)
|
|
{
|
|
// master of nine needs 1 maneuever from 6 different disciplines
|
|
|
|
int totalDiscCount = 0;
|
|
int currentClassAndDiscUsed = 0;
|
|
int i;
|
|
// loop through each possible discipline
|
|
for (i = 0; i <= 256; i++)
|
|
{
|
|
int found = 0;
|
|
// only disciplines that exist are stored, and only those
|
|
// that are used are stored, so we can loop through and
|
|
// find what disciplines we do or don't know.
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(i));
|
|
if (currentDisc != JsonNull())
|
|
found = 1;
|
|
|
|
if (!found)
|
|
{
|
|
json currentDisc = JsonObjectGet(classDisc3Info, IntToString(i));
|
|
if (currentDisc != JsonNull())
|
|
found = 1;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
json currentDisc = JsonObjectGet(discInfo, IntToString(i));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
if (i == discipline)
|
|
currentClassAndDiscUsed = 1;
|
|
found = 1;
|
|
}
|
|
}
|
|
|
|
totalDiscCount += found;
|
|
}
|
|
// if we have more maneuevers than 6, it is not required
|
|
if (totalDiscCount > 6)
|
|
return FALSE;
|
|
// however if we have 6 and this discipline was grabbed we need to make sure it is safe to remove
|
|
if (currentClassAndDiscUsed)
|
|
{
|
|
// if we were to remove this discipline and it is 5 or less total disciplines we have now
|
|
// it is important
|
|
if (totalDiscCount - 1 >= 6)
|
|
return FALSE;
|
|
json currentDisc = JsonObjectGet(discInfo, IntToString(discipline));
|
|
int stance = JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
int maneuver = JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// if we were to remove this discipline and are left with no more than
|
|
// this was important and it can't be removed
|
|
if ((stance + maneuver - 1) >= 1)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (currentClass == CLASS_TYPE_ETERNAL_BLADE)
|
|
{
|
|
//Eternal blade 2 Devoted Spirits OR 2 Diamond Mind
|
|
int nTotal = 0;
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(DISCIPLINE_DEVOTED_SPIRIT));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nTotal >= 2)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc2Info, IntToString(DISCIPLINE_DIAMOND_MIND));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nTotal >= 2)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(DISCIPLINE_DEVOTED_SPIRIT));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nTotal >= 2)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(DISCIPLINE_DIAMOND_MIND));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nTotal >= 2)
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(discipline));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
nTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
if ((nTotal - 1) >= 2)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (currentClass == CLASS_TYPE_SHADOW_SUN_NINJA)
|
|
{
|
|
// Shadow Sun Ninja needs 1 lvl2 Setting Sun OR Shadow Hand maneuever
|
|
// 1 Setting Sun maneuver AND 1 Shadow Hand maneuver
|
|
int nLvl2 = 0;
|
|
int shadowHTotal;
|
|
int settingSTotal;
|
|
|
|
json currentDisc = JsonObjectGet(classDisc2Info, IntToString(DISCIPLINE_SHADOW_HAND));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
shadowHTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
shadowHTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_STANCE)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nLvl2 && shadowHTotal && settingSTotal && (shadowHTotal >= 2 || settingSTotal >= 2))
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc2Info, IntToString(DISCIPLINE_SETTING_SUN));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
settingSTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
settingSTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_STANCE)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nLvl2 && shadowHTotal && settingSTotal && (shadowHTotal >= 2 || settingSTotal >= 2))
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc3Info, IntToString(DISCIPLINE_SHADOW_HAND));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
shadowHTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
shadowHTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_STANCE)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nLvl2 && shadowHTotal && settingSTotal && (shadowHTotal >= 2 || settingSTotal >= 2))
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(classDisc2Info, IntToString(DISCIPLINE_SETTING_SUN));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
settingSTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
settingSTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_STANCE)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
// stances here count as a maneuver so we need to count 2
|
|
// to account for that
|
|
if (nLvl2 && shadowHTotal && settingSTotal && (shadowHTotal >= 2 || settingSTotal >= 2))
|
|
return FALSE;
|
|
}
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(DISCIPLINE_SHADOW_HAND));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
shadowHTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
shadowHTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_STANCE)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
}
|
|
currentDisc = JsonObjectGet(discInfo, IntToString(DISCIPLINE_SETTING_SUN));
|
|
if (currentDisc != JsonNull())
|
|
{
|
|
settingSTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_STANCE)));
|
|
settingSTotal += JsonGetInt(JsonObjectGet(currentDisc, IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_STANCE)));
|
|
nLvl2 += JsonGetInt(JsonObjectGet(currentDisc, "Level2_" + IntToString(MANEUVER_TYPE_MANEUVER)));
|
|
}
|
|
int level = StringToInt(Get2DACache(sFile, "Level", spellbookId));
|
|
if (level == 2)
|
|
nLvl2 -= 1;
|
|
if (discipline == DISCIPLINE_SHADOW_HAND)
|
|
shadowHTotal -= 1;
|
|
else
|
|
settingSTotal -= 1;
|
|
|
|
return !(nLvl2 && shadowHTotal && settingSTotal && (shadowHTotal >= 2 || settingSTotal >= 2));
|
|
}
|
|
}
|
|
|
|
currentClassPos += 1;
|
|
}
|
|
else
|
|
currentClassPos = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
json AddSpellDisciplineInfo(string sFile, int spellbookId, json classDisc)
|
|
{
|
|
json classDiscCopy = classDisc;
|
|
int discipline = StringToInt(Get2DACache(sFile, "Discipline", spellbookId));
|
|
int type = StringToInt(Get2DACache(sFile, "Type", spellbookId));
|
|
int level = StringToInt(Get2DACache(sFile, "Level", spellbookId));
|
|
int prereq = StringToInt(Get2DACache(sFile, "Prereqs", spellbookId));
|
|
|
|
json jDisc = JsonObjectGet(classDisc, IntToString(discipline));
|
|
if (jDisc == JsonNull())
|
|
jDisc = JsonObject();
|
|
|
|
string levelKey = "Level" + IntToString(level) + "_" + IntToString(type);
|
|
int nTypeTotal = (JsonGetInt(JsonObjectGet(jDisc, levelKey)) + 1);
|
|
jDisc = JsonObjectSet(jDisc, levelKey, JsonInt(nTypeTotal));
|
|
|
|
nTypeTotal = (JsonGetInt(JsonObjectGet(jDisc, IntToString(type))) + 1);
|
|
jDisc = JsonObjectSet(jDisc, IntToString(type), JsonInt(nTypeTotal));
|
|
|
|
if (type != MANEUVER_TYPE_MANEUVER
|
|
&& type != MANEUVER_TYPE_STANCE)
|
|
{
|
|
levelKey = "Level" + IntToString(level) + "_" + IntToString(MANEUVER_TYPE_MANEUVER);
|
|
nTypeTotal = (JsonGetInt(JsonObjectGet(jDisc, levelKey)) + 1);
|
|
jDisc = JsonObjectSet(jDisc, levelKey, JsonInt(nTypeTotal));
|
|
|
|
nTypeTotal = (JsonGetInt(JsonObjectGet(jDisc, IntToString(MANEUVER_TYPE_MANEUVER))) + 1);
|
|
jDisc = JsonObjectSet(jDisc, IntToString(MANEUVER_TYPE_MANEUVER), JsonInt(nTypeTotal));
|
|
}
|
|
|
|
string prereqKey = "Prereq_" + IntToString(prereq);
|
|
int nPrereqTotal = (JsonGetInt(JsonObjectGet(jDisc, prereqKey)) + 1);
|
|
jDisc = JsonObjectSet(jDisc, prereqKey, JsonInt(nPrereqTotal));
|
|
|
|
if (type == MANEUVER_TYPE_STANCE)
|
|
{
|
|
nTypeTotal = (JsonGetInt(JsonObjectGet(classDisc, NUI_LEVEL_UP_STANCE_TOTAL)) + 1);
|
|
classDiscCopy = JsonObjectSet(classDiscCopy, NUI_LEVEL_UP_STANCE_TOTAL, JsonInt(nTypeTotal));
|
|
}
|
|
else
|
|
{
|
|
nTypeTotal = (JsonGetInt(JsonObjectGet(classDisc, NUI_LEVEL_UP_MANEUVER_TOTAL)) + 1);
|
|
classDiscCopy = JsonObjectSet(classDiscCopy, NUI_LEVEL_UP_MANEUVER_TOTAL, JsonInt(nTypeTotal));
|
|
}
|
|
|
|
return JsonObjectSet(classDiscCopy, IntToString(discipline), jDisc);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Invokers ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
json GetInvokerKnownListObject(int nClass, object oPC=OBJECT_SELF)
|
|
{
|
|
json knownObject = GetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR + IntToString(nClass));
|
|
if (knownObject == JsonNull())
|
|
knownObject = JsonObject();
|
|
else
|
|
return knownObject;
|
|
|
|
string sFile = GetAMSKnownFileName(nClass);
|
|
int totalRows = Get2DARowCount(sFile);
|
|
int maxInvocLevel = StringToInt(Get2DACache(sFile, "MaxInvocationLevel", totalRows-1));
|
|
json previousInvocList = JsonObject();
|
|
|
|
int i;
|
|
for (i = 1; i <= maxInvocLevel; i++)
|
|
{
|
|
previousInvocList = JsonObjectSet(previousInvocList, IntToString(i), JsonInt(0));
|
|
}
|
|
|
|
for (i = 0; i < totalRows; i++)
|
|
{
|
|
int maxInvocation = StringToInt(Get2DACache(sFile, "MaxInvocationLevel", i));
|
|
int invocationKnown = StringToInt(Get2DACache(sFile, "InvocationKnown", i));
|
|
json invocList = previousInvocList;
|
|
|
|
int previousInvocTotal = 0;
|
|
if (i > 0)
|
|
previousInvocTotal = StringToInt(Get2DACache(sFile, "InvocationKnown", i-1));
|
|
int previousInvocAmount = JsonGetInt(JsonObjectGet(previousInvocList, IntToString(maxInvocation)));
|
|
int currentInvocationAmount = (invocationKnown - previousInvocTotal + previousInvocAmount);
|
|
|
|
invocList = JsonObjectSet(invocList, IntToString(maxInvocation), JsonInt(currentInvocationAmount));
|
|
knownObject = JsonObjectSet(knownObject, IntToString(i+1), invocList);
|
|
previousInvocList = invocList;
|
|
}
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR + IntToString(nClass), knownObject);
|
|
return knownObject;
|
|
}
|
|
|
|
int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE)
|
|
{
|
|
int remaining = 0;
|
|
int nLevel = GetInvokerLevel(oPC, nClass);
|
|
|
|
json knownObject = GetInvokerKnownListObject(nClass, oPC);
|
|
json chosenInv = GetChosenSpellListObject(nClass, oPC);
|
|
json currentLevelKnown = JsonObjectGet(knownObject, IntToString(nLevel));
|
|
|
|
int totalCircles = JsonGetLength(JsonObjectKeys(currentLevelKnown));
|
|
|
|
// logic goes we are given a set amount of invocations at each circle. We can
|
|
// take from a circle above us, but not below us. So we need to make sure
|
|
// we have a legal amount of choices
|
|
int i;
|
|
for (i = 1; i <= totalCircles; i++)
|
|
{
|
|
int currentChosen = 0;
|
|
json chosenSpells = JsonObjectGet(chosenInv, IntToString(i));
|
|
if (chosenSpells != JsonNull())
|
|
{
|
|
int totalChosen = JsonGetLength(chosenSpells);
|
|
int j;
|
|
for (j = 0; j < totalChosen; j++)
|
|
{
|
|
int spellbookId = JsonGetInt(JsonArrayGet(chosenSpells, j));
|
|
// only count non extra invocation choices
|
|
if (!IsExtraChoiceInvocation(nClass, spellbookId, oPC))
|
|
currentChosen += 1;
|
|
}
|
|
}
|
|
|
|
int allowedAtCircle = JsonGetInt(JsonObjectGet(currentLevelKnown, IntToString(i)));
|
|
|
|
remaining = (allowedAtCircle - currentChosen + remaining);
|
|
// if the circle is below the chosen circle and we have a positive remaining,
|
|
// we set it to 0 because we cannot use lower circle spells on higher circle.
|
|
// however if thge value is negative then we carry it over because we
|
|
// have a deficit and need to account for it by using the spells of the
|
|
// next circle.
|
|
if (i < chosenCircle && remaining > 0)
|
|
remaining = 0;
|
|
}
|
|
|
|
|
|
// count extra and epic invocation choices
|
|
if (extra)
|
|
{
|
|
string sFile = GetAMSKnownFileName(nClass);
|
|
int maxCircle = StringToInt(Get2DACache(sFile, "MaxInvocationLevel", nLevel-1));
|
|
|
|
if (GetHasFeat(FEAT_EXTRA_INVOCATION_I, oPC) && (chosenCircle <= (maxCircle-1)))
|
|
{
|
|
int totalExp = GetMaxInvocationCount(oPC, INVOCATION_LIST_EXTRA);
|
|
json expChoices = GetExpandedChoicesList(nClass, oPC);
|
|
int choicesCount = JsonGetLength(JsonObjectKeys(expChoices));
|
|
remaining += (totalExp - choicesCount);
|
|
}
|
|
if (GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_I, oPC))
|
|
{
|
|
int totalExp = GetMaxInvocationCount(oPC, INVOCATION_LIST_EXTRA_EPIC);
|
|
json expChoices = GetEpicExpandedChoicesList(nClass, oPC);
|
|
int choicesCount = JsonGetLength(JsonObjectKeys(expChoices));
|
|
remaining += (totalExp - choicesCount);
|
|
}
|
|
}
|
|
|
|
return remaining;
|
|
}
|
|
|
|
int IsExtraChoiceInvocation(int nClass, int spellbookId, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
string spellId = Get2DACache(sFile, "SpellID", spellbookId);
|
|
json extraChoices = GetExpandedChoicesList(nClass, oPC);
|
|
json chosenSpell = JsonObjectGet(extraChoices, spellId);
|
|
if (chosenSpell != JsonNull())
|
|
return TRUE;
|
|
|
|
extraChoices = GetEpicExpandedChoicesList(nClass, oPC);
|
|
chosenSpell = JsonObjectGet(extraChoices, spellId);
|
|
if (chosenSpell != JsonNull())
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Truenamer ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int GetRemainingTruenameChoices(int nClass, int nType, object oPC=OBJECT_SELF)
|
|
{
|
|
string sFile = GetClassSpellbookFile(nClass);
|
|
json chosenSpells = GetChosenSpellListObject(nClass, oPC);
|
|
json circles = JsonObjectKeys(chosenSpells);
|
|
int totalCircles = JsonGetLength(circles);
|
|
|
|
int remainingChoices = 0;
|
|
|
|
int i;
|
|
for (i = 0; i < totalCircles; i++)
|
|
{
|
|
json spellList = JsonObjectGet(chosenSpells, JsonGetString(JsonArrayGet(circles, i)));
|
|
if (spellList != JsonNull())
|
|
{
|
|
int totalChoices = JsonGetLength(spellList);
|
|
|
|
int j;
|
|
for (j = 0; j < totalChoices; j++)
|
|
{
|
|
int spellbookId = JsonGetInt(JsonArrayGet(spellList, j));
|
|
int lexicon = StringToInt(Get2DACache(sFile, "Lexicon", spellbookId));
|
|
// -1 means count all lexicons
|
|
if (nType == -1 || lexicon == nType)
|
|
remainingChoices += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int maxChoices;
|
|
// if -1 we count all lexicons to get total remaining
|
|
if (nType == -1)
|
|
maxChoices = (GetMaxUtteranceCount(oPC, nClass, LEXICON_CRAFTED_TOOL)
|
|
+ GetMaxUtteranceCount(oPC, nClass, LEXICON_EVOLVING_MIND)
|
|
+ GetMaxUtteranceCount(oPC, nClass, LEXICON_PERFECTED_MAP));
|
|
else
|
|
maxChoices = GetMaxUtteranceCount(oPC, nClass, nType);
|
|
return (maxChoices - remainingChoices);
|
|
}
|
|
|
|
int GetLexiconCircleKnownAtLevel(int nLevel, int nType)
|
|
{
|
|
|
|
string sFile = "cls_true_maxlvl";
|
|
|
|
string columnName;
|
|
if (nType == LEXICON_EVOLVING_MIND)
|
|
columnName = "EvolvingMind";
|
|
else if (nType == LEXICON_CRAFTED_TOOL)
|
|
columnName = "CraftedTool";
|
|
else
|
|
columnName = "PerfectedMap";
|
|
|
|
return StringToInt(Get2DACache(sFile, columnName, nLevel-1));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
/// ///
|
|
/// Archivist ///
|
|
/// ///
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
json GetArchivistNewSpellsList(object oPC=OBJECT_SELF)
|
|
{
|
|
json retValue = GetLocalJson(oPC, NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR);
|
|
if (retValue == JsonNull())
|
|
retValue = JsonArray();
|
|
else
|
|
return retValue;
|
|
|
|
SetLocalJson(oPC, NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR, retValue);
|
|
return retValue;
|
|
}
|