2025/11/20 Update

Updated epic swashbucker tlk.
Added notes on Martial Study.
Updated Shadow Servant to scale with Shadow Master level, per PnP.
Made Disciple of Baalzebul's CHA boost intrinsic.
Swarm of Arrows is an Eldritch Knight epic bonus feat.
Epic eldritch theurge is 11th level, not 21st level.
Updated Shadowdancer weapon proficiencies.
WP: Scythe was a usable feat for Warblade.
Gaseous Form added to iprp_spells.
Set Favoured Soul's Regen X Wounds spells to the correct spell level.
Updated Twinfiend to not suck.
Tweaked GetMaxPossibleHP for Undead & Constructs.
Updated PRCIsFlying() for newer CEP2 wings.
More fixes and updated for NUI levelup menu. (@Rakiov)
Added support for de-leveling AMS classes (@Rakiov)
Zakya Rakshasa have a claw & bite attack.
Added check to end grapples after target death.
Removed debug message in GetHighestSpellAvailableByDescriptor()
Monsters won't summon uncontrolled undead.
Added Signal Event to Luminous Armor.
Corrected Signal Event on Shield of Faith.
This commit is contained in:
Jaysyn904
2025-11-20 21:36:52 -05:00
parent 8e82907d07
commit 80070703b4
54 changed files with 26753 additions and 20944 deletions

View File

@@ -111,7 +111,7 @@ object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,loc
SetLocalObject(oMaster, IntToString(nAssociateType)+"oHench"+IntToString(nCount), oSummon);
SetLocalInt(oSummon, "iAssocNth", nCount);
SetAssociateState(NW_ASC_HAVE_MASTER,TRUE,oSummon);
SetAssociateState(NW_ASC_HAVE_MASTER, TRUE, oSummon);
SetAssociateState(NW_ASC_DISTANCE_2_METERS);
SetAssociateState(NW_ASC_DISTANCE_4_METERS, FALSE);
SetAssociateState(NW_ASC_DISTANCE_6_METERS, FALSE);

View File

@@ -18,6 +18,8 @@
//:://////////////////////////////////////////////
#include "nw_inc_gff"
#include "inc_debug"
#include "prc_inc_racial"
#include "prc_inc_nwscript"
//::---------------------------------------------|
@@ -30,6 +32,8 @@ int GetMaxPossibleHP(object oCreature)
int nMaxHP = 0; // Stores the total maximum hitpoints
int i = 1; // Initialize position for class index
int nConb = GetAbilityModifier(ABILITY_CONSTITUTION, oCreature);
int nRacial = MyPRCGetRacialType(oCreature);
int nSize = PRCGetCreatureSize(oCreature);
// Loop through each class position the creature may have, checking each class in turn
while (TRUE)
@@ -54,8 +58,25 @@ int GetMaxPossibleHP(object oCreature)
i++;
}
if(nRacial == RACIAL_TYPE_CONSTRUCT || nRacial == RACIAL_TYPE_UNDEAD)
{
nConb = 0;
}
nMaxHP += nConb * GetHitDice(oCreature);
if(nRacial == RACIAL_TYPE_CONSTRUCT)
{
if(nSize == CREATURE_SIZE_FINE) nMaxHP += 0;
if(nSize == CREATURE_SIZE_DIMINUTIVE) nMaxHP += 0;
if(nSize == CREATURE_SIZE_TINY) nMaxHP += 0;
if(nSize == CREATURE_SIZE_SMALL) nMaxHP += 10;
if(nSize == CREATURE_SIZE_MEDIUM) nMaxHP += 20;
if(nSize == CREATURE_SIZE_LARGE) nMaxHP += 30;
if(nSize == CREATURE_SIZE_HUGE) nMaxHP += 40;
if(nSize == CREATURE_SIZE_GARGANTUAN) nMaxHP += 60;
}
return nMaxHP;
}
@@ -202,13 +223,44 @@ int json_GetCreatureHD(json jCreature)
json json_RecalcMaxHP(json jCreature, int iHitDieValue)
{
int iHD = json_GetCreatureHD(jCreature);
int iCON = json_GetCONValue(jCreature);
int iMod = GetAbilityModFromValue(iCON);
//:: Retrieve the RacialType field
json jRacialTypeField = JsonObjectGet(jCreature, "Race");
int nRacialType = JsonGetInt(jRacialTypeField);
//:: Retrieve the CreatureSize from the creature appearance field
json jAppearanceField = JsonObjectGet(jCreature, "Appearance_Type");
int nAppearance = JsonGetInt(jAppearanceField);
int nSize = StringToInt(Get2DAString("appearance", "SizeCategory", nAppearance));
//CEP adds other sizes, take them into account too
if(nSize == 20)
nSize = CREATURE_SIZE_DIMINUTIVE;
else if(nSize == 21)
nSize = CREATURE_SIZE_FINE;
else if(nSize == 22)
nSize = CREATURE_SIZE_GARGANTUAN;
else if(nSize == 23)
nSize = CREATURE_SIZE_COLOSSAL;
int iNewMaxHP = (iHitDieValue * iHD);
int nConBonusHP = iMod * iHD;
int iNewMaxHP = (iHitDieValue * iHD); /* nConBonusHP */
if(nRacialType == RACIAL_TYPE_CONSTRUCT)
{
if(nSize == CREATURE_SIZE_FINE) iNewMaxHP += 0;
if(nSize == CREATURE_SIZE_DIMINUTIVE) iNewMaxHP += 0;
if(nSize == CREATURE_SIZE_TINY) iNewMaxHP += 0;
if(nSize == CREATURE_SIZE_SMALL) iNewMaxHP += 10;
if(nSize == CREATURE_SIZE_MEDIUM) iNewMaxHP += 20;
if(nSize == CREATURE_SIZE_LARGE) iNewMaxHP += 30;
if(nSize == CREATURE_SIZE_HUGE) iNewMaxHP += 40;
if(nSize == CREATURE_SIZE_GARGANTUAN) iNewMaxHP += 60;
}
if(DEBUG) DoDebug("prc_inc_json >> json_RecalcMaxHP | New MaxHP is: "+IntToString(iNewMaxHP)+ ".");
//jCreature = GffReplaceShort(jCreature, "MaxHitPoints", iNewMaxHP);
jCreature = GffReplaceShort(jCreature, "MaxHitPoints", iNewMaxHP);
jCreature = GffReplaceShort(jCreature, "CurrentHitPoints", iNewMaxHP);
jCreature = GffReplaceShort(jCreature, "HitPoints", iNewMaxHP);
@@ -425,7 +477,7 @@ json json_AddFeatsFromCreatureVars(json jCreature, int nOriginalHD)
if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Original feat count: " + IntToString(nOriginalFeatCount));
int nAdded = 0;
int i = 1;
int i = 0;
int nMaxIterations = 100; // Safety valve
int nIterations = 0;

View File

@@ -277,6 +277,32 @@ void ClearNaturalWeapons(object oPC)
array_delete(oPC, ARRAY_NAT_PRI_WEAP_ATTACKS);
}
/**
* @brief Adds a natural primary weapon to a creature (PC/NPC).
*
* This function manages a creature's natural primary weapons by storing their
* resource references and attack counts in persistent arrays. If the weapon
* being added is the first natural weapon, it may automatically become the
* creature's active primary natural weapon, unless the creature is a Monk or
* Brawler. Optionally, the weapon can be forced to become the active primary
* weapon regardless of class.
*
* @param oPC The creature object to which the natural weapon will be added.
* @param sResRef The resource reference string of the natural weapon.
* @param nCount (Optional) The number of attacks this natural weapon provides.
* Default is 1.
* @param nForceUse (Optional) If TRUE, forces this weapon to become the active
* primary natural weapon regardless of the creature's class.
* Default is FALSE.
*
* @details
* - Creates persistent arrays for weapon references and attack counts if they
* do not already exist.
* - Checks if the weapon is already present to avoid duplicates.
* - Adds the weapon and attack count to the arrays.
* - Sets the primary natural weapon index to this weapon if it is the first
* natural weapon added, unless the creature is a Monk or Brawler.
*/
void AddNaturalPrimaryWeapon(object oPC, string sResRef, int nCount = 1, int nForceUse = FALSE)
{
int nFirstNaturalWeapon = FALSE;

View File

@@ -363,8 +363,11 @@ int PRCIsFlying(object oCreature)
bFlying = TRUE;
}
if(!bFlying
&& ((nWings > 0 && nWings < 79) || nWings == 90))//CEP and Project Q wing models
bFlying = TRUE;
&& ((nWings > 0 && nWings < 79)
|| (nWings > 1959 && nWings < 1962)
|| (nWings > 1962 && nWings < 1966)
|| nWings == 90))//CEP and Project Q wing models
bFlying = TRUE;
if (GetHasSpellEffect(MOVE_SH_BALANCE_SKY, oCreature))
bFlying = TRUE;

View File

@@ -76,7 +76,7 @@
/* This variable MUST be updated with every new version of the PRC!!! */
const string PRC_VERSION = "PRC8 4.69";
const string PRC_VERSION = "PRC8 4.70";
/* This variable MUST be updated every time 'assemble_spellbooks.bat' is run!!! */

View File

@@ -672,13 +672,13 @@ void UnarmedFists(object oCreature)
// Sacred Fists who break their code get no benefits.
if (GetHasFeat(FEAT_SF_CODE,oCreature)) iSacFist = 0;
// The monk adds all these classes.
int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja;
int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja;
// Ascetic Stalker
if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature))
iMonkEq += iAscetic;
iMonkEq += iAscetic;
// Determine the type of damage the character should do.
string sWeapType;

View File

@@ -3,6 +3,7 @@
#include "psi_inc_psifunc"
#include "inc_lookups"
#include "nw_inc_nui"
#include "tob_inc_tobfunc"
//
// GetCurrentSpellLevel
@@ -153,8 +154,53 @@ json GreyOutButton(json jButton, float w, float h);
//
json CreateGreyOutRectangle(float w, float h);
//
// 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);
void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0);
void CallSpellUnlevelScript(object oPC, int nClass, int nLevel);
void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF);
void RemoveIPFeat(object oPC, int ipFeatID);
void CallSpellUnlevelScript(object oPC, int nClass, int nLevel)
{
SetScriptParam("UnLevel_ClassChoice", IntToString(nClass));
SetScriptParam("UnLevel_LevelChoice", IntToString(nLevel));
ExecuteScript("prc_unlvl_script", oPC);
}
void RemoveIPFeat(object oPC, int ipFeatID)
{
object oSkin = GetPCSkin(oPC);
itemproperty ipTest = GetFirstItemProperty(oSkin);
while(GetIsItemPropertyValid(ipTest))
{
// Check if the itemproperty is a bonus feat that has been marked for removal
if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT)
{
if (GetItemPropertySubType(ipTest) == ipFeatID)
{
if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest));
// If so, remove it
RemoveItemProperty(oSkin, ipTest);
}
}
ipTest = GetNextItemProperty(oSkin);
}
}
int GetCurrentSpellLevel(int nClass, int nLevel)
{
@@ -528,3 +574,39 @@ void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF)
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR);
}
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;
}

View File

@@ -98,21 +98,9 @@ void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF);
//
// CloseNUILevelUpWindow
// Closes the NUI Level Up Window if its open
// setting reset to 1 will make it clear the entire cache as if the NUI was never opened
//
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);
void CloseNUILevelUpWindow(object oPC=OBJECT_SELF, int reset=0);
//
// GetRemainingSpellChoices
@@ -432,7 +420,7 @@ json GetChosenReplaceListObject(object oPC=OBJECT_SELF);
// Returns:
// int:Boolean TRUE if the spell is a expanded knowledge spell, FALSE otherwise
//
int IsExpKnowledgePower(int nClass, int spellbookId);
int IsExpKnowledgePower(int nClass, int spellbookId, object oPC=OBJECT_SELF);
//
// GetExpKnowledgePowerListRequired
@@ -861,7 +849,7 @@ void AddSpellToChosenList(int nClass, int spellbookId, int spellCircle, object o
// 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)
if (IsExpKnowledgePower(nClass, spellbookId, oPC)
|| GetRemainingPowerChoices(nClass, spellCircle, oPC, FALSE) == 0)
{
string sFile = GetClassSpellbookFile(nClass);
@@ -975,8 +963,7 @@ void RemoveSpellFromChosenList(int nClass, int spellbookId, int spellCircle, obj
// 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--)
for (i = -1; i >= -2; i--)
for (i == -1; i >= -2; i--)
{
json expList = (i == -1) ? GetExpandedChoicesList(nClass, oPC) :
GetEpicExpandedChoicesList(nClass, oPC);
@@ -1062,53 +1049,20 @@ void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF)
// 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)
void CloseNUILevelUpWindow(object oPC=OBJECT_SELF, int reset=0)
{
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);
if (reset)
{
ClearLevelUpNUICaches(currentClass, oPC);
}
int nPreviousToken = NuiFindWindow(oPC, NUI_LEVEL_UP_WINDOW_ID);
if (nPreviousToken != 0)
{
@@ -1127,7 +1081,7 @@ int ShouldSpellButtonBeEnabled(int nClass, int circleLevel, int spellbookId, obj
// 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))
if (IsExpKnowledgePower(nClass, spellbookId, oPC))
{
int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC)
+ GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC);
@@ -1540,7 +1494,7 @@ int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF
// 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 > 7)
if (nClass == CLASS_TYPE_FAVOURED_SOUL && circleLevel == 0 && totalSpellsKnown > 6)
totalSpellsKnown = 7;
// logic for spont casters
@@ -1555,23 +1509,11 @@ int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF
if (chosenCircle == circleLevel)
SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, remainingChoices);
if (DEBUG) DoDebug("Remaining spell choices is " + IntToString(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);
}
CloseNUILevelUpWindow(oPC); // Close while selected-class var is still set
ClearLevelUpNUICaches(nClass, oPC);
}
/* void FinishLevelUp(int nClass, object oPC=OBJECT_SELF)
{
RemoveSpells(nClass, oPC);
LearnSpells(nClass, oPC);
@@ -1581,7 +1523,7 @@ void FinishLevelUp(int nClass, object oPC=OBJECT_SELF)
SetPersistantLocalInt(oPC, "LastSpellGainLevel", nLevel);
}
ClearLevelUpNUICaches(nClass, oPC);
} */
}
void ClearLevelUpNUICaches(int nClass, object oPC=OBJECT_SELF)
{
@@ -1678,11 +1620,13 @@ void RemoveSpells(int nClass, object oPC=OBJECT_SELF)
{
string sFile = GetClassSpellbookFile(nClass);
string sSpellBook = GetSpellsKnown_Array(nClass);
string spellsAtLevelList = "SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(GetHitDice(oPC));
// remove the spell from the spellbook
array_extract_int(oPC, sSpellBook, nSpellbookID);
array_extract_int(oPC, spellsAtLevelList, nSpellbookID);
// wipe the spell from the player
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID));
WipeSpellFromHide(ipFeatID, oPC);
RemoveIPFeat(oPC, ipFeatID);
}
}
}
@@ -1793,6 +1737,7 @@ void LearnSpells(int nClass, object oPC=OBJECT_SELF)
// get location of persistant storage on the hide
string sSpellbook = GetSpellsKnown_Array(nClass, nSpellLevel);
if (DEBUG) DoDebug("Adding spell " + IntToString(nSpellbookID) + "to " + sSpellbook);
//object oToken = GetHideToken(oPC);
// Create spells known persistant array if it is missing
@@ -1803,13 +1748,25 @@ void LearnSpells(int nClass, object oPC=OBJECT_SELF)
nSize = 0;
}
string spellsAtLevelList = "SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(GetHitDice(oPC));
int spellsAtLevelSize = persistant_array_get_size(oPC, spellsAtLevelList);
if (spellsAtLevelSize < 0)
{
persistant_array_create(oPC, spellsAtLevelList);
spellsAtLevelSize = 0;
}
// set the list of spells learned at this level
string sFile = GetClassSpellbookFile(nClass);
int spellId = StringToInt(Get2DACache(sFile, "SpellID", nSpellbookID));
persistant_array_set_int(oPC, spellsAtLevelList, spellsAtLevelSize, spellId);
if (DEBUG) DoDebug("Adding spells to array " + spellsAtLevelList);
// 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));
@@ -1989,7 +1946,7 @@ void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int n
//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!");
if (DEBUG) DoDebug("Could not find spellID " + IntToString(spellID) + " in the class's spellbook!");
return;
}
}
@@ -2016,10 +1973,7 @@ void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int n
// 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);
RemoveIPFeat(oPC, ipFeatID);
}
json GetSpellIDsKnown(int nClass, object oPC=OBJECT_SELF, int nList=0)
@@ -2102,7 +2056,7 @@ string ReasonForDisabledSpell(int nClass, int spellbookId, object oPC=OBJECT_SEL
// 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))
if (IsExpKnowledgePower(nClass, spellbookId, oPC))
{
int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC)
+ GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC);
@@ -2336,11 +2290,15 @@ json GetChosenReplaceListObject(object oPC=OBJECT_SELF)
////////////////////////////////////////////////////////////////////////////
int IsExpKnowledgePower(int nClass, int spellbookId)
int IsExpKnowledgePower(int nClass, int spellbookId, object oPC=OBJECT_SELF)
{
string sFile = GetClassSpellbookFile(nClass);
int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId));
return isExp;
if (isExp)
return TRUE;
int featId = StringToInt(Get2DACache(sFile, "FeatID", spellbookId));
int isOuterDomain = (featId) ? !CheckPowerPrereqs(featId, oPC) : FALSE;
return isOuterDomain;
}
json GetCurrentPowerList(object oPC=OBJECT_SELF)
@@ -2360,12 +2318,8 @@ 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)
if (!CheckPowerPrereqs(featId, oPC) || isExp)
{
// and we have a expanded knowledge choice left to make then show
// the button
@@ -2374,10 +2328,12 @@ int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF)
int currentCircle = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CIRCLE_VAR);
int choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC);
if (DEBUG) DoDebug("You still have " + IntToString(choicesLeft) + " expanded power choices left!");
if (choicesLeft && (currentCircle <= (maxLevel-1)))
addPower = TRUE;
choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC);
if (choicesLeft)
if (DEBUG) DoDebug("You still have " + IntToString(choicesLeft) + " epic expanded power choices left!");
if (choicesLeft && (currentCircle <= (maxLevel-1)))
addPower = TRUE;
// otherwise don't show the button.
return addPower;
@@ -2397,7 +2353,7 @@ void LearnPowers(int nClass, object oPC=OBJECT_SELF)
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);
AddPowerKnown(oPC, nClass, nSpellbookID, TRUE, GetHitDice(oPC), expKnow);
}
}
@@ -2584,7 +2540,7 @@ int HasPreRequisitesForManeuver(int nClass, int spellbookId, object oPC=OBJECT_S
if (chosenDisc != JsonNull())
{
int nManCount = (JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_MANEUVER)))
+ JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_STANCE))));
+ JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_STANCE))));
if (nManCount >= prereqs)
return TRUE;
}
@@ -3180,13 +3136,18 @@ json GetInvokerKnownListObject(int nClass, object oPC=OBJECT_SELF)
}
SetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR + IntToString(nClass), knownObject);
if (DEBUG) DoDebug("Printing json representation of allowed invocations for class " + IntToString(nClass));
if (DEBUG) DoDebug(JsonDump(knownObject, 2));
return knownObject;
}
int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE)
{
if (DEBUG) DoDebug ("Getting remaining invocation choices at " + IntToString(chosenCircle) + " circle");
int remaining = 0;
int nLevel = GetInvokerLevel(oPC, nClass);
if (nClass == CLASS_TYPE_DRAGON_SHAMAN) nLevel = GetLevelByClass(nClass, oPC);
if (DEBUG) DoDebug("Invoker level is " + IntToString(nLevel));
json knownObject = GetInvokerKnownListObject(nClass, oPC);
json chosenInv = GetChosenSpellListObject(nClass, oPC);
@@ -3214,8 +3175,10 @@ int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJEC
currentChosen += 1;
}
}
if (DEBUG) DoDebug(IntToString(currentChosen) + " incantations chosen at " + IntToString(chosenCircle) + " circle");
int allowedAtCircle = JsonGetInt(JsonObjectGet(currentLevelKnown, IntToString(i)));
if (DEBUG) DoDebug(IntToString(allowedAtCircle) + " incantations allowed at " + IntToString(chosenCircle) + " circle");
remaining = (allowedAtCircle - currentChosen + remaining);
// if the circle is below the chosen circle and we have a positive remaining,

View File

@@ -93,9 +93,8 @@ void GainPowerPoints(object oChar, int nGain, int bCanExceedMax = FALSE, int bIn
* @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar
* after making the modification.
*/
/*
void GainTemporaryPowerPoints(object oChar, int nGain, float fDuration, int bInform = TRUE);
*/
/**
* Decreases the character's current power point count by up to the given
* amount, limited to not going below 0.