Update PRC8 includes

Update PRC8 includes
This commit is contained in:
Jaysyn904 2025-06-24 13:06:41 -04:00
parent d1a68337cd
commit 19e61b7d08
13 changed files with 4030 additions and 350 deletions

View File

@ -876,6 +876,8 @@ void CreateSwitchNameArray()
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_HARM);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_NEUTRALIZE_POISON);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_REMOVE_DISEASE);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIO_UNLEARN);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_UNLEARN_SPELL_MAXNR);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_BIOWARE_DURATION);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_LOCAL);
array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_NO_HOSTILE);

View File

@ -1549,7 +1549,7 @@ const int FEAT_STIGMATA = 3163;
const int FEAT_SERVHEAVEN = 3355;
const int FEAT_RANGED_SMITE = 3356;
const int FEAT_VOW_PURITY = 5360;
const int FEAT_VOWOFPOVERTY = 26001;
const int FEAT_VOWOFPOVERTY = 26002;
//Vile Feat
const int FEAT_LICHLOVED = 3395;
@ -1868,12 +1868,12 @@ const int FEAT_SANCTIFY_MARTIAL_SICKLE = 3169;
const int FEAT_SANCTIFY_MARTIAL_MINDBLADE = 3623;
const int FEAT_SANCTIFY_MARTIAL_WHIP = 3596;
const int FEAT_SANCTIFY_MARTIAL_TRIDENT = 3597;
const int FEAT_SANCTIFYKISTRIKE = 26002;
const int FEAT_HOLYKISTRIKE = 26003;
const int FEAT_FISTOFHEAVENS = 26004;
const int FEAT_VOWABSTINENCE = 26005;
const int FEAT_VOWCHASTITY = 26006;
const int FEAT_GIFTOFFAITH = 26007;
const int FEAT_SANCTIFYKISTRIKE = 26003;
const int FEAT_HOLYKISTRIKE = 26004;
const int FEAT_FISTOFHEAVENS = 26005;
const int FEAT_VOWABSTINENCE = 26006;
const int FEAT_VOWCHASTITY = 26007;
const int FEAT_GIFTOFFAITH = 26008;
//heartwarder
const int FEAT_CHARISMA_INC1 = 3230;

View File

@ -575,8 +575,8 @@ int PRCGetCasterLevel(object oCaster = OBJECT_SELF)
iReturnLevel = GetLevelByClass(CLASS_TYPE_SHAPECHANGER);
}
// Casting as a bard but don't have any levels in the class
if(iCastingClass == CLASS_TYPE_BARD && !GetLevelByClass(CLASS_TYPE_BARD, oCaster))
// Casting as a bard but don't have any levels in the class //:: Double-dipping?
/* if(iCastingClass == CLASS_TYPE_BARD && !GetLevelByClass(CLASS_TYPE_BARD, oCaster))
{
int nRace = GetRacialType(oCaster);
@ -584,7 +584,7 @@ int PRCGetCasterLevel(object oCaster = OBJECT_SELF)
//otherwise use RHD instead of bard levels
if(nRace == RACIAL_TYPE_GLOURA)
iReturnLevel = GetLevelByClass(CLASS_TYPE_FEY);
}
} */
//Spell Rage ability
if(GetHasSpellEffect(SPELL_SPELL_RAGE, oCaster)
@ -960,8 +960,10 @@ int GetArcanePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID)
}
//:: End Bard Arcane PrC casting calculations
if(nCastingClass == CLASS_TYPE_BARD && nRace == RACIAL_TYPE_GLOURA && !GetLevelByClass(CLASS_TYPE_BARD, oCaster))
if(nCastingClass == CLASS_TYPE_BARD || nCastingClass == CLASS_TYPE_BARD && nRace == RACIAL_TYPE_GLOURA && !GetLevelByClass(CLASS_TYPE_BARD, oCaster))
{
if(DEBUG) DoDebug("prc_inc_castlvl >> Found Fey RHD caster (not bard)");
if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_FEY, oCaster))
nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster);
@ -1065,7 +1067,10 @@ int GetArcanePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID)
nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster);
if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_FEY, oCaster))
{
nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster);
if(DEBUG) DoDebug("prc_inc_castlvl >> Found Fey + Virtuoso PrC. Arcane caster level is "+IntToString(nArcane)+".");
}
if(GetHasFeat(FEAT_WWOC_SPELLCASTING_FEY, oCaster))
nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster);

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -382,17 +382,17 @@ int GetPrCAdjustedClassLevel(int nClass, object oCaster = OBJECT_SELF)
// is it arcane, divine or neither?
if(GetIsArcaneClass(nClass, oCaster) && nClass != CLASS_TYPE_SUBLIME_CHORD)
{
if (GetPrimaryArcaneClass(oCaster) == nClass) // adjust for any PrCs
if (GetPrimaryArcaneClass(oCaster) == nClass) // adjust for any PrCs
iTemp = GetArcanePRCLevels(oCaster, nClass);
}
else if(GetIsDivineClass(nClass, oCaster))
{
if (GetPrimaryDivineClass(oCaster) == nClass) // adjust for any PrCs
iTemp = GetDivinePRCLevels(oCaster, nClass);
if (GetPrimaryDivineClass(oCaster) == nClass) // adjust for any PrCs
iTemp = GetDivinePRCLevels(oCaster, nClass);
}
else // a non-caster class or a PrC
{
return 0;
return 0;
}
// add the caster class levels
return iTemp += GetLevelByClass(nClass, oCaster);

View File

@ -0,0 +1,530 @@
#include "prc_nui_consts"
#include "inc_newspellbook"
#include "psi_inc_psifunc"
#include "inc_lookups"
#include "nw_inc_nui"
//
// GetCurrentSpellLevel
// Gets the current spell level the class can achieve at the current
// caster level (ranging from 0-9)
//
// Arguments:
// nClass:int the ClassID
// nLevel:int the caster level
//
// Returns:
// int the circle the class can achieve currently
//
int GetCurrentSpellLevel(int nClass, int nLevel);
//
// GetMaxSpellLevel
// Gets the highest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest circle that can be achieved
//
int GetMaxSpellLevel(int nClass);
//
// GetMinSpellLevel
// Gets the lowest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the lowest circle that can be achieved
//
int GetMinSpellLevel(int nClass);
//
// GetHighestLevelPossibleInClass
// Given a class Id this will determine what the max level of a class can be
// achieved
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest possible level the class can achieve
//
int GetHighestLevelPossibleInClass(int nClass);
//
// GetClassSpellbookFile
// Gets the class 2da spellbook/ability for the given class Id
//
// Arguments:
// nClass:int the classID
//
// Returns:
// string the 2da file name for the spell/abilities of the ClassID
//
string GetClassSpellbookFile(int nClass);
//
// GetBinderSpellToFeatDictionary
// Sets up the Binder Spell Dictionary that is used to match a binder's vestige
// to their feat. This is constructed based off the binder's known location of
// their feat and spell ranges in the base 2das respectivly. After constructing
// this it will be saved to the player locally as a cached result since we do
// not need to call this again.
//
// Argument:
// oPlayer:object the player
//
// Returns:
// json:Dictionary<String,Int> a dictionary of mapping between the SpellID
// and the FeatID of a vestige ability
//
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF);
//
// GetSpellLevelIcon
// Takes the spell circle int and gets the icon appropriate for it (i.e. 0 turns
// into "ir_cantrips"
//
// Arguments:
// spellLevel:int the spell level we want the icon for
//
// Returns:
// string the spell level icon
//
string GetSpellLevelIcon(int spellLevel);
//
// GetSpellLevelToolTip
// Gets the spell level tool tip text based on the int spell level provided (i.e.
// 0 turns into "Cantrips")
//
// Arguments:
// spellLevel:int the spell level we want the tooltip for
//
// Returns:
// string the spell level toop tip
//
string GetSpellLevelToolTip(int spellLevel);
//
// GetSpellIcon
// Gets the spell icon based off the spellId, or featId supplied
//
// Arguments:
// nClass:int the class Id
// featId:int the featId we can use the icon for
// spellId:int the spell Id we want the icon for
//
// Returns:
// json:String the string of the icon we want.
//
json GetSpellIcon(int spellId, int featId=0, int nClass=0);
string GetSpellName(int spellId, int realSpellID=0, int featId=0, int nClass=0);
//
// GreyOutButton
// Takes NUI Button along with it's width and height and greys it out it with a drawn
// colored rectangle to represent it's not been selected or not valid.
//
// Arguments:
// jButton:json the NUI Button
// w:float the width of the button
// h:float the height of the button
//
// Returns:
// json the NUI button greyed out
//
json GreyOutButton(json jButton, float w, float h);
//
// CreateGreyOutRectangle
// Creates a grey out rectangle for buttons
//
// Arguments:
// w:float the width of the button
// h:float the height of the button
//
// Returns:
// json the transparant black rectangle
//
json CreateGreyOutRectangle(float w, float h);
void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0);
void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF);
int GetCurrentSpellLevel(int nClass, int nLevel)
{
int currentLevel = nLevel;
// ToB doesn't have a concept of spell levels, but still match up to it
if(nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_SHADOWCASTER)
{
return 9;
}
// Binders don't really have a concept of spell level
if (nClass == CLASS_TYPE_BINDER
|| nClass == CLASS_TYPE_DRAGON_SHAMAN) // they can only reach 1st circle
return 1;
//Shadowsmith has no concept of spell levels
if (nClass == CLASS_TYPE_SHADOWSMITH)
return 2;
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
return 4;
// Spont casters have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
int maxLevel = GetMaxSpellLevelForCasterLevel(nClass, currentLevel);
return maxLevel;
}
else
{
// everyone else uses this
string spellLevel2da = GetAMSKnownFileName(nClass);
currentLevel = nLevel - 1; // Level is 1 off of the row in the 2da
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND)
currentLevel = GetManifesterLevel(OBJECT_SELF, nClass, TRUE) - 1;
int totalLevel = Get2DARowCount(spellLevel2da);
// in case we somehow go over bounds just don't :)
if (currentLevel >= totalLevel)
currentLevel = totalLevel - 1;
//Psionics have MaxPowerLevel as their column name
string columnName = "MaxPowerLevel";
//Invokers have MaxInvocationLevel
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
columnName = "MaxInvocationLevel";
// Truenamers have 3 sets of utterances, ranging from 1-6, EvolvingMind covers the entire range
if (nClass == CLASS_TYPE_TRUENAMER)
{
columnName = "EvolvingMind";
spellLevel2da = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
columnName = "VestigeLvl";
spellLevel2da = "cls_bind_binder";
}
// ToB doesn't have a concept of this, but we don't care.
int maxLevel = StringToInt(Get2DACache(spellLevel2da, columnName, currentLevel));
return maxLevel;
}
}
int GetMinSpellLevel(int nClass)
{
// again sponts have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
return GetMinSpellLevelForCasterLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
else
{
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND
|| nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_SHADOWCASTER
|| nClass == CLASS_TYPE_SHADOWSMITH
|| nClass == CLASS_TYPE_BINDER)
return 1;
return GetCurrentSpellLevel(nClass, 1);
}
}
int GetMaxSpellLevel(int nClass)
{
if (nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSION)
return 9;
if (nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_WARMIND)
return 5;
if (nClass == CLASS_TYPE_PSYWAR)
return 6;
return GetCurrentSpellLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
int GetHighestLevelPossibleInClass(int nClass)
{
string sFile;
//sponts have their spells in the classes.2da
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = Get2DACache("classes", "SpellGainTable", nClass);
}
else
{
// everyone else uses this
sFile = GetAMSKnownFileName(nClass);
if (nClass == CLASS_TYPE_TRUENAMER)
{
sFile = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "cls_bind_binder";
}
}
return Get2DARowCount(sFile);
}
string GetClassSpellbookFile(int nClass)
{
string sFile;
// Spontaneous casters use a specific file name structure
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = GetFileForClass(nClass);
}
// everyone else uses this structure
else
{
sFile = GetAMSDefinitionFileName(nClass);
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "vestiges";
}
}
return sFile;
}
string GetSpellLevelIcon(int spellLevel)
{
switch (spellLevel)
{
case 0: return "ir_cantrips";
case 1: return "ir_level1";
case 2: return "ir_level2";
case 3: return "ir_level3";
case 4: return "ir_level4";
case 5: return "ir_level5";
case 6: return "ir_level6";
case 7: return "ir_level789";
case 8: return "ir_level789";
case 9: return "ir_level789";
}
return "";
}
string GetSpellLevelToolTip(int spellLevel)
{
switch (spellLevel)
{
case 0: return "Cantrips";
case 1: return "Level 1";
case 2: return "Level 2";
case 3: return "Level 3";
case 4: return "Level 4";
case 5: return "Level 5";
case 6: return "Level 6";
case 7: return "Level 7";
case 8: return "Level 8";
case 9: return "Level 9";
}
return "";
}
json GetSpellIcon(int spellId,int featId=0,int nClass=0)
{
// Binder's spells don't have the FeatID on the spells.2da, so we have to use
// the mapping we constructed to get it.
if (nClass == CLASS_TYPE_BINDER)
{
json binderDict = GetBinderSpellToFeatDictionary();
int nFeatID = JsonGetInt(JsonObjectGet(binderDict, IntToString(spellId)));
return JsonString(Get2DACache("feat", "Icon", featId));
}
if (featId)
return JsonString(Get2DACache("feat", "Icon", featId));
int masterSpellID = StringToInt(Get2DACache("spells", "Master", spellId));
// if this is a sub radial spell, then we use spell's icon instead
if (masterSpellID)
return JsonString(Get2DACache("spells", "IconResRef", spellId));
// the FeatID holds the accurate spell icon, not the SpellID
int nFeatID = StringToInt(Get2DACache("spells", "FeatID", spellId));
return JsonString(Get2DACache("feat", "Icon", nFeatID));
}
string GetSpellName(int spellId, int realSpellID=0, int featId=0, int nClass=0)
{
if ((nClass == CLASS_TYPE_SHADOWSMITH
|| nClass == CLASS_TYPE_SHADOWCASTER) && spellId)
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId)));
if (nClass == CLASS_TYPE_TRUENAMER && featId)
return GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", featId)));
if (realSpellID)
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", realSpellID)));
if (spellId)
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId)));
if (featId)
return GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", featId)));
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId)));
}
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF)
{
// a dictionary of <SpellID, FeatID>
json binderDict = GetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR);
// if this hasn't been created, create it now.
if (binderDict == JsonNull())
binderDict = JsonObject();
else
return binderDict;
// the starting row for binder spells
int spellIndex = 19070;
// the starting row for binder feats
int featIndex = 9030;
//the end of the binder spells/feats
while (spellIndex <= 19156 && featIndex <= 9104)
{
// get the SpellID tied to the feat
int spellID = StringToInt(Get2DACache("feat", "SPELLID", featIndex));
// if the spellID matches the current index, then this is the spell
// attached to the feat
if (spellID == spellIndex)
{
binderDict = JsonObjectSet(binderDict, IntToString(spellID), JsonInt(featIndex));
// move to next spell/feat
featIndex++;
spellIndex++;
}
// else we have reached a subdial spell
else
{
// loop through until we reach back at spellID
while (spellIndex < spellID)
{
int masterSpell = StringToInt(Get2DACache("spells", "Master", spellIndex));
// add the sub radial to the dict, tied to the master's FeatID
int featId = JsonGetInt(JsonObjectGet(binderDict, IntToString(masterSpell)));
binderDict = JsonObjectSet(binderDict, IntToString(spellIndex), JsonInt(featId));
spellIndex++;
}
// some feats overlap the same FeatID, can cause this to get stuck.
// if it happens then move on
if (spellIndex > spellID)
featIndex++;
}
}
// cache the result
SetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR, binderDict);
return binderDict;
}
json GreyOutButton(json jButton, float w, float h)
{
json retValue = jButton;
json jBorders = JsonArray();
jBorders = JsonArrayInsert(jBorders, CreateGreyOutRectangle(w, h));
return NuiDrawList(jButton, JsonBool(FALSE), jBorders);
}
json CreateGreyOutRectangle(float w, float h)
{
// set the points of the button shape
json jPoints = JsonArray();
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(h));
jPoints = JsonArrayInsert(jPoints, JsonFloat(w));
jPoints = JsonArrayInsert(jPoints, JsonFloat(h));
jPoints = JsonArrayInsert(jPoints, JsonFloat(w));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
return NuiDrawListPolyLine(JsonBool(TRUE), NuiColor(0, 0, 0, 127), JsonBool(TRUE), JsonFloat(2.0), jPoints);
}
void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0)
{
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_FEATID_VAR, featID);
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_SPELLID_VAR, spellId);
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR, realSpellId);
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR, nClass);
ExecuteScript("prc_nui_dsc_view", oPlayer);
}
void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF)
{
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_FEATID_VAR);
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_SPELLID_VAR);
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR);
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR);
}

View File

@ -110,4 +110,49 @@ const string NUI_PRC_PA_TEXT_BIND = "nui_prc_pa_text_bind";
// Left Button Enabled Bind for Power Attack NUI
const string NUI_PRC_PA_LEFT_BUTTON_ENABLED_BIND = "leftButtonEnabled";
// Right Button Enabled Bind for Power Attack NUI
const string NUI_PRC_PA_RIGHT_BUTTON_ENABLED_BIND = "rightButtonEnabled";
const string NUI_PRC_PA_RIGHT_BUTTON_ENABLED_BIND = "rightButtonEnabled";
//////////////////////////////////////////////////
// //
// NUI Level Up //
// //
//////////////////////////////////////////////////
const string NUI_LEVEL_UP_WINDOW_ID = "prcLevelUpNui";
const string NUI_LEVEL_UP_SPELL_CIRCLE_BUTTON_BASEID = "NuiLevelUpCircleButton_";
const string NUI_LEVEL_UP_SPELL_BUTTON_BASEID = "NuiLevelUpSpellButton_";
const string NUI_LEVEL_UP_SPELL_DISABLED_BUTTON_BASEID = "NuiLevelUpDisabledSpellButton_";
const string NUI_LEVEL_UP_SPELL_CHOSEN_BUTTON_BASEID = "NuiLevelUpChosenSpellButton_";
const string NUI_LEVEL_UP_SPELL_CHOSEN_DISABLED_BUTTON_BASEID = "NuiLevelUpDisabledChosenSpellButton_";
const string NUI_LEVEL_UP_DONE_BUTTON = "NuiLevelUpDoneButton";
const string NUI_LEVEL_UP_RESET_BUTTON = "NuiLevelUpResetButton";
const string NUI_LEVEL_UP_SELECTED_CLASS_VAR = "NUILevelUpSelectedClass";
const string NUI_LEVEL_UP_SELECTED_CIRCLE_VAR = "NUILevelUpSelectedCircle";
const string NUI_LEVEL_UP_KNOWN_SPELLS_VAR = "NUILevelUpKnownSpells";
const string NUI_LEVEL_UP_CHOSEN_SPELLS_VAR = "NUILevelUpChosenSpells";
const string NUI_LEVEL_UP_EXPANDED_KNOW_LIST_VAR = "NUILevelUpExpKnowList";
const string NUI_LEVEL_UP_POWER_LIST_VAR = "NUILevelUpPowerList";
const string NUI_LEVEL_UP_DISCIPLINE_INFO_VAR = "GetDisciplineInfoObjectCache_";
const string NUI_LEVEL_UP_SPELLID_LIST_VAR = "NUILevelUpSpellIDList_";
const string NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR = "NUIRemainingChoicesCache";
const string NUI_LEVEL_UP_RELEARN_LIST_VAR = "NUILevelUpRelearnList";
const string NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR = "NUILevelUpArchivistNewSpellsList";
const string NUI_LEVEL_UP_EXPANDED_CHOICES_VAR = "NUIExpandedChoices";
const string NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR = "NUIEpicExpandedChoices";
const int NUI_LEVEL_UP_MANEUVER_PREREQ_LIMIT = 6;
const string NUI_LEVEL_UP_MANEUVER_TOTAL = "ManeuverTotal";
const string NUI_LEVEL_UP_STANCE_TOTAL = "StanceTotal";
const string NUI_LEVEL_UP_SPELLBOOK_OBJECT_CACHE_VAR = "GetSpellListObjectCache_";
const string NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR = "GetInvokerKnownListObjectCache_";
const string NUI_SPELL_DESCRIPTION_FEATID_VAR = "NUISpellDescriptionFeatID";
const string NUI_SPELL_DESCRIPTION_CLASSID_VAR = "NUISpellDescriptionClassID";
const string NUI_SPELL_DESCRIPTION_SPELLID_VAR = "NUISpellDescriptionSpellID";
const string NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR = "NUISpellDescriptionRealSpellID";

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,8 @@
//:: Created By: Rakiov
//:: Created On: 24.05.2005
//:://////////////////////////////////////////////
#include "inc_newspellbook"
#include "psi_inc_psifunc"
#include "inc_lookups"
#include "prc_nui_consts"
#include "prc_nui_com_inc"
//
// GetSpellListForCircle
@ -43,69 +41,6 @@ json GetSpellListForCircle(object oPlayer, int nClass, int circle);
//
json GetSupportedNUISpellbookClasses(object oPlayer);
//
// GetCurrentSpellLevel
// Gets the current spell level the class can achieve at the current
// caster level (ranging from 0-9)
//
// Arguments:
// nClass:int the ClassID
// nLevel:int the caster level
//
// Returns:
// int the circle the class can achieve currently
//
int GetCurrentSpellLevel(int nClass, int nLevel);
//
// GetMaxSpellLevel
// Gets the highest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest circle that can be achieved
//
int GetMaxSpellLevel(int nClass);
//
// GetMinSpellLevel
// Gets the lowest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the lowest circle that can be achieved
//
int GetMinSpellLevel(int nClass);
//
// GetHighestLevelPossibleInClass
// Given a class Id this will determine what the max level of a class can be
// achieved
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest possible level the class can achieve
//
int GetHighestLevelPossibleInClass(int nClass);
//
// GetClassSpellbookFile
// Gets the class 2da spellbook/ability for the given class Id
//
// Arguments:
// nClass:int the classID
//
// Returns:
// string the 2da file name for the spell/abilities of the ClassID
//
string GetClassSpellbookFile(int nClass);
//
// IsSpellKnown
// Returns whether the player with the given class, spell file, and spellbook id
@ -234,23 +169,6 @@ json GetMetaMysteryFeatList();
//
int GetTrueClassIfRHD(object oPlayer, int nClass);
//
// GetBinderSpellToFeatDictionary
// Sets up the Binder Spell Dictionary that is used to match a binder's vestige
// to their feat. This is constructed based off the binder's known location of
// their feat and spell ranges in the base 2das respectivly. After constructing
// this it will be saved to the player locally as a cached result since we do
// not need to call this again.
//
// Argument:
// oPlayer:object the player
//
// Returns:
// json:Dictionary<String,Int> a dictionary of mapping between the SpellID
// and the FeatID of a vestige ability
//
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF);
//
// ShouldAddSpell
// Given a spellId and a class, determines if the spell should be added to the
@ -318,6 +236,18 @@ json GetInvokerEssenceSpellList(int nClass, object oPlayer=OBJECT_SELF);
//
int JsonArrayContainsInt(json list, int item);
//
// IsSpellbookNUIOpen
// Checks to see if the Spellbook NUI is open on a given player.
//
// Arguments:
// oPC:object the player
//
// Returns:
// int:Boolean TRUE if window is open, FALSE otherwise
//
int IsSpellbookNUIOpen(object oPC);
json GetSpellListForCircle(object oPlayer, int nClass, int circle)
{
json retValue = JsonArray();
@ -397,86 +327,6 @@ int ShouldAddSpell(int nClass, int spellId, object oPlayer=OBJECT_SELF)
return TRUE;
}
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF)
{
// a dictionary of <SpellID, FeatID>
json binderDict = GetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR);
// if this hasn't been created, create it now.
if (binderDict == JsonNull())
binderDict = JsonObject();
else
return binderDict;
// the starting row for binder spells
int spellIndex = 19070;
// the starting row for binder feats
int featIndex = 9030;
//the end of the binder spells/feats
while (spellIndex <= 19156 && featIndex <= 9104)
{
// get the SpellID tied to the feat
int spellID = StringToInt(Get2DACache("feat", "SPELLID", featIndex));
// if the spellID matches the current index, then this is the spell
// attached to the feat
if (spellID == spellIndex)
{
binderDict = JsonObjectSet(binderDict, IntToString(spellID), JsonInt(featIndex));
// move to next spell/feat
featIndex++;
spellIndex++;
}
// else we have reached a subdial spell
else
{
// loop through until we reach back at spellID
while (spellIndex < spellID)
{
int masterSpell = StringToInt(Get2DACache("spells", "Master", spellIndex));
// add the sub radial to the dict, tied to the master's FeatID
int featId = JsonGetInt(JsonObjectGet(binderDict, IntToString(masterSpell)));
binderDict = JsonObjectSet(binderDict, IntToString(spellIndex), JsonInt(featId));
spellIndex++;
}
// some feats overlap the same FeatID, can cause this to get stuck.
// if it happens then move on
if (spellIndex > spellID)
featIndex++;
}
}
// cache the result
SetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR, binderDict);
return binderDict;
}
string GetClassSpellbookFile(int nClass)
{
string sFile;
// Spontaneous casters use a specific file name structure
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = GetFileForClass(nClass);
}
// everyone else uses this structure
else
{
sFile = GetAMSDefinitionFileName(nClass);
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "vestiges";
}
}
return sFile;
}
json GetSupportedNUISpellbookClasses(object oPlayer)
{
json retValue = JsonArray();
@ -526,167 +376,6 @@ int IsSpellKnown(object oPlayer, int nClass, int spellId)
return FALSE;
}
int GetCurrentSpellLevel(int nClass, int nLevel)
{
int currentLevel = nLevel;
// ToB doesn't have a concept of spell levels, but still match up to it
if(nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_SHADOWCASTER)
{
return 9;
}
// Binders don't really have a concept of spell level
if (nClass == CLASS_TYPE_BINDER
|| nClass == CLASS_TYPE_DRAGON_SHAMAN) // they can only reach 1st circle
return 1;
//Shadowsmith has no concept of spell levels
if (nClass == CLASS_TYPE_SHADOWSMITH)
return 2;
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
return 4;
// Spont casters have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
int maxLevel = GetMaxSpellLevelForCasterLevel(nClass, currentLevel);
return maxLevel;
}
else
{
// everyone else uses this
string spellLevel2da = GetAMSKnownFileName(nClass);
currentLevel = nLevel - 1; // Level is 1 off of the row in the 2da
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND)
currentLevel = GetManifesterLevel(OBJECT_SELF, nClass, TRUE) - 1;
int totalLevel = Get2DARowCount(spellLevel2da);
// in case we somehow go over bounds just don't :)
if (currentLevel >= totalLevel)
currentLevel = totalLevel - 1;
//Psionics have MaxPowerLevel as their column name
string columnName = "MaxPowerLevel";
//Invokers have MaxInvocationLevel
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
columnName = "MaxInvocationLevel";
// Truenamers have 3 sets of utterances, ranging from 1-6, EvolvingMind covers the entire range
if (nClass == CLASS_TYPE_TRUENAMER)
{
columnName = "EvolvingMind";
spellLevel2da = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
columnName = "VestigeLvl";
spellLevel2da = "cls_bind_binder";
}
// ToB doesn't have a concept of this, but we don't care.
int maxLevel = StringToInt(Get2DACache(spellLevel2da, columnName, currentLevel));
return maxLevel;
}
}
int GetMinSpellLevel(int nClass)
{
// again sponts have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
return GetMinSpellLevelForCasterLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
else
{
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND
|| nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_SHADOWCASTER
|| nClass == CLASS_TYPE_SHADOWSMITH
|| nClass == CLASS_TYPE_BINDER)
return 1;
return GetCurrentSpellLevel(nClass, 1);
}
}
int GetMaxSpellLevel(int nClass)
{
if (nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSION)
return 9;
if (nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_WARMIND)
return 5;
if (nClass == CLASS_TYPE_PSYWAR)
return 6;
return GetCurrentSpellLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
int GetHighestLevelPossibleInClass(int nClass)
{
string sFile;
//sponts have their spells in the classes.2da
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = Get2DACache("classes", "SpellGainTable", nClass);
}
else
{
// everyone else uses this
sFile = GetAMSKnownFileName(nClass);
if (nClass == CLASS_TYPE_TRUENAMER)
{
sFile = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "cls_bind_binder";
}
}
return Get2DARowCount(sFile);
}
int IsClassAllowedToUseNUISpellbook(object oPlayer, int nClass)
{
// This controls who can use the Spellbook NUI, if for some reason you don't
@ -1145,4 +834,15 @@ int JsonArrayContainsInt(json list, int item)
}
return FALSE;
}
}
int IsSpellbookNUIOpen(object oPC)
{
int nPreviousToken = NuiFindWindow(oPC, PRC_SPELLBOOK_NUI_WINDOW_ID);
if (nPreviousToken != 0)
{
return TRUE;
}
return FALSE;
}

View File

@ -41,6 +41,7 @@ const string _MYSTERY_LIST_MISC_ARRAY = "_MysteriesKnownMiscArray";
const string _MYSTERY_LIST_LEVEL_ARRAY = "_MysteriesKnownLevelArray_";
const string _MYSTERY_LIST_GENERAL_ARRAY = "_MysteriesKnownGeneralArray";
#include "shd_inc_shdfunc"
//////////////////////////////////////////////////
/* Function prototypes */

View File

@ -1441,8 +1441,9 @@ int CheckSecondaryPrC(object oPC = OBJECT_SELF)
if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_BARD)) return TRUE;
if (GetHasFeat(FEAT_WWOC_SPELLCASTING_BARD)) return TRUE;
}
else if (bBeguiler)
if (bBeguiler)
{
if(DEBUG) DoDebug("x2_inc_spellhook: CheckSecondaryPrC >>> Entering Beguiler", oPC);
if (GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_BEGUILER)) return TRUE;
if (GetHasFeat(FEAT_AOTS_SPELLCASTING_BEGUILER)) return TRUE;
if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_BEGUILER)) return TRUE;
@ -1492,8 +1493,9 @@ int CheckSecondaryPrC(object oPC = OBJECT_SELF)
}
else if (bDuskblade)
if (bDuskblade)
{
if(DEBUG) DoDebug("x2_inc_spellhook: CheckSecondaryPrC >>> Entering Dusblade", oPC);
if (GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_DUSKBLADE)) return TRUE;
if (GetHasFeat(FEAT_AOTS_SPELLCASTING_DUSKBLADE)) return TRUE;
if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_DUSKBLADE)) return TRUE;
@ -1540,8 +1542,9 @@ int CheckSecondaryPrC(object oPC = OBJECT_SELF)
}
else if (bSorcerer)
if (bSorcerer)
{
if(DEBUG) DoDebug("x2_inc_spellhook: CheckSecondaryPrC >>> Entering Sorcerer", oPC);
if (GetHasFeat(FEAT_ABERRATION_SPELLCASTING_DRIDER)) return TRUE;
if (GetHasFeat(FEAT_MONSTROUS_SPELLCASTING_ARKAMOI)) return TRUE;
if (GetHasFeat(FEAT_MONSTROUS_SPELLCASTING_MARRUTACT)) return TRUE;
@ -1599,8 +1602,9 @@ int CheckSecondaryPrC(object oPC = OBJECT_SELF)
if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SORCERER)) return TRUE;
if (GetHasFeat(FEAT_WWOC_SPELLCASTING_SORCERER)) return TRUE;
}
else if (bWarmage)
if (bWarmage)
{
if(DEBUG) DoDebug("x2_inc_spellhook: CheckSecondaryPrC >>> Entering Warmage", oPC);
if (GetHasFeat(FEAT_AOTS_SPELLCASTING_WARMAGE)) return TRUE;
if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_WARMAGE)) return TRUE;
if (GetHasFeat(FEAT_ANIMA_SPELLCASTING_WARMAGE)) return TRUE;
@ -1662,14 +1666,71 @@ int BardSorcPrCCheck(object oCaster, int nCastingClass, object oSpellCastItem)
return TRUE;
}
//check its a sorc spell
//check its a sorcerer spell
if(nCastingClass == CLASS_TYPE_SORCERER)
{
if (CheckSecondaryPrC(oCaster) == TRUE)
if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> nCastingClass is Sorcerer.", oCaster);
//no need to check further if new spellbooks are disabled
if(GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK))
{
if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sorcerer w/RHD found.", oCaster);
if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> PRC_SORC_DISALLOW_NEWSPELLBOOK.", oCaster);
return TRUE;
}
//check they have sorcerer levels
if(!GetLevelByClass(CLASS_TYPE_SORCERER, oCaster))
{
if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Not a sorcerer.", oCaster);
return TRUE;
}
//check if they are casting via new spellbook
if(GetLocalInt(oCaster, "NSB_Class") != CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster))
{
if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> UltMagus using new spellbook.", oCaster);
return FALSE;
}
//check if they are casting via new spellbook
if(GetLocalInt(oCaster, "NSB_Class") == CLASS_TYPE_SORCERER)
{
if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Using new spellbook.", oCaster);
return TRUE;
}
if(GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster) > 0 && CheckSecondaryPrC(oCaster) == TRUE)
{
if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sublime Chord w/RHD found.", oCaster);
FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE);
return FALSE;
}
if (CheckSecondaryPrC(oCaster) == TRUE)
{
if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sorcerer w/RHD found.", oCaster);
FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE);
return FALSE;
}
//check they have arcane PrC or Draconic Arcane Grace/Breath
if(!(GetArcanePRCLevels(oCaster, nCastingClass) - GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster))
&& !(GetHasFeat(FEAT_DRACONIC_GRACE, oCaster) || GetHasFeat(FEAT_DRACONIC_BREATH, oCaster)))
{
if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> First Sublime Chord check.", oCaster);
return TRUE;
}
//check they have sorcerer in first arcane slot
//if(GetPrimaryArcaneClass() != CLASS_TYPE_SORCERER)
if(GetPrCAdjustedCasterLevelByType(TYPE_ARCANE, oCaster, TRUE) != GetPrCAdjustedCasterLevelByType(CLASS_TYPE_SORCERER, oCaster, TRUE))
{
if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> GetPrCAdjustedCasterLevelByType.", oCaster);
return TRUE;
}
//at this point, they must be using the bioware spellbook
//from a class that adds to bard
FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE);
return FALSE;
}
/* //check its a sorc spell
if(nCastingClass == CLASS_TYPE_SORCERER)
{
//no need to check further if new spellbooks are disabled
if(GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK))
return TRUE;
@ -1708,7 +1769,7 @@ int BardSorcPrCCheck(object oCaster, int nCastingClass, object oSpellCastItem)
//from a class that adds to sorc
FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE);
return FALSE;
}
} */
//check its a bard spell
if(nCastingClass == CLASS_TYPE_BARD)