#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 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 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); }