diff --git a/trunk/include/inc_epicspellfnc.nss b/trunk/include/inc_epicspellfnc.nss new file mode 100644 index 00000000..f345ac5f --- /dev/null +++ b/trunk/include/inc_epicspellfnc.nss @@ -0,0 +1,275 @@ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +int GetFeatForSeed(int nSeedID); +int GetIPForSeed(int nSeedID); +int GetDCForSeed(int nSeedID); +int GetClassForSeed(int nSeedID); +int GetCanLearnSeed(object oPC, int nSeedID); +int GetSeedFromAbrev(string sAbrev); +string GetNameForSeed(int nSeedID); + +int GetDCForSpell(int nSpellID); +int GetFeatForSpell(int nSpellID); +int GetResearchFeatForSpell(int nSpellID); +int GetIPForSpell(int nSpellID); +int GetResearchIPForSpell(int nSpellID); +int GetCastXPForSpell(int nSpellID); +string GetSchoolForSpell(int nSpellID); +int GetR1ForSpell(int nSpellID); +int GetR2ForSpell(int nSpellID); +int GetR3ForSpell(int nSpellID); +int GetR4ForSpell(int nSpellID); +string GetNameForSpell(int nSpellID); +int GetSpellFromAbrev(string sAbrev); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + + + +#include "inc_utility" +//#include "inc_epicspelldef" + +// SEED FUNCTIONS + +int GetFeatForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "FeatID", nSeedID)); +} + +int GetIPForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "FeatIPID", nSeedID)); +} + +int GetDCForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "DC", nSeedID)); +} + +int GetClassForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "Class", nSeedID)); +} + +int GetSeedFromAbrev(string sAbrev) +{ + sAbrev = GetStringLowerCase(sAbrev); + if(GetStringLeft(sAbrev, 8) == "epic_sd_") + sAbrev = GetStringRight(sAbrev, GetStringLength(sAbrev)-8); + int i = 0; + string sLabel = GetStringLowerCase(Get2DACache("epicspellseeds", "LABEL", i)); + while(sLabel != "") + { + if(sAbrev == sLabel) + return i; + i++; + sLabel = GetStringLowerCase(Get2DACache("epicspellseeds", "LABEL", i)); + } + return -1; +} + +string GetNameForSeed(int nSeedID) +{ + int nFeat = GetFeatForSeed(nSeedID); + string sName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))); + return sName; +} + +/* +Bit-flags set in epicspellseeds.2da in Class column +used to restrict access to epic spell seeds for some classes +ie: 13 means that only clerics, sorcerers and wizards can learn that seed (1 + 4 + 8), +all classes can use == 32767 +*/ +int _Class2BitFlag(int nClass) +{ + switch(nClass) + { + case CLASS_TYPE_CLERIC: return 1; + case CLASS_TYPE_DRUID: return 2; + case CLASS_TYPE_SORCERER: return 4; + case CLASS_TYPE_WIZARD: return 8; + case CLASS_TYPE_HEALER: return 16; + case CLASS_TYPE_BEGUILER: return 32; + case CLASS_TYPE_SUBLIME_CHORD: return 64; + case CLASS_TYPE_DREAD_NECROMANCER: return 128; + case CLASS_TYPE_MYSTIC: return 256; + case CLASS_TYPE_ARCHIVIST: return 512; + case CLASS_TYPE_SHAMAN: return 4096; + case CLASS_TYPE_FAVOURED_SOUL: return 8192; + case CLASS_TYPE_WARMAGE: return 16384; + case CLASS_TYPE_UR_PRIEST: return 1; + case CLASS_TYPE_BLIGHTER: return 2; + } + return -1; +} + +int _CheckEpicSpellcastingForClass(object oPC, int nClass) +{ + if(GetHitDice(oPC) < 21) + return FALSE; + + switch(nClass) + { + case CLASS_TYPE_CLERIC: return GetIsEpicCleric(oPC); + case CLASS_TYPE_DRUID: return GetIsEpicDruid(oPC); + case CLASS_TYPE_SORCERER: return GetIsEpicSorcerer(oPC); + case CLASS_TYPE_WIZARD: return GetIsEpicWizard(oPC); + case CLASS_TYPE_HEALER: return GetIsEpicHealer(oPC); + case CLASS_TYPE_BEGUILER: return GetIsEpicBeguiler(oPC); + case CLASS_TYPE_SUBLIME_CHORD: return GetIsEpicSublimeChord(oPC); + case CLASS_TYPE_DREAD_NECROMANCER: return GetIsEpicDreadNecromancer(oPC); + case CLASS_TYPE_ARCHIVIST: return GetIsEpicArchivist(oPC); + case CLASS_TYPE_SHAMAN: return GetIsEpicShaman(oPC); + case CLASS_TYPE_FAVOURED_SOUL: return GetIsEpicFavSoul(oPC); + case CLASS_TYPE_WARMAGE: return GetIsEpicWarmage(oPC); + case CLASS_TYPE_BLIGHTER: return GetIsEpicBlighter(oPC); + case CLASS_TYPE_UR_PRIEST: return GetIsEpicUrPriest(oPC); + } + return FALSE; +} + +int GetCanLearnSeed(object oPC, int nSeedID) +{ + int nRestr = GetClassForSeed(nSeedID); + int i, nClass; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oPC); + if(_CheckEpicSpellcastingForClass(oPC, nClass)//this class has epic spellcasting + && (nRestr & _Class2BitFlag(nClass)))//and was added to class column in epicspellseeds.2da + { + return TRUE; + } + } + return FALSE; +} + +// SPELL FUNCTIONS + +int GetDCForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "DC", nSpellID)); +} + +int GetFeatForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "SpellFeatID", nSpellID)); +} + +int GetResearchFeatForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "ResFeatID", nSpellID)); +} + +int GetIPForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "SpellFeatIPID", nSpellID)); +} + +int GetResearchIPForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "ResFeatIPID", nSpellID)); +} + +int GetCastXPForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "CastingXP", nSpellID)); +} + +string GetSchoolForSpell(int nSpellID) +{ + return Get2DACache("epicspells", "School", nSpellID); +} + +int GetR1ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq1", nSpellID)); +} + +int GetR2ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq2", nSpellID)); +} + +int GetR3ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq3", nSpellID)); +} + +int GetR4ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq4", nSpellID)); +} + +int GetS1ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed1", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS2ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed2", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS3ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed3", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS4ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed4", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS5ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed5", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetSpellFromAbrev(string sAbrev) +{ + sAbrev = GetStringLowerCase(sAbrev); + if(GetStringLeft(sAbrev, 8) == "epic_sp_") + sAbrev = GetStringRight(sAbrev, GetStringLength(sAbrev)-8); + if(DEBUG) DoDebug("sAbrew to check vs: " + sAbrev); + int i = 0; + string sLabel = GetStringLowerCase(Get2DACache("epicspells", "LABEL", i)); + while(sLabel != "") + { + if(DEBUG) DoDebug("sLabel to check vs: " + sLabel); + if(sAbrev == sLabel) + { + if(DEBUG) DoDebug("SpellID: " + IntToString(i)); + return i; + } + i++; + sLabel = GetStringLowerCase(Get2DACache("epicspells", "LABEL", i)); + } + return -1; +} + +string GetNameForSpell(int nSpellID) +{ + int nFeat = GetFeatForSpell(nSpellID); + string sName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))); + return sName; +} \ No newline at end of file diff --git a/trunk/include/inc_newspellbook.nss b/trunk/include/inc_newspellbook.nss new file mode 100644 index 00000000..57217e0f --- /dev/null +++ b/trunk/include/inc_newspellbook.nss @@ -0,0 +1,1340 @@ + + +/* Steps for adding a new spellbook + +Prepared: +Make cls_spgn_*.2da +Make cls_spcr_*.2da +Make blank cls_spell_*.2da +Add cls_spgn_*.2da to classes.2da +Add class entry in prc_classes.2da +Add the spellbook feat (#1999) to cls_feat_*.2da at the appropriate level +Add class to GetSpellbookTypeForClass() below +Add class to GetAbilityScoreForClass() below +Add class to bKnowsAllClassSpells() below if necessary +Add class to GetIsArcaneClass() or GetIsDivineClass() in prc_inc_castlvl as appropriate +Add class to GetCasterLevelModifier() in prc_inc_castlvl if necessary +Add class to SetupLookupStage() in inc_lookups +Add class to GetCasterLvl() in prc_inc_spells +Add Practiced Spellcaster feat to feat.2da and to PracticedSpellcasting() in prc_inc_castlvl +Run the assemble_spellbooks.bat file +Make the prc_* scripts in newspellbook. The filenames can be found under the spell entries for the class in spells.2da. + +Spont: +Make cls_spgn_*.2da +Make cls_spkn_*.2da +Make cls_spcr_*.2da +Make blank cls_spell_*.2da +Add cls_spkn_*.2da and cls_spgn_*.2da to classes.2da +Add class entry in prc_classes.2da +Add class to GetSpellbookTypeForClass() below +Add class to GetAbilityScoreForClass() below +Add class to bKnowsAllClassSpells() below if necessary +Add class to GetIsArcaneClass() or GetIsDivineClass() in prc_inc_castlvl as appropriate +Add class to GetCasterLevelModifier() in prc_inc_castlvl if necessary +Add class to SetupLookupStage() in inc_lookups +Add class to GetCasterLvl() in prc_inc_spells +Add Practiced Spellcaster feat to feat.2da and to PracticedSpellcasting() in prc_inc_castlvl +Add class to prc_amagsys_gain if(CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, MinimumSpellLevel, MaximumSpellLevel)) +Add class to ExecuteScript("prc_amagsys_gain", oPC) list in EvalPRCFeats in prc_inc_function +Run the assemble_spellbooks.bat file +Make the prc_* scripts in newspellbook + +prc_classes.2da entry: +Label - name for the class +Name - tlk file strref +SpellCaster - does the class cast spells? 0 = No, 1 = Yes (used for bonus spellslot item properties) +SBType - S = spontaneous, P = prepared +AL - does the class use Advanced Learning of any type? 0 = No, 1 = Yes +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int GetSpellbookTypeForClass(int nClass); +int GetAbilityScoreForClass(int nClass, object oPC); + +/** + * Determines the given character's DC-modifying ability modifier for + * the given class' spells. Handles split-score casters. + * + * @param nClass The spellcasting class for whose spells to determine ability mod to DC for + * @param oPC The character whose abilities to examine + * @return The DC-modifying ability score's ability modifier value + */ +int GetDCAbilityModForClass(int nClass, object oPC); + +string GetFileForClass(int nClass); +int GetSpellslotLevel(int nClass, object oPC); +int GetItemBonusSlotCount(object oPC, int nClass, int nSpellLevel); +int GetSlotCount(int nLevel, int nSpellLevel, int nAbilityScore, int nClass, object oItemPosessor = OBJECT_INVALID); +int bKnowsAllClassSpells(int nClass); +int GetSpellKnownMaxCount(int nLevel, int nSpellLevel, int nClass, object oPC); +int GetSpellKnownCurrentCount(object oPC, int nSpellLevel, int nClass); +int GetSpellUnknownCurrentCount(object oPC, int nSpellLevel, int nClass); +void AddSpellUse(object oPC, int nSpellbookID, int nClass, string sFile, string sArrayName, int nSpellbookType, object oSkin, int nFeatID, int nIPFeatID, string sIDX = ""); +void RemoveSpellUse(object oPC, int nSpellID, int nClass); +// int GetSpellUses(object oPC, int nSpellID, int nClass); +int GetSpellLevel(int nSpellID, int nClass); +void SetupSpells(object oPC, int nClass); +void CheckAndRemoveFeat(object oHide, itemproperty ipFeat); +void WipeSpellbookHideFeats(object oPC); +void CheckNewSpellbooks(object oPC); +void NewSpellbookSpell(int nClass, int nSpellbookType, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE); +void CastSpontaneousSpell(int nClass, int bInstantSpell = FALSE); +void CastPreparedSpell(int nClass, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/* stored in "prc_inc_sb_const" + Accessed via "prc_inc_core" */ + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +// ** THIS ORDER IS IMPORTANT ** + +//#include "prc_effect_inc" //access via prc_inc_core +//#include "inc_lookups" //access via prc_inc_core +#include "prc_inc_core" +#include "inc_sp_gain_mem" //providing child access to prc_inc_core + //Must load in this order. +//#include "prc_inc_castlvl" //access via prc_inc_core +//#include "prc_inc_descrptr" //access via prc_inc_core + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetSpellbookTypeForClass(int nClass) +{ + switch(nClass) + { + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOHEI: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + case CLASS_TYPE_VIGILANT: + case CLASS_TYPE_WIZARD: + return SPELLBOOK_TYPE_PREPARED; + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BARD: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HARPER: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_WEAVE: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + return SPELLBOOK_TYPE_SPONTANEOUS; + // shapechanger HD count as sorcerer for aranea. + case CLASS_TYPE_SHAPECHANGER: + return SPELLBOOK_TYPE_SPONTANEOUS; + // Multiple races + case CLASS_TYPE_MONSTROUS: + return SPELLBOOK_TYPE_SPONTANEOUS; + // Gloura as Bard + case CLASS_TYPE_FEY: + return SPELLBOOK_TYPE_SPONTANEOUS; + // Drider as Sorc + case CLASS_TYPE_ABERRATION: + return SPELLBOOK_TYPE_SPONTANEOUS; + //outsider HD count as sorc for raks + case CLASS_TYPE_OUTSIDER: { + /// @todo Will eventually need to add a check here to differentiate between races. Not all are sorcerers, just most + return SPELLBOOK_TYPE_SPONTANEOUS; + } + } + return SPELLBOOK_TYPE_INVALID; +} + +int GetAbilityScoreForClass(int nClass, object oPC) +{ + switch(nClass) + { + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_FIST_OF_ZUOKEN: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_PSYWAR: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOHEI: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + case CLASS_TYPE_VIGILANT: + case CLASS_TYPE_WARMIND: + return GetAbilityScore(oPC, ABILITY_WISDOM); + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_PSION: + case CLASS_TYPE_PSYCHIC_ROGUE: + case CLASS_TYPE_SHADOWCASTER: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_WIZARD: + return GetAbilityScore(oPC, ABILITY_INTELLIGENCE); + case CLASS_TYPE_BARD: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HARPER: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_KNIGHT_WEAVE: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + case CLASS_TYPE_WILDER: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + //shapeshifter HD count as sorc for aranea + case CLASS_TYPE_SHAPECHANGER: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Multiple races + case CLASS_TYPE_MONSTROUS: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Gloura as Bard + case CLASS_TYPE_FEY: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Drider as Sorc + case CLASS_TYPE_ABERRATION: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + //outsider HD count as sorc for raks + case CLASS_TYPE_OUTSIDER: { + /// @todo Will eventually need to add a check here to differentiate between races. Not all are sorcerers, just most + return GetAbilityScore(oPC, ABILITY_CHARISMA); + } + } + return GetAbilityScore(oPC, ABILITY_CHARISMA); //default for SLAs? +} + +int GetDCAbilityModForClass(int nClass, object oPC) +{ + switch(nClass) + { + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_FIST_OF_ZUOKEN: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_PSYWAR: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOHEI: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + case CLASS_TYPE_VIGILANT: + case CLASS_TYPE_WARMIND: + return GetAbilityModifier(ABILITY_WISDOM, oPC); + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_PSION: + case CLASS_TYPE_PSYCHIC_ROGUE: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_WIZARD: + return GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + case CLASS_TYPE_BARD: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_HARPER: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_SHADOWCASTER: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + case CLASS_TYPE_WILDER: + return GetAbilityModifier(ABILITY_CHARISMA, oPC); + //shapechanger HD count as sorc for aranea + case CLASS_TYPE_SHAPECHANGER: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Multiple races + case CLASS_TYPE_MONSTROUS: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Gloura as Bard + case CLASS_TYPE_FEY: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Drider as Sorc + case CLASS_TYPE_ABERRATION: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + //outsider HD count as sorc for raks + case CLASS_TYPE_OUTSIDER: { + /// @todo Will eventually need to add a check here to differentiate between races. Not all are sorcerers, just most + return GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + } + return GetAbilityModifier(ABILITY_CHARISMA, oPC); //default for SLAs? +} + +string GetFileForClass(int nClass) +{ + string sFile = Get2DACache("classes", "FeatsTable", nClass); + sFile = "cls_spell" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061231 + //if(DEBUG) DoDebug("GetFileForClass(" + IntToString(nClass) + ") = " + sFile); + return sFile; +} + +int GetSpellslotLevel(int nClass, object oPC) +{ + int nLevel = GetLevelByClass(nClass, oPC); + + //Raks cast as sorcs + if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + nLevel = GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC); + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) //Arkamoi cast as sorcs + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC); + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) //Driders cast as sorcs + nLevel = GetLevelByClass(CLASS_TYPE_ABERRATION, oPC); + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) //Redspawn Arcaniss cast as 3/4 sorcs + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC)*3/4; + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT) //Marrutact cast as 6/7 sorcs + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC)*6/7; + else if(nClass == CLASS_TYPE_BARD && !GetLevelByClass(CLASS_TYPE_BARD, oPC) && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) //Gloura cast as bards + nLevel = GetLevelByClass(CLASS_TYPE_FEY, oPC); + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) //Aranea cast as sorcs + nLevel = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oPC); + + int nArcSpellslotLevel; + int nDivSpellslotLevel; + int i; + for(i = 1; i <= 8; i++) + { + int nTempClass = GetClassByPosition(i, oPC); + //spellcasting prc + int nArcSpellMod = StringToInt(Get2DACache("classes", "ArcSpellLvlMod", nTempClass)); + int nDivSpellMod = StringToInt(Get2DACache("classes", "DivSpellLvlMod", nTempClass)); + /*//special case for combat medic class + if(nTempClass == CLASS_TYPE_COMBAT_MEDIC && (nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_WITCH)) + nArcSpellMod = 1;*/ + + if(nArcSpellMod == 1) + nArcSpellslotLevel += GetLevelByClass(nTempClass, oPC); + else if(nArcSpellMod > 1) + nArcSpellslotLevel += (GetLevelByClass(nTempClass, oPC) + 1) / nArcSpellMod; + if(nDivSpellMod == 1) + nDivSpellslotLevel += GetLevelByClass(nTempClass, oPC); + else if(nDivSpellMod > 1) + nDivSpellslotLevel += (GetLevelByClass(nTempClass, oPC) + 1) / nDivSpellMod; + } + + if(GetPrimaryArcaneClass(oPC) == nClass) + nLevel += nArcSpellslotLevel; + if(GetPrimaryDivineClass(oPC) == nClass) + nLevel += nDivSpellslotLevel; + + // For this special instance, we know that this is the only prestige class + if (nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oPC)) + nLevel = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + + if(DEBUG) DoDebug("GetSpellslotLevel(" + IntToString(nClass) + ", " + GetName(oPC) + ") = " + IntToString(nLevel)); + return nLevel; +} + +int GetItemBonusSlotCount(object oPC, int nClass, int nSpellLevel) +{ + // Value maintained by CheckPRCLimitations() + return GetLocalInt(oPC, "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel)); +} + +int GetSlotCount(int nLevel, int nSpellLevel, int nAbilityScore, int nClass, object oItemPosessor = OBJECT_INVALID) +{ + // Ability score limit rule: Must have casting ability score of at least 10 + spel level to be able to cast spells of that level at all + if(nAbilityScore < nSpellLevel + 10) + return 0; + int nSlots; + string sFile; + /*// Bioware casters use their classes.2da-specified tables + if( nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER) + {*/ + sFile = Get2DACache("classes", "SpellGainTable", nClass); + /*} + // New spellbook casters use the cls_spbk_* tables + else + { + sFile = Get2DACache("classes", "FeatsTable", nClass); + sFile = "cls_spbk" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061231 + }*/ + + string sSlots = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nLevel - 1); + if(sSlots == "") + { + nSlots = -1; + //if(DEBUG) DoDebug("GetSlotCount: Problem getting slot numbers for " + IntToString(nSpellLevel) + " " + IntToString(nLevel) + " " + sFile); + } + else + nSlots = StringToInt(sSlots); + if(nSlots == -1) + return 0; + + // Add spell slots from items + if(GetIsObjectValid(oItemPosessor)) + nSlots += GetItemBonusSlotCount(oItemPosessor, nClass, nSpellLevel); + + // Add spell slots from high ability score. Level 0 spells are exempt + if(nSpellLevel == 0) + return nSlots; + else + { + int nAbilityMod = nClass == CLASS_TYPE_ARCHIVIST ? GetAbilityModifier(ABILITY_WISDOM, oItemPosessor) : (nAbilityScore - 10) / 2; + if(nAbilityMod >= nSpellLevel) // Need an ability modifier at least equal to the spell level to gain bonus slots + nSlots += ((nAbilityMod - nSpellLevel) / 4) + 1; + return nSlots; + } +} + +//if the class doesn't learn all available spells on level-up add it here +int bKnowsAllClassSpells(int nClass) +{ + switch(nClass) + { + //case CLASS_TYPE_WIZARD: + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BARD: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_WEAVE: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + return FALSE; + + // Everyone else + default: + return TRUE; + } + return TRUE; +} + +int GetSpellKnownMaxCount(int nLevel, int nSpellLevel, int nClass, object oPC) +{ + // If the character doesn't have any spell slots available on for this level, it can't know any spells of that level either + /// @todo Check rules. There might be cases where this doesn't hold + if(!GetSlotCount(nLevel, nSpellLevel, GetAbilityScoreForClass(nClass, oPC), nClass)) + return 0; + int nKnown; + string sFile; + // Bioware casters use their classes.2da-specified tables + /*if( nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER) + {*/ + sFile = Get2DACache("classes", "SpellKnownTable", nClass); + /*} + else + { + sFile = Get2DACache("classes", "FeatsTable", nClass); + sFile = "cls_spkn" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061231 + }*/ + + string sKnown = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nLevel - 1); + if(DEBUG) DoDebug("GetSpellKnownMaxCount(" + IntToString(nLevel) + ", " + IntToString(nSpellLevel) + ", " + IntToString(nClass) + ", " + GetName(oPC) + ") = " + sKnown); + if(sKnown == "") + { + nKnown = -1; + //if(DEBUG) DoDebug("GetSpellKnownMaxCount: Problem getting known numbers for " + IntToString(nSpellLevel) + " " + IntToString(nLevel) + " " + sFile); + } + else + nKnown = StringToInt(sKnown); + if(nKnown == -1) + return 0; + + // Bard and Sorcerer only have new spellbook spells known if they have taken prestige classes that increase spellcasting + if(nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_BARD) + { + if((GetLevelByClass(nClass) == nLevel) //no PrC + && !(GetHasFeat(FEAT_DRACONIC_GRACE, oPC) || GetHasFeat(FEAT_DRACONIC_BREATH, oPC))) //no Draconic feats that apply + return 0; + } + return nKnown; +} + +int GetSpellKnownCurrentCount(object oPC, int nSpellLevel, int nClass) +{ + // Check short-term cache + string sClassNum = IntToString(nClass); + if(GetLocalInt(oPC, "GetSKCCCache_" + IntToString(nSpellLevel) + "_" + sClassNum)) + return GetLocalInt(oPC, "GetSKCCCache_" + IntToString(nSpellLevel) + "_" + sClassNum) - 1; + + // Loop over all spells known and count the number of spells of each level known + int i; + int nKnown; + int nKnown0, nKnown1, nKnown2, nKnown3, nKnown4; + int nKnown5, nKnown6, nKnown7, nKnown8, nKnown9; + string sFile = GetFileForClass(nClass); + for(i = 0; i < persistant_array_get_size(oPC, "Spellbook" + sClassNum); i++) + { + int nNewSpellbookID = persistant_array_get_int(oPC, "Spellbook" + sClassNum, i); + int nLevel = StringToInt(Get2DACache(sFile, "Level", nNewSpellbookID)); + switch(nLevel) + { + case 0: nKnown0++; break; case 1: nKnown1++; break; + case 2: nKnown2++; break; case 3: nKnown3++; break; + case 4: nKnown4++; break; case 5: nKnown5++; break; + case 6: nKnown6++; break; case 7: nKnown7++; break; + case 8: nKnown8++; break; case 9: nKnown9++; break; + } + } + + // Pick the level requested for returning + switch(nSpellLevel) + { + case 0: nKnown = nKnown0; break; case 1: nKnown = nKnown1; break; + case 2: nKnown = nKnown2; break; case 3: nKnown = nKnown3; break; + case 4: nKnown = nKnown4; break; case 5: nKnown = nKnown5; break; + case 6: nKnown = nKnown6; break; case 7: nKnown = nKnown7; break; + case 8: nKnown = nKnown8; break; case 9: nKnown = nKnown9; break; + } + if(DEBUG) DoDebug("GetSpellKnownCurrentCount(" + GetName(oPC) + ", " + IntToString(nSpellLevel) + ", " + sClassNum + ") = " + IntToString(nKnown)); + if(DEBUG) DoDebug("GetSpellKnownCurrentCount(i " + IntToString(i) + ", nKnown0 " + IntToString(nKnown0) + ", nKnown1 " + IntToString(nKnown1) + ", nKnown2 " + IntToString(nKnown2) + ", nKnown3 " + IntToString(nKnown3) + ", nKnown4 " + IntToString(nKnown4) + ", nKnown5 " + IntToString(nKnown5) + ", nKnown6 " + IntToString(nKnown6) + ", nKnown7 " + IntToString(nKnown7) + ", nKnown8 " + IntToString(nKnown8) + ", nKnown9 " + IntToString(nKnown9)); + if(DEBUG) DoDebug("GetSpellKnownCurrentCount(persistant_array_get_size "+IntToString(persistant_array_get_size(oPC, "Spellbook" + sClassNum))); + + // Cache the values for 1 second + SetLocalInt(oPC, "GetSKCCCache_0_" + sClassNum, nKnown0 + 1); + SetLocalInt(oPC, "GetSKCCCache_1_" + sClassNum, nKnown1 + 1); + SetLocalInt(oPC, "GetSKCCCache_2_" + sClassNum, nKnown2 + 1); + SetLocalInt(oPC, "GetSKCCCache_3_" + sClassNum, nKnown3 + 1); + SetLocalInt(oPC, "GetSKCCCache_4_" + sClassNum, nKnown4 + 1); + SetLocalInt(oPC, "GetSKCCCache_5_" + sClassNum, nKnown5 + 1); + SetLocalInt(oPC, "GetSKCCCache_6_" + sClassNum, nKnown6 + 1); + SetLocalInt(oPC, "GetSKCCCache_7_" + sClassNum, nKnown7 + 1); + SetLocalInt(oPC, "GetSKCCCache_8_" + sClassNum, nKnown8 + 1); + SetLocalInt(oPC, "GetSKCCCache_9_" + sClassNum, nKnown9 + 1); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_0_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_1_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_2_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_3_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_4_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_5_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_6_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_7_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_8_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_9_" + sClassNum)); + + return nKnown; +} + +int GetSpellUnknownCurrentCount(object oPC, int nSpellLevel, int nClass) +{ + // Get the lookup token created by MakeSpellbookLevelLoop() + string sTag = "SpellLvl_" + IntToString(nClass) + "_Level_" + IntToString(nSpellLevel); + object oCache = GetObjectByTag(sTag); + if(!GetIsObjectValid(oCache)) + { + if(DEBUG) DoDebug("GetSpellUnknownCurrentCount: " + sTag + " is not valid"); + return 0; + } + // Read the total number of spells on the given level and determine how many are already known + int nTotal = array_get_size(oCache, "Lkup"); + int nKnown = GetSpellKnownCurrentCount(oPC, nSpellLevel, nClass); + int nUnknown = nTotal - nKnown; + + if(DEBUG) DoDebug("GetSpellUnknownCurrentCount(" + GetName(oPC) + ", " + IntToString(nSpellLevel) + ", " + IntToString(nClass) + ") = " + IntToString(nUnknown)); + return nUnknown; +} + +void AddSpellUse(object oPC, int nSpellbookID, int nClass, string sFile, string sArrayName, int nSpellbookType, object oSkin, int nFeatID, int nIPFeatID, string sIDX = "") +{ + /* + string sFile = GetFileForClass(nClass); + string sArrayName = "NewSpellbookMem_"+IntToString(nClass); + int nSpellbookType = GetSpellbookTypeForClass(nClass); + object oSkin = GetPCSkin(oPC); + int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); + //add the feat only if they dont already have it + int nIPFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); + */ + object oToken = GetHideToken(oPC); + + // Add the spell use feats and set a marker local that tells for CheckAndRemoveFeat() to skip removing this feat + string sIPFeatID = IntToString(nIPFeatID); + SetLocalInt(oSkin, "NewSpellbookTemp_" + sIPFeatID, TRUE); + AddSkinFeat(nFeatID, nIPFeatID, oSkin, oPC); + + // Increase the current number of uses + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + //sanity test + if(!persistant_array_exists(oPC, sArrayName)) + { + if(DEBUG) DoDebug("ERROR: AddSpellUse: " + sArrayName + " array does not exist, creating"); + persistant_array_create(oPC, sArrayName); + } + + int nUses = persistant_array_get_int(oPC, sArrayName, nSpellbookID); + nUses++; + persistant_array_set_int(oPC, sArrayName, nSpellbookID, nUses); + if(DEBUG) DoDebug("AddSpellUse: " + sArrayName + "[" + IntToString(nSpellbookID) + "] = " + IntToString(array_get_int(oPC, sArrayName, nSpellbookID))); + + //Create index array - to avoid duplicates mark only 1st use of nSpellbookID + if(nUses == 1) + { + if(!persistant_array_exists(oPC, sIDX)) + persistant_array_create(oPC, sIDX); + + persistant_array_set_int(oPC, sIDX, array_get_size(oPC, sIDX), nSpellbookID); + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + //sanity test + if(!persistant_array_exists(oPC, sArrayName)) + { + if(DEBUG) DoDebug("ERROR: AddSpellUse: " + sArrayName + " array does not exist, creating"); + persistant_array_create(oPC, sArrayName); + } + /*int nSpellLevel = StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + int nCount = persistant_array_get_int(oPC, sArrayName, nSpellLevel); + if(nCount < 1) + { + int nLevel = GetSpellslotLevel(nClass, oPC); + int nAbility = GetAbilityScoreForClass(nClass, oPC); + nCount = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + array_set_int(oPC, sArrayName, nSpellLevel, nCount); + }*/ + if(DEBUG) DoDebug("AddSpellUse() called on spontaneous spellbook. nIPFeatID = " + sIPFeatID); + } +} + +void RemoveSpellUse(object oPC, int nSpellID, int nClass) +{ + string sFile = GetFileForClass(nClass); + int nSpellbookID = SpellToSpellbookID(nSpellID); + if(nSpellbookID == -1) + { + if(DEBUG) DoDebug("ERROR: RemoveSpellUse: Unable to resolve spell to spellbookID: " + IntToString(nSpellID) + " in file " + sFile); + return; + } + if(!persistant_array_exists(oPC, "NewSpellbookMem_"+IntToString(nClass))) + { + if(DEBUG) DoDebug("RemoveSpellUse: NewSpellbookMem_" + IntToString(nClass) + " does not exist, creating."); + persistant_array_create(oPC, "NewSpellbookMem_"+IntToString(nClass)); + } + + // Reduce the remaining uses of the given spell by 1 (except never reduce uses below 0). + // Spontaneous spellbooks reduce the number of spells of the spell's level remaining + int nSpellbookType = GetSpellbookTypeForClass(nClass); + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + int nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellbookID); + if(nCount > 0) + persistant_array_set_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellbookID, nCount - 1); + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + int nSpellLevel = StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + int nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(nCount > 0) + persistant_array_set_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel, nCount - 1); + } +} + +int GetSpellLevel(int nSpellID, int nClass) +{ + string sFile = GetFileForClass(nClass); + int nSpellbookID = SpellToSpellbookID(nSpellID); + if(nSpellbookID == -1) + { + if(DEBUG) DoDebug("ERROR: GetSpellLevel: Unable to resolve spell to spellbookID: "+IntToString(nSpellID)+" "+sFile); + return -1; + } + + // get spell level + int nSpellLevel = -1; + string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookID); + + if (sSpellLevel != "") + nSpellLevel = StringToInt(sSpellLevel); + + return nSpellLevel; +} + +//called inside for loop in SetupSpells(), delayed to prevent TMI +void SpontaneousSpellSetupLoop(object oPC, int nClass, string sFile, object oSkin, int i) +{ + int nSpellbookID = persistant_array_get_int(oPC, "Spellbook" + IntToString(nClass), i); + string sIPFeatID = Get2DACache(sFile, "IPFeatID", nSpellbookID); + int nIPFeatID = StringToInt(sIPFeatID); + int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); + //int nRealSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", nSpellbookID)); + SetLocalInt(oSkin, "NewSpellbookTemp_" + sIPFeatID, TRUE); + + AddSkinFeat(nFeatID, nIPFeatID, oSkin, oPC); +} + +void SetupSpells(object oPC, int nClass) +{ + string sFile = GetFileForClass(nClass); + string sClass = IntToString(nClass); + string sArrayName = "NewSpellbookMem_" + sClass; + object oSkin = GetPCSkin(oPC); + int nLevel = GetSpellslotLevel(nClass, oPC); + int nAbility = GetAbilityScoreForClass(nClass, oPC); + int nSpellbookType = GetSpellbookTypeForClass(nClass); + + if(DEBUG) DoDebug("SetupSpells\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nSpellslotLevel = " + IntToString(nLevel) + "\n" + + "nAbility = " + IntToString(nAbility) + "\n" + + "nSpellbookType = " + IntToString(nSpellbookType) + "\n" + + "sFile = " + sFile + "\n" + ); + + // For spontaneous spellbooks, set up an array that tells how many spells of each level they can cast + // And add casting feats for each spell known to the caster's hide + + if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + // Spell slots + int nSpellLevel, nSlots; + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + nSlots = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + persistant_array_set_int(oPC, sArrayName, nSpellLevel, nSlots); + } + + int i; + for(i = 0; i < persistant_array_get_size(oPC, "Spellbook" + sClass); i++) + { //adding feats + SpontaneousSpellSetupLoop(oPC, nClass, sFile, oSkin, i); + } + }// end if - Spontaneous spellbook + + // For prepared spellbooks, add spell uses and use feats according to spells memorised list + else if(nSpellbookType == SPELLBOOK_TYPE_PREPARED && !GetIsBioSpellCastClass(nClass)) + { + int nSpellLevel, nSlot, nSlots, nSpellbookID; + string sArrayName2, sIDX; + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + sArrayName2 = "Spellbook" + IntToString(nSpellLevel) + "_" + sClass; // Minor optimisation: cache the array name string for multiple uses + sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + sClass; + nSlots = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + nSlot; + for(nSlot = 0; nSlot < nSlots; nSlot++) + { + //done when spells are added to it + nSpellbookID = persistant_array_get_int(oPC, sArrayName2, nSlot); + if(nSpellbookID != 0) + { + AddSpellUse(oPC, nSpellbookID, nClass, sFile, sArrayName, nSpellbookType, oSkin, + StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)), + StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)), + sIDX); + } + } + } + } +} + +void CheckAndRemoveFeat(object oHide, itemproperty ipFeat) +{ + int nSubType = GetItemPropertySubType(ipFeat); + if(!GetLocalInt(oHide, "NewSpellbookTemp_" + IntToString(nSubType))) + { + RemoveItemProperty(oHide, ipFeat); + DeleteLocalInt(oHide, "NewSpellbookTemp_" + IntToString(nSubType)); + if(DEBUG) DoDebug("CheckAndRemoveFeat: DeleteLocalInt(oHide, NewSpellbookTemp_" + IntToString(nSubType) + ");"); + if(DEBUG) DoDebug("CheckAndRemoveFeat: Removing item property"); + } + else + { + DeleteLocalInt(oHide, "NewSpellbookTemp_" + IntToString(nSubType)); + if(DEBUG) DoDebug("CheckAndRemoveFeat: DeleteLocalInt(oHide, NewSpellbookTemp_" + IntToString(nSubType) + ");"); + } +} + +void WipeSpellbookHideFeats(object oPC) +{ + object oHide = GetPCSkin(oPC); + itemproperty ipTest = GetFirstItemProperty(oHide); + while(GetIsItemPropertyValid(ipTest)) + { + int nSubType = GetItemPropertySubType(ipTest); + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + ((nSubType > SPELLBOOK_IPRP_FEATS_START && nSubType < SPELLBOOK_IPRP_FEATS_END) || + (nSubType > SPELLBOOK_IPRP_FEATS_START2 && nSubType < SPELLBOOK_IPRP_FEATS_END2)) + ) + { + DelayCommand(1.0f, CheckAndRemoveFeat(oHide, ipTest)); + } + ipTest = GetNextItemProperty(oHide); + } +} + +void CheckNewSpellbooks(object oPC) +{ + WipeSpellbookHideFeats(oPC); + int i; + for(i = 1; i <= 8; i++) + { + int nClass = GetClassByPosition(i, oPC); + int nLevel = GetLevelByClass(nClass, oPC); + + if(DEBUG) DoDebug("CheckNewSpellbooks\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + //if bard/sorc newspellbook is disabled after selecting + //remove those from radial + if( (GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK) && nClass == CLASS_TYPE_BARD) + ||(GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK) && nClass == CLASS_TYPE_SORCERER)) + { + //do nothing + } + else if(nLevel) + { + //Aranea cast as sorcs + if(nClass == CLASS_TYPE_SHAPECHANGER + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) + nClass = CLASS_TYPE_SORCERER; + //raks cast as sorcs + if(nClass == CLASS_TYPE_OUTSIDER + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + nClass = CLASS_TYPE_SORCERER; + + //Arkamoi cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) + nClass = CLASS_TYPE_SORCERER; + + //Redspawn cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) + nClass = CLASS_TYPE_SORCERER; + + //Marrutact cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT) + nClass = CLASS_TYPE_SORCERER; + + //Driders cast as sorcs + if(nClass == CLASS_TYPE_ABERRATION + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) + nClass = CLASS_TYPE_SORCERER; + + //Gloura cast as bards + if(nClass == CLASS_TYPE_FEY + && !GetLevelByClass(CLASS_TYPE_BARD, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) + nClass = CLASS_TYPE_BARD; + //remove persistant locals used to track when all spells cast + string sArrayName = "NewSpellbookMem_"+IntToString(nClass); + if(persistant_array_exists(oPC, sArrayName)) + { + if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED) + { + int nSpellLevel, i, Max; + string sIDX, sSpellbookID, sClass = IntToString(nClass); + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + sClass; + Max = persistant_array_get_size(oPC, sIDX); + for(i = 0; i < Max; i++) + { + sSpellbookID = persistant_array_get_string(oPC, sIDX, i); + if(sSpellbookID != "") + { + DeletePersistantLocalString(oPC, sArrayName+"_"+sSpellbookID); + } + } + persistant_array_delete(oPC, sIDX); + } + } + else + { + persistant_array_delete(oPC, sArrayName); + persistant_array_create(oPC, sArrayName); + } + } + //delay it so wipespellbookhidefeats has time to start to run + //but before the deletes actually happen + DelayCommand(0.1, SetupSpells(oPC, nClass)); + } + } +} + +//NewSpellbookSpell() helper functions +int bTargetingAllowed(int nSpellID); +void CheckPrepSlots(int nClass, int nSpellID, int nSpellbookID, int bIsAction = FALSE); +void CheckSpontSlots(int nClass, int nSpellID, int nSpellSlotLevel, int bIsAction = FALSE); +void DoCleanUp(int nMetamagic); + +void CastSpontaneousSpell(int nClass, int bInstantSpell = FALSE) +{ + //get the spellbook ID + int nFakeSpellID = GetSpellId(); + int nSpellID = GetPowerFromSpellID(nFakeSpellID); + if(nSpellID == -1) nSpellID = 0; + + //Check the target first + if(!bTargetingAllowed(nSpellID)) + return; + + // if OBJECT_SELF is fighting - stop fighting and cast spell + if(GetCurrentAction() == ACTION_ATTACKOBJECT) + ClearAllActions(); + + //if its a subradial spell, get the master + int nMasterFakeSpellID = StringToInt(Get2DACache("spells", "Master", nFakeSpellID)); + if(!nMasterFakeSpellID) + nMasterFakeSpellID = nFakeSpellID; + + int nSpellbookID = SpellToSpellbookID(nMasterFakeSpellID); + + // Paranoia - It should not be possible to get here without having the spells available array existing + if(!persistant_array_exists(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass))) + { + if(DEBUG) DoDebug("ERROR: NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + " array does not exist"); + persistant_array_create(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass)); + } + + int nSpellLevel = StringToInt(Get2DACache(GetFileForClass(nClass), "Level", nSpellbookID)); + + // Make sure the caster has uses of this spell remaining + // 2009-9-20: Add metamagic feat abilities. -N-S + int nMetamagic = GetLocalInt(OBJECT_SELF, "MetamagicFeatAdjust"); + if(nMetamagic) + { + //Need to check if metamagic can be applied to a spell + int nMetaTest; + int nMetaType = HexToInt(Get2DACache("spells", "MetaMagic", nSpellID)); + + int nSpellSlotLevel = nSpellLevel; + switch(nMetamagic) + { + case METAMAGIC_NONE: nMetaTest = 1; break; //no need to change anything + case METAMAGIC_EMPOWER: nMetaTest = nMetaType & 1; nSpellLevel += 2; break; + case METAMAGIC_EXTEND: nMetaTest = nMetaType & 2; nSpellLevel += 1; break; + case METAMAGIC_MAXIMIZE: nMetaTest = nMetaType & 4; nSpellLevel += 3; break; + case METAMAGIC_QUICKEN: nMetaTest = nMetaType & 8; nSpellLevel += 4; break; + case METAMAGIC_SILENT: nMetaTest = nMetaType & 16; nSpellLevel += 1; break; + case METAMAGIC_STILL: nMetaTest = nMetaType & 32; nSpellLevel += 1; break; + } + + if(!nMetaTest)//can't use selected metamagic with this spell + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(OBJECT_SELF, "You can't use "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)))+"with selected metamagic.")); + nSpellLevel = nSpellSlotLevel; + } + else if(nSpellLevel > 9)//now test the spell level + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(OBJECT_SELF, "Modified spell level is to high! Casting spell without metamagic")); + nSpellLevel = nSpellSlotLevel; + } + else if(GetLocalInt(OBJECT_SELF, "PRC_metamagic_state") == 1) + SetLocalInt(OBJECT_SELF, "MetamagicFeatAdjust", 0); + } + + CheckSpontSlots(nClass, nSpellID, nSpellLevel); + if(GetLocalInt(OBJECT_SELF, "NSB_Cast")) + ActionDoCommand(CheckSpontSlots(nClass, nSpellID, nSpellLevel, TRUE)); + else + return; + + // Calculate DC. 10 + spell level on the casting class's list + DC increasing ability mod + //int nDC = 10 + nSpellLevel + GetDCAbilityModForClass(nClass, OBJECT_SELF); + // This is wrong and is breaking things, and is already calculated in the function it calls anyway - Strat + + //remove any old effects + //seems cheat-casting breaks hardcoded removal + //and cant remove effects because I dont know all the targets! + if(!bInstantSpell) + { + //Handle quicken metamagic and Duskblade's Quick Cast + if((nMetamagic & METAMAGIC_QUICKEN) || GetLocalInt(OBJECT_SELF, "QuickCast")) + { + //Adding Auto-Quicken III - deleted after casting has finished. + object oSkin = GetPCSkin(OBJECT_SELF); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpellID)) + StringToInt(Get2DACache("spells", "CastTime", nSpellID)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + DeleteLocalInt(OBJECT_SELF, "QuickCast"); + } + } + + //cast the spell + //dont need to override level, the spellscript will calculate it + //class is read from "NSB_Class" + ActionCastSpell(nSpellID, 0, -1, 0, nMetamagic, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstantSpell); + + //Clean up + ActionDoCommand(DoCleanUp(nMetamagic)); +} + +void CastPreparedSpell(int nClass, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE) +{ + object oPC = OBJECT_SELF; + + //get the spellbook ID + int nFakeSpellID = GetSpellId(); + int nSpellID = GetPowerFromSpellID(nFakeSpellID); + if(nSpellID == -1) nSpellID = 0; + + //Check the target first + if(!bTargetingAllowed(nSpellID)) + return; + + // if OBJECT_SELF is fighting - stop fighting and cast spell + if(GetCurrentAction() == ACTION_ATTACKOBJECT) + ClearAllActions(); + + //if its a subradial spell, get the master + int nMasterFakeSpellID = StringToInt(Get2DACache("spells", "Master", nFakeSpellID)); + if(!nMasterFakeSpellID) + nMasterFakeSpellID = nFakeSpellID; + + int nSpellbookID = SpellToSpellbookID(nMasterFakeSpellID); + + // Paranoia - It should not be possible to get here without having the spells available array existing + if(!persistant_array_exists(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass))) + { + if(DEBUG) DoDebug("ERROR: NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + " array does not exist"); + persistant_array_create(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass)); + } + + int nSpellLevel = StringToInt(Get2DACache(GetFileForClass(nClass), "Level", nSpellbookID)); + + // Make sure the caster has uses of this spell remaining + CheckPrepSlots(nClass, nSpellID, nSpellbookID); + if(GetLocalInt(OBJECT_SELF, "NSB_Cast")) + ActionDoCommand(CheckPrepSlots(nClass, nSpellID, nSpellbookID, TRUE)); + else + return; + + // Calculate DC. 10 + spell level on the casting class's list + DC increasing ability mod + //int nDC = 10 + nSpellLevel + GetDCAbilityModForClass(nClass, OBJECT_SELF); + // This is wrong and is breaking things, and is already calculated in the function it calls anyway - Strat + + //remove any old effects + //seems cheat-casting breaks hardcoded removal + //and cant remove effects because I dont know all the targets! + if(!bInstantSpell) + { + //Handle quicken metamagic and Duskblade's Quick Cast + if((nMetamagic & METAMAGIC_QUICKEN) || GetLocalInt(OBJECT_SELF, "QuickCast")) + { + //Adding Auto-Quicken III - deleted after casting has finished. + object oSkin = GetPCSkin(OBJECT_SELF); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpellID)) + StringToInt(Get2DACache("spells", "CastTime", nSpellID)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + DeleteLocalInt(OBJECT_SELF, "QuickCast"); + } + else if(nClass == CLASS_TYPE_HEALER) + { + if(GetHasFeat(FEAT_EFFORTLESS_HEALING) + && GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) + { + object oSkin = GetPCSkin(OBJECT_SELF); + //all spells from healing subschool except Close Wounds have casting time of 2.5s + float fCastDur = nSpellID == SPELL_CLOSE_WOUNDS ? 1.0f : 2.5f; + itemproperty ipImpCombatCast = ItemPropertyBonusFeat(IP_CONST_NSB_IMP_COMBAT_CAST); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipImpCombatCast, oSkin, fCastDur)); + } + } + } + + //cast the spell + //dont need to override level, the spellscript will calculate it + //class is read from "NSB_Class" + ActionCastSpell(nSpellID, 0, -1, 0, nMetamagic, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstantSpell); + + //Clean up + ActionDoCommand(DoCleanUp(nMetamagic)); +} + +void NewSpellbookSpell(int nClass, int nSpellbookType, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE) +{ + object oPC = OBJECT_SELF; + + // if oPC is fighting - stop fighting and cast spell + if(GetCurrentAction(oPC) == ACTION_ATTACKOBJECT) + ClearAllActions(); + + //get the spellbook ID + int nFakeSpellID = GetSpellId(); + int nSpellID = GetPowerFromSpellID(nFakeSpellID); + if(nSpellID == -1) nSpellID = 0; + + //Check the target first + if(!bTargetingAllowed(nSpellID)) + return; + + //if its a subradial spell, get the master + int nMasterFakeSpellID = StringToInt(Get2DACache("spells", "Master", nFakeSpellID)); + if(!nMasterFakeSpellID) + nMasterFakeSpellID = nFakeSpellID; + + int nSpellbookID = SpellToSpellbookID(nMasterFakeSpellID); + + // Paranoia - It should not be possible to get here without having the spells available array existing + if(!persistant_array_exists(oPC, "NewSpellbookMem_" + IntToString(nClass))) + { + if(DEBUG) DoDebug("ERROR: NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + " array does not exist"); + persistant_array_create(oPC, "NewSpellbookMem_" + IntToString(nClass)); + } + + string sFile = GetFileForClass(nClass); + int nSpellLevel = StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + + // Make sure the caster has uses of this spell remaining + // 2009-9-20: Add metamagic feat abilities. -N-S + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + CheckPrepSlots(nClass, nSpellID, nSpellbookID); + if(GetLocalInt(oPC, "NSB_Cast")) + ActionDoCommand(CheckPrepSlots(nClass, nSpellID, nSpellbookID, TRUE)); + else + return; + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nMetamagic = GetLocalInt(oPC, "MetamagicFeatAdjust"); + if(nMetamagic) + { + //Need to check if metamagic can be applied to a spell + int nMetaTest; + int nMetaType = HexToInt(Get2DACache("spells", "MetaMagic", nSpellID)); + + int nSpellSlotLevel = nSpellLevel; + switch(nMetamagic) + { + case METAMAGIC_NONE: nMetaTest = 1; break; //no need to change anything + case METAMAGIC_EMPOWER: nMetaTest = nMetaType & 1; nSpellLevel += 2; break; + case METAMAGIC_EXTEND: nMetaTest = nMetaType & 2; nSpellLevel += 1; break; + case METAMAGIC_MAXIMIZE: nMetaTest = nMetaType & 4; nSpellLevel += 3; break; + case METAMAGIC_QUICKEN: nMetaTest = nMetaType & 8; nSpellLevel += 4; break; + case METAMAGIC_SILENT: nMetaTest = nMetaType & 16; nSpellLevel += 1; break; + case METAMAGIC_STILL: nMetaTest = nMetaType & 32; nSpellLevel += 1; break; + } + + if(!nMetaTest)//can't use selected metamagic with this spell + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "You can't use "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)))+"with selected metamagic.")); + nSpellLevel = nSpellSlotLevel; + } + else if(nSpellLevel > 9)//now test the spell level + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "Modified spell level is to high! Casting spell without metamagic")); + nSpellLevel = nSpellSlotLevel; + } + else if(GetLocalInt(oPC, "PRC_metamagic_state") == 1) + SetLocalInt(oPC, "MetamagicFeatAdjust", 0); + } + + CheckSpontSlots(nClass, nSpellID, nSpellLevel); + if(GetLocalInt(oPC, "NSB_Cast")) + ActionDoCommand(CheckSpontSlots(nClass, nSpellID, nSpellLevel, TRUE)); + else + return; + } + + // Calculate DC. 10 + spell level on the casting class's list + DC increasing ability mod + //int nDC = 10 + nSpellLevel + GetDCAbilityModForClass(nClass, OBJECT_SELF); + // This is wrong and is breaking things, and is already calculated in the function it calls anyway - Strat + + //remove any old effects + //seems cheat-casting breaks hardcoded removal + //and cant remove effects because I dont know all the targets! + if(!bInstantSpell) + { + //Handle quicken metamagic and Duskblade's Quick Cast + if((nMetamagic & METAMAGIC_QUICKEN) || GetLocalInt(oPC, "QuickCast")) + { + //Adding Auto-Quicken III - deleted after casting has finished. + object oSkin = GetPCSkin(oPC); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpellID)) + StringToInt(Get2DACache("spells", "CastTime", nSpellID)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + DeleteLocalInt(oPC, "QuickCast"); + } + else if(nClass == CLASS_TYPE_HEALER) + { + if(GetHasFeat(FEAT_EFFORTLESS_HEALING) + && GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) + { + object oSkin = GetPCSkin(oPC); + //all spells from healing subschool except Close Wounds have casting time of 2.5s + float fCastDur = nSpellID == SPELL_CLOSE_WOUNDS ? 1.0f : 2.5f; + itemproperty ipImpCombatCast = ItemPropertyBonusFeat(IP_CONST_NSB_IMP_COMBAT_CAST); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipImpCombatCast, oSkin, fCastDur)); + } + } + } + + //cast the spell + //dont need to override level, the spellscript will calculate it + //class is read from "NSB_Class" + ActionCastSpell(nSpellID, 0, -1, 0, nMetamagic, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstantSpell); + + //Clean up + ActionDoCommand(DoCleanUp(nMetamagic)); +} + +int bTargetingAllowed(int nSpellID) +{ + object oTarget = GetSpellTargetObject(); + if(GetIsObjectValid(oTarget)) + { + int nTargetType = ~(HexToInt(Get2DACache("spells", "TargetType", nSpellID))); + + //test targetting self + if(oTarget == OBJECT_SELF) + { + if(nTargetType & 1) + { + if(DEBUG) DoDebug("bTargetingAllowed: You cannot target yourself."); + return FALSE; + } + } + //test targetting others + else if(GetObjectType(oTarget) == OBJECT_TYPE_CREATURE) + { + if(nTargetType & 2) + { + if(DEBUG) DoDebug("bTargetingAllowed: You cannot target creatures."); + return FALSE; + } + } + } + return TRUE; +} + +void CheckPrepSlots(int nClass, int nSpellID, int nSpellbookID, int bIsAction = FALSE) +{ + DeleteLocalInt(OBJECT_SELF, "NSB_Cast"); + int nCount = persistant_array_get_int(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass), nSpellbookID); + if(DEBUG) DoDebug("NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(nSpellbookID) + "] = " + IntToString(nCount)); + if(nCount < 1) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID))); + // "You have no castings of " + sSpellName + " remaining" + string sMessage = ReplaceChars(GetStringByStrRef(16828411), "", sSpellName); + + FloatingTextStringOnCreature(sMessage, OBJECT_SELF, FALSE); + if(bIsAction) + ClearAllActions(); + } + else + { + SetLocalInt(OBJECT_SELF, "NSB_Cast", 1); + if(bIsAction) + { + SetLocalInt(OBJECT_SELF, "NSB_Class", nClass); + SetLocalInt(OBJECT_SELF, "NSB_SpellbookID", nSpellbookID); + } + } +} + +void CheckSpontSlots(int nClass, int nSpellID, int nSpellSlotLevel, int bIsAction = FALSE) +{ + DeleteLocalInt(OBJECT_SELF, "NSB_Cast"); + int nCount = persistant_array_get_int(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass), nSpellSlotLevel); + if(DEBUG) DoDebug("NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(nSpellSlotLevel) + "] = " + IntToString(nCount)); + if(nCount < 1) + { + // "You have no castings of spells of level " + IntToString(nSpellLevel) + " remaining" + string sMessage = ReplaceChars(GetStringByStrRef(16828409), "", IntToString(nSpellSlotLevel)); + FloatingTextStringOnCreature(sMessage, OBJECT_SELF, FALSE); + if(bIsAction) + ClearAllActions(); + } + else + { + SetLocalInt(OBJECT_SELF, "NSB_Cast", 1); + if(bIsAction) + { + SetLocalInt(OBJECT_SELF, "NSB_Class", nClass); + SetLocalInt(OBJECT_SELF, "NSB_SpellLevel", nSpellSlotLevel); + } + } +} + +void DoCleanUp(int nMetamagic) +{ + if(nMetamagic & METAMAGIC_QUICKEN) + { + object oSkin = GetPCSkin(OBJECT_SELF); + RemoveItemProperty(oSkin, ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN)); + } + DeleteLocalInt(OBJECT_SELF, "NSB_Class"); + DeleteLocalInt(OBJECT_SELF, "NSB_SpellLevel"); + DeleteLocalInt(OBJECT_SELF, "NSB_SpellbookID"); +} \ No newline at end of file diff --git a/trunk/include/inv_inc_invfunc.nss b/trunk/include/inv_inc_invfunc.nss new file mode 100644 index 00000000..c78ec4d4 --- /dev/null +++ b/trunk/include/inv_inc_invfunc.nss @@ -0,0 +1,496 @@ +//:://///////////////////////////////////////////// +//:: Invocation include: Miscellaneous +//:: inv_inc_invfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Invocation implementation. + + Also acts as inclusion nexus for the general + invocation includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Fox + @date Created - 2008.1.25 + + Updated for .35 by Jaysyn 2023/03/10 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int INVOCATION_DRACONIC = 1; +const int INVOCATION_WARLOCK = 2; + +const int INVOCATION_LEAST = 2; +const int INVOCATION_LESSER = 4; +const int INVOCATION_GREATER = 6; +const int INVOCATION_DARK = 8; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's invocation list the currently casted + * invocation is cast from. + * + * @param oInvoker A creature invoking at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetInvokingClass(object oInvoker = OBJECT_SELF); + +/** + * Determines the given creature's Invoker level. If a class is specified, + * then returns the Invoker level for that class. Otherwise, returns + * the Invoker level for the currently active invocation. + * + * @param oInvoker The creature whose Invoker level to determine + * @param nSpecificClass The class to determine the creature's Invoker + * level in. + * @param bPracticedInvoker If this is set, it will add the bunus from + * Practiced Invoker feat. + * @return The Invoker level + */ +int GetInvokerLevel(object oInvoker = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID, int bPracticedInvoker = TRUE); + +/** + * Determines whether a given creature uses Invocations. + * Requires either levels in an invocation-related class or + * natural Invocation ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use Invocations, FALSE otherwise. + */ +int GetIsInvocationUser(object oCreature); + +/** + * Determines the given creature's highest undmodified Invoker level among it's + * invoking classes. + * + * @param oCreature Creature whose highest Invoker level to determine + * @return The highest unmodified Invoker level the creature can have + */ +int GetHighestInvokerLevel(object oCreature); + +/** + * Determines whether a given class is an invocation-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is an invocation-related class, FALSE otherwise + */ +int GetIsInvocationClass(int nClass); + +/** + * Gets the level of the invocation being currently cast. + * WARNING: Return value is not defined when an invocation is not being cast. + * + * @param oInvoker The creature currently casting an invocation + * @return The level of the invocation being cast + */ +int GetInvocationLevel(object oInvoker); + +/** + * Returns the name of the invocation + * + * @param nSpellId SpellId of the invocation + */ +string GetInvocationName(int nSpellId); + +/** + * Calculates how many invoker levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added invoker levels for + * @return The number of invoker levels gained + */ +int GetInvocationPRCLevels(object oCaster); + +/** + * Determines which of the character's classes is their highest or first invocation + * casting class, if any. This is the one which gains invoker level raise benefits + * from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first invocation casting class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryInvocationClass(object oCreature = OBJECT_SELF); + +/** + * Determines the position of a creature's first invocation casting class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first invocation class {1, 2, 3} or 0 if + * the creature possesses no levels in invocation classes. + */ +int GetFirstInvocationClassPosition(object oCreature = OBJECT_SELF); + +/** + * Ruterns the number of damage dices that oInvokers eldritch blast has + * + * @param oInvoker Creature whose blast to test + * @param nInvokerLevel Invoker level + * @return The number of damage dices + */ +int GetBlastDamageDices(object oInvoker, int nInvokerLevel); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "prc_alterations" +#include "inv_inc_invknown" +#include "inv_inc_invoke" +#include "inv_inc_blast" +#include "prc_add_spell_dc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetInvokingClass(object oInvoker = OBJECT_SELF) +{ + return GetLocalInt(oInvoker, PRC_INVOKING_CLASS) - 1; +} + +/*int PracticedInvoker(object oInvoker, int iInvokingClass, int iInvokingLevels) +{ + int nFeat; + int iAdjustment = GetHitDice(oInvoker) - iInvokingLevels; + if(iAdjustment > 4) iAdjustment = 4; + if(iAdjustment < 0) iAdjustment = 0; + + switch(iInvokingClass) + { + case CLASS_TYPE_DRAGONFIRE_ADEPT: nFeat = FEAT_PRACTICED_INVOKER_DRAGONFIRE_ADEPT; break; + case CLASS_TYPE_WARLOCK: nFeat = FEAT_PRACTICED_INVOKER_WARLOCK; break; + default: return 0; + } + + if(GetHasFeat(nFeat, oInvoker)) + return iAdjustment; + + return 0; +}*/ + +int GetInvokerLevel(object oInvoker = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID, int bPracticedInvoker = TRUE) +{ + int nAdjust = GetLocalInt(oInvoker, PRC_CASTERLEVEL_ADJUSTMENT); + int nLevel = GetLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE); + + // For when you want to assign the caster level. + if(nLevel) + { + if(DEBUG) SendMessageToPC(oInvoker, "Forced-level Invoking at level " + IntToString(GetCasterLevel(oInvoker))); + //DelayCommand(1.0, DeleteLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE)); + return nLevel + nAdjust; + } + + if(nSpecificClass == CLASS_TYPE_INVALID) + nSpecificClass = GetInvokingClass(oInvoker); + + if(nSpecificClass != -1) + { + if(!GetIsInvocationClass(nSpecificClass)) + return 0; + + if(nSpecificClass == CLASS_TYPE_DRAGON_SHAMAN) + nLevel = max(GetLevelByClass(nSpecificClass, oInvoker) - 4, 1); // Can't go below 1 + else + nLevel = GetLevelByClass(nSpecificClass, oInvoker); + if(DEBUG) DoDebug("Invoker Class Level is: " + IntToString(nLevel)); + if(GetPrimaryInvocationClass(oInvoker) == nSpecificClass) + { + //Invoker level is class level + any arcane spellcasting or invoking levels in any PRCs + nLevel += GetInvocationPRCLevels(oInvoker); + } + /*if(bPracticedInvoker) + nLevel += PracticedInvoker(oInvoker, nSpecificClass, nLevel);*/ + } + else + nLevel = GetLevelByClass(GetPrimaryInvocationClass(oInvoker), oInvoker); + + nLevel += nAdjust; + SetLocalInt(oInvoker, "InvokerLevel", nLevel); + return nLevel; +} + +int GetIsInvocationUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCreature) || + GetLevelByClass(CLASS_TYPE_WARLOCK, oCreature) || + GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oCreature) + ); +} + + +int GetHighestInvokerLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetInvokerLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestInvokerLevel(object oCreature) +{ + return max(max(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetInvokerLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetInvokerLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetInvokerLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsInvocationClass(int nClass) +{ + int bTest = nClass == CLASS_TYPE_DRAGONFIRE_ADEPT + || nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGON_SHAMAN; + return bTest; +} + +int GetInvocationLevel(object oInvoker) +{ + return GetLocalInt(oInvoker, PRC_INVOCATION_LEVEL); +} + +string GetInvocationName(int nSpellId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))); +} + +int GetInvocationPRCLevels(object oCaster) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_HELLFIRE_WARLOCK, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + //_some_ arcane spellcasting levels boost invocations + if(GetLocalInt(oCaster, "INV_Caster") == 2) + nLevel += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2 + + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster) + + GetLevelByClass(CLASS_TYPE_MAESTER, oCaster) + + (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + return nLevel; +} + +int GetPrimaryInvocationClass(object oCreature = OBJECT_SELF) +{ + int nClass; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) //: Kinda pointless for .35 + { + int nInvocationPos = GetFirstInvocationClassPosition(oCreature); + if (!nInvocationPos) return CLASS_TYPE_INVALID; // no invoking class + + nClass = GetClassByPosition(nInvocationPos, oCreature); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsInvocationClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsInvocationClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsInvocationClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsInvocationClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsInvocationClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsInvocationClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsInvocationClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsInvocationClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstInvocationClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsInvocationClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsInvocationClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsInvocationClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsInvocationClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsInvocationClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsInvocationClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsInvocationClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsInvocationClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int GetInvocationSaveDC(object oTarget, object oCaster, int nSpellID = -1) +{ + int nDC; + // For when you want to assign the caster DC + //this does not take feat/race/class into account, it is an absolute override + if (GetLocalInt(oCaster, PRC_DC_TOTAL_OVERRIDE) != 0) + { + nDC = GetLocalInt(oCaster, PRC_DC_TOTAL_OVERRIDE); + DoDebug("Forced-DC PRC_DC_TOTAL_OVERRIDE casting at DC " + IntToString(nDC)); + return nDC; + } + // For when you want to assign the caster DC + //this does take feat/race/class into account, it only overrides the baseDC + if(GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE) > 0) + { + nDC = GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE); + if(DEBUG) DoDebug("Forced Base-DC casting at DC " + IntToString(nDC)); + } + else + { + if(nSpellID == -1) nSpellID = PRCGetSpellId(); + //10+spelllevel+stat(cha default) + nDC = 10; + nDC += StringToInt(Get2DACache("Spells", "Innate", nSpellID)); + nDC += GetAbilityModifier(ABILITY_CHARISMA, oCaster); + } + nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, 0); + + return nDC; +} + +void ClearInvocationLocalVars(object oPC) +{ + //Invocations + if (DEBUG) DoDebug("Clearing invocation flags"); + DeleteLocalObject(oPC, "ChillingFog"); + //Endure Exposure wearing off + array_delete(oPC, "BreathProtected"); + DeleteLocalInt(oPC, "DragonWard"); + + //cleaning targets of Endure exposure cast by resting caster + if (array_exists(oPC, "BreathProtectTargets")) + { + if(DEBUG) DoDebug("Checking for casts of Endure Exposure"); + int nBPTIndex = 0; + int bCasterDone = FALSE; + int bTargetDone = FALSE; + object oBreathTarget; + while(!bCasterDone) + { + oBreathTarget = array_get_object(oPC, "BreathProtectTargets", nBPTIndex); + if(DEBUG) DoDebug("Possible target: " + GetName(oBreathTarget) + " - " + ObjectToString(oBreathTarget)); + if(oBreathTarget != OBJECT_INVALID) + { + //replace caster with target... always immune to own breath, so good way to erase caster from array without deleting whole array + int nBPIndex = 0; + + while(!bTargetDone) + { + if(DEBUG) DoDebug("Checking " + GetName(oBreathTarget)); + //if it matches, remove and end + if(array_get_object(oBreathTarget, "BreathProtected", nBPIndex) == oPC) + { + array_set_object(oBreathTarget, "BreathProtected", nBPIndex, oBreathTarget); + bTargetDone = TRUE; + if(DEBUG) DoDebug("Found caster, clearing."); + } + //if it is not end of array, keep going + else if(array_get_object(oBreathTarget, "BreathProtected", nBPTIndex) != OBJECT_INVALID) + { + nBPIndex++; + } + else + bTargetDone = TRUE; + + } + + nBPTIndex++; + bTargetDone = FALSE; + + } + else + { + array_delete(oPC, "BreathProtectTargets"); + bCasterDone = TRUE; + } + } + } +} + +// Test main +//void main(){} diff --git a/trunk/include/moi_inc_moifunc.nss b/trunk/include/moi_inc_moifunc.nss new file mode 100644 index 00000000..05a10903 --- /dev/null +++ b/trunk/include/moi_inc_moifunc.nss @@ -0,0 +1,1559 @@ +//:://///////////////////////////////////////////// +//:: Meldshaping/Incarnum main include: Miscellaneous +//:: moi_inc_moifunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Meldshaping. + + Also acts as inclusion nexus for the general + meldshaping includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2019.12.28 + + Updated for .35 by Jaysyn 2023/03/10 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Test Void +//void main () {} + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the given creature's Meldshaper level. + * + * @param oMeldshaper The creature whose Meldshaper level to determine + * @param nSpecificClass The class to determine the creature's Meldshaper + * level in. + * @param nMeld The meld to test, since Incarnum does level by meld + * + * @return The Meldshaper level + */ +int GetMeldshaperLevel(object oMeldshaper, int nSpecificClass, int nMeld); + +/** + * Determines the given creature's highest unmodified Meldshaper level among its + * Meldshaping classes. + * + * @param oMeldshaper Creature whose highest Meldshaper level to determine + * @return The highest unmodified Meldshaper level the creature can have + */ +int GetHighestMeldshaperLevel(object oMeldshaper); + +/** + * Determines whether a given class is a Incarnum-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a Incarnum-related class, FALSE otherwise + */ +int GetIsIncarnumClass(int nClass); + +/** + * Calculates how many Meldshaper levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oMeldshaper Creature to calculate added Meldshaper levels for + * @return The number of Meldshaper levels gained + */ +int GetIncarnumPRCLevels(object oMeldshaper); + +/** + * Determines which of the character's classes is their highest or first + * Meldshaping class, if any. This is the one which gains Meldshaper + * level raise benefits from prestige classes. + * + * @param oMeldshaper Creature whose classes to test + * @return CLASS_TYPE_* of the first Meldshaping class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryIncarnumClass(object oMeldshaper = OBJECT_SELF); + +/** + * Determines the position of a creature's first Meldshaping class, if any. + * + * @param oMeldshaper Creature whose classes to test + * @return The position of the first Meldshaping class {1, 2, 3} or 0 if + * the creature possesses no levels in Meldshaping classes. + */ +int GetFirstIncarnumClassPosition(object oMeldshaper = OBJECT_SELF); + +/** + * Checks every second to see if temporary essentia has been lost + * + * @param oMeldshaper The meldshaper + */ +void SpawnTempEssentiaChecker(object oMeldshaper); + +/** + * Returns total value of temporary essentia for the meldshaper + * + * @param oMeldshaper The meldshaper + * + * @return Total value of temporary essentia + */ +int GetTemporaryEssentia(object oMeldshaper); + +/** + * Essentia put into feats is locked away for 24 hours/until next rest + * + * @param oMeldshaper The meldshaper + * + * @return Total value of locked essentia + */ +int GetFeatLockedEssentia(object oMeldshaper); + +/** + * Total essentia a character has access to + * + * @param oMeldshaper The meldshaper + * + * @return Total value of essentia available + */ +int GetTotalEssentia(object oMeldshaper); + +/** + * Total essentia a character has access to, minus feat locked essentia + * + * @param oMeldshaper The meldshaper + * + * @return Total value of essentia available, minus feat locked essentia + */ +int GetTotalUsableEssentia(object oMeldshaper); + +/** + * Returns the slot associated to a given chakra + * + * @param nChakra Chakra constant + * + * @return Slot constant + */ +int ChakraToSlot(int nChakra); + +/** + * Returns the total binds the character can have for that class and level + * + * @param oMeldshaper The meldshaper + * @param nClass The class to check + * + * @return Slot constant + */ +int GetMaxBindCount(object oMeldshaper, int nClass); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_natweap" +#include "prc_inc_function" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetMeldshaperLevel(object oMeldshaper, int nSpecificClass, int nMeld) +{ + int nLevel; + + if (DEBUG) DoDebug("GetMeldshaperLevel: "+GetName(oMeldshaper)+" is a "+IntToString(nSpecificClass)); + + if(GetIsIncarnumClass(nSpecificClass)) + { + // Meldshaper level is class level + prestige + nLevel = GetLevelByClass(nSpecificClass, oMeldshaper); + if(nLevel) + { + // Prevents people double-dipping prestige levels + if (nSpecificClass == GetPrimaryIncarnumClass(oMeldshaper)) + { + nLevel += GetIncarnumPRCLevels(oMeldshaper); + nLevel += GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper); // Necrocarnate is here because it doesn't add to anything other than meldshaper level + } + if (nSpecificClass == CLASS_TYPE_SOULBORN) nLevel /= 2; + } + } + + if(DEBUG) DoDebug("Meldshaper Level: " + IntToString(nLevel)); + // Being bound to the Totem Chakra increases the level by one. + if (GetIsMeldBound(oMeldshaper, nMeld) == CHAKRA_TOTEM) nLevel++; + return nLevel; +} + +int GetIncarnumLevelForClass(int nSpecificClass, object oMeldshaper) +{ + int nLevel; + + if (DEBUG) DoDebug("GetMeldshaperLevel: "+GetName(oMeldshaper)+" is a "+IntToString(nSpecificClass)); + + if(GetIsIncarnumClass(nSpecificClass)) + { + // Meldshaper level is class level + prestige + nLevel = GetLevelByClass(nSpecificClass, oMeldshaper); + if(nLevel) + { + // Prevents people double-dipping prestige levels + if (nSpecificClass == GetPrimaryIncarnumClass(oMeldshaper)) nLevel += GetIncarnumPRCLevels(oMeldshaper); + } + } + if(nSpecificClass == CLASS_TYPE_UMBRAL_DISCIPLE || nSpecificClass == CLASS_TYPE_INCANDESCENT_CHAMPION || nSpecificClass == CLASS_TYPE_NECROCARNATE) + nLevel = GetLevelByClass(nSpecificClass, oMeldshaper); + + if(DEBUG) DoDebug("GetIncarnumLevelForClass: " + IntToString(nLevel)); + return nLevel; +} + +int GetHighestMeldshaperLevel(object oMeldshaper) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oMeldshaper) != CLASS_TYPE_INVALID) + { + nTemp = GetMeldshaperLevel(oMeldshaper, GetClassByPosition(n, oMeldshaper), -1); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestMeldshaperLevel(object oMeldshaper) +{ + return max(max(GetClassByPosition(1, oMeldshaper) != CLASS_TYPE_INVALID ? GetMeldshaperLevel(oMeldshaper, GetClassByPosition(1, oMeldshaper), -1) : 0, + GetClassByPosition(2, oMeldshaper) != CLASS_TYPE_INVALID ? GetMeldshaperLevel(oMeldshaper, GetClassByPosition(2, oMeldshaper), -1) : 0 + ), + GetClassByPosition(3, oMeldshaper) != CLASS_TYPE_INVALID ? GetMeldshaperLevel(oMeldshaper, GetClassByPosition(3, oMeldshaper), -1) : 0 + ); +} */ + +int GetIsIncarnumClass(int nClass) +{ + return nClass == CLASS_TYPE_INCARNATE + || nClass == CLASS_TYPE_SOULBORN + || nClass == CLASS_TYPE_TOTEMIST + || nClass == CLASS_TYPE_SPINEMELD_WARRIOR; +} + +int GetIncarnumPRCLevels(object oMeldshaper) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oMeldshaper); + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper); + + // These two don't add at 1st level + if (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper)) + nLevel += GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) - 1; + // Totem Rager + if (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >= 6) + nLevel += (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) -2; + else if (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) + nLevel += (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) -1; + //This is an odd one + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper)) + { + nLevel += (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper)+1)/2; + + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper) >= 10) nLevel += 1; + } + + /*if (GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oMeldshaper)) + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oMeldshaper) - 1; +*/ + return nLevel; +} + +int GetPrimaryIncarnumClass(object oMeldshaper = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nIncarnumPos = GetFirstIncarnumClassPosition(oMeldshaper); + if (!nIncarnumPos) return CLASS_TYPE_INVALID; // no Blade Magic Meldshaping class + + nClass = GetClassByPosition(nIncarnumPos, oMeldshaper); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oMeldshaper); + nClass2 = GetClassByPosition(2, oMeldshaper); + nClass3 = GetClassByPosition(3, oMeldshaper); + nClass4 = GetClassByPosition(4, oMeldshaper); + nClass5 = GetClassByPosition(5, oMeldshaper); + nClass6 = GetClassByPosition(6, oMeldshaper); + nClass7 = GetClassByPosition(7, oMeldshaper); + nClass8 = GetClassByPosition(8, oMeldshaper); + + if(GetIsIncarnumClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oMeldshaper); + if(GetIsIncarnumClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oMeldshaper); + if(GetIsIncarnumClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oMeldshaper); + if(GetIsIncarnumClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oMeldshaper); + if(GetIsIncarnumClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oMeldshaper); + if(GetIsIncarnumClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oMeldshaper); + if(GetIsIncarnumClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oMeldshaper); + if(GetIsIncarnumClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oMeldshaper); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstIncarnumClassPosition(object oMeldshaper = OBJECT_SELF) +{ + if (GetIsIncarnumClass(GetClassByPosition(1, oMeldshaper))) + return 1; + if (GetIsIncarnumClass(GetClassByPosition(2, oMeldshaper))) + return 2; + if (GetIsIncarnumClass(GetClassByPosition(3, oMeldshaper))) + return 3; + if (GetIsIncarnumClass(GetClassByPosition(4, oMeldshaper))) + return 4; + if (GetIsIncarnumClass(GetClassByPosition(5, oMeldshaper))) + return 5; + if (GetIsIncarnumClass(GetClassByPosition(6, oMeldshaper))) + return 6; + if (GetIsIncarnumClass(GetClassByPosition(7, oMeldshaper))) + return 7; + if (GetIsIncarnumClass(GetClassByPosition(8, oMeldshaper))) + return 8; + + return 0; +} + +string GetMeldFile(int nClass = -1) +{ + //string sFile; + //if (nClass == CLASS_TYPE_INCARNATE) sFile = "cls_meld_incarn"; + + return "soulmelds"; +} + +string GetMeldshapingClassFile(int nClass) +{ + string sFile; + if (nClass == CLASS_TYPE_INCARNATE) sFile = "cls_mlkn_incarn"; + else if (nClass == CLASS_TYPE_SOULBORN) sFile = "cls_mlkn_soulbn"; + else if (nClass == CLASS_TYPE_TOTEMIST) sFile = "cls_mlkn_totem"; + else if (nClass == CLASS_TYPE_SPINEMELD_WARRIOR) sFile = "cls_mlkn_spnmld"; + else if (nClass == CLASS_TYPE_UMBRAL_DISCIPLE) sFile = "cls_mlkn_umbral"; + else if (nClass == CLASS_TYPE_INCANDESCENT_CHAMPION) sFile = "cls_mlkn_incand"; + else if (nClass == CLASS_TYPE_NECROCARNATE) sFile = "cls_mlkn_necrnm"; + + return sFile; +} + +int GetMeldshapingClass(object oMeldshaper) +{ + int nClass = -1; + // If there's levels in the class and haven't already done it + if (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) && !GetLocalInt(oMeldshaper, "FirstMeldDone")) nClass = CLASS_TYPE_INCARNATE; + else if (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) && !GetLocalInt(oMeldshaper, "SecondMeldDone")) nClass = CLASS_TYPE_SOULBORN; + else if (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) && !GetLocalInt(oMeldshaper, "ThirdMeldDone")) nClass = CLASS_TYPE_TOTEMIST; + else if (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper) && !GetLocalInt(oMeldshaper, "FourthMeldDone")) nClass = CLASS_TYPE_SPINEMELD_WARRIOR; + if (DEBUG) DoDebug("GetMeldshapingClass is "+IntToString(nClass)); + return nClass; +} + +int GetMaxShapeSoulmeldCount(object oMeldshaper, int nClass) +{ + int nMax = StringToInt(Get2DACache(GetMeldshapingClassFile(nClass), "Soulmelds", GetIncarnumLevelForClass(nClass, oMeldshaper)-1)); + if (nClass == GetPrimaryIncarnumClass(oMeldshaper)) + { + nMax += StringToInt(Get2DACache(GetMeldshapingClassFile(CLASS_TYPE_NECROCARNATE), "Soulmelds", GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper))); + int i; + for(i = FEAT_BONUS_SOULMELD_1; i <= FEAT_BONUS_SOULMELD_10; i++) + if(GetHasFeat(i, oMeldshaper)) nMax++; + } + + int nCon = GetAbilityScore(oMeldshaper, ABILITY_CONSTITUTION, TRUE)-10; + if (GetHasFeat(FEAT_UNDEAD_MELDSHAPER, oMeldshaper)) nCon = GetAbilityScore(oMeldshaper, ABILITY_WISDOM, TRUE)-10; + //Limited to Con score - 10 or class limit, whichever is less + nMax = min(nMax, nCon); + + if (DEBUG) DoDebug("GetMaxShapeSoulmeldCount is "+IntToString(nMax)); + return nMax; +} + +int GetTotalSoulmeldCount(object oMeldshaper) +{ + int nMax = GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_INCARNATE); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_SOULBORN); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_TOTEMIST); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_SPINEMELD_WARRIOR); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_NECROCARNATE); + + if (DEBUG) DoDebug("GetTotalSoulmeldCount is "+IntToString(nMax)); + return nMax; +} + +int GetMaxBindCount(object oMeldshaper, int nClass) +{ + int nMax = StringToInt(Get2DACache(GetMeldshapingClassFile(nClass), "ChakraBinds", GetIncarnumLevelForClass(nClass, oMeldshaper)-1)); + if (nClass == GetPrimaryIncarnumClass(oMeldshaper)) + { + nMax += StringToInt(Get2DACache(GetMeldshapingClassFile(CLASS_TYPE_NECROCARNATE), "ChakraBinds", GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper))); + int i; + for(i = FEAT_EXTRA_CHAKRA_BIND_1; i <= FEAT_EXTRA_CHAKRA_BIND_10; i++) + if(GetHasFeat(i, oMeldshaper)) nMax += 1; + } + if (DEBUG) DoDebug("GetMaxBindCount is "+IntToString(nMax)); + return nMax; +} + +void ShapeSoulmeld(object oMeldshaper, int nMeld) +{ + PRCRemoveSpellEffects(nMeld, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(nMeld, oMeldshaper, FALSE); + ActionCastSpellOnSelf(nMeld); + if (DEBUG) DoDebug("Shaping Soulmeld "+IntToString(nMeld)+" on "+GetName(oMeldshaper)); +} + +void MarkMeldShaped(object oMeldshaper, int nMeld, int nClass) +{ + if (DEBUG) DoDebug("MarkMeldShaped nMeld is "+IntToString(nMeld)); + int nCont = TRUE; + int nTest, i; + while (nCont) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i)); + if (DEBUG) DoDebug("MarkMeldShaped nTest is "+IntToString(nTest)); + if (!nTest) // If it's blank + { + SetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i), nMeld); + if (DEBUG) DoDebug("MarkMeldShaped SetLocal"); + nCont = FALSE; // Break the loop + } + else + i++; // Increment the counter to check + } +} + +void ClearMeldShapes(object oMeldshaper) +{ + object oSkin = GetPCSkin(oMeldshaper); + ScrubPCSkin(oMeldshaper, oSkin); + int i; + for (i = 0; i <= 22; i++) + { + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "BoundMeld"+IntToString(i)); + int nTest = GetLocalInt(oMeldshaper, "SpellInvestCheck"+IntToString(i)); + if (nTest) + DeleteLocalInt(oMeldshaper, "SpellEssentia"+IntToString(nTest)); + DeleteLocalInt(oMeldshaper, "SpellInvestCheck"+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedBladeMeld"+IntToString(i)); + } + for (i = 18700; i < 18799; i++) + { + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i)); + } + for (i = 8869; i < 8889; i++) + { + DeleteLocalInt(oMeldshaper, "FeatEssentia"+IntToString(i)); + } + DeleteLocalInt(oMeldshaper, "ArcaneFocusBound"); + if (GetLocalInt(oMeldshaper, "DiademPurelight")) + { + SetLocalInt(oMeldshaper, "PRCInLight", GetLocalInt(oMeldshaper, "PRCInLight")-1); + DeleteLocalInt(oMeldshaper, "DiademPurelight"); + } + + if (GetLocalInt(oMeldshaper, "PlanarChasubleLimit") > 7) + DeleteLocalInt(oMeldshaper, "PlanarChasubleLimit"); + else + SetLocalInt(oMeldshaper, "PlanarChasubleLimit", GetLocalInt(oMeldshaper, "PlanarChasubleLimit")+1); + + DeleteLocalInt(oMeldshaper, "GorgonMaskLimit"); + DeleteLocalInt(oMeldshaper, "IncarnateAvatarSpeed"); + DeleteLocalInt(oMeldshaper, "LamiaBeltSpeed"); + DeleteLocalInt(oMeldshaper, "LifebondVestmentsTimer"); + DeleteLocalInt(oMeldshaper, "MidnightAugPower"); + DeleteLocalInt(oMeldshaper, "MeldEssentia18973"); // MELD_DUSKLING_SPEED + DeleteLocalInt(oMeldshaper, "MeldEssentia18691"); // MELD_SPINE_ENHANCEMENT + DeleteLocalInt(oMeldshaper, "MeldEssentia18687"); // MELD_IRONSOUL_SHIELD + DeleteLocalInt(oMeldshaper, "MeldEssentia18685"); // MELD_IRONSOUL_ARMOR + DeleteLocalInt(oMeldshaper, "MeldEssentia18683"); // MELD_IRONSOUL_WEAPON + DeleteLocalInt(oMeldshaper, "MeldEssentia18681"); // MELD_UMBRAL_STEP + DeleteLocalInt(oMeldshaper, "MeldEssentia18679"); // MELD_UMBRAL_SHADOW + DeleteLocalInt(oMeldshaper, "MeldEssentia18677"); // MELD_UMBRAL_SIGHT + DeleteLocalInt(oMeldshaper, "MeldEssentia18675"); // MELD_UMBRAL_SOUL + DeleteLocalInt(oMeldshaper, "MeldEssentia18673"); // MELD_UMBRAL_KISS + DeleteLocalInt(oMeldshaper, "MeldEssentia18670"); // MELD_INCANDESCENT_STRIKE + DeleteLocalInt(oMeldshaper, "MeldEssentia18668"); // MELD_INCANDESCENT_HEAL + DeleteLocalInt(oMeldshaper, "MeldEssentia18666"); // MELD_INCANDESCENT_COUNTENANCE + DeleteLocalInt(oMeldshaper, "MeldEssentia18663"); // MELD_INCANDESCENT_RAY + DeleteLocalInt(oMeldshaper, "MeldEssentia18659"); // MELD_INCANDESCENT_AURA + DeleteLocalInt(oMeldshaper, "MeldEssentia18634"); // MELD_WITCH_MELDSHIELD + DeleteLocalInt(oMeldshaper, "MeldEssentia18636"); // MELD_WITCH_DISPEL + DeleteLocalInt(oMeldshaper, "MeldEssentia18638"); // MELD_WITCH_SHACKLES + DeleteLocalInt(oMeldshaper, "MeldEssentia18640"); // MELD_WITCH_ABROGATION + DeleteLocalInt(oMeldshaper, "MeldEssentia18642"); // MELD_WITCH_SPIRITFLAY + DeleteLocalInt(oMeldshaper, "MeldEssentia18644"); // MELD_WITCH_INTEGUMENT + DeleteLocalInt(oMeldshaper, "NecrocarnumCircletPen"); + DeleteLocalInt(oMeldshaper, "AstralVambraces"); + DeleteLocalInt(oMeldshaper, "TemporaryEssentia"); + DestroyObject(GetItemPossessedBy(oMeldshaper, "moi_incarnatewpn")); // Remove any weapons created by Incarnate Weapon + + // Clean up any the natural weapons that are lying around + ClearNaturalWeapons(oMeldshaper); + // Nuke the creature weapons. If the normal form is supposed to have natural weapons, they'll get re-constructed + if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oMeldshaper))) MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oMeldshaper)); + if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oMeldshaper))) MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oMeldshaper)); + if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oMeldshaper))) MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oMeldshaper)); + + if (GetLocalInt(oMeldshaper, "ClearEventTotem")) ClearEventScriptList(oMeldshaper, EVENT_ITEM_ONHIT, TRUE, TRUE); +} + +int GetShapedMeldsCount(object oMeldshaper) +{ + int i, nCount, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + } + if (DEBUG) DoDebug("GetTotalShapedMelds is "+IntToString(nCount)); + return nCount; +} + +int GetTotalShapedMelds(object oMeldshaper, int nClass) +{ + int i, nCount, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + } + if (DEBUG) DoDebug("GetTotalShapedMelds is "+IntToString(nCount)); + return nCount; +} + +int CheckSplitChakra(object oMeldshaper, int nSlot) +{ + if (nSlot == INVENTORY_SLOT_HEAD && GetHasFeat(FEAT_SPLIT_CHAKRA_CROWN , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_BOOTS && GetHasFeat(FEAT_SPLIT_CHAKRA_FEET , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_ARMS && GetHasFeat(FEAT_SPLIT_CHAKRA_HANDS , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_ARMS && GetHasFeat(FEAT_SPLIT_CHAKRA_ARMS , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_HEAD && GetHasFeat(FEAT_SPLIT_CHAKRA_BROW , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_CLOAK && GetHasFeat(FEAT_SPLIT_CHAKRA_SHOULDERS, oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_NECK && GetHasFeat(FEAT_SPLIT_CHAKRA_THROAT , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_BELT && GetHasFeat(FEAT_SPLIT_CHAKRA_WAIST , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_CHEST && GetHasFeat(FEAT_SPLIT_CHAKRA_HEART , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_CHEST && GetHasFeat(FEAT_SPLIT_CHAKRA_SOUL , oMeldshaper)) return TRUE; + + return FALSE; +} + +void BindMeldToChakra(object oMeldshaper, int nMeld, int nChakra, int nClass) +{ + // Make sure it's not in use already, and that you have any binds to make + if (!GetIsChakraBound(oMeldshaper, nChakra) && GetMaxBindCount(oMeldshaper, nClass)) + { + //FloatingTextStringOnCreature("BindMeldToChakra: nMeld "+IntToString(nMeld)+" nChakra "+IntToString(nChakra), oMeldshaper); + SetLocalInt(oMeldshaper, "BoundMeld"+IntToString(nChakra), nMeld); + ShapeSoulmeld(oMeldshaper, nMeld); + int nSlot = ChakraToSlot(DoubleChakraToChakra(nChakra)); // Can't have an item in a bound slot, unless Split Chakra + if (!CheckSplitChakra(oMeldshaper, nSlot)) ForceUnequip(oMeldshaper, GetItemInSlot(nSlot, oMeldshaper), nSlot); + } +} + +int GetTotalBoundMelds(object oMeldshaper) +{ + int i, nCount, nTest; + for (i = 0; i <= 22; i++) + { + nTest = GetLocalInt(oMeldshaper, "BoundMeld"+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + } + if (DEBUG) DoDebug("GetTotalBoundMelds is "+IntToString(nCount)); + return nCount; +} + +int GetIsChakraUsed(object oMeldshaper, int nChakra, int nClass) +{ + int nTest = GetLocalInt(oMeldshaper, "UsedMeld"+IntToString(nClass)+IntToString(nChakra)); + + if (DEBUG) DoDebug("GetIsChakraUsed is "+IntToString(nTest)); + return nTest; +} + +void SetChakraUsed(object oMeldshaper, int nMeld, int nChakra, int nClass) +{ + // This isn't the same as binding, but can only have one soulmeld in each chakra + // Each class has its own limit on this, as per p20 of MoI + if (!GetIsChakraUsed(oMeldshaper, nChakra, nClass)) + { + SetLocalInt(oMeldshaper, "UsedMeld"+IntToString(nClass)+IntToString(nChakra), nMeld); + } +} + +int ChakraToSlot(int nChakra) +{ + if (nChakra == CHAKRA_CROWN ) return INVENTORY_SLOT_HEAD; + if (nChakra == CHAKRA_FEET ) return INVENTORY_SLOT_BOOTS; + if (nChakra == CHAKRA_HANDS ) return INVENTORY_SLOT_ARMS; + if (nChakra == CHAKRA_ARMS ) return INVENTORY_SLOT_ARMS; + if (nChakra == CHAKRA_BROW ) return INVENTORY_SLOT_HEAD; + if (nChakra == CHAKRA_SHOULDERS) return INVENTORY_SLOT_CLOAK; + if (nChakra == CHAKRA_THROAT ) return INVENTORY_SLOT_NECK; + if (nChakra == CHAKRA_WAIST ) return INVENTORY_SLOT_BELT; + if (nChakra == CHAKRA_HEART ) return INVENTORY_SLOT_CHEST; + if (nChakra == CHAKRA_SOUL ) return INVENTORY_SLOT_CHEST; + if (nChakra == CHAKRA_TOTEM ) return -1; // no slot associated + + return -1; +} + +void ChakraBindUnequip(object oMeldshaper, object oItem) +{ + int nTest = FALSE; + if (GetItemInSlot(INVENTORY_SLOT_HEAD, oMeldshaper) == oItem && (GetIsChakraBound(oMeldshaper, CHAKRA_CROWN) || GetIsChakraBound(oMeldshaper, CHAKRA_BROW))) nTest = INVENTORY_SLOT_HEAD + 1; + else if (GetItemInSlot(INVENTORY_SLOT_BOOTS, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_FEET)) nTest = INVENTORY_SLOT_BOOTS + 1; + else if (GetItemInSlot(INVENTORY_SLOT_ARMS, oMeldshaper) == oItem && (GetIsChakraBound(oMeldshaper, CHAKRA_ARMS) || GetIsChakraBound(oMeldshaper, CHAKRA_HANDS))) nTest = INVENTORY_SLOT_ARMS + 1; + else if (GetItemInSlot(INVENTORY_SLOT_CLOAK, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_SHOULDERS)) nTest = INVENTORY_SLOT_CLOAK + 1; + else if (GetItemInSlot(INVENTORY_SLOT_NECK, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_THROAT)) nTest = INVENTORY_SLOT_NECK + 1; + else if (GetItemInSlot(INVENTORY_SLOT_BELT, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_WAIST)) nTest = INVENTORY_SLOT_BELT + 1; + else if (GetItemInSlot(INVENTORY_SLOT_CHEST, oMeldshaper) == oItem && (GetIsChakraBound(oMeldshaper, CHAKRA_SOUL) || GetIsChakraBound(oMeldshaper, CHAKRA_HEART))) nTest = INVENTORY_SLOT_CHEST + 1; + if (DEBUG) DoDebug("ChakraBindUnequip is "+IntToString(nTest-1)); + if (nTest && !CheckSplitChakra(oMeldshaper, nTest-1) && GetIsItemPropertyValid(GetFirstItemProperty(oItem)) && oItem != GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oMeldshaper) && oItem != GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oMeldshaper) && + oItem != GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oMeldshaper) && oItem != GetItemInSlot(INVENTORY_SLOT_CARMOUR, oMeldshaper)) // If it's bound you can't equip in that slot + { + nTest = nTest - 1; + ForceUnequip(oMeldshaper, GetItemInSlot(nTest, oMeldshaper), nTest); + FloatingTextStringOnCreature("You cannot equip a magical item when you have bound a meld to the same chakra!", oMeldshaper, FALSE); + } +} + +string ChakraToString(int nChakra) +{ + string sReturn = ""; + if (nChakra == CHAKRA_CROWN || nChakra == CHAKRA_DOUBLE_CROWN ) sReturn = "Crown"; + if (nChakra == CHAKRA_FEET || nChakra == CHAKRA_DOUBLE_FEET ) sReturn = "Feet"; + if (nChakra == CHAKRA_HANDS || nChakra == CHAKRA_DOUBLE_HANDS ) sReturn = "Hands"; + if (nChakra == CHAKRA_ARMS || nChakra == CHAKRA_DOUBLE_ARMS ) sReturn = "Arms"; + if (nChakra == CHAKRA_BROW || nChakra == CHAKRA_DOUBLE_BROW ) sReturn = "Brow"; + if (nChakra == CHAKRA_SHOULDERS || nChakra == CHAKRA_DOUBLE_SHOULDERS) sReturn = "Shoulders"; + if (nChakra == CHAKRA_THROAT || nChakra == CHAKRA_DOUBLE_THROAT ) sReturn = "Throat"; + if (nChakra == CHAKRA_WAIST || nChakra == CHAKRA_DOUBLE_WAIST ) sReturn = "Waist"; + if (nChakra == CHAKRA_HEART || nChakra == CHAKRA_DOUBLE_HEART ) sReturn = "Heart"; + if (nChakra == CHAKRA_SOUL || nChakra == CHAKRA_DOUBLE_SOUL ) sReturn = "Soul"; + if (nChakra == CHAKRA_TOTEM || nChakra == CHAKRA_DOUBLE_TOTEM ) sReturn = "Totem"; + + if (DEBUG) DoDebug("ChakraToString is "+IntToString(nChakra)+", Return is "+sReturn); + return sReturn; +} + +int GetCanBindChakra(object oMeldshaper, int nChakra) +{ + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 2)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 4)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 4)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 16)) return TRUE; + else if (nChakra == CHAKRA_SOUL && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 19)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 18)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 18)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 5)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 5)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 5)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 17)) return TRUE; + else if (nChakra == CHAKRA_TOTEM && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 2)) return TRUE; + + if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper) >= 7)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 8)) return TRUE; + + if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 4)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 6)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 10)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=4)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=4)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=4)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=9)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=9)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=9)) return TRUE; + + if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 11)) return TRUE; + else if (nChakra == CHAKRA_SOUL && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 13)) return TRUE; + + if (nChakra == CHAKRA_CROWN && GetHasFeat(FEAT_OPEN_LEAST_CHAKRA_CROWN, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_FEET && GetHasFeat(FEAT_OPEN_LEAST_CHAKRA_FEET , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_HANDS && GetHasFeat(FEAT_OPEN_LEAST_CHAKRA_HANDS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_ARMS && GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_ARMS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_BROW && GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_BROW , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_SHOULDERS, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_THROAT && GetHasFeat(FEAT_OPEN_GREATER_CHAKRA_THROAT , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_WAIST && GetHasFeat(FEAT_OPEN_GREATER_CHAKRA_WAIST , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_HEART && GetHasFeat(FEAT_OPEN_HEART_CHAKRA , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_SOUL && GetHasFeat(FEAT_OPEN_SOUL_CHAKRA , oMeldshaper)) return TRUE; + + if (nChakra == CHAKRA_DOUBLE_CROWN && GetHasFeat(FEAT_DOUBLE_CHAKRA_CROWN, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_FEET && GetHasFeat(FEAT_DOUBLE_CHAKRA_FEET , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_HANDS && GetHasFeat(FEAT_DOUBLE_CHAKRA_HANDS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_ARMS && GetHasFeat(FEAT_DOUBLE_CHAKRA_ARMS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_BROW && GetHasFeat(FEAT_DOUBLE_CHAKRA_BROW , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_SHOULDERS && GetHasFeat(FEAT_DOUBLE_CHAKRA_SHOULDERS, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_THROAT && GetHasFeat(FEAT_DOUBLE_CHAKRA_THROAT , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_WAIST && GetHasFeat(FEAT_DOUBLE_CHAKRA_WAIST , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_HEART && GetHasFeat(FEAT_DOUBLE_CHAKRA_HEART , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_SOUL && GetHasFeat(FEAT_DOUBLE_CHAKRA_SOUL , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_TOTEM && GetHasFeat(FEAT_DOUBLE_CHAKRA_TOTEM , oMeldshaper)) return TRUE; + + return FALSE; +} + +int GetMaxEssentiaCount(object oMeldshaper, int nClass) +{ + int nMax = StringToInt(Get2DACache(GetMeldshapingClassFile(nClass), "Essentia", GetIncarnumLevelForClass(nClass, oMeldshaper)-1)); + if (DEBUG) DoDebug("GetMaxEssentiaCount is "+IntToString(nMax)); + return nMax; +} + +void SpawnTempEssentiaChecker(object oMeldshaper) +{ + int nCur = GetTemporaryEssentia(oMeldshaper); + int nPrv = GetLocalInt(oMeldshaper, "TempEssTest"); + int nRed = nPrv - nCur; + // If we've lost some + if (nPrv > nCur) + { + int i, nCount, nTest; + for (i = 18700; i < 18799; i++) + { + nTest = GetLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(i)); + if (nTest) // If it's not blank + { + if (DEBUG) DoDebug("Found "+IntToString(nTest)+" Temp Essentia in Meld "+IntToString(i)); + // There's still some temp essentia left in the meld + if (nTest > nRed) + { + int nChange = nTest-nRed; + SetLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(i), nChange); + SetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i), GetEssentiaInvested(oMeldshaper, i)-nChange); + ShapeSoulmeld(oMeldshaper, i); + if (DEBUG) DoDebug("Reducing Essentia in Meld "+IntToString(i)+" by "+IntToString(nChange)); + } + else // Means the reduction is higher than the temp essentia ammount + { + // No more temp essentia here + DeleteLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(i)); + SetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i), GetEssentiaInvested(oMeldshaper, i)-nTest); + ShapeSoulmeld(oMeldshaper, i); + if (DEBUG) DoDebug("Deleted Temp Essentia in Meld "+IntToString(i)+", total "+IntToString(nTest)); + } + + // Reduce the remaining amount of reduction by the amount we just used up + nRed = nRed - nTest; + if (DEBUG) DoDebug("Remaining Temp Essentia to reduce "+IntToString(nRed)); + if (0 >= nRed) break; // End the loop if we're done + } + } + } + // If we still have some left + if (nCur > 0) + { + SetLocalInt(oMeldshaper, "TempEssTest", nCur); + DelayCommand(1.0, SpawnTempEssentiaChecker(oMeldshaper)); + } + else + DeleteLocalInt(oMeldshaper, "TempEssTest"); +} + +void InvestEssentia(object oMeldshaper, int nMeld, int nEssentia) +{ + // Use up expanded soulmeld capacity + if (nEssentia > 1000) + { + nEssentia -= 1000; + SetIsSoulmeldCapacityUsed(oMeldshaper, nMeld); + } + int nClass = GetMeldShapedClass(oMeldshaper, nMeld); + // Special capacity of 1/2 class level + if (nMeld == MELD_SPINE_ENHANCEMENT) nEssentia = min(nEssentia, (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)/2)); + else if (nEssentia > GetMaxEssentiaCapacity(oMeldshaper, nClass, nMeld)) nEssentia = GetMaxEssentiaCapacity(oMeldshaper, nClass, nMeld); + // Can't invest more than you have + if (nEssentia > GetTotalUsableEssentia(oMeldshaper)) nEssentia = GetTotalUsableEssentia(oMeldshaper); + + // All of this garbage to handle temporary essentia + if (GetLocalInt(oMeldshaper, "InvestingTempEssentia")) + { + SetLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(nMeld), nEssentia); + SpawnTempEssentiaChecker(oMeldshaper); + DeleteLocalInt(oMeldshaper, "InvestingTempEssentia"); + } + + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nMeld))), oMeldshaper, FALSE); + SetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(nMeld), nEssentia); + SetLocalInt(oMeldshaper, "EssentiaRound"+IntToString(nMeld), TRUE); // This is used by Melds that only trigger on the round that essentia is invested into it + DelayCommand(6.0, DeleteLocalInt(oMeldshaper, "EssentiaRound"+IntToString(nMeld))); +} + +int GetTotalEssentiaInvested(object oMeldshaper) +{ + int i, nCount, nTest; + for (i = 18700; i < 18799; i++) + { + nTest = GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i)); + if (nTest) // If it's not blank + nCount += nTest; + } + nCount += GetFeatLockedEssentia(oMeldshaper); + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_SPINE_ENHANCEMENT)); // MELD_SPINE_ENHANCEMENT + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_SHIELD)); // MELD_IRONSOUL_SHIELD + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_ARMOR)); // MELD_IRONSOUL_ARMOR + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_WEAPON)); // MELD_IRONSOUL_WEAPON + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_STEP )); // MELD_UMBRAL_STEP + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SHADOW)); // MELD_UMBRAL_SHADOW + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SIGHT )); // MELD_UMBRAL_SIGHT + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SOUL )); // MELD_UMBRAL_SOUL + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_KISS )); // MELD_UMBRAL_KISS + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_DUSKLING_SPEED)); // MELD_DUSKLING_SPEED + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_STRIKE)); // MELD_INCANDESCENT_STRIKE + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_HEAL )); // MELD_INCANDESCENT_HEAL + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_COUNTENANCE)); // MELD_INCANDESCENT_COUNTENANCE + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_RAY )); // MELD_INCANDESCENT_RAY + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_AURA )); // MELD_INCANDESCENT_AURA + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_MELDSHIELD)); // MELD_WITCH_MELDSHIELD + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_DISPEL )); // MELD_WITCH_DISPEL + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SHACKLES )); // MELD_WITCH_SHACKLES + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_ABROGATION)); // MELD_WITCH_ABROGATION + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SPIRITFLAY)); // MELD_WITCH_SPIRITFLAY + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_INTEGUMENT)); // MELD_WITCH_INTEGUMENT + if (DEBUG) DoDebug("GetTotalEssentiaInvested is "+IntToString(nCount)); + return nCount; +} + +int EssentiaIDToRealID(int nEssentiaID) +{ + int i, nReal, nTest; + for (i = 1; i < 100; i++) + { + nTest = StringToInt(Get2DACache("soulmelds", "EssentiaID", i)); + if (nTest == nEssentiaID) // If it's been found + { + nReal = StringToInt(Get2DACache("soulmelds", "SpellID", i)); + break; + } + } + + if (nEssentiaID == 18690) nReal = MELD_SPINE_ENHANCEMENT; + else if (nEssentiaID == 18686) nReal = MELD_IRONSOUL_SHIELD; + else if (nEssentiaID == 18684) nReal = MELD_IRONSOUL_ARMOR; + else if (nEssentiaID == 18682) nReal = MELD_IRONSOUL_WEAPON; + else if (nEssentiaID == 18680) nReal = MELD_UMBRAL_STEP; + else if (nEssentiaID == 18678) nReal = MELD_UMBRAL_SHADOW; + else if (nEssentiaID == 18676) nReal = MELD_UMBRAL_SIGHT; + else if (nEssentiaID == 18674) nReal = MELD_UMBRAL_SOUL; + else if (nEssentiaID == 18672) nReal = MELD_UMBRAL_KISS; + else if (nEssentiaID == 18669) nReal = MELD_INCANDESCENT_STRIKE; + else if (nEssentiaID == 18667) nReal = MELD_INCANDESCENT_HEAL ; + else if (nEssentiaID == 18665) nReal = MELD_INCANDESCENT_COUNTENANCE; + else if (nEssentiaID == 18662) nReal = MELD_INCANDESCENT_RAY ; + else if (nEssentiaID == 18661) nReal = MELD_INCANDESCENT_AURA ; + else if (nEssentiaID == 18633) nReal = MELD_WITCH_MELDSHIELD; + else if (nEssentiaID == 18635) nReal = MELD_WITCH_DISPEL; + else if (nEssentiaID == 18637) nReal = MELD_WITCH_SHACKLES; + else if (nEssentiaID == 18639) nReal = MELD_WITCH_ABROGATION; + else if (nEssentiaID == 18641) nReal = MELD_WITCH_SPIRITFLAY; + else if (nEssentiaID == 18643) nReal = MELD_WITCH_INTEGUMENT; + + if (DEBUG) DoDebug("EssentiaToRealID: nEssentiaID "+IntToString(nEssentiaID)+" nReal "+IntToString(nReal)); + return nReal; // Return the real spellID +} + +void DrainEssentia(object oMeldshaper) +{ + //FloatingTextStringOnCreature("DrainEssentia", oMeldshaper); + int i; + for (i = 18700; i < 18799; i++) + { + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i)); + } + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_DUSKLING_SPEED)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_SPINE_ENHANCEMENT)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_SHIELD)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_ARMOR)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_WEAPON)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_STEP )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SHADOW)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SIGHT )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SOUL )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_KISS )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_STRIKE)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_HEAL )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_COUNTENANCE)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_RAY )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_AURA )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_MELDSHIELD)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_DISPEL )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SHACKLES )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_ABROGATION)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SPIRITFLAY)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_INTEGUMENT)); +} + +void WipeMelds(object oMeldshaper) +{ + //FloatingTextStringOnCreature("WipeMelds", oMeldshaper); + int i; + for (i = 18700; i < 18799; i++) + { + PRCRemoveSpellEffects(i, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(i, oMeldshaper, FALSE); + } + PRCRemoveSpellEffects(MELD_DUSKLING_SPEED, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_DUSKLING_SPEED, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_SPINE_ENHANCEMENT, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_SPINE_ENHANCEMENT, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_IRONSOUL_SHIELD, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_IRONSOUL_SHIELD, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_IRONSOUL_ARMOR, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_IRONSOUL_ARMOR, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_IRONSOUL_WEAPON, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_IRONSOUL_WEAPON, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_STEP, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_STEP, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_SHADOW, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_SHADOW, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_SIGHT, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_SIGHT, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_SOUL, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_SOUL, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_KISS, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_KISS, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_INCANDESCENT_STRIKE, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_INCANDESCENT_STRIKE, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_WITCH_MELDSHIELD, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_WITCH_MELDSHIELD, oMeldshaper, FALSE); + for (i = 18647; i < 18657; i++) + { + PRCRemoveSpellEffects(i, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(i, oMeldshaper, FALSE); + } +} + +void ReshapeMelds(object oMeldshaper) +{ + // Check each class and run the loop + if (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper)) + { + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_INCARNATE nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + if (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper)) + { + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_SOULBORN nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + if (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper)) + { + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_TOTEMIST nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + if (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)) + { + ActionCastSpellOnSelf(MELD_SPINE_ENHANCEMENT); + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_TOTEMIST nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + int nIron = GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper); + if (nIron) + { + ActionCastSpellOnSelf(MELD_IRONSOUL_SHIELD); + if (nIron >= 5) ActionCastSpellOnSelf(MELD_IRONSOUL_ARMOR); + if (nIron >= 9) ActionCastSpellOnSelf(MELD_IRONSOUL_WEAPON); + } + int nUmbral = GetLevelByClass(CLASS_TYPE_UMBRAL_DISCIPLE, oMeldshaper); + if (nUmbral) + { + ActionCastSpellOnSelf(MELD_UMBRAL_STEP); + if (nUmbral >= 3) ActionCastSpellOnSelf(MELD_UMBRAL_SHADOW); + if (nUmbral >= 7) ActionCastSpellOnSelf(MELD_UMBRAL_SIGHT); + if (nUmbral >= 9) ActionCastSpellOnSelf(MELD_UMBRAL_SOUL); + if (nUmbral >= 10) ActionCastSpellOnSelf(MELD_UMBRAL_KISS); + } + int nIncand = GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oMeldshaper); + if (nIncand) + { + ActionCastSpellOnSelf(MELD_INCANDESCENT_STRIKE); + } + if(GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper)) ActionCastSpellOnSelf(MELD_WITCH_MELDSHIELD); + if (GetRacialType(oMeldshaper) == RACIAL_TYPE_DUSKLING) ActionCastSpellOnSelf(MELD_DUSKLING_SPEED); +} + +int EssentiaToD4(int nEssentia) +{ + if (nEssentia == 1 ) return IP_CONST_DAMAGEBONUS_1d4; + else if (nEssentia == 2) return IP_CONST_DAMAGEBONUS_2d4; + else if (nEssentia == 3) return IP_CONST_DAMAGEBONUS_3d4; + else if (nEssentia == 4) return IP_CONST_DAMAGEBONUS_4d4; + else if (nEssentia == 5) return IP_CONST_DAMAGEBONUS_5d4; + else if (nEssentia == 6) return IP_CONST_DAMAGEBONUS_6d4; + else if (nEssentia == 7) return IP_CONST_DAMAGEBONUS_7d4; + else if (nEssentia == 8) return IP_CONST_DAMAGEBONUS_8d4; + else if (nEssentia == 9) return IP_CONST_DAMAGEBONUS_9d4; + else if (nEssentia >= 10) return IP_CONST_DAMAGEBONUS_10d4; + + return -1; +} + +int IncarnateAlignment(object oMeldshaper) +{ + int nReturn = FALSE; + + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_LAWFUL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_NEUTRAL) + { + nReturn = TRUE; + } + else if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_CHAOTIC && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_NEUTRAL) + { + nReturn = TRUE; + } + else if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_NEUTRAL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_GOOD) + { + nReturn = TRUE; + } + else if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_NEUTRAL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_EVIL) + { + nReturn = TRUE; + } + + return nReturn; +} + +int GetOpposition(object oMeldshaper, object oTarget) +{ + int nReturn = FALSE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_LAWFUL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_GOOD && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL || GetAlignmentLawChaos(oTarget) == ALIGNMENT_CHAOTIC)) + nReturn = TRUE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_LAWFUL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_EVIL && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD || GetAlignmentLawChaos(oTarget) == ALIGNMENT_CHAOTIC)) + nReturn = TRUE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_CHAOTIC && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_GOOD && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL || GetAlignmentLawChaos(oTarget) == ALIGNMENT_LAWFUL)) + nReturn = TRUE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_CHAOTIC && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_EVIL && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD || GetAlignmentLawChaos(oTarget) == ALIGNMENT_LAWFUL)) + nReturn = TRUE; + + return nReturn; +} + +void SetTemporaryEssentia(object oMeldshaper, int nEssentia) +{ + if (DEBUG) DoDebug("Set Temporary Essentia from "+IntToString(GetLocalInt(oMeldshaper, "TemporaryEssentia"))+" to "+IntToString(GetLocalInt(oMeldshaper, "TemporaryEssentia")+nEssentia)); + SetLocalInt(oMeldshaper, "TemporaryEssentia", GetLocalInt(oMeldshaper, "TemporaryEssentia")+nEssentia); +} + +int GetTemporaryEssentia(object oMeldshaper) +{ + return GetLocalInt(oMeldshaper, "TemporaryEssentia"); +} + +int GetMaxEssentiaCapacityFeat(object oMeldshaper) +{ + int nMax = 1; // Always can invest one + int nHD = GetHitDice(oMeldshaper); + if (nHD >= 31) nMax = 5; + else if (nHD >= 18) nMax = 4; + else if (nHD >= 12) nMax = 3; + else if (nHD >= 6) nMax = 2; + + if (GetLocalInt(oMeldshaper, "DivineSoultouch")) nMax += 1; + if (GetLocalInt(oMeldshaper, "IncandescentOverload")) nMax += max(GetAbilityModifier(ABILITY_CHARISMA, oMeldshaper), 1); + if (GetHasFeat(FEAT_IMPROVED_ESSENTIA_CAPACITY, oMeldshaper)) nMax += 1; + + // Don't allow more than they have + if (nMax > GetTotalUsableEssentia(oMeldshaper)) nMax = GetTotalUsableEssentia(oMeldshaper); + + if (DEBUG) DoDebug("GetMaxEssentiaCapacityFeat: nHD "+IntToString(nHD)+" nMax "+IntToString(nMax)); + return nMax; +} + +void SapphireSmiteUses(object oMeldshaper, int nEssentia) +{ + int i; + + for (i = 1; i <= nEssentia; i++) + { + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_GOOD_ALIGN); // Fist of Raziel + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_EVIL); // Paladin + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_GOOD); // Blackguard + IncrementRemainingFeatUses(oMeldshaper, FEAT_KIAI_SMITE); // CW Samurai + IncrementRemainingFeatUses(oMeldshaper, FEAT_CRUSADER_SMITE); // Crusader + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_UNDEAD); // Soldier of Light + IncrementRemainingFeatUses(oMeldshaper, FEAT_SHADOWBANE_SMITE); // Shadowbane + IncrementRemainingFeatUses(oMeldshaper, FEAT_KILLOREN_ASPECT_D); // Killoren + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_OPPOSITION); // Soulborn + IncrementRemainingFeatUses(oMeldshaper, FEAT_CULTIST_SMITE_MAGE); // Cultist of the Shattered Peak + } +} + +void AzureEnmity(object oMeldshaper, int nEssentia) +{ + effect eLink = EffectLinkEffects(EffectSkillIncrease(SKILL_BLUFF, nEssentia), EffectSkillIncrease(SKILL_LISTEN, nEssentia)); + eLink = EffectLinkEffects(eLink, EffectSkillIncrease(SKILL_SENSE_MOTIVE, nEssentia)); + eLink = EffectLinkEffects(eLink, EffectSkillIncrease(SKILL_SPOT, nEssentia)); + eLink = EffectLinkEffects(eLink, EffectDamageIncrease(IPGetDamageBonusConstantFromNumber(nEssentia), DAMAGE_TYPE_BASE_WEAPON)); + + if(GetHasFeat(FEAT_FAVORED_ENEMY_ABERRATION, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ABERRATION)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_ANIMAL, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ANIMAL)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_BEAST, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_BEAST)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_CONSTRUCT, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_CONSTRUCT)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_DRAGON, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_DRAGON)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_DWARF, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_DWARF)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_ELEMENTAL, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ELEMENTAL)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_ELF, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ELF)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_FEY, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_FEY)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_GIANT, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_GIANT)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_GNOME, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_GNOME)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_GOBLINOID, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_GOBLINOID)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HALFELF, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HALFELF)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HALFLING, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HALFLING)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HALFORC, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HALFORC)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HUMAN, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMAN)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_MAGICAL_BEAST, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_MAGICAL_BEAST)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_MONSTROUS, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_MONSTROUS)), oMeldshaper, 9999.0); + if(GetHasFeat(RACIAL_TYPE_HUMANOID_ORC, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_ORC)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_OUTSIDER, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_OUTSIDER)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_REPTILIAN, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_REPTILIAN)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_SHAPECHANGER, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_SHAPECHANGER)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_UNDEAD, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_UNDEAD)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_VERMIN, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_VERMIN)), oMeldshaper, 9999.0); +} + +void DoFeatBonus(object oMeldshaper, int nFeat, int nEssentia) +{ + effect eLink; + if (nFeat == FEAT_CERULEAN_FORTITUDE) eLink = EffectSavingThrowIncrease(SAVING_THROW_FORT, nEssentia); + else if (nFeat == FEAT_CERULEAN_REFLEXES) eLink = EffectSavingThrowIncrease(SAVING_THROW_REFLEX, nEssentia); + else if (nFeat == FEAT_CERULEAN_WILL) eLink = EffectSavingThrowIncrease(SAVING_THROW_WILL, nEssentia); + else if (nFeat == FEAT_AZURE_TALENT) ExecuteScript("moi_ft_aztalent", oMeldshaper); + else if (nFeat == FEAT_AZURE_TOUGHNESS) eLink = EffectTemporaryHitpoints(3*nEssentia); + else if (nFeat == FEAT_HEALING_SOUL) FeatUsePerDay(oMeldshaper, FEAT_HEALING_SOUL, -1, 0, nEssentia); + else if (nFeat == FEAT_SAPPHIRE_SMITE) SapphireSmiteUses(oMeldshaper, nEssentia); + else if (nFeat == FEAT_AZURE_ENMITY) AzureEnmity(oMeldshaper, nEssentia); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(eLink), oMeldshaper, 9999.0); +} + +void InvestEssentiaFeat(object oMeldshaper, int nFeat, int nEssentia) +{ + // Jump out if there's no feat + if (!GetHasFeat(nFeat, oMeldshaper)) return; + + int nMax = GetMaxEssentiaCapacityFeat(oMeldshaper); + + // Bonuses to specific feat caps + if (nFeat == FEAT_COBALT_RAGE && GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) nMax++; + + // No breaking the rules + if (nEssentia > nMax) nEssentia = nMax; + // Can't invest more than you have + if (nEssentia > GetTotalUsableEssentia(oMeldshaper)) nEssentia = GetTotalUsableEssentia(oMeldshaper); + + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))), oMeldshaper); + DoFeatBonus(oMeldshaper, nFeat, nEssentia); + SetLocalInt(oMeldshaper, "FeatEssentia"+IntToString(nFeat), nEssentia); +} + +void DoClassBonus(object oMeldshaper, int nFeat, int nEssentia) +{ + +} + +void InvestEssentiaClass(object oMeldshaper, int nFeat, int nEssentia) +{ + // Jump out if there's no feat + if (!GetHasFeat(nFeat, oMeldshaper)) return; + + // No breaking the rules + if (nEssentia > GetMaxEssentiaCapacityFeat(oMeldshaper)) nEssentia = GetMaxEssentiaCapacityFeat(oMeldshaper); + // Can't invest more than you have + if (nEssentia > GetTotalUsableEssentia(oMeldshaper)) nEssentia = GetTotalUsableEssentia(oMeldshaper); + + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))), oMeldshaper); + DoClassBonus(oMeldshaper, nFeat, nEssentia); + SetLocalInt(oMeldshaper, "ClassEssentia"+IntToString(nFeat), nEssentia); +} + +void InvestEssentiaSpell(object oMeldshaper, int nFeat, int nEssentia) +{ + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nFeat))), oMeldshaper); + SetLocalInt(oMeldshaper, "SpellEssentia"+IntToString(nFeat), nEssentia); + if (!GetLocalInt(oMeldshaper, "SpellInvestCheck1")) SetLocalInt(oMeldshaper, "SpellInvestCheck1", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck2")) SetLocalInt(oMeldshaper, "SpellInvestCheck2", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck3")) SetLocalInt(oMeldshaper, "SpellInvestCheck3", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck4")) SetLocalInt(oMeldshaper, "SpellInvestCheck4", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck5")) SetLocalInt(oMeldshaper, "SpellInvestCheck5", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck6")) SetLocalInt(oMeldshaper, "SpellInvestCheck6", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck7")) SetLocalInt(oMeldshaper, "SpellInvestCheck7", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck8")) SetLocalInt(oMeldshaper, "SpellInvestCheck8", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck9")) SetLocalInt(oMeldshaper, "SpellInvestCheck9", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck10")) SetLocalInt(oMeldshaper, "SpellInvestCheck10", nFeat); +} + +int GetFeatLockedEssentia(object oMeldshaper) +{ + int nTotal, i, nTest; + for (i = 8869; i < 8889; i++) + { + nTest = GetLocalInt(oMeldshaper, "FeatEssentia"+IntToString(i)); + if (nTest) // If it's not blank + nTotal += nTest; + } + for (i = 0; i < 11; i++) + { + nTest = GetLocalInt(oMeldshaper, "SpellInvestCheck"+IntToString(i)); + if (nTest) // If it's not blank + nTotal += GetLocalInt(oMeldshaper, "SpellEssentia"+IntToString(nTest)); + } + if (DEBUG) DoDebug("GetFeatLockedEssentia return value "+IntToString(nTotal)); + return nTotal; +} + +int IncarnumFeats(object oMeldshaper) +{ + int nEssentia; + + if (GetHasFeat(FEAT_AZURE_ENMITY, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_CERULEAN_FORTITUDE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_CERULEAN_REFLEXES, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_CERULEAN_WILL, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TALENT, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TOUCH, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TOUGHNESS, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TURNING, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_WILD_SHAPE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_CHARGE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_EXPERTISE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_POWER, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_RAGE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_HEALING_SOUL, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_INCARNUM_SPELLSHAPING, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_INDIGO_STRIKE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_MIDNIGHT_AUGMENTATION, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_MIDNIGHT_DODGE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_MIDNIGHT_METAMAGIC, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_PSYCARNUM_BLADE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_SAPPHIRE_SMITE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_SOULTOUCHED_SPELLCASTING, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_BONUS_ESSENTIA, oMeldshaper)) + { + nEssentia += 1; + if(GetIsIncarnumUser(oMeldshaper)) + nEssentia += 1; // Yes, this is correct + } + if (GetRacialType(oMeldshaper) == RACIAL_TYPE_DUSKLING) nEssentia += 1; + if (GetRacialType(oMeldshaper) == RACIAL_TYPE_AZURIN) nEssentia += 1; + + int i; + for(i = FEAT_EPIC_ESSENTIA_1; i <= FEAT_EPIC_ESSENTIA_6; i++) + if(GetHasFeat(i, oMeldshaper)) nEssentia += 3; + + if (DEBUG) DoDebug("IncarnumFeats return value "+IntToString(nEssentia)); + return nEssentia; +} + +void AddNecrocarnumEssentia(object oMeldshaper, int nEssentia) +{ + int nNecrocarnum = GetLocalInt(oMeldshaper, "NecrocarnumEssentia"); + SetLocalInt(oMeldshaper, "NecrocarnumEssentia", nNecrocarnum+nEssentia); + // It lasts for 24 hours, then remove it back out + DelayCommand(HoursToSeconds(24), SetLocalInt(oMeldshaper, "NecrocarnumEssentia", GetLocalInt(oMeldshaper, "NecrocarnumEssentia")-nEssentia)); +} + +int GetTotalEssentia(object oMeldshaper) +{ + int nEssentia; + + if (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_INCARNATE); + if (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_SOULBORN); + if (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_TOTEMIST); + if (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_SPINEMELD_WARRIOR); + if (GetLevelByClass(CLASS_TYPE_UMBRAL_DISCIPLE, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_UMBRAL_DISCIPLE); + if (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper)) nEssentia += GetLocalInt(oMeldshaper, "NecrocarnumEssentia"); + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper) >= 2) nEssentia += 1; + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper) >= 6) nEssentia += 1; + + nEssentia += IncarnumFeats(oMeldshaper); + if (DEBUG) DoDebug("GetTotalEssentia return value "+IntToString(nEssentia)); + return nEssentia; +} + +int GetTotalUsableEssentia(object oMeldshaper) +{ + return GetTotalEssentia(oMeldshaper) - GetFeatLockedEssentia(oMeldshaper); +} + +int GetIncarnumFeats(object oMeldshaper) +{ + int nTotal, i; + for (i = 8868; i < 8910; i++) + { + if (GetHasFeat(i, oMeldshaper)) + nTotal += 1; + } + if (DEBUG) DoDebug("GetIncarnumFeats return value "+IntToString(nTotal)); + return nTotal; +} + +int GetIsBlademeldUsed(object oMeldshaper, int nChakra) +{ + int nTest = GetLocalInt(oMeldshaper, "UsedBladeMeld"+IntToString(nChakra)); + + if (DEBUG) DoDebug("GetIsBlademeldUsed is "+IntToString(nTest)); + return nTest; +} + +void SetBlademeldUsed(object oMeldshaper, int nChakra) +{ + if (!GetIsBlademeldUsed(oMeldshaper, nChakra)) + { + SetLocalInt(oMeldshaper, "UsedBladeMeld"+IntToString(nChakra), nChakra); + } +} + +string GetBlademeldDesc(int nChakra) +{ + string sString; + if (nChakra == CHAKRA_CROWN ) sString = "You gain a +4 bonus on disarm checks and on Sense Motive"; + if (nChakra == CHAKRA_FEET ) sString = "You gain a +2 bonus on Initiative"; + if (nChakra == CHAKRA_HANDS ) sString = "You gain a +1 bonus on damage"; + if (nChakra == CHAKRA_ARMS ) sString = "You gain a +2 bonus on attack rolls"; + if (nChakra == CHAKRA_BROW ) sString = "You gain the Blind-Fight feat"; + if (nChakra == CHAKRA_SHOULDERS) sString = "You gain immunity to criticals but not sneak attacks"; + if (nChakra == CHAKRA_THROAT ) sString = "At will as a standard action, each enemy within 60 feet who can hear you shout must save or become shaken for 1 round (Will DC 10 + incarnum blade level + Con modifier)."; + if (nChakra == CHAKRA_WAIST ) sString = "You gain a +10 bonus on checks made to avoid being bull rushed, grappled, tripped, or overrun. You also gain Uncanny Dodge"; + if (nChakra == CHAKRA_HEART ) sString = "You gain temporary hit points equal to twice your character level (maximum +40)"; + if (nChakra == CHAKRA_SOUL ) sString = "You break DR up to +3, and deal 1d6 damage to creatures of one opposed alignment"; + + return sString; +} + +int ChakraToBlademeld(int nChakra) +{ + int nMeld; + if (nChakra == CHAKRA_CROWN ) nMeld = MELD_BLADEMELD_CROWN; + if (nChakra == CHAKRA_FEET ) nMeld = MELD_BLADEMELD_FEET; + if (nChakra == CHAKRA_HANDS ) nMeld = MELD_BLADEMELD_HANDS; + if (nChakra == CHAKRA_ARMS ) nMeld = MELD_BLADEMELD_ARMS; + if (nChakra == CHAKRA_BROW ) nMeld = MELD_BLADEMELD_BROW; + if (nChakra == CHAKRA_SHOULDERS) nMeld = MELD_BLADEMELD_SHOULDERS; + if (nChakra == CHAKRA_THROAT ) nMeld = MELD_BLADEMELD_THROAT; + if (nChakra == CHAKRA_WAIST ) nMeld = MELD_BLADEMELD_WAIST; + if (nChakra == CHAKRA_HEART ) nMeld = MELD_BLADEMELD_HEART; + if (nChakra == CHAKRA_SOUL ) nMeld = MELD_BLADEMELD_SOUL; + + return nMeld; +} \ No newline at end of file diff --git a/trunk/include/nw_o2_coninclude.nss b/trunk/include/nw_o2_coninclude.nss new file mode 100644 index 00000000..4a250bd0 --- /dev/null +++ b/trunk/include/nw_o2_coninclude.nss @@ -0,0 +1,4791 @@ +//:://///////////////////////////////////////////// +//:: NW_O2_CONINCLUDE.nss +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + This include file handles the random treasure + distribution for treasure from creatures and containers + + [ ] Documented +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent, Andrew +//:: Created On: November - May +//:: +//:: Updated for .35 by Jaysyn 2023/03/10 +//::////////////////////////////////////////////// +// :: MODS +// April 23 2002: Removed animal parts. They were silly. +// May 6 2002: Added Undead to the EXCLUSION treasure list (they drop nothing now) +// - redistributed treasure (to lessen amoun t of armor and increase 'class specific treasure' +// - Rangers with heavy armor prof. will be treated as Fighters else as Barbarians +// - Gave wizards, druids and monk their own function +// MAY 29 2002: Removed the heal potion from treasure +// Moved nymph cloak +4 to treasure bracket 6 +// Added Monk Enhancement items to random treasure + +#include "prc_class_const" + +// * --------- +// * CONSTANTS +// * --------- + +// * tweaking constants + + // * SIX LEVEL RANGES + const int RANGE_1_MIN = 0; + const int RANGE_1_MAX = 5; + const int RANGE_2_MIN = 6; + const int RANGE_2_MAX = 8; + + const int RANGE_3_MIN = 9; + const int RANGE_3_MAX = 10; + + const int RANGE_4_MIN = 11; + const int RANGE_4_MAX = 13; + + const int RANGE_5_MIN = 14; + const int RANGE_5_MAX = 16; + + const int RANGE_6_MIN = 17; + const int RANGE_6_MAX = 100; + + // * NUMBER OF ITEMS APPEARING + const int NUMBER_LOW_ONE = 100; const int NUMBER_MED_ONE = 60; const int NUMBER_HIGH_ONE = 40; const int NUMBER_BOSS_ONE = 100; + const int NUMBER_LOW_TWO = 0; const int NUMBER_MED_TWO = 30; const int NUMBER_HIGH_TWO = 40; const int NUMBER_BOSS_TWO = 0; + const int NUMBER_LOW_THREE = 0; const int NUMBER_MED_THREE = 10; const int NUMBER_HIGH_THREE = 20; const int NUMBER_BOSS_THREE = 0; + + const int NUMBER_BOOK_ONE = 75; + const int NUMBER_BOOK_TWO = 20; + const int NUMBER_BOOK_THREE = 5; + + // * AMOUNT OF GOLD BY VALUE + const float LOW_MOD_GOLD = 0.5; const float MEDIUM_MOD_GOLD = 1.0; const float HIGH_MOD_GOLD = 3.0; + // * FREQUENCY OF ITEM TYPE APPEARING BY TREASURE TYPE + const int LOW_PROB_BOOK = 1; const int MEDIUM_PROB_BOOK = 1; const int HIGH_PROB_BOOK =1; + const int LOW_PROB_ANIMAL = 0; const int MEDIUM_PROB_ANIMAL = 0; const int HIGH_PROB_ANIMAL = 0; + const int LOW_PROB_JUNK = 2; const int MEDIUM_PROB_JUNK = 1; const int HIGH_PROB_JUNK = 1; + const int LOW_PROB_GOLD = 43; const int MEDIUM_PROB_GOLD = 38; const int HIGH_PROB_GOLD = 15; + const int LOW_PROB_GEM = 9; const int MEDIUM_PROB_GEM = 15; const int HIGH_PROB_GEM = 15; + const int LOW_PROB_JEWEL = 4; const int MEDIUM_PROB_JEWEL = 6; const int HIGH_PROB_JEWEL = 15; + const int LOW_PROB_ARCANE = 3; const int MEDIUM_PROB_ARCANE = 3; const int HIGH_PROB_ARCANE = 3; + const int LOW_PROB_DIVINE = 3; const int MEDIUM_PROB_DIVINE = 3; const int HIGH_PROB_DIVINE = 3; + const int LOW_PROB_AMMO = 10; const int MEDIUM_PROB_AMMO = 5; const int HIGH_PROB_AMMO = 3; + const int LOW_PROB_KIT = 5; const int MEDIUM_PROB_KIT = 5; const int HIGH_PROB_KIT = 5; + const int LOW_PROB_POTION =17; const int MEDIUM_PROB_POTION = 20; const int HIGH_PROB_POTION= 9; + const int LOW_PROB_TABLE2 = 3; const int MEDIUM_PROB_TABLE2 = 3; const int HIGH_PROB_TABLE2= 30; + + +// * readability constants + +const int TREASURE_LOW = 1; +const int TREASURE_MEDIUM = 2; +const int TREASURE_HIGH = 3; +const int TREASURE_BOSS = 4; +const int TREASURE_BOOK = 5; + + +// * JUMP_LEVEL is used in a Specific item function +// * in the case where a generic item is called for within that function +// * it will create a generic item by adding JUMP_LEVEL to the character's +// * hit die for the purposes of the treasure evaluation. +// * May 2002: Lowered JUMP_LEVEL from 3 to 2 + +const int JUMP_LEVEL = 2; + + +//* Declarations + void CreateGenericExotic(object oTarget, object oAdventurer, int nModifier = 0); + void CreateGenericMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateSpecificMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateGenericDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateSpecificDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateGenericWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateSpecificWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0); + int nDetermineClassToUse(object oCharacter); + + +// * +// * IMPLEMENTATION +// * + +// * Comment the speakstring in to debug treasure generation +void dbSpeak(string s) +{ +// SpeakString(s); +} + +//* made this function to help with debugging +void dbCreateItemOnObject(string sItemTemplate, object oTarget = OBJECT_SELF, int nStackSize = 1) +{ +/* + if (sItemTemplate == "") + { + PrintString("blank item passed into dbCreateItemOnObject. Please report as bug to Brent."); + } + dbSpeak(sItemTemplate); +*/ + + //sItemTemplate = GetStringLowerCase + + if (nStackSize == 1) + { + // * checks to see if this is a throwing item and if it is + // * it creates more + + string sRoot = GetSubString(sItemTemplate, 0, 6); + //dbSpeak("ROOT: " + sRoot); + if (GetStringLowerCase(sRoot) == "nw_wth") + { + nStackSize = Random(30) + 1; + } + } + object oItem = CreateItemOnObject(sItemTemplate, oTarget, nStackSize); +/* + if (GetIsObjectValid(oItem) == FALSE && sItemTemplate != "NW_IT_GOLD001") + { + + // * check to see if item is there in a stack, if not give warning + if (GetIsObjectValid(GetItemPossessedBy(oTarget, GetStringUpperCase(sItemTemplate))) == FALSE && + GetIsObjectValid(GetItemPossessedBy(oTarget, GetStringLowerCase(sItemTemplate))) == FALSE) + { + PrintString("**DESIGN***"); + PrintString("******" + sItemTemplate + " is an invalid item template. Please report as bug to Brent."); + PrintString("*******"); + } + } +*/ +} + + +// * +// * GET FUNCTIONS +// * + +// * Returns the object that either last opened the container or destroyed it +object GetLastOpener() +{ + if (GetIsObjectValid(GetLastOpenedBy()) == TRUE) + { + //dbSpeak("LastOpener: GetLastOpenedBy " + GetTag(GetLastOpenedBy())); + return GetLastOpenedBy(); + } + else + if (GetIsObjectValid(GetLastKiller()) == TRUE) + { + //dbSpeak("LastOpener: GetLastAttacker"); + return GetLastKiller(); + } + //dbSpeak("LastOpener: The Object is Invalid you weenie!"); + return OBJECT_INVALID; +} + +//:://///////////////////////////////////////////// +//:: GetRange +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Returns true if nHD matches the correct + level range for the indicated nCategory. + (i.e., First to Fourth level characters + are considered Range1) +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: +//::////////////////////////////////////////////// +int GetRange(int nCategory, int nHD) +{ + int nMin = 0; int nMax = 0; + switch (nCategory) + { + case 6: nMin = RANGE_6_MIN; nMax = RANGE_6_MAX; break; + case 5: nMin = RANGE_5_MIN; nMax = RANGE_5_MAX; break; + case 4: nMin = RANGE_4_MIN; nMax = RANGE_4_MAX; break; + case 3: nMin = RANGE_3_MIN; nMax = RANGE_3_MAX; break; + case 2: nMin = RANGE_2_MIN; nMax = RANGE_2_MAX; break; + case 1: nMin = RANGE_1_MIN; nMax = RANGE_1_MAX; break; + } + + //dbSpeak("nMin = " + IntToString(nMin)); + //dbSpeak("nMax = " + IntToString(nMax)); + //dbSpeak("GetRange.nHD = " + IntToString(nHD)); + if (nHD >= nMin && nHD <= nMax) + { + return TRUE; + } + + return FALSE; + +} + +//:://///////////////////////////////////////////// +//:: GetNumberOfItems +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Returns the number of items to create. +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: +//::////////////////////////////////////////////// +int GetNumberOfItems(int nTreasureType) +{ + int nItems = 0; + int nRandom = 0; + + int nProbThreeItems = 0; + int nProbTwoItems = 0; + int nProbOneItems = 0; + + if (nTreasureType == TREASURE_LOW) + { + nProbThreeItems = NUMBER_LOW_THREE; + nProbTwoItems = NUMBER_LOW_TWO; + nProbOneItems = NUMBER_LOW_ONE; + } + else + if (nTreasureType == TREASURE_MEDIUM) + { + nProbThreeItems = NUMBER_MED_THREE; + nProbTwoItems = NUMBER_MED_TWO; + nProbOneItems = NUMBER_MED_ONE; + } + else + if (nTreasureType == TREASURE_HIGH) + { + nProbThreeItems = NUMBER_HIGH_THREE; + nProbTwoItems = NUMBER_HIGH_TWO; + nProbOneItems = NUMBER_HIGH_ONE; + } + else + if (nTreasureType == TREASURE_BOSS) + { + nProbThreeItems = NUMBER_BOSS_THREE; + nProbTwoItems = NUMBER_BOSS_TWO; + nProbOneItems = NUMBER_BOSS_ONE; + } + else + if (nTreasureType == TREASURE_BOOK) + { + nProbThreeItems = NUMBER_BOOK_THREE; + nProbTwoItems = NUMBER_BOOK_TWO; + nProbOneItems = NUMBER_BOOK_ONE; + } + + + nRandom = d100(); + if (nRandom <= nProbThreeItems) + { + nItems = 3; + } + else + if (nRandom <= nProbTwoItems + nProbThreeItems) + { + nItems = 2; + } + else + { + nItems = 1; + } + + // * May 13 2002: Cap number of items, in case of logic error + if (nItems > 3) + { + nItems = 3; + } + + return nItems; +} + + +// * +// * TREASURE GENERATION FUNCTIONS +// * + // * + // * Non-Scaling Treasure + // * + void CreateBook(object oTarget) + { + int nBook1 = Random(31) + 1; + string sRes = "NW_IT_BOOK01"; + + if (nBook1 < 10) + { + sRes = "NW_IT_BOOK00" + IntToString(nBook1); + } + else + { + sRes = "NW_IT_BOOK0" + IntToString(nBook1); + } + //dbSpeak("Create book"); + dbCreateItemOnObject(sRes, oTarget); + } + + void CreateAnimalPart(object oTarget) + { + + string sRes = ""; + int nResult = Random(3) + 1; + switch (nResult) + { + case 1: sRes = "NW_IT_MSMLMISC20"; break; + case 2: sRes = "NW_IT_MMIDMISC05"; break; + case 3: sRes = "NW_IT_MMIDMISC06"; break; + } + //dbSpeak("animal"); + dbCreateItemOnObject(sRes, oTarget); + } + + void CreateJunk(object oTarget) + { + string sRes = "NW_IT_TORCH001"; + int NUM_ITEMS = 6; + int nResult = Random(NUM_ITEMS) + 1; + int nKit = 0; + switch (nResult) + { + case 1: sRes = "NW_IT_MPOTION021"; break; //ale + case 2: sRes = "NW_IT_MPOTION021"; break; // ale + case 3: sRes = "NW_IT_MPOTION023"; break; // wine + case 4: sRes = "NW_IT_MPOTION021"; break; // ale + case 5: sRes = "NW_IT_MPOTION022"; break; // spirits + case 6: sRes = "NW_IT_TORCH001"; break; //torch + } + //dbSpeak("CreateJunk"); + dbCreateItemOnObject(sRes, oTarget); + } + // * + // * Scaling Treasure + // * + void CreateGold(object oTarget, object oAdventurer, int nTreasureType, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + int nAmount = 0; + + if (GetRange(1, nHD)) + { + nAmount = d10(); + } + else if (GetRange(2, nHD)) + { + nAmount = d20(); + } + else if (GetRange(3, nHD)) + { + nAmount = d20(2); + } + else if (GetRange(4, nHD)) + { + nAmount = d20(5); + } + else if (GetRange(5, nHD)) + { + nAmount = d20(8); + } + else if (GetRange(6, nHD)) + { + nAmount = d20(10); + } + float nMod = 0.0; + if (nTreasureType == TREASURE_LOW) nMod = LOW_MOD_GOLD; + else if (nTreasureType == TREASURE_MEDIUM) nMod = MEDIUM_MOD_GOLD; + else if (nTreasureType == TREASURE_HIGH) nMod = HIGH_MOD_GOLD; + + // * always at least 1gp is created + nAmount = FloatToInt(nAmount * nMod); + if (nAmount <= 0) + { + nAmount = 1; + } + //dbSpeak("gold"); + dbCreateItemOnObject("NW_IT_GOLD001", oTarget, nAmount); + } + void CreateGem(object oTarget, object oAdventurer, int nTreasureType, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sGem = "nw_it_gem001"; + if (GetRange(1, nHD)) + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sGem = "nw_it_gem001"; break; + case 2: sGem = "nw_it_gem007"; break; + case 3: sGem = "nw_it_gem002"; break; + case 4: case 5: sGem = "nw_it_gem004"; break; + case 6: case 7: sGem = "nw_it_gem014"; break; + case 8: sGem = "nw_it_gem003"; break; + case 9: sGem = "nw_it_gem015"; break; + } + } + else if (GetRange(2, nHD)) // 30 GP Avg; 150 gp Max + { + int nRandom = d12(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem001"; break; + case 2: sGem = "nw_it_gem007"; break; + case 3: sGem = "nw_it_gem002"; break; + case 4: sGem = "nw_it_gem004"; break; + case 5: case 6: sGem = "nw_it_gem014"; break; + case 7: case 8: sGem = "nw_it_gem003"; break; + case 9: case 10: sGem = "nw_it_gem015"; break; + case 11: sGem = "nw_it_gem011"; break; + case 12: sGem = "nw_it_gem013"; break; + } + + } + else if (GetRange(3, nHD)) // 75GP Avg; 500 gp max + { + int nRandom = d2(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + } + + } + else if (GetRange(4, nHD)) // 150 gp avg; 1000 gp max + { + int nRandom = d3(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + case 3: sGem = "nw_it_gem008"; break; + } + } + else if (GetRange(5, nHD)) // 300 gp avg; any + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + case 3: case 4: sGem = "nw_it_gem008"; break; + case 5: sGem = "nw_it_gem009"; break; + case 6: sGem = "nw_it_gem009"; break; + } + } + else if (GetRange(6, nHD))// * Anything higher than level 15 500 gp avg; any + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + case 3: case 4: sGem = "nw_it_gem008"; break; + case 5: sGem = "nw_it_gem009"; break; + case 6: sGem = "nw_it_gem009"; break; + case 7: sGem = "nw_it_gem006"; break; + case 8: sGem = "nw_it_gem012"; break; + } + } + //dbSpeak("Create Gem"); + dbCreateItemOnObject(sGem, oTarget, 1); + } + void CreateJewel(object oTarget, object oAdventurer, int nTreasureType, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sJewel = ""; + + if (GetRange(1, nHD)) // 15 gp avg; 75 gp max + { + int nRandom = d2(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: sJewel = "nw_it_mneck020"; break; + } + } + else if (GetRange(2, nHD)) // 30 GP Avg; 150 gp Max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: case 3: sJewel = "nw_it_mneck020"; break; + case 4: sJewel = "nw_it_mring022"; break; + case 5: case 6: sJewel = "nw_it_mneck023"; break; } + } + else if (GetRange(3, nHD)) // 75GP Avg; 500 gp max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: case 3: sJewel = "nw_it_mneck020"; break; + case 4: case 5: sJewel = "nw_it_mring022"; break; + case 6: sJewel = "nw_it_mneck021"; break; + } + } + else if (GetRange(4, nHD)) // 150 gp avg; 1000 gp max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: sJewel = "nw_it_mring022"; break; + case 3: case 4: case 5: sJewel = "nw_it_mneck021"; break; + case 6: sJewel = "nw_it_mring023"; break; + } + } + else if (GetRange(5, nHD)) // 300 gp avg; any + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring022"; break; + case 2: case 3: sJewel = "nw_it_mneck021"; break; + case 4: case 5: case 6: sJewel = "nw_it_mring023"; break; + case 7: case 8: sJewel = "nw_it_mneck022"; break; + } + } + else if (GetRange(6, nHD)) + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring022"; break; + case 2: sJewel = "nw_it_mneck021"; break; + case 3: case 4: sJewel = "nw_it_mring023"; break; + case 5: case 6: sJewel = "nw_it_mneck022"; break; + } + } + //dbSpeak("Create Jewel"); + + dbCreateItemOnObject(sJewel, oTarget, 1); + + } + // * returns the valid upper limit for any arcane spell scroll + int TrimLevel(int nScroll, int nLevel) + { int nMax = 5; + switch (nLevel) + { + case 0: nMax = 4; break; + case 1: nMax = 13; break; + case 2: nMax = 21; break; + case 3: nMax = 15; break; + case 4: nMax = 17; break; + case 5: nMax = 13; break; + case 6: nMax = 14; break; + case 7: nMax = 8; break; + case 8: nMax = 9; break; + case 9: nMax = 12; break; + } + if (nScroll > nMax) nScroll = nMax; + return nScroll; + + } + // * nModifier is to 'raise' the level of the oAdventurer + void CreateArcaneScroll(object oTarget, object oAdventurer, int nModifier = 0) + { + int nMaxSpells = 21; + int nHD = GetHitDice(oAdventurer) + nModifier; + int nScroll = 1; + int nLevel = 1; + + if (GetRange(1, nHD)) // l 1-2 + { + nLevel = d2(); + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(2, nHD)) // l 1-4 + { + nLevel = d4(); + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(3, nHD)) // l 2-6 + { + nLevel = d6(); + if (nLevel < 2) nLevel = 2; + + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(4, nHD)) // l 3-8 + { + nLevel = d8(); + if (nLevel < 3) nLevel = 3; + + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(5, nHD)) // l 4-9 + { + nLevel = d8() + 1; + if (nLevel < 4) nLevel = 4; + + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(6, nHD)) // 5 -9 + { + nLevel = d8() + 1; + if (nLevel < 5) nLevel = 5; + + nScroll = Random(nMaxSpells) + 1; + } + + // * Trims the level of the scroll to match the max # of scrolls in each level range + nScroll = TrimLevel(nScroll, nLevel); + + string sRes = "nw_it_sparscr216"; + + if (nScroll < 10) + { + sRes = "NW_IT_SPARSCR" + IntToString(nLevel) + "0" + IntToString(nScroll); + } + else + { + sRes = "NW_IT_SPARSCR" + IntToString(nLevel) + IntToString(nScroll); + } + dbCreateItemOnObject(sRes, oTarget, 1); + } + + void CreateDivineScroll(object oTarget, object oAdventurer, int nModifier=0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sScroll = ""; + if (GetRange(1, nHD)) + { + int nRandom = d4(); + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr201"; break; + case 2: sScroll = "nw_it_spdvscr202"; break; + case 3: sScroll = "nw_it_spdvscr203"; break; + case 4: sScroll = "nw_it_spdvscr204"; break; + } + } + else if (GetRange(2, nHD)) + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr201"; break; + case 2: sScroll = "nw_it_spdvscr202";break; + case 3: sScroll = "nw_it_spdvscr203"; break; + case 4: sScroll = "nw_it_spdvscr204"; break; + case 5: sScroll = "nw_it_spdvscr301"; break; + case 6: sScroll = "nw_it_spdvscr302"; break; + case 7: sScroll = "nw_it_spdvscr401"; break; + case 8: sScroll = "nw_it_spdvscr402"; break; + } + + } + else if (GetRange(3, nHD)) + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr201"; break; + case 2: sScroll = "nw_it_spdvscr202"; break; + case 3: sScroll = "nw_it_spdvscr203"; break; + case 4: sScroll = "nw_it_spdvscr204"; break; + case 5: sScroll = "nw_it_spdvscr301"; break; + case 6: sScroll = "nw_it_spdvscr302"; break; + case 7: sScroll = "nw_it_spdvscr401"; break; + case 8: sScroll = "nw_it_spdvscr402"; break; + case 9: sScroll = "nw_it_spdvscr501"; break; + } + + } + else + { + int nRandom = Random(7) + 1; + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr301"; break; + case 2: sScroll = "nw_it_spdvscr302"; break; + case 3: sScroll = "nw_it_spdvscr401"; break; + case 4: sScroll = "nw_it_spdvscr402"; break; + case 5: sScroll = "nw_it_spdvscr501"; break; + case 6: sScroll = "nw_it_spdvscr701"; break; + case 7: sScroll = "nw_it_spdvscr702"; break; + } + } + //dbSpeak("Divine Scroll"); + + dbCreateItemOnObject(sScroll, oTarget, 1); + + } + void CreateAmmo(object oTarget, object oAdventurer, int nModifier=0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sAmmo = ""; + + if (GetRange(1, nHD)) // * 200 gp max + { + int nRandom = d3(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wambo001"; break; + case 3: sAmmo = "nw_wambu001"; break; + } + } + else if (GetRange(2, nHD)) // * 800 gp max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wambo001"; break; + case 3: sAmmo = "nw_wambu001"; break; + case 4: sAmmo = "nw_wammar001"; break; + case 5: sAmmo = "nw_wammbo001"; break; + case 6: sAmmo = "nw_wammbo002"; break; + } + } + else if (GetRange(3, nHD)) // * - 2500 gp + { + int nRandom = d20(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wambo001"; break; + case 3: sAmmo = "nw_wambu001"; break; + case 4: sAmmo = "nw_wammar001"; break; + case 5: sAmmo = "nw_wammbo001"; break; + case 6: sAmmo = "nw_wammbo002"; break; + case 7: sAmmo = "nw_wammbo003"; break; + case 8: sAmmo = "nw_wammbu002"; break; + case 9: sAmmo = "nw_wammar002"; break; + case 10: sAmmo = "nw_wammar001"; break; + case 11: sAmmo = "nw_wammar003"; break; + case 12: sAmmo = "nw_wammar004"; break; + case 13: sAmmo = "nw_wammar005"; break; + case 14: sAmmo = "nw_wammar006"; break; + case 15: sAmmo = "nw_wammbo004"; break; + case 16: sAmmo = "nw_wammbo005"; break; + case 17: sAmmo = "nw_wammbu004"; break; + case 18: sAmmo = "nw_wammbu005"; break; + case 19: sAmmo = "nw_wammbu006"; break; + case 20: sAmmo = "nw_wammbu007"; break; + } + } + else + { + int nRandom = d20(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wammbu001"; break; + case 3: sAmmo = "nw_wammbu003"; break; + case 4: sAmmo = "nw_wammar001"; break; + case 5: sAmmo = "nw_wammbo001"; break; + case 6: sAmmo = "nw_wammbo002"; break; + case 7: sAmmo = "nw_wammbo003"; break; + case 8: sAmmo = "nw_wammbu002"; break; + case 9: sAmmo = "nw_wammar002"; break; + case 10: sAmmo = "nw_wammar001"; break; + case 11: sAmmo = "nw_wammar003"; break; + case 12: sAmmo = "nw_wammar004"; break; + case 13: sAmmo = "nw_wammar005"; break; + case 14: sAmmo = "nw_wammar006"; break; + case 15: sAmmo = "nw_wammbo004"; break; + case 16: sAmmo = "nw_wammbo005"; break; + case 17: sAmmo = "nw_wammbu004"; break; + case 18: sAmmo = "nw_wammbu005"; break; + case 19: sAmmo = "nw_wammbu006"; break; + case 20: sAmmo = "nw_wammbu007"; break; + } + } + //dbSpeak("ammo"); + dbCreateItemOnObject(sAmmo, oTarget, Random(30) + 1); // create up to 30 of the specified ammo type + } + + void CreateTrapKit(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sKit = ""; + if (GetRange(1, nHD)) // 200 + { + int nRandom = d3(); + switch (nRandom) + { + case 1: sKit = "nw_it_trap001"; break; + case 2: sKit = "nw_it_trap029"; break; + case 3: sKit = "nw_it_trap033"; break; + } + } + else if (GetRange(2, nHD)) // 800 + { + int nRandom = d12(); + switch (nRandom) + { + case 1: sKit = "nw_it_trap001"; break; + case 2: sKit = "nw_it_trap029"; break; + case 3: sKit = "nw_it_trap033"; break; + case 4: sKit = "nw_it_trap002"; break; + case 5: sKit = "nw_it_trap030"; break; + case 6: sKit = "nw_it_trap037"; break; + case 7: sKit = "nw_it_trap034"; break; + case 8: sKit = "nw_it_trap005"; break; + case 9: sKit = "nw_it_trap038"; break; + case 10: sKit = "nw_it_trap041"; break; + case 11: sKit = "nw_it_trap003"; break; + case 12: sKit = "nw_it_trap031"; break; + } + + } + else if (GetRange(3, nHD)) // 200 - 2500 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap002"; break; + case 2: sKit = "nw_it_trap030"; break; + case 3: sKit = "nw_it_trap037"; break; + case 4: sKit = "nw_it_trap034"; break; + case 5: sKit = "nw_it_trap005"; break; + case 6: sKit = "nw_it_trap038"; break; + case 7: sKit = "nw_it_trap041"; break; + case 8: sKit = "nw_it_trap003"; break; + case 9: sKit = "nw_it_trap031"; break; + case 10: sKit = "nw_it_trap035"; break; + case 11: sKit = "nw_it_trap006"; break; + case 12: sKit = "nw_it_trap042"; break; + case 13: sKit = "nw_it_trap004"; break; + case 14: sKit = "nw_it_trap032"; break; + case 15: sKit = "nw_it_trap039"; break; + case 16: sKit = "nw_it_trap009"; break; + case 17: sKit = "nw_it_trap036"; break; + } + + } + else if (GetRange(4, nHD)) // 800 - 10000 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap035"; break; + case 2: sKit = "nw_it_trap006"; break; + case 3: sKit = "nw_it_trap042"; break; + case 4: sKit = "nw_it_trap004"; break; + case 5: sKit = "nw_it_trap032"; break; + case 6: sKit = "nw_it_trap039"; break; + case 7: sKit = "nw_it_trap009"; break; + case 8: sKit = "nw_it_trap036"; break; + case 9: sKit = "nw_it_trap013"; break; + case 10: sKit = "nw_it_trap040"; break; + case 11: sKit = "nw_it_trap007"; break; + case 12: sKit = "nw_it_trap043"; break; + case 13: sKit = "nw_it_trap010"; break; + case 14: sKit = "nw_it_trap017"; break; + case 15: sKit = "nw_it_trap021"; break; + case 16: sKit = "nw_it_trap014"; break; + case 17: sKit = "nw_it_trap025"; break; + case 18: sKit = "nw_it_trap008"; break; + case 19: sKit = "nw_it_trap044"; break; + } + + } + else if (GetRange(5, nHD)) // 2000 -16500 + { + int nRandom = Random(18) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap039"; break; + case 2: sKit = "nw_it_trap009"; break; + case 3: sKit = "nw_it_trap036"; break; + case 4: sKit = "nw_it_trap013"; break; + case 5: sKit = "nw_it_trap040"; break; + case 6: sKit = "nw_it_trap007"; break; + case 7: sKit = "nw_it_trap043"; break; + case 8: sKit = "nw_it_trap010"; break; + case 9: sKit = "nw_it_trap017"; break; + case 10: sKit = "nw_it_trap021"; break; + case 11: sKit = "nw_it_trap014"; break; + case 12: sKit = "nw_it_trap025"; break; + case 13: sKit = "nw_it_trap008"; break; + case 14: sKit = "nw_it_trap044"; break; + case 15: sKit = "nw_it_trap018"; break; + case 16: sKit = "nw_it_trap011"; break; + case 17: sKit = "nw_it_trap022"; break; + case 18: sKit = "nw_it_trap026"; break; + } + + } + else if (GetRange(6, nHD)) // 2000 - ? + { + int nRandom = Random(27) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap039"; break; + case 2: sKit = "nw_it_trap009"; break; + case 3: sKit = "nw_it_trap036"; break; + case 4: sKit = "nw_it_trap013"; break; + case 5: sKit = "nw_it_trap040"; break; + case 6: sKit = "nw_it_trap007"; break; + case 7: sKit = "nw_it_trap043"; break; + case 8: sKit = "nw_it_trap010"; break; + case 9: sKit = "nw_it_trap017"; break; + case 10: sKit = "nw_it_trap021"; break; + case 11: sKit = "nw_it_trap014"; break; + case 12: sKit = "nw_it_trap025"; break; + case 13: sKit = "nw_it_trap008"; break; + case 14: sKit = "nw_it_trap044"; break; + case 15: sKit = "nw_it_trap018"; break; + case 16: sKit = "nw_it_trap011"; break; + case 17: sKit = "nw_it_trap022"; break; + case 18: sKit = "nw_it_trap026"; break; + case 19: sKit = "nw_it_trap015"; break; + case 20: sKit = "nw_it_trap012"; break; + case 21: sKit = "nw_it_trap019"; break; + case 22: sKit = "nw_it_trap023"; break; + case 23: sKit = "nw_it_trap016"; break; + case 24: sKit = "nw_it_trap027"; break; + case 25: sKit = "nw_it_trap020"; break; + case 26: sKit = "nw_it_trap024"; break; + case 27: sKit = "nw_it_trap028"; break; + } + + } + //dbSpeak("Create Trapkit"); + dbCreateItemOnObject(sKit, oTarget, 1); + + } + void CreateHealingKit(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sKit = ""; + if (GetRange(1, nHD)) // 200 + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit001"; break; + } + } + else if (GetRange(2, nHD)) // 800 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit001"; break; + case 2: sKit = "nw_it_medkit002"; break; + } + + } + else if (GetRange(3, nHD)) // 200 - 2500 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit002"; break; + case 2: sKit = "nw_it_medkit003"; break; + } + + } + else if (GetRange(4, nHD)) // 800 - 10000 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit003";break; + case 2: sKit = "nw_it_medkit004"; break; + } + + } + else if (GetRange(5, nHD)) // 2000 -16500 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit003"; break; + case 2: sKit = "nw_it_medkit004";break; + } + + } + else if (GetRange(6, nHD)) // 2000 - ? + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit003"; break; + case 2: sKit = "nw_it_medkit004";break; + } + + } + //dbSpeak("Create Healing Kit"); + + dbCreateItemOnObject(sKit, oTarget, 1); + + } + void CreateLockPick(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sKit = ""; + if (GetRange(1, nHD)) // 200 + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sKit = "nw_it_picks001"; break; + case 2: sKit = "nw_it_picks002"; break; + case 3: sKit = "nw_it_picks001"; break; + case 4: sKit = "nw_it_picks001"; break; + case 5: sKit = "nw_it_picks001"; break; + case 6: sKit = "nw_it_picks001"; break; + case 7: sKit = "nw_it_picks001"; break; + case 8: sKit = "nw_it_picks001"; break; + } + } + else if (GetRange(2, nHD)) // 800 + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sKit = "nw_it_picks001"; break; + case 2: sKit = "nw_it_picks002"; break; + case 3: sKit = "nw_it_picks003"; break; + case 4: sKit = "nw_it_picks002"; break; + case 5: sKit = "nw_it_picks002"; break; + case 6: sKit = "nw_it_picks002"; break; + } + + } + else if (GetRange(3, nHD)) // 200 - 2500 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks003"; break; + case 2: sKit = "nw_it_picks004"; break; + } + + } + else if (GetRange(4, nHD)) // 800 - 10000 + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks004"; break; + } + + } + else if (GetRange(5, nHD)) // 2000 -16500 + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks004"; break; + } + + } + else if (GetRange(6, nHD)) // 2000 - ? + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks004"; break; + } + + } + //dbSpeak("Create Lockpick"); + + dbCreateItemOnObject(sKit, oTarget, 1); + + } + void CreateKit(object oTarget, object oAdventurer, int nModifier = 0) + { + // * April 23 2002: Major restructuring of this function + // * to allow me to + + switch (Random(8) + 1) + { + case 1: CreateTrapKit(oTarget, oAdventurer, nModifier); break; + case 2: case 3: case 4: case 5: CreateHealingKit(oTarget, oAdventurer, nModifier); break; + case 6: case 7: case 8: CreateLockPick(oTarget, oAdventurer, nModifier); break; + } + } + + void CreatePotion(object oTarget, object oAdventurer, int nModifier = 0) + { + string sPotion = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) + { + int nRandom = d10(); + switch (nRandom) + { + case 1: case 2: case 3: case 4: sPotion = "nw_it_mpotion001"; break; + case 5: case 6: case 7: sPotion = "nw_it_mpotion020"; break; + case 8: sPotion = "nw_it_mpotion002"; break; + case 9: sPotion = "nw_it_mpotion009"; break; + case 10: sPotion = "nw_it_mpotion005"; break; + } + + } + else if (GetRange(2, nHD)) + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: sPotion = "nw_it_mpotion001"; break; + case 4: case 5: case 6: case 7: case 8: sPotion = "nw_it_mpotion020"; break; + case 9: case 10: case 11: case 12: sPotion = "nw_it_mpotion002"; break; + case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + else if (GetRange(3, nHD)) + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 9: case 10: case 11: case 12: + case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + else if (GetRange(4, nHD)) + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 9: case 10: case 11: case 12: sPotion = "nw_it_mpotion003"; break; + case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + else // keep 5 and 6 the same + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 9: sPotion = "nw_it_mpotion003" ; + case 10: case 11: case 12: case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + //dbSpeak("Create Potion"); + dbCreateItemOnObject(sPotion, oTarget, 1); + } + //:://///////////////////////////////////////////// + //:: CreateTable2GenericItem + //:: Copyright (c) 2002 Bioware Corp. + //::////////////////////////////////////////////// + /* + Creates an item based upon the class of + oAdventurer + */ + //::////////////////////////////////////////////// + //:: Created By: Brent + //:: Created On: + //::////////////////////////////////////////////// + void CreateGenericMiscItem(object oTarget, object oAdventurer, int nModifier=0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sItem = ""; + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mglove004"; break; + case 2: sItem = "nw_it_mglove004"; break; + case 3: sItem = "nw_it_mglove005"; break; + case 4: sItem = "nw_it_mglove006"; break; + case 5: sItem = "nw_it_mglove007"; break; + case 6: sItem = "nw_it_mglove008"; break; + case 7: sItem = "nw_it_mglove009"; break; + case 8: sItem = "nw_mcloth006"; break; + case 9: sItem = "nw_it_mglove012"; break; + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(25) + 1; + switch (nRandom) + { + case 1: sItem = "nw_mcloth006"; break; + case 2: sItem = "nw_it_mring009"; break; + case 3: sItem = "nw_it_mring009"; break; + case 4: sItem = "nw_it_mring010"; break; + case 5: sItem = "nw_it_mring011"; break; + case 6: sItem = "nw_it_mboots010"; break; + case 7: sItem = "nw_it_mneck024"; break; + case 8: sItem = "nw_mcloth007"; break; + case 9: sItem = "nw_it_mring024"; break; + case 10: sItem = "nw_it_mring012"; break; + case 11: sItem = "nw_mcloth008"; break; + case 12: sItem = "nw_it_mglove010"; break; + case 13: sItem = "nw_it_mglove011"; break; + case 14: sItem = "nw_it_mglove013"; break; + case 15: sItem = "nw_it_mglove014"; break; + case 16: sItem = "nw_it_mglove015"; break; + case 17: sItem = "nw_maarcl097"; break; + case 18: sItem = "nw_maarcl097"; break; + case 19: sItem = "nw_maarcl099"; break; + case 20: sItem = "nw_it_mneck032"; break; + case 21: sItem = "nw_mcloth010"; break; + case 22: sItem = "nw_it_mbracer002"; break; + case 23: sItem = "nw_it_mneck001"; break; + case 24: sItem = "nw_maarcl055"; break; + case 25: sItem = "nw_mcloth009"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(44) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mring009"; break; + case 2: sItem = "nw_it_mring009"; break; + case 3: sItem = "nw_it_mring010"; break; + case 4: sItem = "nw_it_mring011"; break; + case 5: sItem = "nw_it_mboots010"; break; + case 6: sItem = "nw_it_mneck024"; break; + case 7: sItem = "nw_mcloth007"; break; + case 8: sItem = "nw_it_mring024"; break; + case 9: sItem = "nw_it_mring012"; break; + case 10: sItem = "nw_mcloth008"; break; + case 11: sItem = "nw_it_mglove010"; break; + case 12: sItem = "nw_it_mglove011"; break; + case 13: sItem = "nw_it_mglove013"; break; + case 14: sItem = "nw_it_mglove014"; break; + case 15: sItem = "nw_it_mglove015"; break; + case 16: sItem = "nw_it_contain003"; break; + case 17: sItem = "nw_maarcl097"; break; + case 18: sItem = "nw_maarcl099"; break; + case 19: sItem = "nw_it_mneck032"; break; + case 20: sItem = "nw_mcloth010"; break; + case 21: sItem = "nw_it_mbracer002"; break; + case 22: sItem = "nw_it_mneck001"; break; + case 23: sItem = "nw_maarcl055"; break; + case 24: sItem = "nw_mcloth009"; break; + case 25: sItem = "nw_it_mring001"; break; + case 26: sItem = "nw_it_mboots001"; break; + case 27: sItem = "nw_it_mbracer001"; break; + case 28: sItem = "nw_it_mneck007"; break; + case 29: sItem = "nw_maarcl096"; break; + case 30: sItem = "nw_it_mglove003"; break; + case 31: sItem = "nw_it_contain004"; break; + case 32: sItem = "nw_it_mneck031"; break; + case 33: sItem = "nw_it_mring006"; break; + case 34: sItem = "nw_it_mneck006"; break; + case 35: sItem = "nw_it_mneck029"; break; + case 36: sItem = "nw_it_mring013"; break; + case 37: sItem = "nw_it_mboots011"; break; + case 38: sItem = "nw_it_mneck025"; break; + case 39: sItem = "nw_it_mbelt009"; break; + case 40: sItem = "nw_it_mbelt010"; break; + case 41: sItem = "nw_it_mbelt011"; break; + case 42: sItem = "nw_it_mring025"; break; + case 43: sItem = "nw_it_mring025"; break; + case 44: sItem = "nw_maarcl031"; break; + + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(48) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mring001"; break; + case 2: sItem = "nw_it_mboots001"; break; + case 3: sItem = "nw_it_mbracer001"; break; + case 4: sItem = "nw_it_mneck007"; break; + case 5: sItem = "nw_maarcl096"; break; + case 6: sItem = "nw_it_mglove003"; break; + case 7: sItem = "nw_it_mneck031"; break; + case 8: sItem = "nw_it_mneck031"; break; + case 9: sItem = "nw_it_mring006"; break; + case 10: sItem = "nw_it_mneck006"; break; + case 11: sItem = "nw_it_mneck029"; break; + case 12: sItem = "nw_it_mring013"; break; + case 13: sItem = "nw_it_mboots011"; break; + case 14: sItem = "nw_it_mneck025"; break; + case 15: sItem = "nw_it_mbelt009"; break; + case 16: sItem = "nw_it_mbelt010"; break; + case 17: sItem = "nw_it_mbelt011"; break; + case 18: sItem = "nw_it_mring025"; break; + case 19: sItem = "nw_it_mring025"; break; + case 20: sItem = "nw_it_mbracer007"; break; + case 21: sItem = "nw_it_mbracer007"; break; + case 22: sItem = "nw_it_mneck012"; break; + case 23: sItem = "nw_maarcl088"; break; + case 24: sItem = "nw_it_mboots012"; break; + case 25: sItem = "nw_it_mneck026"; break; + case 26: sItem = "nw_it_mboots006"; break; + case 27: sItem = "nw_it_mbracer003"; break; + case 28: sItem = "nw_it_mneck008"; break; + case 29: sItem = "nw_it_mring008"; break; + case 30: sItem = "nw_maarcl056"; break; + case 31: sItem = "nw_maarcl092"; break; + case 32: sItem = "nw_it_mring014"; break; + case 33: sItem = "nw_it_mneck016"; break; + case 34: sItem = "nw_it_mboots013"; break; + case 35: sItem = "nw_it_mneck027"; break; + case 36: sItem = "nw_it_mbracer008"; break; + case 37: sItem = "nw_it_mneck013"; break; + case 38: sItem = "nw_maarcl089"; break; + case 39: sItem = "nw_it_mbelt012"; break; + case 40: sItem = "nw_it_mbelt013"; break; + case 41: sItem = "nw_it_mbelt014"; break; + case 42: sItem = "nw_it_mring027"; break; + case 43: sItem = "nw_it_mboots007"; break; + case 44: sItem = "nw_it_mbracer004"; break; + case 45: sItem = "nw_it_mneck009"; break; + case 46: sItem = "nw_it_mring018"; break; + case 47: sItem = "nw_maarcl093"; break; + case 48: sItem = "nw_it_mboots002"; break; + + } + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(42) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mbracer007"; break; + case 2: sItem = "nw_it_mbracer007"; break; + case 3: sItem = "nw_it_mneck012"; break; + case 4: sItem = "nw_maarcl088"; break; + case 5: sItem = "nw_it_mboots012"; break; + case 6: sItem = "nw_it_mneck026"; break; + case 7: sItem = "nw_it_mboots006"; break; + case 8: sItem = "nw_it_mbracer003"; break; + case 9: sItem = "nw_it_mneck008"; break; + case 10: sItem = "nw_it_mring008"; break; + case 11: sItem = "nw_maarcl056"; break; + case 12: sItem = "nw_maarcl092"; break; + case 13: sItem = "nw_it_mring014"; break; + case 14: sItem = "nw_it_mneck016"; break; + case 15: sItem = "nw_it_mboots013"; break; + case 16: sItem = "nw_it_mneck027"; break; + case 17: sItem = "nw_it_mbracer008"; break; + case 18: sItem = "nw_it_mneck013"; break; + case 19: sItem = "nw_maarcl089"; break; + case 20: sItem = "nw_it_mbelt012"; break; + case 21: sItem = "nw_it_mbelt013"; break; + case 22: sItem = "nw_it_mbelt014"; break; + case 23: sItem = "nw_it_mring027"; break; + case 24: sItem = "nw_it_mboots007"; break; + case 25: sItem = "nw_it_mbracer004"; break; + case 26: sItem = "nw_it_mneck009"; break; + case 27: sItem = "nw_it_mring018"; break; + case 28: sItem = "nw_maarcl093"; break; + case 29: sItem = "nw_it_mboots002"; break; + case 30: sItem = "nw_it_mboots014"; break; + case 31: sItem = "nw_it_mneck028"; break; + case 32: sItem = "nw_it_mring015"; break; + case 33: sItem = "nw_it_mbracer009"; break; + case 34: sItem = "nw_it_mneck014"; break; + case 35: sItem = "nw_maarcl090"; break; + case 36: sItem = "nw_it_mring028"; break; + case 37: sItem = "nw_it_mneck017"; break; + case 38: sItem = "nw_it_mboots008"; break; + case 39: sItem = "nw_it_mbracer005"; break; + case 40: sItem = "nw_it_mneck010"; break; + case 41: sItem = "nw_it_mmidmisc02"; break; + case 42: sItem = "nw_it_mring019"; break; + } + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(30) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mring027"; break; + case 2: sItem = "nw_it_mboots007"; break; + case 3: sItem = "nw_it_mbracer004"; break; + case 4: sItem = "nw_it_mneck009"; break; + case 5: sItem = "nw_it_mring018"; break; + case 6: sItem = "nw_maarcl093"; break; + case 7: sItem = "nw_it_mboots002"; break; + case 8: sItem = "nw_it_mboots014"; break; + case 9: sItem = "nw_it_mneck028"; break; + case 10: sItem = "nw_it_mring015"; break; + case 11: sItem = "nw_it_mbracer009"; break; + case 12: sItem = "nw_it_mneck014"; break; + case 13: sItem = "nw_maarcl090"; break; + case 14: sItem = "nw_it_mring028"; break; + case 15: sItem = "nw_it_mneck017"; break; + case 16: sItem = "nw_it_mboots008"; break; + case 17: sItem = "nw_it_mbracer005"; break; + case 18: sItem = "nw_it_mneck010"; break; + case 19: sItem = "nw_it_mmidmisc02"; break; + case 20: sItem = "nw_maarcl094"; break; + case 21: sItem = "nw_it_mring019"; break; + case 22: sItem = "nw_it_mring016"; break; + case 23: sItem = "nw_it_mbracer010"; break; + case 24: sItem = "nw_it_mneck015"; break; + case 25: sItem = "nw_maarcl091"; break; + case 26: sItem = "nw_it_mboots009"; break; + case 27: sItem = "nw_it_mbracer006"; break; + case 28: sItem = "nw_it_mneck011"; break; + case 29: sItem = "nw_maarcl095"; break; + case 30: sItem = "nw_it_mneck018"; break; + } + } + //dbSpeak("Create Misc"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + + // * this function just returns an item that is more appropriate + // * for this class. Only wizards, sorcerers, clerics, monks, rogues and bards get this + void CreateGenericClassItem(object oTarget, object oAdventurer, int nSpecific =0) + { + + + if (GetLevelByClass(CLASS_TYPE_DRUID, oAdventurer)>= 1) + { + if (nSpecific == 0) + { + CreateGenericDruidWeapon(oTarget, oAdventurer); + } + else + { + CreateSpecificDruidWeapon(oTarget, oAdventurer); + } + } + else + if (GetLevelByClass(CLASS_TYPE_WIZARD, oAdventurer)>= 1 || GetLevelByClass(CLASS_TYPE_SORCERER, oAdventurer) >= 1) + { + // * 30% chance of getting a magic scroll else get a weapon suited for a wizard + if (Random(100) + 1 > 70) + { + // * grab an arcane scroll as if the wizard had +4 levels + CreateArcaneScroll(oTarget, oAdventurer, 4); + } + else + if (nSpecific == 0) + { + CreateGenericWizardWeapon(oTarget, oAdventurer); + } + else + { + CreateSpecificWizardWeapon(oTarget, oAdventurer); + } + + + } + else + if (GetLevelByClass(CLASS_TYPE_CLERIC, oAdventurer)>= 1) + { + int nRandom = Random(4) + 1; + string sItem = "nw_it_medkit001"; + switch (nRandom) + { + case 1: sItem = "nw_it_medkit001"; break; + case 2: sItem = "nw_it_medkit002"; break; + case 3: sItem = "nw_it_medkit003"; break; + case 4: sItem = "nw_it_medkit004"; break; + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + else + if (GetLevelByClass(CLASS_TYPE_MONK, oAdventurer)>= 1) + { + //dbSpeak("in monk function"); + if (nSpecific == 0) + { + CreateGenericMonkWeapon(oTarget, oAdventurer); + } + else + { + CreateSpecificMonkWeapon(oTarget, oAdventurer); + } + } + else + if (GetLevelByClass(CLASS_TYPE_ROGUE, oAdventurer)>= 1) + { + // * give a misc item as if a couple levels higher + CreateGenericMiscItem(oTarget, oAdventurer, 2); + } + else + if (GetLevelByClass(CLASS_TYPE_BARD, oAdventurer)>= 1) + { + // * give a misc item as if a couple levels higher + CreateGenericMiscItem(oTarget, oAdventurer, 2); + } + + } + void CreateGenericRodStaffWand(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn013"; break; + case 2: sItem = "nw_wmgwn006"; break; + case 3: sItem = "nw_it_gem002"; break; // gem for variety + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn013"; break; + case 2: sItem = "nw_wmgwn006"; break; + case 3: sItem = "nw_it_gem002"; break;// gem for variety + } + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(4) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn006"; break; + case 2: sItem = "nw_wmgwn004"; break; + case 3: sItem = "nw_wmgrd002"; break; + case 4: sItem = "nw_wmgwn012"; break; + } + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn004"; break; + case 2: sItem = "nw_wmgwn002"; break; + case 3: sItem = "nw_wmgwn007"; break; + case 4: sItem = "nw_wmgwn003"; break; + case 5: sItem = "nw_wmgwn010"; break; + case 6: sItem = "nw_wmgwn011"; break; + case 7: sItem = "nw_wmgwn005"; break; + case 8: sItem = "nw_wmgwn008"; break; + case 9: sItem = "nw_wmgwn009"; break; + case 10: sItem = "nw_wmgrd002"; break; + case 11: sItem = "nw_wmgwn012"; break; + } + + } + else // * 2500 - 16500 + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sItem = "nw_wmgwn002"; break; + case 2: sItem = "nw_wmgwn007"; break; + case 3: sItem = "nw_wmgwn003"; break; + case 4: sItem = "nw_wmgwn010"; break; + case 5: sItem = "nw_wmgwn011"; break; + case 6: sItem = "nw_wmgwn005"; break; + case 7: sItem = "nw_wmgwn008"; break; + case 8: sItem = "nw_wmgwn009"; break; + } + + } + //dbSpeak("Generic Rod staff wand"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + + void CreateGenericMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wbwsl001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspka001"; break; + case 7: sItem = "nw_wbwxh001"; break; + case 8: sItem = "nw_waxhn001"; break; + case 9: sItem = "nw_wbwxl001"; break; + case 10: sItem = "nw_wthmsh002"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wbwsl001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspka001"; break; + case 7: sItem = "nw_wbwxh001"; break; + case 8: sItem = "nw_waxhn001"; break; + case 9: sItem = "nw_wbwxl001"; break; + case 10: sItem = "nw_wthmsh002"; break; + case 11: sItem = "nw_wbwmsl001"; break; + case 12: sItem = "nw_wbwmxh002"; break; + case 13: sItem = "nw_wthmsh008"; break; + case 14: sItem = "nw_wbwmxl002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsl001"; break; + case 2: sItem = "nw_wbwmxh002"; break; + case 3: sItem = "nw_wthmsh008"; break; + case 4: sItem = "nw_wbwmxl002"; break; + case 5: sItem = "nw_wthmsh009"; break; + case 6: sItem = "nw_wblmcl002"; break; + case 7: sItem = "nw_wdbmqs002"; break; + case 8: sItem = "nw_wswmdg002"; break; + case 9: sItem = "nw_wspmka002"; break; + case 10: sItem = "nw_waxmhn002"; break; + case 11: sItem = "nw_wbwmsl009"; break; + case 12: sItem = "nw_wbwmxh008"; break; + case 13: sItem = "nw_wbwmxl008"; break; + } + + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh009"; break; + case 2: sItem = "nw_wblmcl002"; break; + case 3: sItem = "nw_wdbmqs002"; break; + case 4: sItem = "nw_wswmdg002"; break; + case 5: sItem = "nw_wspmka002"; break; + case 6: sItem = "nw_waxmhn002"; break; + case 7: sItem = "nw_wbwmsl009"; break; + case 8: sItem = "nw_wbwmxh008"; break; + case 9: sItem = "nw_wbwmxl008"; break; + case 10: sItem = "nw_wbwmsl010"; break; + case 11: sItem = "nw_wbwmxh009"; break; + case 12: sItem = "nw_wbwmxl009"; break; + case 13: sItem = "nw_wblmcl010"; break; + case 14: sItem = "nw_wdbmqs008"; break; + case 15: sItem = "nw_wswmdg008"; break; + case 16: sItem = "nw_wspmka008"; break; + case 17: sItem = "nw_waxmhn010"; break; + } + } + else // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsl010"; break; + case 2: sItem = "nw_wbwmxh009"; break; + case 3: sItem = "nw_wbwmxl009"; break; + case 4: sItem = "nw_wblmcl010"; break; + case 5: sItem = "nw_wdbmqs008"; break; + case 6: sItem = "nw_wswmdg008"; break; + case 7: sItem = "nw_wspmka008"; break; + case 8: sItem = "nw_waxmhn010"; break; + case 9: sItem = "nw_wblmcl011"; break; + case 10: sItem = "nw_wdbmqs009"; break; + case 11: sItem = "nw_wswmdg009"; break; + case 12: sItem = "nw_wspmka009"; break; + case 13: sItem = "nw_waxmhn011"; break; + } + } + //dbSpeak("Generic Monk Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 800 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh003"; break; + case 2: sItem = "nw_wthmsh006"; break; + case 3: CreateGenericMonkWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; break; + } + + } + else if (GetRange(2, nHD)) // * 2500 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh003"; break; + case 2: sItem = "nw_wthmsh006"; break; + case 3: sItem = "nw_wthmsh004"; break; + case 4: sItem = "nw_wthmsh007"; break; + case 5: sItem = "NW_IT_MGLOVE016"; break; + case 6: sItem = "NW_IT_MGLOVE021"; break; + case 7: sItem = "NW_IT_MGLOVE026"; break; + case 8: CreateGenericMonkWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(21) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh006"; break; + case 2: sItem = "nw_wthmsh004"; break; + case 3: sItem = "nw_wthmsh007"; break; + case 4: sItem = "nw_wbwmsl005"; break; + case 5: sItem = "nw_wbwmxh005"; break; + case 6: sItem = "nw_wspmka004"; break; + case 7: sItem = "nw_wbwmxl005"; break; + case 8: sItem = "nw_wspmka007"; break; + case 9: sItem = "nw_wswmdg006"; break; + case 10: sItem = "nw_wspmka005"; break; + case 11: sItem = "NW_IT_MGLOVE016"; break; + case 12: sItem = "NW_IT_MGLOVE021"; break; + case 13: sItem = "NW_IT_MGLOVE026"; break; + + case 14: sItem = "NW_IT_MGLOVE017"; break; + case 15: sItem = "NW_IT_MGLOVE022"; break; + case 16: sItem = "NW_IT_MGLOVE027"; break; + + case 17: sItem = "NW_IT_MGLOVE018"; break; + case 18: sItem = "NW_IT_MGLOVE023"; break; + case 19: sItem = "NW_IT_MGLOVE028"; break; + + case 20: sItem = "NW_IT_MGLOVE029"; break; + case 21: sItem = "NW_IT_MGLOVE030"; break; + + + } + + } + else if (GetRange(4, nHD)) // * 2500 -16500 + { + int nRandom = Random(22) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsl005"; break; + case 2: sItem = "nw_wbwmxh005"; break; + case 3: sItem = "nw_wspmka004"; break; + case 4: sItem = "nw_wbwmxl005"; break; + case 5: sItem = "nw_wspmka007"; break; + case 6: sItem = "nw_wswmdg006"; break; + case 7: sItem = "nw_wspmka005"; break; + case 8: sItem = "nw_wblmcl004"; break; + case 9: sItem = "nw_wblmcl003"; break; + case 10: sItem = "nw_wbwmsl003"; break; + case 11: sItem = "nw_wbwmxh003"; break; + case 12: sItem = "nw_waxmhn004"; break; + case 13: sItem = "nw_wbwmxl003"; break; + + case 14: sItem = "NW_IT_MGLOVE017"; break; + case 15: sItem = "NW_IT_MGLOVE022"; break; + + case 16: sItem = "NW_IT_MGLOVE018"; break; + case 17: sItem = "NW_IT_MGLOVE023"; break; + case 18: sItem = "NW_IT_MGLOVE028"; break; + + case 19: sItem = "NW_IT_MGLOVE029"; break; + case 20: sItem = "NW_IT_MGLOVE030"; break; + + case 21: sItem = "NW_IT_MGLOVE019"; break; + case 22: sItem = "NW_IT_MGLOVE024"; break; + + + } + + } + else // * 16000 + + { + int nRandom = Random(24) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl003"; break; + case 2: sItem = "nw_wspmka006"; break; + case 3: sItem = "nw_wbwmxl004"; break; + case 4: sItem = "nw_wspmka003"; break; + case 5: sItem = "nw_wbwmxl007"; break; + case 6: sItem = "nw_waxmhn003"; break; + case 7: sItem = "nw_wblmcl005"; break; + case 8: sItem = "nw_wswmdg004"; break; + case 9: sItem = "nw_wbwmsl007"; break; + case 10: sItem = "nw_wbwmxh004"; break; + case 11: sItem = "nw_waxmhn005"; break; + case 12: sItem = "nw_wbwmxh007"; break; + case 13: sItem = "nw_wswmdg003"; break; + case 14: sItem = "nw_wswmdg007"; break; + case 15: sItem = "nw_wbwmsl006"; break; + case 16: sItem = "nw_wbwmsl008"; break; + case 17: sItem = "nw_wblmcl006"; break; + case 18: sItem = "nw_wbwmsl004"; break; + case 19: sItem = "nw_waxmhn006"; break; + case 20: sItem = "nw_wbwmxh006"; break; + case 21: sItem = "nw_wswmdg005"; break; + case 22: sItem = "nw_wbwmxl006"; break; + + case 23: sItem = "NW_IT_MGLOVE020"; break; + case 24: sItem = "NW_IT_MGLOVE025"; break; + + } + + } + //dbSpeak("Specific Monk Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + + void CreateGenericDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspsc001"; break; + case 7: sItem = "nw_wswsc001"; break; + case 8: sItem = "nw_wthmdt002"; break; + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspsc001"; break; + case 7: sItem = "nw_wswsc001"; break; + case 8: sItem = "nw_wthmdt002"; break; + case 9: sItem = "nw_wthmdt005"; break; + case 10: sItem = "nw_wbwmsl001"; break; + case 11: sItem = "nw_wthmdt008"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt005"; break; + case 2: sItem = "nw_wbwmsl001"; break; + case 3: sItem = "nw_wthmdt008"; break; + case 4: sItem = "nw_wthmdt009"; break; + case 5: sItem = "nw_wthmdt006"; break; + case 6: sItem = "nw_wblmcl002"; break; + case 7: sItem = "nw_wdbmqs002"; break; + case 8: sItem = "nw_wplmss002"; break; + case 9: sItem = "nw_wswmdg002"; break; + case 10: sItem = "nw_wspmsc002"; break; + case 11: sItem = "nw_wswmsc002"; break; + case 12: sItem = "nw_wthmdt003"; break; + case 13: sItem = "nw_wbwmsl009"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt009"; break; + case 2: sItem = "nw_wthmdt006"; break; + case 3: sItem = "nw_wblmcl002"; break; + case 4: sItem = "nw_wdbmqs002"; break; + case 5: sItem = "nw_wplmss002"; break; + case 6: sItem = "nw_wswmdg002"; break; + case 7: sItem = "nw_wspmsc002"; break; + case 8: sItem = "nw_wswmsc002"; break; + case 9: sItem = "nw_wthmdt003"; break; + case 10: sItem = "nw_wbwmsl009"; break; + case 11: sItem = "nw_wthmdt007"; break; + case 12: sItem = "nw_wthmdt004"; break; + case 13: sItem = "nw_wbwmsl010"; break; + case 14: sItem = "nw_wblmcl010"; break; + case 15: sItem = "nw_wdbmqs008"; break; + case 16: sItem = "nw_wplmss010"; break; + case 17: sItem = "nw_wswmdg008"; break; + case 18: sItem = "nw_wspmsc010"; break; + case 19: sItem = "nw_wswmsc010"; break; + } + + } + else // * 2500 - 16500 + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt007"; break; + case 2: sItem = "nw_wthmdt004"; break; + case 3: sItem = "nw_wbwmsl010"; break; + case 4: sItem = "nw_wblmcl010"; break; + case 5: sItem = "nw_wdbmqs008"; break; + case 6: sItem = "nw_wplmss010"; break; + case 7: sItem = "nw_wswmdg008"; break; + case 8: sItem = "nw_wspmsc010"; break; + case 9: sItem = "nw_wswmsc010"; break; + case 10: sItem = "nw_wblmcl011"; break; + case 11: sItem = "nw_wdbmqs009"; break; + case 12: sItem = "nw_wplmss011"; break; + case 13: sItem = "nw_wswmdg009"; break; + case 14: sItem = "nw_wspmsc011"; break; + case 15: sItem = "nw_wswmsc011"; break; + } + + } + //dbSpeak("Generic Druid weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + + } + void CreateSpecificDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericDruidWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + + } + else if (GetRange(2, nHD)) // * 2500 + { + CreateGenericDruidWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmsl005"; break; + case 4: sItem = "nw_wswmdg006"; break; + case 5: CreateGenericDruidWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 -16500 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmsl005"; break; + case 4: sItem = "nw_wswmdg006"; break; + case 5: sItem = "nw_wblmcl004"; break; + case 6: sItem = "nw_wdbmqs004"; break; + case 7: sItem = "nw_wblmcl003"; break; + case 8: sItem = "nw_wbwmsl003"; break; + case 9: sItem = "nw_wswmsc004"; break; + case 10: sItem = "nw_wplmss005"; break; + } + + } + else // * 16000 + + { + int nRandom = Random(18) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs003"; break; + case 2: sItem = "nw_wblmcl005"; break; + case 3: sItem = "nw_wplmss007"; break; + case 4: sItem = "nw_wswmdg004"; break; + case 5: sItem = "nw_wbwmsl007"; break; + case 6: sItem = "nw_wplmss006"; break; + case 7: sItem = "nw_wswmsc006"; break; + case 8: sItem = "nw_wswmdg003"; break; + case 9: sItem = "nw_wswmdg007"; break; + case 10: sItem = "nw_wswmsc007"; break; + case 11: sItem = "nw_wbwmsl006"; break; + case 12: sItem = "nw_wbwmsl008"; break; + case 13: sItem = "nw_wdbmqs007"; break; + case 14: sItem = "nw_wblmcl006"; break; + case 15: sItem = "nw_wbwmsl004"; break; + case 16: sItem = "nw_wswmsc005"; break; + case 17: sItem = "nw_wplmss004"; break; + case 18: sItem = "nw_wswmdg005"; break; + } + + } + //dbSpeak("specific druid weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + + void CreateGenericWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblcl001"; break; + case 2: sItem = "nw_wdbqs001"; break; + case 3: sItem = "nw_wswdg001"; break; + case 4: sItem = "nw_wbwxh001"; break; + case 5: sItem = "nw_wbwxl001"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblcl001"; break; + case 2: sItem = "nw_wdbqs001"; break; + case 3: sItem = "nw_wswdg001"; break; + case 4: sItem = "nw_wbwxh001"; break; + case 5: sItem = "nw_wbwxl001"; break; + case 6: sItem = "nw_wbwmxl002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl002"; break; + case 2: sItem = "nw_wblmcl002"; break; + case 3: sItem = "nw_wdbmqs002"; break; + case 4: sItem = "nw_wswmdg002"; break; + case 5: sItem = "nw_wbwmxh008"; break; + case 6: sItem = "nw_wbwmxl008"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmcl002"; break; + case 2: sItem = "nw_wdbmqs002"; break; + case 3: sItem = "nw_wswmdg002"; break; + case 4: sItem = "nw_wbwmxh008"; break; + case 5: sItem = "nw_wbwmxl008"; break; + case 6: sItem = "nw_wbwmxh009"; break; + case 7: sItem = "nw_wbwmxl009"; break; + case 8: sItem = "nw_wblmcl010"; break; + case 9: sItem = "nw_wdbmqs008"; break; + case 10: sItem = "nw_wswmdg008"; break; + } + + } + else // * 2500 - 16500 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxh009"; break; + case 2: sItem = "nw_wbwmxl009"; break; + case 3: sItem = "nw_wblmcl010"; break; + case 4: sItem = "nw_wdbmqs008"; break; + case 5: sItem = "nw_wswmdg008"; break; + case 6: sItem = "nw_wblmcl011"; break; + case 7: sItem = "nw_wdbmqs009"; break; + case 8: sItem = "nw_wswmdg009"; break; + } + + } + //dbSpeak("Generic Wizard or Sorcerer Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + void CreateSpecificWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericWizardWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + } + else if (GetRange(2, nHD)) // * 2500 + { + CreateGenericWizardWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmxh005"; break; + case 4: sItem = "nw_wbwmxl005"; break; + case 5: sItem = "nw_wswmdg006"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 -16500 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmxh005"; break; + case 4: sItem = "nw_wbwmxl005"; break; + case 5: sItem = "nw_wswmdg006"; break; + case 6: sItem = "nw_wblmcl004"; break; + case 7: sItem = "nw_wdbmqs004"; break; + case 8: sItem = "nw_wblmcl003"; break; + case 9: sItem = "nw_wbwmxh003"; break; + case 10: sItem = "nw_wbwmxl003"; break; + } + + } + else // * 16000 + + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl003"; break; + case 2: sItem = "nw_wdbmqs003"; break; + case 3: sItem = "nw_wbwmxl004"; break; + case 4: sItem = "nw_wbwmxl007"; break; + case 5: sItem = "nw_wblmcl005"; break; + case 6: sItem = "nw_wswmdg004"; break; + case 7: sItem = "nw_wbwmxh004"; break; + case 8: sItem = "nw_wbwmxh007"; break; + case 9: sItem = "nw_wswmdg003"; break; + case 10: sItem = "nw_wswmdg007"; break; + case 11: sItem = "nw_wdbmqs007"; break; + case 12: sItem = "nw_wblmcl006"; break; + case 13: sItem = "nw_wbwmxh006"; break; + case 14: sItem = "nw_wswmdg005"; break; + case 15: sItem = "nw_wbwmxl006"; break; + } + + } + //dbSpeak("Specific Wizard or Sorcerer Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + + void CreateGenericSimple(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = d12(); + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wbwsl001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wdbqs001"; break; + case 6: sItem = "nw_wswdg001"; break; + case 7: sItem = "nw_wblml001"; break; + case 8: sItem = "nw_wbwxh001"; break; + case 9: sItem = "nw_wspsc001"; break; + case 10: sItem = "nw_wblms001"; break; + case 11: sItem = "nw_wbwxl001"; break; + case 12: sItem = "nw_wthmdt002"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wbwsl001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wdbqs001"; break; + case 6: sItem = "nw_wswdg001"; break; + case 7: sItem = "nw_wblml001"; break; + case 8: sItem = "nw_wbwxh001"; break; + case 9: sItem = "nw_wspsc001"; break; + case 10: sItem = "nw_wblms001"; break; + case 11: sItem = "nw_wbwxl001"; break; + case 12: sItem = "nw_wthmdt002"; break; + case 13: sItem = "nw_wthmdt005"; break; + case 14: sItem = "nw_wbwmsl001"; break; + case 15: sItem = "nw_wbwmxh002"; break; + case 16: sItem = "nw_wthmdt008"; break; + case 17: sItem = "nw_wbwmxl002"; break; + } + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt005"; break; + case 2: sItem = "nw_wbwmsl001"; break; + case 3: sItem = "nw_wbwmxh002"; break; + case 4: sItem = "nw_wthmdt008"; break; + case 5: sItem = "nw_wbwmxl002"; break; + case 6: sItem = "nw_wthmdt009"; break; + case 7: sItem = "nw_wthmdt006"; break; + case 8: sItem = "nw_wblmcl002"; break; + case 9: sItem = "nw_wplmss002"; break; + case 10: sItem = "nw_wdbmqs002"; break; + case 11: sItem = "nw_wswmdg002"; break; + case 12: sItem = "nw_wblmml002"; break; + case 13: sItem = "nw_wspmsc002"; break; + case 14: sItem = "nw_wblmms002"; break; + case 15: sItem = "nw_wthmdt003"; break; + case 16: sItem = "nw_wthmdt003"; break; + case 17: sItem = "nw_wbwmsl009"; break; + case 18: sItem = "nw_wbwmxh008"; break; + case 19: sItem = "nw_wbwmxl008"; break; + } + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(27) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt009"; break; + case 2: sItem = "nw_wthmdt006"; break; + case 3: sItem = "nw_wblmcl002"; break; + case 4: sItem = "nw_wplmss002"; break; + case 5: sItem = "nw_wdbmqs002"; break; + case 6: sItem = "nw_wswmdg002"; break; + case 7: sItem = "nw_wblmml002"; break; + case 8: sItem = "nw_wspmsc002"; break; + case 9: sItem = "nw_wblmms002"; break; + case 10: sItem = "nw_wthmdt003"; break; + case 11: sItem = "nw_wthmdt003"; break; + case 12: sItem = "nw_wbwmsl009"; break; + case 13: sItem = "nw_wbwmxh008"; break; + case 14: sItem = "nw_wbwmxl008"; break; + case 15: sItem = "nw_wthmdt007"; break; + case 16: sItem = "nw_wthmdt004"; break; + case 17: sItem = "nw_wbwmsl010"; break; + case 18: sItem = "nw_wbwmxh009"; break; + case 19: sItem = "nw_wbwmxl009"; break; + case 20: sItem = "nw_wbwmsl005"; break; + case 21: sItem = "nw_wblmcl010"; break; + case 22: sItem = "nw_wplmss010"; break; + case 23: sItem = "nw_wdbmqs008"; break; + case 24: sItem = "nw_wswmdg008"; break; + case 25: sItem = "nw_wblmml011"; break; + case 26: sItem = "nw_wspmsc010"; break; + case 27: sItem = "nw_wblmms010"; break; + + + + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(23) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt007"; break; + case 2: sItem = "nw_wthmdt004"; break; + case 3: sItem = "nw_wbwmsl010"; break; + case 4: sItem = "nw_wbwmxh009"; break; + case 5: sItem = "nw_wbwmxl009"; break; + case 6: sItem = "nw_wbwmsl005"; break; + case 7: sItem = "nw_wblmcl010"; break; + case 8: sItem = "nw_wplmss010"; break; + case 9: sItem = "nw_wdbmqs008"; break; + case 10: sItem = "nw_wswmdg008"; break; + case 11: sItem = "nw_wblmml011"; break; + case 12: sItem = "nw_wspmsc010"; break; + case 13: sItem = "nw_wblmms010"; break; + case 14: sItem = "nw_wblmms010"; break; + case 15: sItem = "nw_wblmms010"; break; + case 16: sItem = "nw_wblmms010"; break; + case 17: sItem = "nw_wblmcl011"; break; + case 18: sItem = "nw_wplmss011"; break; + case 19: sItem = "nw_wdbmqs009"; break; + case 20: sItem = "nw_wswmdg009"; break; + case 21: sItem = "nw_wblmml012"; break; + case 22: sItem = "nw_wspmsc011"; break; + case 23: sItem = "nw_wblmms011"; break; + + + + } + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(7) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmcl011"; break; + case 2: sItem = "nw_wplmss011"; break; + case 3: sItem = "nw_wdbmqs009"; break; + case 4: sItem = "nw_wswmdg009"; break; + case 5: sItem = "nw_wblmml012"; break; + case 6: sItem = "nw_wspmsc011"; break; + case 7: sItem = "nw_wblmms011"; break; + + + + } + } + //dbSpeak("Create Generic SImple; Specific = " + IntToString(nModifier)); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericMartial(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + + int nHD = GetHitDice(oAdventurer) +nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthax001"; break; + case 2: sItem = "nw_wblhl001"; break; + case 3: sItem = "nw_waxhn001"; break; + case 4: sItem = "nw_wblfl001"; break; + case 5: sItem = "nw_waxbt001"; break; + case 6: sItem = "nw_wplhb001"; break; + case 7: sItem = "nw_wswss001"; break; + case 8: sItem = "nw_wblhw001"; break; + case 9: sItem = "nw_wblfh001"; break; + case 10: sItem = "nw_wswls001"; break; + case 11: sItem = "nw_wswsc001"; break; + case 12: sItem = "nw_waxgr001"; break; + case 13: sItem = "nw_wswrp001"; break; + case 14: sItem = "nw_wbwsh001"; break; + case 15: sItem = "nw_wswbs001"; break; + case 16: sItem = "nw_wswgs001"; break; + case 17: sItem = "nw_wbwln001"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(20) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthax001"; break; + case 2: sItem = "nw_wblhl001"; break; + case 3: sItem = "nw_waxhn001"; break; + case 4: sItem = "nw_wblfl001"; break; + case 5: sItem = "nw_waxbt001"; break; + case 6: sItem = "nw_wplhb001"; break; + case 7: sItem = "nw_wswss001"; break; + case 8: sItem = "nw_wblhw001"; break; + case 9: sItem = "nw_wblfh001"; break; + case 10: sItem = "nw_wswls001"; break; + case 11: sItem = "nw_wswsc001"; break; + case 12: sItem = "nw_waxgr001"; break; + case 13: sItem = "nw_wswrp001"; break; + case 14: sItem = "nw_wbwsh001"; break; + case 15: sItem = "nw_wswbs001"; break; + case 16: sItem = "nw_wswgs001"; break; + case 17: sItem = "nw_wbwln001"; break; + case 18: sItem = "nw_wthmax002"; break; + case 19: sItem = "nw_wbwmsh002"; break; + case 20: sItem = "nw_wbwmln002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(20) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmax002"; break; + case 2: sItem = "nw_wbwmsh002"; break; + case 3: sItem = "nw_wbwmln002"; break; + case 4: sItem = "nw_wblmhl002"; break; + case 5: sItem = "nw_waxmhn002"; break; + case 6: sItem = "nw_wblmfl002"; break; + case 7: sItem = "nw_waxmbt002"; break; + case 8: sItem = "nw_wplmhb002"; break; + case 9: sItem = "nw_wblmhw002"; break; + case 10: sItem = "nw_wblmfh002"; break; + case 11: sItem = "nw_wswmls002"; break; + case 12: sItem = "nw_wswmsc002"; break; + case 13: sItem = "nw_waxmgr002"; break; + case 14: sItem = "nw_wswmrp002"; break; + case 15: sItem = "nw_wswmbs002"; break; + case 16: sItem = "nw_wswmgs002"; break; + case 17: sItem = "nw_wthmax008"; break; + case 18: sItem = "nw_wbwmsh008"; break; + case 19: sItem = "nw_wbwmln008"; break; + case 20: sItem = "nw_wswmss002"; break; + + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(33) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmhl002"; break; + case 2: sItem = "nw_waxmhn002"; break; + case 3: sItem = "nw_wblmfl002"; break; + case 4: sItem = "nw_waxmbt002"; break; + case 5: sItem = "nw_wplmhb002"; break; + case 6: sItem = "nw_wblmhw002"; break; + case 7: sItem = "nw_wblmfh002"; break; + case 8: sItem = "nw_wswmls002"; break; + case 9: sItem = "nw_wswmsc002"; break; + case 10: sItem = "nw_waxmgr002"; break; + case 11: sItem = "nw_wswmrp002"; break; + case 12: sItem = "nw_wswmbs002"; break; + case 13: sItem = "nw_wswmgs002"; break; + case 14: sItem = "nw_wthmax008"; break; + case 15: sItem = "nw_wbwmsh008"; break; + case 16: sItem = "nw_wbwmln008"; break; + case 17: sItem = "nw_wbwmsh009"; break; + case 18: sItem = "nw_wbwmln009"; break; + case 19: sItem = "nw_wblmhl010"; break; + case 20: sItem = "nw_waxmhn010"; break; + case 21: sItem = "nw_wblmfl010"; break; + case 22: sItem = "nw_waxmbt010"; break; + case 23: sItem = "nw_wplmhb010"; break; + case 24: sItem = "nw_wblmhw011"; break; + case 25: sItem = "nw_wblmfh010"; break; + case 26: sItem = "nw_wswmls010"; break; + case 27: sItem = "nw_waxmgr009"; break; + case 28: sItem = "nw_wswmbs009"; break; + case 29: sItem = "nw_wswmgs011"; break; + case 30: sItem = "nw_wswmrp010"; break; + case 31: sItem = "nw_wswmsc010"; break; + case 32: sItem = "nw_wswmss002"; break; + case 33: sItem = "nw_wswmss009"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(20) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsh009"; break; + case 2: sItem = "nw_wbwmln009"; break; + case 3: sItem = "nw_wblmhl010"; break; + case 4: sItem = "nw_waxmhn010"; break; + case 5: sItem = "nw_wblmfl010"; break; + case 6: sItem = "nw_waxmbt010"; break; + case 7: sItem = "nw_wplmhb010"; break; + case 8: sItem = "nw_wblmhw011"; break; + case 9: sItem = "nw_wblmfh010"; break; + case 10: sItem = "nw_wswmls010"; break; + case 11: sItem = "nw_waxmgr009"; break; + case 12: sItem = "nw_wswmbs009"; break; + case 13: sItem = "nw_wswmgs011"; break; + case 14: sItem = "nw_wthmax009"; break; + case 15: sItem = "nw_wswmrp010"; break; + case 16: sItem = "nw_wswmrp011"; break; + case 17: sItem = "nw_wswmsc010"; break; + case 18: sItem = "nw_wswmss009"; break; + case 19: sItem = "nw_wswmsc011"; break; + case 20: sItem = "nw_wswmss011"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmax009"; break; + case 2: sItem = "nw_waxmhn011"; break; + case 3: sItem = "nw_wblmfl011"; break; + case 4: sItem = "nw_waxmbt011"; break; + case 5: sItem = "nw_wplmhb011"; break; + case 6: sItem = "nw_wblmhw012"; break; + case 7: sItem = "nw_wblmfh011"; break; + case 8: sItem = "nw_wswmls012"; break; + case 9: sItem = "nw_waxmgr011"; break; + case 10: sItem = "nw_wswmbs010"; break; + case 11: sItem = "nw_wswmgs012"; break; + case 12: sItem = "nw_wswmrp011"; break; + case 13: sItem = "nw_wswmsc011"; break; + case 14: sItem = "nw_wswmss011"; break; + } + + } + + //dbSpeak("Create Generic Martial"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericExotic(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wspka001"; break; + case 3: sItem = "nw_wspku001"; break; + case 4: sItem = "nw_wplsc001"; break; + case 5: sItem = "nw_wdbax001"; break; + case 6: sItem = "nw_wdbma001"; break; + case 7: sItem = "nw_wswka001"; break; + case 8: sItem = "nw_wthmsh002"; break; + case 9: sItem = "nw_wdbsw001"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wspka001"; break; + case 3: sItem = "nw_wspku001"; break; + case 4: sItem = "nw_wplsc001"; break; + case 5: sItem = "nw_wdbax001"; break; + case 6: sItem = "nw_wdbma001"; break; + case 7: sItem = "nw_wswka001"; break; + case 8: sItem = "nw_wthmsh002"; break; + case 9: sItem = "nw_wdbsw001"; break; + case 10: sItem = "nw_wthmsh005"; break; + case 11: sItem = "nw_wspmka002"; break; + case 12: sItem = "nw_wspmku002"; break; + case 13: sItem = "nw_wplmsc002"; break; + case 14: sItem = "nw_wdbmax002"; break; + case 15: sItem = "nw_wdbmma002"; break; + case 16: sItem = "nw_wswmka002"; break; + case 17: sItem = "nw_wdbmsw002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbsw001"; break; + case 2: sItem = "nw_wthmsh005"; break; + case 3: sItem = "nw_wspmka002"; break; + case 4: sItem = "nw_wspmku002"; break; + case 5: sItem = "nw_wplmsc002"; break; + case 6: sItem = "nw_wdbmax002"; break; + case 7: sItem = "nw_wdbmma002"; break; + case 8: sItem = "nw_wswmka002"; break; + case 9: sItem = "nw_wdbmsw002"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh005"; break; + case 2: sItem = "nw_wspmka002"; break; + case 3: sItem = "nw_wspmku002"; break; + case 4: sItem = "nw_wplmsc002"; break; + case 5: sItem = "nw_wdbmax002"; break; + case 6: sItem = "nw_wdbmma002"; break; + case 7: sItem = "nw_wswmka002"; break; + case 8: sItem = "nw_wdbmsw002"; break; + case 9: sItem = "nw_wthmsh008"; break; + case 10: sItem = "nw_wspmka008"; break; + case 11: sItem = "nw_wspmku008"; break; + case 12: sItem = "nw_wplmsc010"; break; + case 13: sItem = "nw_wdbmax010"; break; + case 14: sItem = "nw_wdbmma010"; break; + case 15: sItem = "nw_wswmka010"; break; + case 16: sItem = "nw_wdbmsw010"; break; + case 17: sItem = "nw_wthmsh009"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wspmka008"; break; + case 2: sItem = "nw_wspmku008"; break; + case 3: sItem = "nw_wplmsc010"; break; + case 4: sItem = "nw_wdbmax010"; break; + case 5: sItem = "nw_wdbmma010"; break; + case 6: sItem = "nw_wswmka010"; break; + case 7: sItem = "nw_wdbmsw010"; break; + case 8: sItem = "nw_wthmsh009"; break; + case 9: sItem = "nw_wspmka009"; break; + case 10: sItem = "nw_wspmku009"; break; + case 11: sItem = "nw_wplmsc011"; break; + case 12: sItem = "nw_wdbmax011"; break; + case 13: sItem = "nw_wdbmma011"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmsw010"; break; + case 2: sItem = "nw_wthmsh009"; break; + case 3: sItem = "nw_wspmka009"; break; + case 4: sItem = "nw_wspmku009"; break; + case 5: sItem = "nw_wplmsc011"; break; + case 6: sItem = "nw_wdbmax011"; break; + case 7: sItem = "nw_wdbmma011"; break; + case 8: sItem = "nw_wswmka011"; break; + case 9: sItem = "nw_wdbmsw011"; break; + } + + } + //dbSpeak("Create generic exotic"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericLightArmor(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_aarcl009"; break; + case 2: sItem = "nw_ashsw001"; break; + case 3: sItem = "nw_aarcl001"; break; + case 4: sItem = "nw_aarcl002"; break; + case 5: sItem = "nw_aarcl012"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_aarcl009"; break; + case 2: sItem = "nw_ashsw001"; break; + case 3: sItem = "nw_aarcl001"; break; + case 4: sItem = "nw_aarcl002"; break; + case 5: sItem = "nw_aarcl012"; break; + case 6: sItem = "nw_maarcl043"; break; + case 7: sItem = "nw_ashmsw002"; break; + case 8: sItem = "nw_maarcl044"; break; + case 9: sItem = "nw_maarcl045"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl043"; break; + case 2: sItem = "nw_ashmsw002"; break; + case 3: sItem = "nw_maarcl044"; break; + case 4: sItem = "nw_maarcl045"; break; + case 5: sItem = "nw_maarcl072"; break; + case 6: sItem = "nw_ashmsw008"; break; + case 7: sItem = "nw_maarcl071"; break; + case 8: sItem = "nw_maarcl075"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl072"; break; + case 2: sItem = "nw_ashmsw008"; break; + case 3: sItem = "nw_maarcl071"; break; + case 4: sItem = "nw_maarcl075"; break; + case 5: sItem = "nw_maarcl084"; break; + case 6: sItem = "nw_ashmsw009"; break; + case 7: sItem = "nw_maarcl083"; break; + case 8: sItem = "nw_maarcl087"; break; + case 9: sItem = "nw_maarcl079"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl084"; break; + case 2: sItem = "nw_ashmsw009"; break; + case 3: sItem = "nw_maarcl083"; break; + case 4: sItem = "nw_maarcl087"; break; + case 5: sItem = "nw_maarcl079"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl084"; break; + case 2: sItem = "nw_ashmsw009"; break; + case 3: sItem = "nw_maarcl083"; break; + case 4: sItem = "nw_maarcl087"; break; + case 5: sItem = "nw_maarcl079"; break; + } + + } + //dbSpeak("Create Generic light"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericMediumArmor(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sItem = ""; + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_arhe001"; break; + case 2: sItem = "nw_arhe002"; break; + case 3: sItem = "nw_arhe003"; break; + case 4: sItem = "nw_arhe004"; break; + case 5: sItem = "nw_arhe005"; break; + case 6: sItem = "nw_aarcl008"; break; + case 7: sItem = "nw_ashlw001"; break; + case 8: sItem = "nw_aarcl003"; break; + case 9: sItem = "nw_aarcl004"; break; + case 10: sItem = "nw_aarcl010"; break; + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_arhe001"; break; + case 2: sItem = "nw_arhe002"; break; + case 3: sItem = "nw_arhe003"; break; + case 4: sItem = "nw_arhe004"; break; + case 5: sItem = "nw_arhe005"; break; + case 6: sItem = "nw_aarcl008"; break; + case 7: sItem = "nw_ashlw001"; break; + case 8: sItem = "nw_aarcl003"; break; + case 9: sItem = "nw_aarcl004"; break; + case 10: sItem = "nw_aarcl010"; break; + case 11: sItem = "nw_maarcl047"; break; + case 12: sItem = "nw_ashmlw002"; break; + case 13: sItem = "nw_maarcl046"; break; + case 14: sItem = "nw_maarcl048"; break; + case 15: sItem = "nw_maarcl035"; break; + case 16: sItem = "nw_maarcl049"; break; + case 17: sItem = "nw_maarcl050"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl047"; break; + case 2: sItem = "nw_ashmlw002"; break; + case 3: sItem = "nw_maarcl046"; break; + case 4: sItem = "nw_maarcl048"; break; + case 5: sItem = "nw_maarcl035"; break; + case 6: sItem = "nw_maarcl049"; break; + case 7: sItem = "nw_maarcl050"; break; + case 8: sItem = "nw_maarcl070"; break; + case 9: sItem = "nw_ashmlw008"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl035"; break; + case 2: sItem = "nw_maarcl049"; break; + case 3: sItem = "nw_maarcl050"; break; + case 4: sItem = "nw_maarcl070"; break; + case 5: sItem = "nw_ashmlw008"; break; + case 6: sItem = "nw_maarcl067"; break; + case 7: sItem = "nw_maarcl073"; break; + case 8: sItem = "nw_maarcl065"; break; + case 9: sItem = "nw_maarcl066"; break; + case 10: sItem = "nw_maarcl082"; break; + case 11: sItem = "nw_ashmlw009"; break; + case 12: sItem = "nw_maarcl085"; break; + case 13: sItem = "nw_maarcl077"; break; + case 14: sItem = "nw_maarcl078"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl070"; break; + case 2: sItem = "nw_ashmlw008"; break; + case 3: sItem = "nw_maarcl067"; break; + case 4: sItem = "nw_maarcl073"; break; + case 5: sItem = "nw_maarcl065"; break; + case 6: sItem = "nw_maarcl066"; break; + case 7: sItem = "nw_maarcl082"; break; + case 8: sItem = "nw_ashmlw009"; break; + case 9: sItem = "nw_maarcl085"; break; + case 10: sItem = "nw_maarcl077"; break; + case 11: sItem = "nw_maarcl078"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl070"; break; + case 2: sItem = "nw_ashmlw008"; break; + case 3: sItem = "nw_maarcl067"; break; + case 4: sItem = "nw_maarcl073"; break; + case 5: sItem = "nw_maarcl065"; break; + case 6: sItem = "nw_maarcl066"; break; + case 7: sItem = "nw_maarcl082"; break; + case 8: sItem = "nw_ashmlw009"; break; + case 9: sItem = "nw_maarcl085"; break; + case 10: sItem = "nw_maarcl077"; break; + case 11: sItem = "nw_maarcl078"; break; + } + + } + //dbSpeak("Create Generic medium"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericHeavyArmor(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashto001"; break; + case 2: sItem = "nw_aarcl005"; break; + case 3: sItem = "nw_aarcl011"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashto001"; break; + case 2: sItem = "nw_aarcl005"; break; + case 3: sItem = "nw_aarcl011"; break; + case 4: sItem = "nw_aarcl006"; break; + case 5: sItem = "nw_ashmto002"; break; + case 6: sItem = "nw_maarcl051"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_aarcl005"; break; + case 2: sItem = "nw_aarcl011"; break; + case 3: sItem = "nw_aarcl006"; break; + case 4: sItem = "nw_ashmto002"; break; + case 5: sItem = "nw_maarcl051"; break; + case 6: sItem = "nw_maarcl052"; break; + case 7: sItem = "nw_aarcl007"; break; + case 8: sItem = "nw_maarcl053"; break; + case 9: sItem = "nw_ashmto008"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl051"; break; + case 2: sItem = "nw_maarcl052"; break; + case 3: sItem = "nw_aarcl007"; break; + case 4: sItem = "nw_maarcl053"; break; + case 5: sItem = "nw_ashmto008"; break; + case 6: sItem = "nw_maarcl064"; break; + case 7: sItem = "nw_maarcl074"; break; + case 8: sItem = "nw_maarcl069"; break; + case 9: sItem = "nw_maarcl068"; break; + case 10: sItem = "nw_ashmto003"; break; + case 11: sItem = "nw_ashmto009"; break; + case 12: sItem = "nw_maarcl076"; break; + case 13: sItem = "nw_maarcl086"; break; + case 14: sItem = "nw_maarcl081"; break; + case 15: sItem = "nw_maarcl080"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashmto008"; break; + case 2: sItem = "nw_maarcl064"; break; + case 3: sItem = "nw_maarcl074"; break; + case 4: sItem = "nw_maarcl069"; break; + case 5: sItem = "nw_maarcl068"; break; + case 6: sItem = "nw_ashmto009"; break; + case 7: sItem = "nw_maarcl076"; break; + case 8: sItem = "nw_maarcl086"; break; + case 9: sItem = "nw_maarcl081"; break; + case 10: sItem = "nw_maarcl080"; break; + } + + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashmto009"; break; + case 2: sItem = "nw_maarcl076"; break; + case 3: sItem = "nw_maarcl086"; break; + case 4: sItem = "nw_maarcl081"; break; + case 5: sItem = "nw_maarcl080"; break; + } + + } + // dbSpeak("Create Generic heavy"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + // * + // * SPECIC TREASURE ITEMS (re: Named Items) + // * + void CreateSpecificMiscItem(object oTarget,object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericMiscItem(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericMiscItem(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_maarcl057"; break; + case 3: sItem = "nw_it_mbelt005"; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl057"; break; + case 2: sItem = "nw_it_mbelt005"; break; + case 3: sItem = "nw_maarcl101"; break; + case 4: sItem = "nw_maarcl102"; break; + case 5: sItem = "nw_maarcl103"; break; + case 6: sItem = "nw_it_mglove001"; break; + case 7: sItem = "nw_maarcl100"; break; + case 8: sItem = "nw_it_mbracer011"; break; + case 9: sItem = "nw_it_mmidmisc04"; break; + case 10: sItem = "nw_it_mring003"; break; + case 11: sItem = "nw_it_mbelt006"; break; + case 12: sItem = "nw_it_mbelt002"; break; + case 13: sItem = "nw_it_mmidmisc03"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl101"; break; + case 2: sItem = "nw_maarcl101"; break; + case 3: sItem = "nw_maarcl102"; break; + case 4: sItem = "nw_maarcl103"; break; + case 5: sItem = "nw_it_mglove001"; break; + case 6: sItem = "nw_maarcl100"; break; + case 7: sItem = "nw_it_mbracer011"; break; + case 8: sItem = "nw_it_mmidmisc04"; break; + case 9: sItem = "nw_it_mring003"; break; + case 10: sItem = "nw_it_mbelt006"; break; + case 11: sItem = "nw_it_mbelt002"; break; + case 12: sItem = "nw_it_mmidmisc03"; break; + case 13: sItem = "nw_it_mring002"; break; + case 14: sItem = "nw_it_mbelt004"; break; + case 15: sItem = "nw_it_mring005"; break; + case 16: sItem = "nw_it_mboots005"; break; + case 17: sItem = "nw_it_mring007"; break; + case 18: sItem = "nw_it_mneck003"; break; + case 19: sItem = "nw_it_mbelt007"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mbelt002"; break; + case 2: sItem = "nw_it_mbelt002"; break; + case 3: sItem = "nw_it_mmidmisc03"; break; + case 4: sItem = "nw_it_mring002"; break; + case 5: sItem = "nw_it_mbelt004"; break; + case 6: sItem = "nw_it_mring005"; break; + case 7: sItem = "nw_it_mboots005"; break; + case 8: sItem = "nw_it_mring007"; break; + case 9: sItem = "nw_it_mneck003"; break; + case 10: sItem = "nw_it_mbelt007"; break; + case 11: sItem = "nw_it_mboots004"; break; + case 12: sItem = "nw_it_mboots003"; break; + case 13: sItem = "nw_it_mneck005"; break; + case 14: sItem = "nw_it_mbelt008"; break; + case 15: sItem = "nw_it_mring020"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mboots004"; break; + case 2: sItem = "nw_it_mboots004"; break; + case 3: sItem = "nw_it_mboots003"; break; + case 4: sItem = "nw_it_mneck005"; break; + case 5: sItem = "nw_it_mbelt008"; break; + case 6: sItem = "nw_it_mring020"; break; + case 7: sItem = "nw_it_mbelt001"; break; + case 8: sItem = "nw_it_mring017"; break; + case 9: sItem = "nw_mcloth001"; break; + case 10: sItem = "nw_it_mneck019"; break; + case 11: sItem = "nw_it_mneck002"; break; + case 12: sItem = "nw_it_mneck004"; break; + case 13: sItem = "nw_it_mmidmisc01"; break; + case 14: sItem = "nw_mcloth002"; break; + case 15: sItem = "nw_mcloth003"; break; + case 16: sItem = "nw_mcloth004"; break; + case 17: sItem = "nw_it_mbelt003"; break; + // * new items + case 18: sItem = "NW_IT_MBELT020"; break; + case 19: sItem = "NW_IT_MBELT021"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificRodStaffWand(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericRodStaffWand(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericRodStaffWand(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(4) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgst004"; break; + case 2: sItem = "nw_wmgst006"; break; + case 3: sItem = "nw_wmgmrd003"; break; + case 4: sItem = "nw_wmgst004"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(7) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgmrd003"; break; + case 2: sItem = "nw_wmgst006"; break; + case 3: sItem = "nw_wmgmrd003"; break; + case 4: sItem = "nw_wmgst004"; break; + case 5: sItem = "nw_wmgst005"; break; + case 6: sItem = "nw_wmgmrd004"; break; + case 7: sItem = "nw_wmgrd002"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmcl012"; break; + case 2: sItem = "nw_wmgmrd003"; break; + case 3: sItem = "nw_wmgst004"; break; + case 4: sItem = "nw_wmgst005"; break; + case 5: sItem = "nw_wblmcl012"; break; + case 6: sItem = "nw_wmgmrd004"; break; + case 7: sItem = "nw_wmgst002"; break; + case 8: sItem = "nw_wmgmrd005"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgmrd004"; break; + case 2: sItem = "nw_wmgst002"; break; + case 3: sItem = "nw_wmgmrd005"; break; + case 4: sItem = "nw_wmgmrd002"; break; + case 5: sItem = "nw_wmgst003"; break; + case 6: sItem = "nw_wblmcl012"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + + + void CreateSpecificSimple(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericSimple(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericSimple(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs005"; break; + case 3: sItem = "nw_wdbmqs006"; break; + case 4: sItem = "nw_wbwmxh005"; break; + case 5: sItem = "nw_wbwmxl005"; break; + case 6: sItem = "nw_wswmdg006"; break; + case 7: sItem = "nw_wblmml006"; break; + case 8: sItem = "nw_wspmsc004"; break; + case 9: sItem = "nw_wblmms007"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(22) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs006"; break; + case 2: sItem = "nw_wdbmqs005"; break; + case 3: sItem = "nw_wdbmqs006"; break; + case 4: sItem = "nw_wbwmxh005"; break; + case 5: sItem = "nw_wbwmxl005"; break; + case 6: sItem = "nw_wswmdg006"; break; + case 7: sItem = "nw_wblmml006"; break; + case 8: sItem = "nw_wspmsc004"; break; + case 9: sItem = "nw_wblmms007"; break; + case 10: sItem = "nw_wblmms003"; break; + case 11: sItem = "nw_wblmcl004"; break; + case 12: sItem = "nw_wspmsc006"; break; + case 13: sItem = "nw_wspmsc006"; break; + case 14: sItem = "nw_wdbmqs004"; break; + case 15: sItem = "nw_wblmcl003"; break; + case 16: sItem = "nw_wbwmsl003"; break; + case 17: sItem = "nw_wbwmxh003"; break; + case 18: sItem = "nw_wspmsc003"; break; + case 19: sItem = "nw_wplmss005"; break; + case 20: sItem = "nw_wplmss005"; break; + case 21: sItem = "nw_wbwmxl003"; break; + case 22: sItem = "nw_wblmml004"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(27) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmms003"; break; + case 2: sItem = "nw_wblmms003"; break; + case 3: sItem = "nw_wblmcl004"; break; + case 4: sItem = "nw_wspmsc006"; break; + case 5: sItem = "nw_wspmsc006"; break; + case 6: sItem = "nw_wdbmqs004"; break; + case 7: sItem = "nw_wblmcl003"; break; + case 8: sItem = "nw_wbwmsl003"; break; + case 9: sItem = "nw_wbwmxh003"; break; + case 10: sItem = "nw_wspmsc003"; break; + case 11: sItem = "nw_wplmss005"; break; + case 12: sItem = "nw_wplmss005"; break; + case 13: sItem = "nw_wbwmxl003"; break; + case 14: sItem = "nw_wblmml004"; break; + case 15: sItem = "nw_wdbmqs003"; break; + case 16: sItem = "nw_wbwmxl004"; break; + case 17: sItem = "nw_wbwmxl007"; break; + case 18: sItem = "nw_wblmml005"; break; + case 19: sItem = "nw_wblmcl005"; break; + case 20: sItem = "nw_wplmss007"; break; + case 21: sItem = "nw_wswmdg004"; break; + case 22: sItem = "nw_wbwmsl007"; break; + case 23: sItem = "nw_wblmml007"; break; + case 24: sItem = "nw_wblmml007"; break; + case 25: sItem = "nw_wbwmxh004"; break; + case 26: sItem = "nw_wplmss006"; break; + case 27: sItem = "nw_wbwmxh007"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(31) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl003"; break; + case 2: sItem = "nw_wbwmxl003"; break; + case 3: sItem = "nw_wblmml004"; break; + case 4: sItem = "nw_wdbmqs003"; break; + case 5: sItem = "nw_wbwmxl004"; break; + case 6: sItem = "nw_wbwmxl007"; break; + case 7: sItem = "nw_wblmml005"; break; + case 8: sItem = "nw_wblmcl005"; break; + case 9: sItem = "nw_wplmss007"; break; + case 10: sItem = "nw_wswmdg004"; break; + case 11: sItem = "nw_wbwmsl007"; break; + case 12: sItem = "nw_wblmml007"; break; + case 13: sItem = "nw_wblmml007"; break; + case 14: sItem = "nw_wbwmxh004"; break; + case 15: sItem = "nw_wplmss006"; break; + case 16: sItem = "nw_wbwmxh007"; break; + case 17: sItem = "nw_wblmms006"; break; + case 18: sItem = "nw_wswmdg003"; break; + case 19: sItem = "nw_wswmdg007"; break; + case 20: sItem = "nw_wblmms004"; break; + case 21: sItem = "nw_wbwmsl006"; break; + case 22: sItem = "nw_wbwmsl008"; break; + case 23: sItem = "nw_wblmml008"; break; + case 24: sItem = "nw_wdbmqs007"; break; + case 25: sItem = "nw_wblmcl006"; break; + case 26: sItem = "nw_wbwmsl004"; break; + case 27: sItem = "nw_wbwmxh006"; break; + case 28: sItem = "nw_wplmss004"; break; + case 29: sItem = "nw_wswmdg005"; break; + case 30: sItem = "nw_wbwmxl006"; break; + case 31: sItem = "nw_wspmsc005"; break; + + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificMartial(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericMartial(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericMartial(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_wthmax005"; break; + case 3: sItem = "nw_wthmax007"; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmax003"; break; + case 2: sItem = "nw_wthmax005"; break; + case 3: sItem = "nw_wthmax007"; break; + case 4: sItem = "nw_wthmax003"; break; + case 5: sItem = "nw_wthmax004"; break; + case 6: sItem = "nw_wthmax006"; break; + case 7: sItem = "nw_wswmrp004"; break; + case 8: sItem = "nw_wswmrp004"; break; + case 9: sItem = "nw_wblmfl004"; break; + case 10: sItem = "nw_wblmhl004"; break; + case 11: sItem = "nw_wbwmsh003"; break; + case 12: sItem = "nw_wblmhw006"; break; + case 13: sItem = "nw_wblmhw006"; break; + case 14: sItem = "nw_wbwmln004"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(28) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmfl005"; break; + case 2: sItem = "nw_wthmax007"; break; + case 3: sItem = "nw_wthmax003"; break; + case 4: sItem = "nw_wthmax004"; break; + case 5: sItem = "nw_wthmax006"; break; + case 6: sItem = "nw_wswmrp004"; break; + case 7: sItem = "nw_wswmrp004"; break; + case 8: sItem = "nw_wblmfl004"; break; + case 9: sItem = "nw_wblmhl004"; break; + case 10: sItem = "nw_wbwmsh003"; break; + case 11: sItem = "nw_wblmhw006"; break; + case 12: sItem = "nw_wblmhw006"; break; + case 13: sItem = "nw_wbwmln004"; break; + case 14: sItem = "nw_wblmfl005"; break; + case 15: sItem = "nw_wswmgs006"; break; + case 16: sItem = "nw_waxmgr003"; break; + case 17: sItem = "nw_wplmhb004"; break; + case 18: sItem = "nw_wblmhw005"; break; + case 19: sItem = "nw_wblmfh004"; break; + case 20: sItem = "nw_wblmfh008"; break; + case 21: sItem = "nw_wbwmsh006"; break; + case 22: sItem = "nw_wswmsc004"; break; + case 23: sItem = "nw_waxmgr006"; break; + case 24: sItem = "nw_wswmrp005"; break; + case 25: sItem = "nw_wswmls007"; break; + case 26: sItem = "nw_wswmgs004"; break; + case 27: sItem = "nw_waxmhn004"; break; + case 28: sItem = "nw_wswmbs005"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(42) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmhw006"; break; + case 2: sItem = "nw_wblmhw006"; break; + case 3: sItem = "nw_wblmhw006"; break; + case 4: sItem = "nw_wbwmln004"; break; + case 5: sItem = "nw_wblmfl005"; break; + case 6: sItem = "nw_wswmgs006"; break; + case 7: sItem = "nw_waxmgr003"; break; + case 8: sItem = "nw_wplmhb004"; break; + case 9: sItem = "nw_wblmhw005"; break; + case 10: sItem = "nw_wblmfh004"; break; + case 11: sItem = "nw_wblmfh008"; break; + case 12: sItem = "nw_wbwmsh006"; break; + case 13: sItem = "nw_wswmsc004"; break; + case 14: sItem = "nw_waxmgr006"; break; + case 15: sItem = "nw_wswmrp005"; break; + case 16: sItem = "nw_wswmls007"; break; + case 17: sItem = "nw_wswmgs004"; break; + case 18: sItem = "nw_waxmhn004"; break; + case 19: sItem = "nw_wswmbs005"; break; + case 20: sItem = "nw_wblmhl005"; break; + case 21: sItem = "nw_wblmhl011"; break; + case 22: sItem = "nw_wswmss005"; break; + case 23: sItem = "nw_wplmhb003"; break; + case 24: sItem = "nw_wbwmln007"; break; + case 25: sItem = "nw_wbwmln007"; break; + case 26: sItem = "nw_wbwmsh007"; break; + case 27: sItem = "nw_waxmbt006"; break; + case 28: sItem = "nw_wswmbs006"; break; + case 29: sItem = "nw_wblmfl007"; break; + case 30: sItem = "nw_waxmhn003"; break; + case 31: sItem = "nw_wblmhl006"; break; + case 32: sItem = "nw_wblmfl006"; break; + case 33: sItem = "nw_wswmls005"; break; + case 34: sItem = "nw_wswmss004"; break; + case 35: sItem = "nw_wbwmln006"; break; + case 36: sItem = "nw_wblmhw003"; break; + case 37: sItem = "nw_wblmfh006"; break; + case 38: sItem = "nw_wswmsc006"; break; + case 39: sItem = "nw_waxmhn005"; break; + case 40: sItem = "nw_wblmfh003"; break; + case 41: sItem = "nw_wswmls006"; break; + case 42: sItem = "nw_wswmrp007"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(55) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmhl005"; break; + case 2: sItem = "nw_wblmhl005"; break; + case 3: sItem = "nw_wblmhl011"; break; + case 4: sItem = "nw_wswmss005"; break; + case 5: sItem = "nw_wplmhb003"; break; + case 6: sItem = "nw_wbwmln007"; break; + case 7: sItem = "nw_wbwmln007"; break; + case 8: sItem = "nw_wbwmsh007"; break; + case 9: sItem = "nw_waxmbt006"; break; + case 10: sItem = "nw_wswmbs006"; break; + case 11: sItem = "nw_wblmfl007"; break; + case 12: sItem = "nw_waxmhn003"; break; + case 13: sItem = "nw_wblmhl006"; break; + case 14: sItem = "nw_wblmfl006"; break; + case 15: sItem = "nw_wswmls005"; break; + case 16: sItem = "nw_wswmss004"; break; + case 17: sItem = "nw_wbwmln006"; break; + case 18: sItem = "nw_wblmhw003"; break; + case 19: sItem = "nw_wblmfh006"; break; + case 20: sItem = "nw_wswmsc006"; break; + case 21: sItem = "nw_waxmhn005"; break; + case 22: sItem = "nw_wblmfh003"; break; + case 23: sItem = "nw_wswmls006"; break; + case 24: sItem = "nw_wswmrp007"; break; + case 25: sItem = "nw_wswmgs005"; break; + case 26: sItem = "nw_wswmgs005"; break; + case 27: sItem = "nw_waxmgr005"; break; + case 28: sItem = "nw_wplmhb007"; break; + case 29: sItem = "nw_wswmsc007"; break; + case 30: sItem = "nw_wswmrp006"; break; + case 31: sItem = "nw_wswmss006"; break; + case 32: sItem = "nw_wblmhl009"; break; + case 33: sItem = "nw_wswmbs007"; break; + case 34: sItem = "nw_wbwmln005"; break; + case 35: sItem = "nw_wblmfh005"; break; + case 36: sItem = "nw_wswmgs003"; break; + case 37: sItem = "nw_waxmbt003"; break; + case 38: sItem = "nw_wswmls004"; break; + case 39: sItem = "nw_wbwmsh005"; break; + case 40: sItem = "nw_wbwmsh005"; break; + case 41: sItem = "nw_waxmbt004"; break; + case 42: sItem = "nw_waxmbt004"; break; + case 43: sItem = "nw_wblmhl003"; break; + case 44: sItem = "nw_wblmhl003"; break; + case 45: sItem = "nw_wswmbs003"; break; + case 46: sItem = "nw_waxmbt005"; break; + case 47: sItem = "nw_waxmhn006"; break; + case 48: sItem = "nw_wswmss003"; break; + case 49: sItem = "nw_wswmsc005"; break; + case 50: sItem = "nw_wplmhb006"; break; + case 51: sItem = "nw_wbwmsh004"; break; + case 52: sItem = "nw_wswmbs004"; break; + case 53: sItem = "nw_wbwmln003"; break; + case 54: sItem = "nw_wblmhw004"; break; + case 55: sItem = "nw_waxmgr004"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificExotic(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericExotic(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_wthmsh003"; break; + case 3: sItem = "nw_wthmsh006"; break; + } + + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: CreateGenericExotic(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_wthmsh003"; break; + case 3: sItem = "nw_wthmsh006"; break; + case 4: sItem = "nw_wthmsh004"; break; + case 5: sItem = "nw_wthmsh007"; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh006"; break; + case 2: sItem = "nw_wthmsh006"; break; + case 3: sItem = "nw_wthmsh004"; break; + case 4: sItem = "nw_wthmsh007"; break; + case 5: sItem = "nw_wspmku006"; break; + case 6: sItem = "nw_wdbmma003"; break; + case 7: sItem = "nw_wswmka005"; break; + case 8: sItem = "nw_wspmka004"; break; + case 9: sItem = "nw_wspmka007"; break; + case 10: sItem = "nw_wdbmax006"; break; + case 11: sItem = "nw_wdbmsw006"; break; + case 12: sItem = "nw_wspmku005"; break; + case 13: sItem = "nw_wdbmsw007"; break; + case 14: sItem = "nw_wspmka005"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(16) + 1; + switch (nRandom) + { + case 1:sItem = "nw_wthmsh007"; break; + case 2: sItem = "nw_wthmsh007"; break; + case 3: sItem = "nw_wspmku006"; break; + case 4: sItem = "nw_wdbmma003"; break; + case 5: sItem = "nw_wswmka005"; break; + case 6: sItem = "nw_wspmka004"; break; + case 7: sItem = "nw_wspmka007"; break; + case 8: sItem = "nw_wdbmax006"; break; + case 9: sItem = "nw_wdbmsw006"; break; + case 10: sItem = "nw_wspmku005"; break; + case 11: sItem = "nw_wdbmsw007"; break; + case 12: sItem = "nw_wspmka005"; break; + case 13: sItem = "nw_wplmsc003"; break; + case 14: sItem = "nw_wdbmax005"; break; + case 15: sItem = "nw_wspmku004"; break; + case 16: sItem = "nw_wdbmma005"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wplmsc003"; break; + case 2: sItem = "nw_wspmka005"; break; + case 3: sItem = "nw_wplmsc003"; break; + case 4: sItem = "nw_wdbmax005"; break; + case 5: sItem = "nw_wspmku004"; break; + case 6: sItem = "nw_wdbmma005"; break; + case 7: sItem = "nw_wdbmma005"; break; + case 8: sItem = "nw_wdbmax004"; break; + case 9: sItem = "nw_wdbmma004"; break; + case 10: sItem = "nw_wswmka007"; break; + case 11: sItem = "nw_wdbmsw005"; break; + case 12: sItem = "nw_wspmka006"; break; + case 13: sItem = "nw_wspmka003"; break; + case 14: sItem = "nw_wdbmax007"; break; + case 15: sItem = "nw_wplmsc006"; break; + case 16: sItem = "nw_wspmku007"; break; + case 17: sItem = "nw_wdbmma006"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(21) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmma005"; break; + case 2: sItem = "nw_wdbmma005"; break; + case 3: sItem = "nw_wdbmma005"; break; + case 4: sItem = "nw_wdbmax004"; break; + case 5: sItem = "nw_wdbmma004"; break; + case 6: sItem = "nw_wswmka007"; break; + case 7: sItem = "nw_wdbmsw005"; break; + case 8: sItem = "nw_wspmka006"; break; + case 9: sItem = "nw_wspmka003"; break; + case 10: sItem = "nw_wdbmax007"; break; + case 11: sItem = "nw_wplmsc006"; break; + case 12: sItem = "nw_wspmku007"; break; + case 13: sItem = "nw_wdbmma006"; break; + case 14: sItem = "nw_wspmku003"; break; + case 15: sItem = "nw_wswmka006"; break; + case 16: sItem = "nw_wplmsc005"; break; + case 17: sItem = "nw_wplmsc005"; break; + case 18: sItem = "nw_wswmka004"; break; + case 19: sItem = "nw_wswmka004"; break; + case 20: sItem = "nw_wdbmsw004"; break; + case 21: sItem = "nw_wplmsc004"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificLightArmor(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericLightArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericLightArmor(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_ashmsw011"; break; + case 3: sItem = "nw_ashmsw010"; break; + } + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashmsw011"; break; + case 2: sItem = "nw_ashmsw011"; break; + case 3: sItem = "nw_ashmsw010"; break; + case 4: sItem = "nw_maarcl011"; break; + case 5: sItem = "nw_ashmsw006"; break; + case 6: sItem = "nw_maarcl017"; break; + case 7: sItem = "nw_ashmsw005"; break; + case 8: sItem = "nw_maarcl013"; break; + case 9: sItem = "nw_maarcl012"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl011"; break; + case 2: sItem = "nw_maarcl011"; break; + case 3: sItem = "nw_ashmsw006"; break; + case 4: sItem = "nw_maarcl017"; break; + case 5: sItem = "nw_ashmsw005"; break; + case 6: sItem = "nw_maarcl013"; break; + case 7: sItem = "nw_maarcl012"; break; + case 8: sItem = "nw_ashmsw004"; break; + case 9: sItem = "nw_maarcl006"; break; + case 10: sItem = "nw_maarcl032"; break; + case 11: sItem = "nw_maarcl003"; break; + case 12: sItem = "nw_maarcl002"; break; + case 13: sItem = "nw_maarcl007"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl012"; break; + case 2: sItem = "nw_maarcl012"; break; + case 3: sItem = "nw_ashmsw004"; break; + case 4: sItem = "nw_maarcl006"; break; + case 5: sItem = "nw_maarcl032"; break; + case 6: sItem = "nw_maarcl003"; break; + case 7: sItem = "nw_maarcl002"; break; + case 8: sItem = "nw_maarcl005"; break; + case 9: sItem = "nw_ashmsw003"; break; + case 10: sItem = "nw_maarcl001"; break; + case 11: sItem = "nw_maarcl034"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl005"; break; + case 2: sItem = "nw_maarcl005"; break; + case 3: sItem = "nw_ashmsw003"; break; + case 4: sItem = "nw_maarcl001"; break; + case 5: sItem = "nw_maarcl034"; break; + case 6: sItem = "nw_maarcl008"; break; + case 7: sItem = "nw_ashmsw007"; break; + case 8: sItem = "nw_maarcl033"; break; + case 9: sItem = "nw_mcloth005"; break; + case 10: sItem = "nw_maarcl009"; break; + case 11: sItem = "nw_maarcl004"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificMediumArmor(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericMediumArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericMediumArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_armhe008"; break; + case 2: sItem = "nw_armhe008"; break; + case 3: sItem = "nw_armhe007"; break; + case 4: sItem = "nw_armhe009"; break; + case 5: sItem = "nw_armhe010"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_armhe008"; break; + case 2: sItem = "nw_armhe008"; break; + case 3: sItem = "nw_armhe007"; break; + case 4: sItem = "nw_armhe009"; break; + case 5: sItem = "nw_armhe010"; break; + case 6: sItem = "nw_armhe006"; break; + case 7: sItem = "nw_ashmlw007"; break; + case 8: sItem = "nw_ashmlw005"; break; + case 9: sItem = "nw_maarcl016"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(12) + 1; + switch (nRandom) + { + case 1: sItem = "nw_armhe009"; break; + case 2: sItem = "nw_armhe009"; break; + case 3: sItem = "nw_armhe010"; break; + case 4: sItem = "nw_armhe006"; break; + case 5: sItem = "nw_ashmlw007"; break; + case 6: sItem = "nw_ashmlw005"; break; + case 7: sItem = "nw_maarcl016"; break; + case 8: sItem = "nw_maarcl036"; break; + case 9: sItem = "nw_ashmlw004"; break; + case 10: sItem = "nw_maarcl037"; break; + case 11: sItem = "nw_maarcl040"; break; + case 12: sItem = "nw_ashmlw006"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(12) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl016"; break; + case 2: sItem = "nw_maarcl016"; break; + case 3: sItem = "nw_maarcl036"; break; + case 4: sItem = "nw_ashmlw004"; break; + case 5: sItem = "nw_maarcl037"; break; + case 6: sItem = "nw_maarcl040"; break; + case 7: sItem = "nw_ashmlw006"; break; + case 8: sItem = "nw_ashmlw003"; break; + case 9: sItem = "nw_maarcl014"; break; + case 10: sItem = "nw_maarcl039"; break; + case 11: sItem = "nw_maarcl010"; break; + case 12: sItem = "nw_maarcl015"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificHeavyArmor(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericHeavyArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericHeavyArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl026"; break; + case 2: sItem = "nw_maarcl026"; break; + case 3: sItem = "nw_maarcl021"; break; + case 4: sItem = "nw_ashmto003"; break; + case 5: sItem = "nw_maarcl029"; break; + case 6: sItem = "nw_maarcl020"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl021"; break; + case 2: sItem = "nw_maarcl026"; break; + case 3: sItem = "nw_maarcl021"; break; + case 4: sItem = "nw_ashmto003"; break; + case 5: sItem = "nw_maarcl029"; break; + case 6: sItem = "nw_maarcl020"; break; + case 7: sItem = "nw_ashmto006"; break; + case 8: sItem = "nw_maarcl041"; break; + case 9: sItem = "nw_ashmto005"; break; + case 10: sItem = "nw_ashmto007"; break; + case 11: sItem = "nw_ashmto010"; break; + case 12: sItem = "nw_maarcl022"; break; + case 13: sItem = "nw_maarcl018"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl020"; break; + case 2: sItem = "nw_maarcl020"; break; + case 3: sItem = "nw_ashmto006"; break; + case 4: sItem = "nw_maarcl041"; break; + case 5: sItem = "nw_ashmto005"; break; + case 6: sItem = "nw_ashmto007"; break; + case 7: sItem = "nw_ashmto010"; break; + case 8: sItem = "nw_maarcl022"; break; + case 9: sItem = "nw_maarcl018"; break; + case 10: sItem = "nw_maarcl024"; break; + case 11: sItem = "nw_ashmto011"; break; + case 12: sItem = "nw_maarcl042"; break; + case 13: sItem = "nw_maarcl054"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl018"; break; + case 2: sItem = "nw_maarcl018"; break; + case 3: sItem = "nw_maarcl024"; break; + case 4: sItem = "nw_ashmto011"; break; + case 5: sItem = "nw_maarcl042"; break; + case 6: sItem = "nw_maarcl054"; break; + case 7: sItem = "nw_ashmto004"; break; + case 8: sItem = "nw_maarcl025"; break; + case 9: sItem = "nw_maarcl028"; break; + case 10: sItem = "nw_maarcl027"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + + } + // * if nSpecific is = 1 then spawn in 'named' items at the higher levels + void CreateTable2Item(object oTarget, object oAdventurer, int nSpecific=0) + { + //dbSpeak("In CreateTable2Item"); + string sItem = ""; + int nProbMisc = 0; + int nProbClass = 0; + int nProbRodStaffWand = 0; + int nProbSimple = 0; + int nProbMartial = 0; + int nProbExotic = 0; + int nProbLight = 0; + int nProbMedium = 0; + int nProbHeavy = 0; + + int nSpecialRanger = 0; // 2 Means to treat the ranger as a barbarian. A 1 is to treat it as a fighter + + + // * May 2002: Changed using Preston's multiclass function + // * it randomly chooses one of your classes + int nClass = nDetermineClassToUse(oAdventurer); + + + // * SPECIAL RANGER BEHAVIOR + // * If the ranger has the Heavy Armor proficiency, will treat the ranger + if ( nClass == CLASS_TYPE_RANGER && GetHasFeat(FEAT_ARMOR_PROFICIENCY_HEAVY)) + { + nSpecialRanger = 1; + } + else + if (nClass == CLASS_TYPE_RANGER) + { + nSpecialRanger = 2; + } + + + + //* SETUP probabilities based on Class + if ( nClass == CLASS_TYPE_FIGHTER || nClass == CLASS_TYPE_PALADIN || nSpecialRanger == 1 + || nClass == CLASS_TYPE_ANTI_PALADIN || nClass == CLASS_TYPE_BRAWLER || nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_DUSKBLADE || nClass == CLASS_TYPE_KNIGHT || nClass == CLASS_TYPE_MARSHAL + || nClass == CLASS_TYPE_PSYWAR || nClass == CLASS_TYPE_SOHEI) + { + //dbSpeak("I am fighter or paladin or heavy ranger"); + nProbMisc = 20; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 5; + nProbMartial = 20; + nProbExotic = 10; + nProbLight = 5; + nProbMedium = 15; + nProbHeavy = 20; + } + else + if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER) + { + //dbSpeak("I am wizard or sorcerer"); + nProbMisc = 40; + nProbClass = 30; + nProbRodStaffWand = 15; + nProbSimple = 3; + nProbMartial = 3; + nProbExotic = 3; + nProbLight = 2; + nProbMedium = 2; + nProbHeavy = 2; + } + else + if (nClass == CLASS_TYPE_BARBARIAN || nSpecialRanger == 2 || nClass == CLASS_TYPE_BOWMAN + || nClass == CLASS_TYPE_HEXBLADE || nClass == CLASS_TYPE_WARBLADE) + { + //dbSpeak("I am barbarian or light ranger"); + + nProbMisc = 20; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 17; + nProbMartial = 27; + nProbExotic = 15; + nProbLight = 8; + nProbMedium = 5; + nProbHeavy = 3; + } + else + if (nClass == CLASS_TYPE_ARCHIVIST || nClass == CLASS_TYPE_DRAGON_SHAMAN || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_MYSTIC || nClass == CLASS_TYPE_WARMAGE || nClass == CLASS_TYPE_TEMPLAR) + { + //type 1 + nProbMisc = 25; + nProbClass = 0; + nProbRodStaffWand = 15; + nProbSimple = 15; + nProbMartial = 8; + nProbExotic = 6; + nProbLight = 15; + nProbMedium = 10; + nProbHeavy = 6; + } + else + if (nClass == CLASS_TYPE_NOBLE || nClass == CLASS_TYPE_SWASHBUCKLER || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_ULTIMATE_RANGER) + { + //type 2 + nProbMisc = 27; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 15; + nProbMartial = 20; + nProbExotic = 10; + nProbLight = 10; + nProbMedium = 8; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_BEGUILER || nClass == CLASS_TYPE_DREAD_NECROMANCER || nClass == CLASS_TYPE_HEALER + || nClass == CLASS_TYPE_SCOUT || nClass == CLASS_TYPE_SHAMAN || nClass == CLASS_TYPE_SOULKNIFE + || nClass == CLASS_TYPE_TRUENAMER || nClass == CLASS_TYPE_WARLOCK || nClass == CLASS_TYPE_WILDER) + { + //type 3 + nProbMisc = 45; + nProbClass = 0; + nProbRodStaffWand = 7; + nProbSimple = 15; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 15; + nProbMedium = 4; + nProbHeavy = 4; + } + else + if (nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || nClass == CLASS_TYPE_PSION || nClass == CLASS_TYPE_WITCH) + { + //type 4 + nProbMisc = 50; + nProbClass = 0; + nProbRodStaffWand = 10; + nProbSimple = 20; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 4; + nProbMedium = 3; + nProbHeavy = 3; + } + else + if (nClass == CLASS_TYPE_NINJA) + { + //type 5 + nProbMisc = 45; + nProbClass = 0; + nProbRodStaffWand = 2; + nProbSimple = 12; + nProbMartial = 6; + nProbExotic = 26; + nProbLight = 3; + nProbMedium = 3; + nProbHeavy = 3; + } + else + if (nClass == CLASS_TYPE_CW_SAMURAI || nClass == CLASS_TYPE_SAMURAI) + { + //type 6 + nProbMisc = 25; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 5; + nProbMartial = 10; + nProbExotic = 20; + nProbLight = 10; + nProbMedium = 20; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_CLERIC) + { + //dbSpeak("I am cleric"); + + nProbMisc = 20; + nProbClass = 10; + nProbRodStaffWand = 10; + nProbSimple = 25; + nProbMartial = 7; + nProbExotic = 5; + nProbLight = 5; + nProbMedium = 8; + nProbHeavy = 10; + } + else + if (nClass == CLASS_TYPE_DRUID) + { + //dbSpeak("I am druid"); + + nProbMisc = 20; + nProbClass = 25; + nProbRodStaffWand = 15; + nProbSimple = 10; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 10; + nProbMedium = 5; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_MONK) + { + //dbSpeak("I am monk"); + nProbMisc = 20; + nProbClass = 50; + nProbRodStaffWand = 2; + nProbSimple = 7; + nProbMartial = 2; + nProbExotic = 7; + nProbLight = 4; + nProbMedium = 4; + nProbHeavy = 4; + } + else + if (nClass == CLASS_TYPE_ROGUE || nClass == CLASS_TYPE_PSYCHIC_ROGUE) + { + //dbSpeak("I am rogue"); + + nProbMisc = 25; + nProbClass = 10; + nProbRodStaffWand = 10; + nProbSimple = 25; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 10; + nProbMedium = 5; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_BARD) + { + //dbSpeak("I am bard"); + + nProbMisc = 25; + nProbClass = 5; + nProbRodStaffWand = 5; + nProbSimple = 25; + nProbMartial = 10; + nProbExotic = 10; + nProbLight = 10; + nProbMedium = 5; + nProbHeavy = 5; + } + //else + //{ + // dbSpeak("No Valid Class"); + //} + //dbSpeak("Table2Item: After Class Distribution"); + //* Create Items based on Probabilities + int nRandom = d100(); + if (nRandom <= nProbMisc) + { + if (nSpecific == 0) CreateGenericMiscItem(oTarget, oAdventurer); + else CreateSpecificMiscItem(oTarget, oAdventurer); + + } + else + if (nRandom <= nProbMisc + nProbClass) + { // * no need for a seperate specific function here + CreateGenericClassItem(oTarget, oAdventurer, nSpecific); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand) + { + if (nSpecific == 0) CreateGenericRodStaffWand(oTarget, oAdventurer); + else CreateSpecificRodStaffWand(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple) + { + if (nSpecific == 0) CreateGenericSimple(oTarget, oAdventurer); + else CreateSpecificSimple(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial) + { + + if (nSpecific == 0) CreateGenericMartial(oTarget, oAdventurer); + else CreateSpecificMartial(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic) + { + if (nSpecific == 0) CreateGenericExotic(oTarget, oAdventurer); + else CreateSpecificExotic(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic + nProbLight) + { + if (nSpecific == 0) CreateGenericLightArmor(oTarget, oAdventurer); + else CreateSpecificLightArmor(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic + nProbLight + nProbMedium) + { + if (nSpecific == 0) CreateGenericMediumArmor(oTarget, oAdventurer); + else CreateSpecificMediumArmor(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic + nProbLight + nProbMedium + nProbHeavy) + { + if (nSpecific == 0) CreateGenericHeavyArmor(oTarget, oAdventurer); + else CreateSpecificHeavyArmor(oTarget, oAdventurer); + } + //else + //{ + // dbSpeak("Generic Generic or Specific; error: 3524"); + //} + } + +//:://///////////////////////////////////////////// +//:: GenerateTreasure +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Generate Treasure + NOTE: When used by NPCs, the treasure is scaled + to how powerful the NPC is. + + If used by containers, it is scaled by how + powerful the PC is. + + PARAMETERS + oLastOpener = The creature that opened the container + oCreateOn = The place to put the treasure. If this is + invalid then the treasure is placed on oLastOpener + + +*/ +//::////////////////////////////////////////////// +//:: Created By: Andrew +//:: Created On: +//::////////////////////////////////////////////// +void GenerateTreasure(int nTreasureType, object oLastOpener, object oCreateOn) +{ + + //dbSpeak("*********************NEW TREASURE*************************"); + + // * abort treasure if no one opened the container + if (GetIsObjectValid(oLastOpener) == FALSE) + { + //dbSpeak("Aborted. No valid Last Opener"); + return; + } + + // * if no valid create on object, then create on oLastOpener + if (oCreateOn == OBJECT_INVALID) + { + oCreateOn = oLastOpener; + } + + // * if an Animal then generate 100% animal treasure + + // not done yet + // * VARIABLES + int nProbBook = 0; + int nProbAnimal = 0; + int nProbJunk = 0; + int nProbGold = 0; + int nProbGem = 0; + int nProbJewel = 0; + int nProbArcane = 0; + int nProbDivine = 0; + int nProbAmmo = 0; + int nProbKit = 0; + int nProbPotion = 0; + int nProbTable2 = 0; + + int nSpecific = 0; + int i = 0; + int nNumberItems = GetNumberOfItems(nTreasureType); + + // * Set Treasure Type Values + if (nTreasureType == TREASURE_LOW) + { + nProbBook = LOW_PROB_BOOK; + nProbAnimal = LOW_PROB_ANIMAL; + nProbJunk = LOW_PROB_JUNK; + nProbGold = LOW_PROB_GOLD; + nProbGem = LOW_PROB_GEM; + nProbJewel = LOW_PROB_JEWEL; + nProbArcane = LOW_PROB_ARCANE; + nProbDivine = LOW_PROB_DIVINE; + nProbAmmo = LOW_PROB_AMMO ; + nProbKit = LOW_PROB_KIT; + nProbPotion = LOW_PROB_POTION; + nProbTable2 = LOW_PROB_TABLE2; + } + else if (nTreasureType == TREASURE_MEDIUM) + { + nProbBook = MEDIUM_PROB_BOOK; + nProbAnimal = MEDIUM_PROB_ANIMAL; + nProbJunk = MEDIUM_PROB_JUNK; + nProbGold = MEDIUM_PROB_GOLD; + nProbGem = MEDIUM_PROB_GEM; + nProbJewel = MEDIUM_PROB_JEWEL; + nProbArcane = MEDIUM_PROB_ARCANE; + nProbDivine = MEDIUM_PROB_DIVINE; + nProbAmmo = MEDIUM_PROB_AMMO ; + nProbKit = MEDIUM_PROB_KIT; + nProbPotion = MEDIUM_PROB_POTION; + nProbTable2 = MEDIUM_PROB_TABLE2; + } + else if (nTreasureType == TREASURE_HIGH) + { + nProbBook = HIGH_PROB_BOOK; + nProbAnimal = HIGH_PROB_ANIMAL; + nProbJunk = HIGH_PROB_JUNK; + nProbGold = HIGH_PROB_GOLD; + nProbGem = HIGH_PROB_GEM; + nProbJewel = HIGH_PROB_JEWEL; + nProbArcane = HIGH_PROB_ARCANE; + nProbDivine = HIGH_PROB_DIVINE; + nProbAmmo = HIGH_PROB_AMMO ; + nProbKit = HIGH_PROB_KIT; + nProbPotion = HIGH_PROB_POTION; + nProbTable2 = HIGH_PROB_TABLE2; + } + else if (nTreasureType == TREASURE_BOSS) + { //dbSpeak("boss"); + nProbTable2 = 100; + nSpecific = 1; + } + else if (nTreasureType == TREASURE_BOOK) + { + nProbBook = 90; + nProbArcane = 6; + nProbDivine = 4; + } + + //dbSpeak("Generate Treasure nSpecific = " + IntToString(nSpecific)); + + for (i = 1; i <= nNumberItems; i++) + { + int nRandom = d100(); + if (nRandom <= nProbBook) + CreateBook(oCreateOn); // * Book + else if (nRandom <= nProbBook + nProbAnimal) + CreateAnimalPart(oCreateOn); // * Animal + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk) + CreateJunk(oCreateOn); // * Junk + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold) + CreateGold(oCreateOn, oLastOpener, nTreasureType); // * Gold + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem) + CreateGem(oCreateOn, oLastOpener, nTreasureType); // * Gem + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel) + CreateJewel(oCreateOn, oLastOpener, nTreasureType); // * Jewel + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane) + CreateArcaneScroll(oCreateOn, oLastOpener); // * Arcane Scroll + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine) + CreateDivineScroll(oCreateOn, oLastOpener); // * Divine Scroll + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo) + CreateAmmo(oCreateOn, oLastOpener); // * Ammo + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo + nProbKit) + CreateKit(oCreateOn, oLastOpener); // * Healing, Trap, or Thief kit + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo + nProbKit + nProbPotion) + CreatePotion(oCreateOn, oLastOpener); // * Potion + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo + nProbKit + nProbPotion + nProbTable2) + { + CreateTable2Item(oCreateOn, oLastOpener, nSpecific); // * Weapons, Armor, Misc - Class based + } + //else + // dbSpeak("other stuff"); + + + + } +} +void GenerateLowTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_LOW, oLastOpener, oCreateOn); +} +void GenerateMediumTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_MEDIUM, oLastOpener, oCreateOn); +} +void GenerateHighTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_HIGH, oLastOpener, oCreateOn); +} +void GenerateBossTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_BOSS, oLastOpener, oCreateOn); +} +void GenerateBookTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_BOOK, oLastOpener, oCreateOn); +} +//:://///////////////////////////////////////////// +//:: GenerateNPCTreasure +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Preferrably called from OnSpawn scripts. + Use the random treasure functions to generate + appropriate treasure for the creature to drop. +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: January 2002 +//::////////////////////////////////////////////// + +void GenerateNPCTreasure(int nTreasureValue=1, object oTreasureGetter=OBJECT_SELF, object oKiller=OBJECT_SELF) +{ + //DestroyObject(OBJECT_SELF); + // * if I am an animal ,then give me animal stuff instead + if (GetObjectType(oTreasureGetter) == OBJECT_TYPE_CREATURE) + { + if ( + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_UNDEAD) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_ANIMAL) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_BEAST) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_MAGICAL_BEAST) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_VERMIN) + ) + { + //CreateAnimalPart(oTreasureGetter); + // April 23 2002: Removed animal parts. They are silly. + return; + } + } + + if (nTreasureValue == 1) + { + // April 2002: 30% chance of not getting any treasure now + // if a creature + if (Random(100)+1 >= 75) + { + GenerateTreasure(TREASURE_LOW, oTreasureGetter, oKiller); + } + } + else + if (nTreasureValue == 2) + { + GenerateTreasure(TREASURE_MEDIUM, oTreasureGetter, oKiller); + } + else + if (nTreasureValue == 3) + { + GenerateTreasure(TREASURE_HIGH, oTreasureGetter, oKiller); + } + else + if (nTreasureValue == 4) + { + GenerateBossTreasure(oKiller, oTreasureGetter); + } + +} + +// * +// * Theft Prevention +// * + +//:://///////////////////////////////////////////// +//:: ShoutDisturbed +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + +*/ +//::////////////////////////////////////////////// +//:: Created By: +//:: Created On: +//::////////////////////////////////////////////// + +// * Container shouts if disturbed +void ShoutDisturbed() +{ + if (GetIsDead(OBJECT_SELF) == TRUE) + { + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget)) + { + if (GetFactionEqual(oTarget, OBJECT_SELF) == TRUE) + { + // * Make anyone who is a member of my faction hostile if I am violated + object oAttacker = GetLastAttacker(); + SetIsTemporaryEnemy(oAttacker,oTarget); + AssignCommand(oTarget, ActionAttack(oAttacker)); + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + } + } + else if (GetIsOpen(OBJECT_SELF) == TRUE) + { + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget)) + { + if (GetFactionEqual(oTarget, OBJECT_SELF) == TRUE) + { + // * Make anyone who is a member of my faction hostile if I am violated + object oAttacker = GetLastOpener(); + SetIsTemporaryEnemy(oAttacker,oTarget); + AssignCommand(oTarget, ActionAttack(oAttacker)); + + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + } + } +} + +int nGetIsBaseClass(int nClass) +{ + return (nClass <= CLASS_TYPE_WIZARD || + nClass == CLASS_TYPE_ANTI_PALADIN || + nClass == CLASS_TYPE_ARCHIVIST || + nClass == CLASS_TYPE_BEGUILER || + nClass == CLASS_TYPE_BOWMAN || + nClass == CLASS_TYPE_BRAWLER || + nClass == CLASS_TYPE_CRUSADER || + nClass == CLASS_TYPE_DRAGON_SHAMAN || + nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || + nClass == CLASS_TYPE_DREAD_NECROMANCER || + nClass == CLASS_TYPE_DUSKBLADE || + nClass == CLASS_TYPE_FAVOURED_SOUL || + nClass == CLASS_TYPE_HEALER || + nClass == CLASS_TYPE_HEXBLADE || + nClass == CLASS_TYPE_KNIGHT || + nClass == CLASS_TYPE_MARSHAL || + nClass == CLASS_TYPE_MYSTIC || + nClass == CLASS_TYPE_NINJA || + nClass == CLASS_TYPE_NOBLE || + nClass == CLASS_TYPE_PSION || + nClass == CLASS_TYPE_PSYWAR || + nClass == CLASS_TYPE_PSYCHIC_ROGUE || + nClass == CLASS_TYPE_SAMURAI || + nClass == CLASS_TYPE_CW_SAMURAI || + nClass == CLASS_TYPE_SCOUT || + nClass == CLASS_TYPE_SHAMAN || + nClass == CLASS_TYPE_SOHEI || + nClass == CLASS_TYPE_SOULKNIFE || + nClass == CLASS_TYPE_SWASHBUCKLER || + nClass == CLASS_TYPE_SWORDSAGE || + nClass == CLASS_TYPE_TRUENAMER || + nClass == CLASS_TYPE_ULTIMATE_RANGER || + nClass == CLASS_TYPE_WARBLADE || + nClass == CLASS_TYPE_WARLOCK || + nClass == CLASS_TYPE_WARMAGE || + nClass == CLASS_TYPE_WILDER || + nClass == CLASS_TYPE_WITCH || + nClass == CLASS_TYPE_TEMPLAR); +} + +//:://///////////////////////////////////////////// +//:: Determine Class to Use +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Determines which of a NPCs three classes to + use in the random treasure system +*/ +//::////////////////////////////////////////////// +//:: Created By: Preston Watamaniuk +//:: Created On: April 4, 2002 +//::////////////////////////////////////////////// + +int nDetermineClassToUse(object oCharacter) +{ + int nClass; + int nTotal = GetHitDice(oCharacter); + //dbSpeak("Hit dice " + IntToString(nTotal)); + if (nTotal < 1) + { + nTotal = 1; + } +/* + float fTotal = IntToFloat(nTotal); + //if (GetIsObjectValid(oCharacter) == FALSE) + //{ + // dbSpeak("DetermineClassToUse: This character is invalid"); + //} + + int nClass1 = GetClassByPosition(1, oCharacter); + int nState1 = FloatToInt((IntToFloat(GetLevelByClass(nClass1, oCharacter)) / fTotal) * 100); + //dbSpeak("Level 1 Class Level = " + IntToString(GetLevelByClass(nClass1,oCharacter))); + + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + GetTag(oCharacter) + "Class 1 " + IntToString(nState1)); + //dbSpeak("State 1 " + IntToString(nState1)); + int nClass2 = GetClassByPosition(2, oCharacter); + int nState2 = FloatToInt((IntToFloat(GetLevelByClass(nClass2, oCharacter)) / fTotal) * 100) + nState1; + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + GetTag(oCharacter) + "Class 2 " + IntToString(nState2)); + + int nClass3 = GetClassByPosition(3, oCharacter); + int nState3 = FloatToInt((IntToFloat(GetLevelByClass(nClass3, oCharacter)) / fTotal) * 100) + nState2; + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + GetTag(oCharacter) + "Class 3 " + IntToString(nState3)); +*/ + int nClass1 = GetClassByPosition(1, oCharacter); + int nClass2 = GetClassByPosition(2, oCharacter); + int nClass3 = GetClassByPosition(3, oCharacter); + int nClass4 = GetClassByPosition(4, oCharacter); + int nClass5 = GetClassByPosition(5, oCharacter); + int nClass6 = GetClassByPosition(6, oCharacter); + int nClass7 = GetClassByPosition(7, oCharacter); + int nClass8 = GetClassByPosition(8, oCharacter); + + int nState1 = GetLevelByClass(nClass1, oCharacter) * 100 / nTotal; + int nState2 = GetLevelByClass(nClass2, oCharacter) * 100 / nTotal + nState1; + int nState3 = GetLevelByClass(nClass3, oCharacter) * 100 / nTotal + nState2; + int nState4 = GetLevelByClass(nClass4, oCharacter) * 100 / nTotal + nState3; + int nState5 = GetLevelByClass(nClass5, oCharacter) * 100 / nTotal + nState4; + int nState6 = GetLevelByClass(nClass6, oCharacter) * 100 / nTotal + nState5; + int nState7 = GetLevelByClass(nClass7, oCharacter) * 100 / nTotal + nState6; + + // nState8 will always be 100 if there is an eigth class, or 0 if there isn't + //int nState8 = GetLevelByClass(nClass3, oCharacter) * 100 / nTotal + nState7; + + // correct for unrecognized classes - assumes the first class will be a non-prestige player class + if(nClass2 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass2)) + { + nClass2 = CLASS_TYPE_INVALID; + nState1 = nState2; + } + if(nClass3 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass3)) + { + nClass3 = CLASS_TYPE_INVALID; + nState1 = nState3; + } + if(nClass4 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass4)) + { + nClass4 = CLASS_TYPE_INVALID; + nState1 = nState4; + } + if(nClass5 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass5)) + { + nClass5 = CLASS_TYPE_INVALID; + nState1 = nState5; + } + if(nClass6 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass6)) + { + nClass6 = CLASS_TYPE_INVALID; + nState1 = nState6; + } + if(nClass7 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass7)) + { + nClass7 = CLASS_TYPE_INVALID; + nState1 = nState7; + } + if(nClass8 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass8)) + { + nClass8 = CLASS_TYPE_INVALID; + if(nClass7 != CLASS_TYPE_INVALID) + nState7 = 100; + else nState1 = 100; + } + + int nUseClass = d100(); + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + "D100 Roll " +IntToString(nUseClass)); + + + //dbSpeak("Before comparison : " + IntToString(nClass1)); + if(nUseClass <= nState1) + { + nClass = nClass1; + } + else if(nUseClass > nState1 && nUseClass <= nState2) + { + nClass = nClass2; + } + else + { + // might be possible to end up here by accident because of a rounding error + // so just in case... + if(nClass3 == CLASS_TYPE_INVALID) nClass = nClass1; + else nClass = nClass3; + } + + //dbSpeak("Class from determineClass " + IntToString(nClass)); + return nClass; +} + +//:: Test Void +//void main () {} + diff --git a/trunk/include/prc_inc_castlvl.nss b/trunk/include/prc_inc_castlvl.nss new file mode 100644 index 00000000..58c911f0 --- /dev/null +++ b/trunk/include/prc_inc_castlvl.nss @@ -0,0 +1,1823 @@ + + +/** + * @file + * + * This file contains PRCGetCasterLevel() and all its accessory functions. + * Functions that modify caster level go in this include. Keep out irrelevent + * functions. If this ends up like prc_inc_spells, you get slapped. + */ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the caster level when used in spells. You can use PRCGetCasterLevel() + * to determine a caster level from within a true spell script. In spell-like- + * abilities & items, it will only return GetCasterLevel. + * + * @param oCaster The creature casting the spell. + * + * @return The caster level the spell was cast at. + */ +int PRCGetCasterLevel(object oCaster = OBJECT_SELF); + +/** + * A lookup for caster level progression for divine and arcane base classes + * @return an int that can be used in caster level calculations note: these use int division + */ +int GetCasterLevelModifier(int nClass); + +/** + * To override for custom spellcasting classes. Looks for the + * local int "PRC_CASTERCLASS_OVERRIDE" on oCaster. If set, + * this is used as the casting class, else GetLastSpellCastClass() + * is used. + * + * @param oCaster The creature that last cast a spell + * + * @return The class used to cast the spell. + */ +int PRCGetLastSpellCastClass(object oCaster = OBJECT_SELF); + +/** + * Returns if the given class is an arcane class. + * + * Arcane base classes are *hardcoded* into here, so new arcane + * base classes need adding to this function. + * Note: PrCs with their own spellbook eg. assassin count as base casters for this function + * + * @param oCaster The creature to check (outsiders can have sorc caster levels) + * + * @return TRUE if nClass is an arcane spellcasting class, FALSE otherwise + */ +int GetIsArcaneClass(int nClass, object oCaster = OBJECT_SELF); + +/** + * Returns if the given class is an arcane class. + * + * Divine base classes are *hardcoded* into here, so new divine + * base classes need adding to this function. + * Note: PrCs with their own spellbook eg. blackguard count as base casters for this function + * + * @param oCaster The creature to check (not currently used) + * + * @return TRUE if nClass is a divine spellcasting class, FALSE otherwise + */ +int GetIsDivineClass(int nClass, object oCaster = OBJECT_SELF); + +// Returns the best "natural" arcane levels of the PC in question. Does not +// consider feats that situationally adjust caster level. +int GetLevelByTypeArcane(object oCaster = OBJECT_SELF); + +// Returns the best "natural" divine levels of the PC in question. Does not +// consider feats that situationally adjust caster level. +int GetLevelByTypeDivine(object oCaster = OBJECT_SELF); + +/** + * Works out the total arcane caster levels from arcane PrCs. + * + * Arcane prestige classes are *hardcoded* into this function, so new arcane caster + * classes need adding to it. Rakshasa RHD count as sorc PrC levels if they also have some levels in sorc + * note: PrCs with their own spellbook eg. assassin are not PrCs for this function + * + * @param oCaster The creature to check + * @param nCastingClass Casting class, only ever used for UM + * + * @return Number of arcane caster levels contributed by PrCs. + */ +int GetArcanePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID); + +/** + * Works out the total divine caster levels from arcane PrCs. + * + * Divine prestige classes are *hardcoded* into this function, so new divine caster + * classes need adding to it. + * note: PrCs with their own spellbook eg. blackguard are not PrCs for this function + * + * @param oCaster The creature to check + * + * @return Number of divine caster levels contributed by PrCs. + */ +int GetDivinePRCLevels(object oCaster); + +/** + * Gets the position of the first arcane base class. + * + * @param oCaster The creature to check + * + * @return The position (1,2 or 3) of the first arcane *base* class of oCaster + */ +int GetFirstArcaneClassPosition(object oCaster = OBJECT_SELF); + +/** + * Gets the position of the first divine base class. + * + * @param oCaster The creature to check + * + * @return The position (1,2 or 3) of the first divine *base* class of oCaster + */ +int GetFirstDivineClassPosition(object oCaster = OBJECT_SELF); + +/** + * Gets the highest or first (by position) *base* arcane class type or, + * if oCaster has no arcane class levels, returns CLASS_TYPE_INVALID. + * + * This will get rakshasa RHD 'class' if oCaster doesn't have sorc levels. + * + * @param oCaster The creature to check + * + * @return CLASS_TYPE_* of first base arcane class or CLASS_TYPE_INVALID + */ +int GetPrimaryArcaneClass(object oCaster = OBJECT_SELF); + +/** + * Gets the highest first (by position) *base* divine class type or, + * if oCaster has no divine class levels, returns CLASS_TYPE_INVALID. + * + * @param oCaster The creature to check + * + * @return CLASS_TYPE_* of first base divine class or CLASS_TYPE_INVALID + */ +int GetPrimaryDivineClass(object oCaster = OBJECT_SELF); + +/** + * Gets the highest *base* arcane or divine class type or, + * if oCaster has no spellcasting class levels, returns CLASS_TYPE_INVALID. + * + * @param oCaster The creature to check + * + * @return CLASS_TYPE_* of first base arcane/divine class or CLASS_TYPE_INVALID + */ +int GetPrimarySpellcastingClass(object oCaster = OBJECT_SELF); + +/** + * Gets the caster level adjustment from the Practiced Spellcaster feats. + * + * @param oCaster The creature to check + * @param iCastingClass The CLASS_TYPE* that the spell was cast by. + * @param iCastingLevels The caster level for the spell calculated so far + * ie. BEFORE Practiced Spellcaster. + */ +int PracticedSpellcasting(object oCaster, int iCastingClass, int iCastingLevels); + +/** + * Returns the spell school of the spell passed to it. + * + * @param iSpellId The spell to get the school of. + * + * @return The SPELL_SCHOOL_* of the spell. + */ +int GetSpellSchool(int iSpellId); + +/** + * Healing spell filter. + * + * Gets if the given spell is a healing spell based on a hardcoded list. New + * healing spells need to be added to this. + * + * @author GaiaWerewolf + * @date 18 July 2005 + * + * @param nSpellId The spell to check + * + * @return TRUE if it is a healing spell, FALSE otherwise. + */ +int GetIsHealingSpell(int nSpellId); + +/** + * Gets the contribution of the archmage's High Arcana Spell Power + * feat to caster level. + * + * @param oCaster The creature to check + * + * @return caster level modifier from archmage Spell Power feats. + */ +int ArchmageSpellPower(object oCaster); + +/** + * Gets the caster level modifier from the Shadow Weave feat. + * + * Schools of Enchantment, Illusion, and Necromancy, and spells with the darkness + * descriptor altered by +1, Evocation or Transmutation (except spells with the + * darkness descriptor) altered by -1. + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier for Shadow Weave feat. + */ +int ShadowWeave(object oCaster, int iSpellID, int nSpellSchool = -1); + +/** + * Gets the caster level modifier from the Divination Power class feature. + * + * Divination spells +1/3 Unseen Seer levels, all others -1/3 Unseer Seer levels + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier for Divination Power feat. + */ +int DivinationPower(object oCaster, int nSpellSchool); + +/** + * Handles feats that modify caster level of spells with the fire + * descriptor. + * + * Currently this is Disciple of Meph's Fire Adept feat and Bloodline of Fire feat. + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for fire related feats. + */ +int FireAdept(object oCaster, int iSpellID); + +/** + * Handles feats that modify caster level of spells with the air + * descriptor. + * + * Currently this is the Air Mephling's Type feat + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for fire related feats. + */ +int AirAdept(object oCaster, int iSpellID); + +/** + * Handles feats that modify caster level of spells with the air + * descriptor. + * + * Currently this is the Air Mephling's Type feat + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for fire related feats. + */ +int WaterAdept(object oCaster, int iSpellID); + +/** + * Handles feats that modify caster level of spells with the earth + * descriptor. + * + * Currently this is Drift Magic feat. + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for earth related feats. + */ +int DriftMagic(object oCaster, int iSpellID); + +/** + * Soulcaster boost to caster level based on invested essentia + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier + */ +int Soulcaster(object oCaster, int iSpellID); + +/** + * Gets the caster level modifier from the Storm Magic feat. + * + * Get +1 caster level if raining or snowing in area + * + * @param oCaster The creature to check + * + * @return Caster level modifier for Storm Magic feat. + */ +int StormMagic(object oCaster); + +/** + * Gets the caster level modifier from the Cormanthyran Moon Magic feat. + * + * Get +1 caster level if outdoors, at night, with no rain. + * + * @param oCaster The creature to check + * + * @return Caster level modifier for Cormanthyran Moon Magic feat. + */ +int CormanthyranMoonMagic(object oCaster); + +/** + * Gets the caster level modifier from various domains. + * + * @param oCaster The creature to check + * @param nSpellID The spell ID of the spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier from domain powers + */ +int DomainPower(object oCaster, int nSpellID, int nSpellSchool = -1); + +/** + * Gets the caster level modifier from the Therapeutic Mantle Meld. + * + * @param oCaster The creature to check + * + * @return caster level modifier + */ +int TherapeuticMantle(object oCaster, int nSpellID); + +/** + * Gets the caster level modifier from the antipaladin's Death Knell SLA. + * + * @param oCaster The creature to check + * + * @return caster level modifier from the Death Knell SLA + */ +int DeathKnell(object oCaster); + +/** + * Gets the caster level modifier from the Draconic Power feat. + * + * Feat gives +1 to caster level. + * + * @param oCaster The creature to check + * + * @return caster level modifier from the Draconic power feat. + */ +int DraconicPower(object oCaster = OBJECT_SELF); + +/** + * Gets the caster level modifier from Song of Arcane Power effect. + * + * @param oCaster The creature to check + * + * @return caster level modifier from the Draconic power feat. + */ +int SongOfArcanePower(object oCaster = OBJECT_SELF); + +/** + * Gets the caster level modifier to necromancy spells for the + * True Necromancer PrC (all spellcasting levels are counted, both + * arcane and divine). + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * @param sType "ARCANE" or "DIVINE" spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier for True Necro + */ +int TrueNecromancy(object oCaster, int iSpellID, string sType, int nSpellSchool = -1); + +// Nentyar Hunter casting boost +int Nentyar(object oCaster, int nCastingClass); + +// +1 on spells that target armor or shields +int ShieldDwarfWarder(object oCaster); + +// +1 while this feat is active +int DarkSpeech(object oCaster); + +// Adds 1/2 level in all other casting classes. +int UrPriestCL(object oCaster, int nCastingClass); + +// Adds Druid levels to Blighter caster level +int BlighterCL(object oCaster, int nCastingClass); + +//ebonfowl: Adds CL boosts from reserve feats +int ReserveFeatCL(object oCaster, int iSpellId); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "prc_racial_const" +// Not needed as it has acccess via prc_inc_newip +//#include "prc_inc_nwscript" // gets inc_2da_cache, inc_debug, prc_inc_switch +#include "prc_inc_newip" +//#include "prc_inc_spells" +#include "prc_inc_descrptr" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +// stolen from prcsp_archmaginc.nss, modified to work in FireAdept() function +string _GetChangedElementalType(int nSpellID, object oCaster = OBJECT_SELF) +{ + string spellType = Get2DACache("spells", "ImmunityType", nSpellID);//lookup_spell_type(spell_id); + string sType = GetLocalString(oCaster, "archmage_mastery_elements_name"); + + if (sType == "") sType = spellType; + + return sType; +} + +//ebonfowl: Adding this function to check if a spell belongs to a given domain based on the Reserve Feat 2das +//Only works with Death, Destruction and War domains as only those domain 2das have been created +int GetIsFromDomain (int iSpellId, string sDomain) +{ + string sFile = "prc_desc_" + sDomain; + + int i; + int nListSpellID; + + for (i = 0; i < 15; i++) // Adjust max i to reflect something close to the highest row number in the 2das + { + nListSpellID = StringToInt(Get2DACache(sFile, "SpellID", i)); + if (nListSpellID == iSpellId) return TRUE; + } + return FALSE; +} + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +int GetCasterLevelModifier(int nClass) +{ + switch(nClass) // do not change to return zero as this is used as a divisor + { + // add in new base half-caster classes here + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_ANTI_PALADIN: + return 2; + } + return 1; // normal progression +} + +int PRCGetCasterLevel(object oCaster = OBJECT_SELF) +{ + int nAdjust = GetLocalInt(oCaster, PRC_CASTERLEVEL_ADJUSTMENT);//this is for builder use + nAdjust += GetLocalInt(oCaster, "TrueCasterLens"); + nAdjust += GetHasSpellEffect(SPELL_VIRTUOSO_MAGICAL_MELODY, oCaster); + + // For when you want to assign the caster level. + int iReturnLevel = GetLocalInt(oCaster, PRC_CASTERLEVEL_OVERRIDE); + if (iReturnLevel) + { + if (DEBUG) DoDebug("PRCGetCasterLevel: found override caster level = "+IntToString(iReturnLevel)+" with adjustment = " + IntToString(nAdjust)+", original level = "+IntToString(GetCasterLevel(oCaster))); + return iReturnLevel+nAdjust; + } + + // if we made it here, iReturnLevel = 0; + + int iCastingClass = PRCGetLastSpellCastClass(oCaster); // might be CLASS_TYPE_INVALID + if(iCastingClass == CLASS_TYPE_SUBLIME_CHORD) + iCastingClass = GetPrimaryArcaneClass(oCaster); + int iSpellId = PRCGetSpellId(oCaster); + object oItem = PRCGetSpellCastItem(oCaster); + + // Item Spells + // this check is unreliable because of Bioware's implementation (GetSpellCastItem returns + // the last object from which a spell was cast, even if we are not casting from an item) + if(GetIsObjectValid(oItem)) + { + int nType = GetBaseItemType(oItem); + if(DEBUG) DoDebug("PRCGetCasterLevel: found valid item = "+GetName(oItem)); + // double check, just to make sure + if(GetItemPossessor(oItem) == oCaster) // likely item casting + { + if(GetPRCSwitch(PRC_STAFF_CASTER_LEVEL) + && ((nType == BASE_ITEM_MAGICSTAFF) || + (nType == BASE_ITEM_CRAFTED_STAFF)) + ) + { + iCastingClass = GetPrimarySpellcastingClass(oCaster); + } + else + { + //code for getting new ip type + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL) + { + int nSubType = GetItemPropertySubType(ipTest); + nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType)); + if(nSubType == iSpellId) + { + iReturnLevel = GetItemPropertyCostTableValue(ipTest); + if (DEBUG) DoDebug("PRCGetCasterLevel: caster level from item = "+IntToString(iReturnLevel)); + break; // exit the while loop + } + } + ipTest = GetNextItemProperty(oItem); + } + // if we didn't find a caster level on the item, it must be Bioware item casting + if(!iReturnLevel) + { + iReturnLevel = GetCasterLevel(oCaster); + if (DEBUG) DoDebug("PRCGetCasterLevel: bioware item casting with caster level = "+IntToString(iReturnLevel)); + } + } + + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) + { + if (DEBUG) DoDebug("PRCGetCasterLevel - Casting Item is a Wand at level "+IntToString(iReturnLevel)); + if (GetHasFeat(FEAT_RECKLESS_WAND_WIELDER, oCaster) && GetLocalInt(oCaster, "RecklessWand")) // This burns an extra charge to increase caster level by 2 + { + if (DEBUG) DoDebug("PRCGetCasterLevel - Reckless Wand Active"); + if (GetItemCharges(oItem) > 0) // Make sure we have an extra charge to burn + { + iReturnLevel += 2; + if (!GetLocalInt(oCaster, "RecklessWandDelay")) SetItemCharges(oItem, GetItemCharges(oItem)-1); + SetLocalInt(oCaster, "RecklessWandDelay", TRUE); + DelayCommand(0.5, DeleteLocalInt(oCaster, "RecklessWandDelay")); + if (DEBUG) DoDebug("PRCGetCasterLevel - Reckless Wand Triggered at level "+IntToString(iReturnLevel)); + } + } + if (GetHasFeat(FEAT_WAND_MASTERY, oCaster)) + iReturnLevel += 2; + } + } + if (DEBUG) DoDebug("PRCGetCasterLevel: total item casting caster level = "+IntToString(iReturnLevel)); + } + + // get spell school here as many of the following fns use it + int nSpellSchool = GetSpellSchool(iSpellId); + + // no item casting, and arcane caster? + if(!iReturnLevel && GetIsArcaneClass(iCastingClass, oCaster)) + { + iReturnLevel = GetLevelByClass(iCastingClass, oCaster) / GetCasterLevelModifier(iCastingClass); + + // Casting as a sorc but don't have any levels in the class + if(iCastingClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oCaster)) + { + int nRace = GetRacialType(oCaster); + + //if the player has sorcerer levels, then it counts as a prestige class + //otherwise use RHD instead of sorc levels + if(nRace == RACIAL_TYPE_RAKSHASA) + iReturnLevel = GetLevelByClass(CLASS_TYPE_OUTSIDER); + else if(nRace == RACIAL_TYPE_DRIDER) + iReturnLevel = GetLevelByClass(CLASS_TYPE_ABERRATION); + else if(nRace == RACIAL_TYPE_ARKAMOI) + iReturnLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS); + else if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + iReturnLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS)*3/4; + else if(nRace == RACIAL_TYPE_MARRUTACT) + iReturnLevel = (GetLevelByClass(CLASS_TYPE_MONSTROUS)*6/7)-1; + else if(nRace == RACIAL_TYPE_ARANEA) + 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)) + { + int nRace = GetRacialType(oCaster); + + //if the player has bard levels, then it counts as a prestige class + //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) + && (nSpellSchool == SPELL_SCHOOL_ABJURATION + || nSpellSchool == SPELL_SCHOOL_CONJURATION + || nSpellSchool == SPELL_SCHOOL_EVOCATION + || nSpellSchool == SPELL_SCHOOL_NECROMANCY + || nSpellSchool == SPELL_SCHOOL_TRANSMUTATION)) + { + iReturnLevel = GetHitDice(oCaster); + } + + else if(GetPrimaryArcaneClass(oCaster) == iCastingClass) + iReturnLevel += GetArcanePRCLevels(oCaster); + else if(GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster)) + iReturnLevel += GetArcanePRCLevels(oCaster, iCastingClass); + + iReturnLevel += PracticedSpellcasting(oCaster, iCastingClass, iReturnLevel); + + iReturnLevel += TrueNecromancy(oCaster, iSpellId, "ARCANE", nSpellSchool) + + ShadowWeave(oCaster, iSpellId, nSpellSchool) + + FireAdept(oCaster, iSpellId) + + AirAdept(oCaster, iSpellId) + + WaterAdept(oCaster, iSpellId) + + ArchmageSpellPower(oCaster) + + StormMagic(oCaster) + + CormanthyranMoonMagic(oCaster) + + DomainPower(oCaster, iSpellId, nSpellSchool) + + DivinationPower(oCaster, nSpellSchool) + + DeathKnell(oCaster) + + DraconicPower(oCaster) + + DriftMagic(oCaster, iSpellId) + + Soulcaster(oCaster, iSpellId) + + TherapeuticMantle(oCaster, iSpellId) + + DarkSpeech(oCaster) + + ShieldDwarfWarder(oCaster) + + SongOfArcanePower(oCaster) + + ReserveFeatCL(oCaster, iSpellId); + + if (GetLocalInt(oCaster, "CaptureMagic")) + { + iReturnLevel += GetLocalInt(oCaster, "CaptureMagic"); + DeleteLocalInt(oCaster, "CaptureMagic"); + } + + // Get stance level bonus for Jade Phoenix Mage + if(GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster)) + { + if (_GetChangedElementalType(iSpellId, oCaster) == "Fire" && GetLocalInt(oCaster, "ToB_JPM_FireB")) + iReturnLevel += 3; + iReturnLevel += GetLocalInt(oCaster, "ToB_JPM_MystP"); + } + // Abjurant Champion uses its Base AB as Caster Level if higher + if(GetHasFeat(FEAT_MARTIAL_ARCANIST)) + { + //Get the caster's base AB + int nBaseAB = GetBaseAttackBonus(oCaster); + if(nBaseAB > iReturnLevel) + { + iReturnLevel = nBaseAB; + } + } + if (DEBUG) DoDebug("PRCGetCasterLevel: total arcane caster level = "+IntToString(iReturnLevel)); + } + + // no item casting and divine caster? + else if(!iReturnLevel && GetIsDivineClass(iCastingClass, oCaster)) + { + iReturnLevel = GetLevelByClass(iCastingClass, oCaster) / GetCasterLevelModifier(iCastingClass); + if(GetPrimaryDivineClass(oCaster) == iCastingClass) + iReturnLevel += GetDivinePRCLevels(oCaster); + + iReturnLevel += PracticedSpellcasting(oCaster, iCastingClass, iReturnLevel); + + iReturnLevel += TrueNecromancy(oCaster, iSpellId, "DIVINE", nSpellSchool) + + ShadowWeave(oCaster, iSpellId, nSpellSchool) + + FireAdept(oCaster, iSpellId) + + StormMagic(oCaster) + + CormanthyranMoonMagic(oCaster) + + Nentyar(oCaster, iCastingClass) + + DomainPower(oCaster, iSpellId, nSpellSchool) + + DriftMagic(oCaster, iSpellId) + + AirAdept(oCaster, iSpellId) + + WaterAdept(oCaster, iSpellId) + + Soulcaster(oCaster, iSpellId) + + ShieldDwarfWarder(oCaster) + + DarkSpeech(oCaster) + + DeathKnell(oCaster) + + UrPriestCL(oCaster, iCastingClass) + + BlighterCL(oCaster, iCastingClass) + + ReserveFeatCL(oCaster, iSpellId); + + if (DEBUG) DoDebug("PRCGetCasterLevel: total divine caster level = "+IntToString(iReturnLevel)); + } + + //at this point it must be a SLA or similar + if(!iReturnLevel) + { + iReturnLevel = GetCasterLevel(oCaster); + if (DEBUG) DoDebug("PRCGetCasterLevel: bioware caster level = "+IntToString(iReturnLevel)); + } + + iReturnLevel -= GetLocalInt(oCaster, "WoLCasterPenalty"); + if (GetLocalInt(oCaster, "EldritchDisrupt")) + iReturnLevel -= 4; + if (GetLocalInt(oCaster, "EldritchVortex")) + iReturnLevel -= 4; + if (DEBUG) DoDebug("PRCGetCasterLevel: caster level pre adjust = "+IntToString(iReturnLevel)); + iReturnLevel += nAdjust; + if (DEBUG) DoDebug("PRCGetCasterLevel: total caster level = "+IntToString(iReturnLevel)); + + return iReturnLevel; +} + +int PRCGetLastSpellCastClass(object oCaster = OBJECT_SELF) +{ + // note that a barbarian has a class type constant of zero. So nClass == 0 could in principle mean + // that a barbarian cast the spell, However, barbarians cannot cast spells, so it doesn't really matter + // beware of Barbarians with UMD, though. Also watch out for spell like abilities + // might have to provide a fix for these (for instance: if(nClass == -1) nClass = 0; + int nClass = GetLocalInt(oCaster, PRC_CASTERCLASS_OVERRIDE); + if(nClass) + { + if(DEBUG) DoDebug("PRCGetLastSpellCastClass: found override caster class = "+IntToString(nClass)+", original class = "+IntToString(GetLastSpellCastClass())); + return nClass; + } + nClass = GetLastSpellCastClass(); + //if casting class is invalid and the spell was not cast form an item it was probably cast from the new spellbook + int NSB_Class = GetLocalInt(oCaster, "NSB_Class"); + if(nClass == CLASS_TYPE_INVALID && GetSpellCastItem() == OBJECT_INVALID && NSB_Class) + nClass = NSB_Class; + + if(DEBUG) DoDebug("PRCGetLastSpellCastClass: returning caster class = "+IntToString(nClass)+" NSB_Class = "+IntToString(NSB_Class)); + return nClass; +} + +int GetIsArcaneClass(int nClass, object oCaster = OBJECT_SELF) +{ + return nClass == CLASS_TYPE_ASSASSIN + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_BEGUILER + || nClass == CLASS_TYPE_CELEBRANT_SHARESS + || nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK + || nClass == CLASS_TYPE_DREAD_NECROMANCER + || nClass == CLASS_TYPE_DUSKBLADE + || nClass == CLASS_TYPE_HARPER + || nClass == CLASS_TYPE_HEXBLADE + || nClass == CLASS_TYPE_KNIGHT_WEAVE + || nClass == CLASS_TYPE_SHADOWLORD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_SUBLIME_CHORD + || nClass == CLASS_TYPE_SUEL_ARCHANAMACH + || nClass == CLASS_TYPE_WARMAGE + || nClass == CLASS_TYPE_WIZARD + || (nClass == CLASS_TYPE_SHAPECHANGER + && GetRacialType(oCaster) == RACIAL_TYPE_ARANEA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_OUTSIDER + && GetRacialType(oCaster) == RACIAL_TYPE_RAKSHASA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_ABERRATION + && GetRacialType(oCaster) == RACIAL_TYPE_DRIDER + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_ARKAMOI + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_REDSPAWN_ARCANISS + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_MARRUTACT + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_FEY + && GetRacialType(oCaster) == RACIAL_TYPE_GLOURA + && !GetLevelByClass(CLASS_TYPE_BARD)); +} + +int GetIsDivineClass(int nClass, object oCaster = OBJECT_SELF) +{ + return nClass == CLASS_TYPE_ARCHIVIST + || nClass == CLASS_TYPE_BLACKGUARD + || nClass == CLASS_TYPE_BLIGHTER + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_HEALER + || nClass == CLASS_TYPE_JUSTICEWW + || nClass == CLASS_TYPE_KNIGHT_CHALICE + || nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE + || nClass == CLASS_TYPE_NENTYAR_HUNTER + || nClass == CLASS_TYPE_OCULAR + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER + || nClass == CLASS_TYPE_SHAMAN + || nClass == CLASS_TYPE_SLAYER_OF_DOMIEL + || nClass == CLASS_TYPE_SOHEI + || nClass == CLASS_TYPE_SOLDIER_OF_LIGHT + || nClass == CLASS_TYPE_UR_PRIEST + || nClass == CLASS_TYPE_VASSAL + || nClass == CLASS_TYPE_VIGILANT; +} + +int GetArcanePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) +{ + int nArcane; + int nOozeMLevel = GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + int nUM = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + int nFirstClass = GetClassByPosition(1, oCaster); + int nSecondClass = GetClassByPosition(2, oCaster); + int nThirdClass = GetClassByPosition(3, oCaster); + int nFourthClass = GetClassByPosition(4, oCaster); + int nFifthClass = GetClassByPosition(5, oCaster); + int nSixthClass = GetClassByPosition(6, oCaster); + int nSeventhClass = GetClassByPosition(7, oCaster); + int nEightClass = GetClassByPosition(8, oCaster); + + if (GetFirstArcaneClassPosition(oCaster)) nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster) + + GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster) + + GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster) + + GetLevelByClass(CLASS_TYPE_ARCANE_HIEROPHANT, oCaster) + + GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster) + + GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster) + + GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster) + + GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster) + + GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster) + + GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT,oCaster) + + GetLevelByClass(CLASS_TYPE_FMM, oCaster) + + GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) + + GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster) + + GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster) + + GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster) + + GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster) + + GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster) + + GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster) + + GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster) + + GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster) + + GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster) + + GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster) + + GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster) + + GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster) + + GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster) + + GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster) + + GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster) + + + (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) + nArcane += nClass - 3 + d6(); + + //The following changes are to prevent a mage/invoker from gaining bonus caster levels in both base classes. + + if(GetLocalInt(oCaster, "INV_Caster") == 1 || + (!GetLevelByClass(CLASS_TYPE_WARLOCK, oCaster) && !GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCaster))) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2 + + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster) + + GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + + /* oozemaster levels count towards arcane caster level if: + * + * first class slot is arcane OR + * first class slot is NOT divine AND second class slot is arcane OR + * first AND second class slot is NOT divine AND 3rd class slot is arcane + */ + if (nOozeMLevel) //:: [PRC .35] This needs marker feats. + { + if (GetIsArcaneClass(nFirstClass, oCaster) + || (!GetIsDivineClass(nFirstClass, oCaster) + && GetIsArcaneClass(nSecondClass, oCaster)) + || (!GetIsDivineClass(nFirstClass, oCaster) + && !GetIsDivineClass(nSecondClass, oCaster) + && GetIsArcaneClass(nThirdClass, oCaster))) + nArcane += nOozeMLevel / 2; + } + + if (nUM) + { + int nBoost = nUM - 1; //Prep caster always loses a level on first level of the class + if (nUM >= 4) nBoost = nUM - 2; + if (nUM >= 7) nBoost = nUM - 3; + nArcane += nBoost; + + if (nCastingClass == CLASS_TYPE_SORCERER) + { + int nBoost = 1; //Sorcerer gets the lost levels back + if (nUM >= 4) nBoost = 2; + if (nUM >= 7) nBoost = 3; + nArcane += nBoost; + } + } + if(GetLevelByClass(CLASS_TYPE_SORCERER, oCaster)) + { + int nRace = GetRacialType(oCaster); + + //includes RHD HD as sorc + //if they have sorcerer levels, then it counts as a prestige class + //otherwise its used instead of sorc levels + if(nRace == RACIAL_TYPE_ARANEA) + nArcane += GetLevelByClass(CLASS_TYPE_SHAPECHANGER); + if(nRace == RACIAL_TYPE_RAKSHASA) + nArcane += GetLevelByClass(CLASS_TYPE_OUTSIDER); + if(nRace == RACIAL_TYPE_DRIDER) + nArcane += GetLevelByClass(CLASS_TYPE_ABERRATION); + if(nRace == RACIAL_TYPE_ARKAMOI) + nArcane += GetLevelByClass(CLASS_TYPE_MONSTROUS); + if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + nArcane += GetLevelByClass(CLASS_TYPE_MONSTROUS)*3/4; + if(nRace == RACIAL_TYPE_MARRUTACT) + nArcane += (GetLevelByClass(CLASS_TYPE_MONSTROUS)*6/7)-1; + } + + if(GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + int nRace = GetRacialType(oCaster); + + //includes RHD HD as bard + //if they have bard levels, then it counts as a prestige class + //otherwise its used instead of bard levels + if(nRace == RACIAL_TYPE_GLOURA) + nArcane += GetLevelByClass(CLASS_TYPE_FEY); + } + + return nArcane; +} + +int GetDivinePRCLevels(object oCaster) +{ + int nDivine; + int nOozeMLevel = GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + int nFirstClass = GetClassByPosition(1, oCaster); + int nSecondClass = GetClassByPosition(2, oCaster); + int nThirdClass = GetClassByPosition(3, oCaster); + int nFourthClass = GetClassByPosition(4, oCaster); + int nFifthClass = GetClassByPosition(5, oCaster); + int nSixthClass = GetClassByPosition(6, oCaster); + int nSeventhClass = GetClassByPosition(7, oCaster); + int nEightClass = GetClassByPosition(8, oCaster); + + // This section accounts for full progression classes + nDivine += GetLevelByClass(CLASS_TYPE_ARCANE_HIEROPHANT, oCaster) + + GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster) + + GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster) + + GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster) + + GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster) + + GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster) + + GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster) + + GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster) + + GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster) + + GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster) + + GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster) + + GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster) + + GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster) + + GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster) + + GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster) + + GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster) + + GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster) + + GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster) + + GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster) + + GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster) + + GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster) + + + (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2 + + + (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if (!GetHasFeat(FEAT_SF_CODE, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if (nOozeMLevel) //:: [PRC .35] This needs marker feats. + { + if (GetIsDivineClass(nFirstClass, oCaster) + || (!GetIsArcaneClass(nFirstClass, oCaster) + && GetIsDivineClass(nSecondClass, oCaster)) + || (!GetIsArcaneClass(nFirstClass, oCaster) + && !GetIsArcaneClass(nSecondClass, oCaster) + && GetIsDivineClass(nThirdClass, oCaster))) + nDivine += nOozeMLevel / 2; + } + + return nDivine; +} + +int GetFirstArcaneClassPosition(object oCaster = OBJECT_SELF) +{ + int i; + for(i = 1; i < 9; i++) + { + if(GetIsArcaneClass(GetClassByPosition(i, oCaster), oCaster)) + return i; + } + + return 0; +} + +int GetFirstDivineClassPosition(object oCaster = OBJECT_SELF) +{ + int i; + for(i = 1; i < 9; i++) + { + if(GetIsDivineClass(GetClassByPosition(i, oCaster), oCaster)) + return i; + } + + return 0; +} + +int GetPrimaryArcaneClass(object oCaster = OBJECT_SELF) +{ + int nClass; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int iArcanePos = GetFirstArcaneClassPosition(oCaster); + if (!iArcanePos) return CLASS_TYPE_INVALID; // no arcane casting class + + nClass = GetClassByPosition(iArcanePos, oCaster); + } + else + { + int i, nClassTmp, nClassLvl; + for(i = 1; i < 9; i++) + { + nClassTmp = GetClassByPosition(i, oCaster); + if(GetIsArcaneClass(nClassTmp, oCaster) && nClassTmp != CLASS_TYPE_SUBLIME_CHORD) + { + if(GetLevelByClass(nClassTmp, oCaster) > nClassLvl) + { + nClass = nClassTmp; + nClassLvl = GetLevelByClass(nClass, oCaster); + } + } + } + if(!nClassLvl) + return CLASS_TYPE_INVALID; + } + + //raks, Arkamoi, driders and dragons cast as sorcs + if(nClass == CLASS_TYPE_OUTSIDER + || nClass == CLASS_TYPE_SHAPECHANGER + || nClass == CLASS_TYPE_ABERRATION + || nClass == CLASS_TYPE_DRAGON + || nClass == CLASS_TYPE_MONSTROUS) + nClass = CLASS_TYPE_SORCERER; + + if(nClass == CLASS_TYPE_FEY) + nClass = CLASS_TYPE_BARD; + + return nClass; +} + +int GetPrimaryDivineClass(object oCaster = OBJECT_SELF) +{ + int nClass; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int iDivinePos = GetFirstDivineClassPosition(oCaster); + if (!iDivinePos) return CLASS_TYPE_INVALID; // no Divine casting class + + nClass = GetClassByPosition(iDivinePos, oCaster); + } + else + { + int i, nClassTmp, nClassLvl; + for(i = 1; i < 9; i++) + { + nClassTmp = GetClassByPosition(i, oCaster); + if(GetIsDivineClass(nClassTmp, oCaster)) + { + if(GetLevelByClass(nClassTmp, oCaster) > nClassLvl) + { + nClass = nClassTmp; + nClassLvl = GetLevelByClass(nClass, oCaster); + } + } + } + if(!nClassLvl) + return CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetPrimarySpellcastingClass(object oCaster = OBJECT_SELF) +{ + int bFirst = GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE); + int nClass; + + int i, nClassTmp, nClassLvl; + for(i = 1; i < 9; i++) + { + nClassTmp = GetClassByPosition(i, oCaster); + if(GetIsArcaneClass(nClassTmp, oCaster) + || GetIsDivineClass(nClassTmp, oCaster) + && nClassTmp != CLASS_TYPE_SUBLIME_CHORD) + { + if(bFirst) + { + return nClass; + } + else if(GetLevelByClass(nClassTmp, oCaster) > nClassLvl) + { + nClass = nClassTmp; + nClassLvl = GetLevelByClass(nClass, oCaster); + } + } + } + if(!nClassLvl) + return CLASS_TYPE_INVALID; + + return nClass; +} + +int PracticedSpellcasting(object oCaster, int iCastingClass, int iCastingLevels) +{ + int nFeat; + int iAdjustment = GetHitDice(oCaster) - iCastingLevels; + if (iAdjustment > 4) iAdjustment = 4; + if (iAdjustment < 0) iAdjustment = 0; + + switch(iCastingClass) + { + case CLASS_TYPE_BARD: nFeat = FEAT_PRACTICED_SPELLCASTER_BARD; break; + case CLASS_TYPE_SORCERER: nFeat = FEAT_PRACTICED_SPELLCASTER_SORCERER; break; + case CLASS_TYPE_WIZARD: nFeat = FEAT_PRACTICED_SPELLCASTER_WIZARD; break; + case CLASS_TYPE_CLERIC: nFeat = FEAT_PRACTICED_SPELLCASTER_CLERIC; break; + case CLASS_TYPE_DRUID: nFeat = FEAT_PRACTICED_SPELLCASTER_DRUID; break; + case CLASS_TYPE_PALADIN: nFeat = FEAT_PRACTICED_SPELLCASTER_PALADIN; break; + case CLASS_TYPE_RANGER: nFeat = FEAT_PRACTICED_SPELLCASTER_RANGER; break; + case CLASS_TYPE_ASSASSIN: nFeat = FEAT_PRACTICED_SPELLCASTER_ASSASSIN; break; + case CLASS_TYPE_BLACKGUARD: nFeat = FEAT_PRACTICED_SPELLCASTER_BLACKGUARD; break; + case CLASS_TYPE_OCULAR: nFeat = FEAT_PRACTICED_SPELLCASTER_OCULAR; break; + case CLASS_TYPE_HEXBLADE: nFeat = FEAT_PRACTICED_SPELLCASTER_HEXBLADE; break; + case CLASS_TYPE_DUSKBLADE: nFeat = FEAT_PRACTICED_SPELLCASTER_DUSKBLADE; break; + case CLASS_TYPE_HEALER: nFeat = FEAT_PRACTICED_SPELLCASTER_HEALER; break; + case CLASS_TYPE_KNIGHT_CHALICE: nFeat = FEAT_PRACTICED_SPELLCASTER_KNIGHT_CHALICE; break; + case CLASS_TYPE_NENTYAR_HUNTER: nFeat = FEAT_PRACTICED_SPELLCASTER_NENTYAR; break; + case CLASS_TYPE_VASSAL: nFeat = FEAT_PRACTICED_SPELLCASTER_VASSAL; break; + case CLASS_TYPE_UR_PRIEST: nFeat = FEAT_PRACTICED_SPELLCASTER_UR_PRIEST; break; + case CLASS_TYPE_SOLDIER_OF_LIGHT: nFeat = FEAT_PRACTICED_SPELLCASTER_SOLDIER_OF_LIGHT; break; + case CLASS_TYPE_SHADOWLORD: nFeat = FEAT_PRACTICED_SPELLCASTER_SHADOWLORD; break; + case CLASS_TYPE_JUSTICEWW: nFeat = FEAT_PRACTICED_SPELLCASTER_JUSTICEWW; break; + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: nFeat = FEAT_PRACTICED_SPELLCASTER_KNIGHT_MIDDLECIRCLE; break; + case CLASS_TYPE_SHAMAN: nFeat = FEAT_PRACTICED_SPELLCASTER_SHAMAN; break; + case CLASS_TYPE_SLAYER_OF_DOMIEL: nFeat = FEAT_PRACTICED_SPELLCASTER_SLAYER_OF_DOMIEL; break; + case CLASS_TYPE_SUEL_ARCHANAMACH: nFeat = FEAT_PRACTICED_SPELLCASTER_SUEL_ARCHANAMACH; break; + case CLASS_TYPE_FAVOURED_SOUL: nFeat = FEAT_PRACTICED_SPELLCASTER_FAVOURED_SOUL; break; + case CLASS_TYPE_SOHEI: nFeat = FEAT_PRACTICED_SPELLCASTER_SOHEI; break; + case CLASS_TYPE_CELEBRANT_SHARESS: nFeat = FEAT_PRACTICED_SPELLCASTER_CELEBRANT_SHARESS; break; + case CLASS_TYPE_WARMAGE: nFeat = FEAT_PRACTICED_SPELLCASTER_WARMAGE; break; + case CLASS_TYPE_DREAD_NECROMANCER: nFeat = FEAT_PRACTICED_SPELLCASTER_DREAD_NECROMANCER; break; + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: nFeat = FEAT_PRACTICED_SPELLCASTER_CULTIST; break; + case CLASS_TYPE_ARCHIVIST: nFeat = FEAT_PRACTICED_SPELLCASTER_ARCHIVIST; break; + case CLASS_TYPE_BEGUILER: nFeat = FEAT_PRACTICED_SPELLCASTER_BEGUILER; break; + case CLASS_TYPE_BLIGHTER: nFeat = FEAT_PRACTICED_SPELLCASTER_BLIGHTER; break; + case CLASS_TYPE_HARPER: nFeat = FEAT_PRACTICED_SPELLCASTER_HARPER; break; + default: return 0; + } + + if(GetHasFeat(nFeat, oCaster)) + return iAdjustment; + + return 0; +} + +int GetSpellSchool(int iSpellId) +{ + string sSpellSchool = Get2DACache("spells", "School", iSpellId);//lookup_spell_school(iSpellId); + + if (sSpellSchool == "A") return SPELL_SCHOOL_ABJURATION; + else if (sSpellSchool == "C") return SPELL_SCHOOL_CONJURATION; + else if (sSpellSchool == "D") return SPELL_SCHOOL_DIVINATION; + else if (sSpellSchool == "E") return SPELL_SCHOOL_ENCHANTMENT; + else if (sSpellSchool == "V") return SPELL_SCHOOL_EVOCATION; + else if (sSpellSchool == "I") return SPELL_SCHOOL_ILLUSION; + else if (sSpellSchool == "N") return SPELL_SCHOOL_NECROMANCY; + else if (sSpellSchool == "T") return SPELL_SCHOOL_TRANSMUTATION; + else return SPELL_SCHOOL_GENERAL; + + return -1; +} + +/*int GetIsHealingSpell(int nSpellId) +{ + if ( nSpellId == SPELL_CURE_CRITICAL_WOUNDS + || nSpellId == SPELL_CURE_LIGHT_WOUNDS + || nSpellId == SPELL_CURE_MINOR_WOUNDS + || nSpellId == SPELL_CURE_MODERATE_WOUNDS + || nSpellId == SPELL_CURE_SERIOUS_WOUNDS + || nSpellId == SPELL_GREATER_RESTORATION + || nSpellId == SPELL_HEAL + || nSpellId == SPELL_HEALING_CIRCLE + || nSpellId == SPELL_MASS_HEAL + || nSpellId == SPELL_MONSTROUS_REGENERATION + || nSpellId == SPELL_REGENERATE + //End of stock NWN spells + || nSpellId == SPELL_MASS_CURE_LIGHT + || nSpellId == SPELL_MASS_CURE_MODERATE + || nSpellId == SPELL_MASS_CURE_SERIOUS + || nSpellId == SPELL_MASS_CURE_CRITICAL + || nSpellId == SPELL_PANACEA + ) + return TRUE; + + return FALSE; +}*/ + +int ArchmageSpellPower(object oCaster) +{ + if(GetHasFeat(FEAT_SPELL_POWER_V, oCaster)) + return 5; + if(GetHasFeat(FEAT_SPELL_POWER_IV, oCaster)) + return 4; + if(GetHasFeat(FEAT_SPELL_POWER_III, oCaster)) + return 3; + if(GetHasFeat(FEAT_SPELL_POWER_II, oCaster)) + return 2; + if(GetHasFeat(FEAT_SPELL_POWER_I, oCaster)) + return 1; + + return 0; +} + +int ShadowWeave(object oCaster, int iSpellID, int nSpellSchool = -1) +{ + if(!GetHasFeat(FEAT_SHADOWWEAVE,oCaster)) + return 0; + + if (nSpellSchool == -1) + nSpellSchool = GetSpellSchool(iSpellID); + + // Bonus for spells of Enhancement, Necromancy and Illusion schools and spells with Darkness descriptor + if(nSpellSchool == SPELL_SCHOOL_ENCHANTMENT + || nSpellSchool == SPELL_SCHOOL_NECROMANCY + || nSpellSchool == SPELL_SCHOOL_ILLUSION + || GetHasDescriptor(iSpellID, DESCRIPTOR_DARKNESS)) + { + return 1; + } + // Penalty to spells of Evocation and Transmutation schools, except for those with Darkness descriptor + else if(nSpellSchool == SPELL_SCHOOL_EVOCATION + || nSpellSchool == SPELL_SCHOOL_TRANSMUTATION) + { + return -1; + } + + return 0; +} + +int AirAdept(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_AIR)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_AIR_MEPHLING, oCaster)) + nBoost += 1; + + return nBoost; +} + +int WaterAdept(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_WATER)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_WATER_MEPHLING, oCaster)) + nBoost += 1; + + return nBoost; +} + +int FireAdept(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_FIRE)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_FIRE_ADEPT, oCaster)) + nBoost += 1; + + if(GetHasFeat(FEAT_FIRE_MEPHLING, oCaster)) + nBoost += 1; + + if(GetHasFeat(FEAT_BLOODLINE_OF_FIRE, oCaster)) + nBoost += 2; + + if(GetRacialType(oCaster) == RACIAL_TYPE_REDSPAWN_ARCANISS) + nBoost += 2; + + return nBoost; +} + +int DriftMagic(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_EARTH)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_DRIFT_MAGIC, oCaster)) + nBoost += 1; + + if(GetHasFeat(FEAT_EARTH_MEPHLING, oCaster)) + nBoost += 1; + + return nBoost; +} +/*int DriftMagic(object oCaster, int iSpellID) +{ + if(GetHasDescriptor(iSpellID, DESCRIPTOR_EARTH) && GetHasFeat(FEAT_DRIFT_MAGIC, oCaster)) + return 1; + + else if(GetHasDescriptor(iSpellID, DESCRIPTOR_EARTH) && GetHasFeat(FEAT_EARTH_MEPHLING, oCaster)) + return 1; + + return 0; +}*/ + +int Soulcaster(object oCaster, int iSpellID) +{ + if(GetLocalInt(oCaster, "SpellEssentia"+IntToString(iSpellID))) + { + int nReturn = GetLocalInt(oCaster, "SpellEssentia"+IntToString(iSpellID)); + DelayCommand(1.0, DeleteLocalInt(oCaster, "SpellEssentia"+IntToString(iSpellID))); + return nReturn; + } + + return 0; +} + +int StormMagic(object oCaster) +{ + if(!GetHasFeat(FEAT_STORMMAGIC, oCaster) && !GetHasFeat(FEAT_FROZEN_MAGIC, oCaster)) return 0; + + int nBoost = 0; + + int nWeather = GetWeather(GetArea(oCaster)); + if(nWeather == WEATHER_RAIN && GetHasFeat(FEAT_STORMMAGIC, oCaster)) + nBoost += 1; + if(nWeather == WEATHER_SNOW && GetHasFeat(FEAT_STORMMAGIC, oCaster)) + nBoost += 1; + if(nWeather == WEATHER_SNOW && GetHasFeat(FEAT_FROZEN_MAGIC, oCaster)) + nBoost += 2; + if (GetLocalInt(GetArea(oCaster),"FrozenMagic") && GetHasFeat(FEAT_FROZEN_MAGIC, oCaster) && nWeather != WEATHER_SNOW) + nBoost += 2; + + return nBoost; +} + +int DivinationPower(object oCaster, int nSpellSchool) +{ + int nClass = GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + if(!nClass) return 0; + + int nBoost = (nClass + 2) / 3; + + if (nSpellSchool != SPELL_SCHOOL_DIVINATION) + nBoost *= -1; // Negative if it's not a divination spell + + return nBoost; +} + +int CormanthyranMoonMagic(object oCaster) +{ + if (!GetHasFeat(FEAT_CORMANTHYRAN_MOON_MAGIC, oCaster)) return 0; + + object oArea = GetArea(oCaster); + + // The moon must be visible. Thus, outdoors, at night, with no rain. + if (GetWeather(oArea) != WEATHER_RAIN && GetWeather(oArea) != WEATHER_SNOW && + GetIsNight() && !GetIsAreaInterior(oArea)) + { + return 2; + } + return 0; +} + +int Nentyar(object oCaster, int nCastingClass) +{ + if (nCastingClass == CLASS_TYPE_NENTYAR_HUNTER) + { + int nBonus = GetLevelByClass(CLASS_TYPE_DRUID, oCaster) + GetLevelByClass(CLASS_TYPE_CLERIC, oCaster) + GetLevelByClass(CLASS_TYPE_RANGER, oCaster)/2; + return nBonus; + } + return 0; +} + +int ShieldDwarfWarder(object oCaster) +{ + if (!GetHasFeat(FEAT_SHIELD_DWARF_WARDER, oCaster)) return 0; + + object oTarget = PRCGetSpellTargetObject(); + // If it's an item that grants an AC bonus + int nBase = GetBaseItemType(oTarget); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD || nBase == BASE_ITEM_ARMOR) + return 1; + + return 0; +} + +int DarkSpeech(object oCaster) +{ + if (GetHasSpellEffect(SPELL_DARK_SPEECH_POWER, oCaster)) + { + ExecuteScript("prc_dark_power", oCaster); + return 1; + } + return 0; +} + +int DomainPower(object oCaster, int nSpellID, int nSpellSchool = -1) +{ + int nBonus = 0; + if (nSpellSchool == -1) + nSpellSchool = GetSpellSchool(nSpellID); + + // Boosts Caster level with the Illusion school by 1 + if (nSpellSchool == SPELL_SCHOOL_ILLUSION && GetHasFeat(FEAT_DOMAIN_POWER_GNOME, oCaster)) + nBonus += 1; + + // Boosts Caster level with the Illusion school by 1 + if (nSpellSchool == SPELL_SCHOOL_ILLUSION && GetHasFeat(FEAT_DOMAIN_POWER_ILLUSION, oCaster)) + nBonus += 1; + + // Boosts Caster level with healing spells + if (GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING) && GetHasFeat(FEAT_HEALING_DOMAIN_POWER, oCaster)) + nBonus += 1; + + // Boosts Caster level with the Divination school by 1 + if (nSpellSchool == SPELL_SCHOOL_DIVINATION && GetHasFeat(FEAT_KNOWLEDGE_DOMAIN_POWER, oCaster)) + nBonus += 1; + + // Boosts Caster level with evil spells by 1 + if (GetHasDescriptor(nSpellID, DESCRIPTOR_EVIL) && GetHasFeat(FEAT_EVIL_DOMAIN_POWER, oCaster)) + nBonus += 1; + + // Boosts Caster level with good spells by 1 + if (GetHasDescriptor(nSpellID, DESCRIPTOR_GOOD) && GetHasFeat(FEAT_GOOD_DOMAIN_POWER, oCaster)) + nBonus += 1; + + return nBonus; +} + +int TherapeuticMantle(object oCaster, int nSpellID) +{ + int nReturn; + // Boosts Caster level with healing spells + if (GetIsMeldBound(oCaster, MELD_THERAPEUTIC_MANTLE) == CHAKRA_SHOULDERS && GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) + nReturn = GetEssentiaInvested(oCaster, MELD_THERAPEUTIC_MANTLE); + + return nReturn; +} + +int DeathKnell(object oCaster) +{ + // If you do have the spell effect, return a +1 bonus to caster level + return GetHasSpellEffect(SPELL_DEATH_KNELL, oCaster); +} + +int DraconicPower(object oCaster = OBJECT_SELF) +{ + return GetHasFeat(FEAT_DRACONIC_POWER, oCaster); +} + +int SongOfArcanePower(object oCaster = OBJECT_SELF) +{ + int nBonus = GetLocalInt(oCaster, "SongOfArcanePower"); + DeleteLocalInt(oCaster, "SongOfArcanePower"); + + int nLevel = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + nBonus += (nLevel + 2)/3; + + if(nBonus) + return nBonus; + + return 0; +} + +int TrueNecromancy(object oCaster, int iSpellID, string sType, int nSpellSchool = -1) +{ + int iTNLevel = GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + if (!iTNLevel) + return 0; + if (nSpellSchool == -1) + nSpellSchool = GetSpellSchool(iSpellID); + if (nSpellSchool != SPELL_SCHOOL_NECROMANCY) + return 0; + + if (sType == "ARCANE") + return GetLevelByTypeDivine(oCaster); // TN and arcane levels already added. + + if (sType == "DIVINE") + return GetLevelByTypeArcane(oCaster); + + return 0; +} + +int UrPriestCL(object oCaster, int nCastingClass) +{ + // Add 1/2 levels in all other casting classes except cleric + if (nCastingClass == CLASS_TYPE_UR_PRIEST) + { + int nTotal = 0; + int iFirstDivine = GetPrimaryDivineClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + /*if (iClass1 == CLASS_TYPE_PALADIN || iClass1 == CLASS_TYPE_RANGER) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_PALADIN || iClass2 == CLASS_TYPE_RANGER) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_PALADIN || iClass3 == CLASS_TYPE_RANGER) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + + if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster); + if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster); + if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + + if (!GetIsDivineClass(iClass1, oCaster) || iClass1 == CLASS_TYPE_UR_PRIEST) iClass1Lev = 0; + if (!GetIsDivineClass(iClass2, oCaster) || iClass2 == CLASS_TYPE_UR_PRIEST) iClass2Lev = 0; + if (!GetIsDivineClass(iClass3, oCaster) || iClass3 == CLASS_TYPE_UR_PRIEST) iClass3Lev = 0; + + nTotal += iClass1Lev + iClass2Lev + iClass3Lev; + if (DEBUG) DoDebug("UrPriestCL Divine - iClass1Lev "+IntToString(iClass1Lev)+" iClass2Lev "+IntToString(iClass2Lev)+" iClass3Lev "+IntToString(iClass3Lev));*/ + + int iFirstArcane = GetPrimaryArcaneClass(oCaster); + iClass1 = GetClassByPosition(1, oCaster); + iClass2 = GetClassByPosition(2, oCaster); + iClass3 = GetClassByPosition(3, oCaster); + iClass4 = GetClassByPosition(4, oCaster); + iClass5 = GetClassByPosition(5, oCaster); + iClass6 = GetClassByPosition(6, oCaster); + iClass7 = GetClassByPosition(7, oCaster); + iClass8 = GetClassByPosition(8, oCaster); + + iClass1Lev = GetLevelByPosition(1, oCaster); + iClass2Lev = GetLevelByPosition(2, oCaster); + iClass3Lev = GetLevelByPosition(3, oCaster); + iClass4Lev = GetLevelByPosition(4, oCaster); + iClass5Lev = GetLevelByPosition(5, oCaster); + iClass6Lev = GetLevelByPosition(6, oCaster); + iClass7Lev = GetLevelByPosition(7, oCaster); + iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster); + if (iClass4 == iFirstArcane) iClass4Lev += GetArcanePRCLevels(oCaster); + if (iClass5 == iFirstArcane) iClass5Lev += GetArcanePRCLevels(oCaster); + if (iClass6 == iFirstArcane) iClass6Lev += GetArcanePRCLevels(oCaster); + if (iClass7 == iFirstArcane) iClass7Lev += GetArcanePRCLevels(oCaster); + if (iClass8 == iFirstArcane) iClass8Lev += GetArcanePRCLevels(oCaster); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass5Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass6Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass7Lev); + + if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0; + + + nTotal += iClass1Lev + iClass2Lev + iClass3Lev + iClass4Lev + iClass5Lev + iClass6Lev + iClass7Lev + iClass8Lev; + + if (DEBUG) DoDebug("UrPriestCL Arcane - iClass1Lev "+IntToString(iClass1Lev)+" iClass2Lev " + +IntToString(iClass2Lev)+" iClass3Lev " + +IntToString(iClass3Lev)+" iClass4Lev " + +IntToString(iClass4Lev)+" iClass5Lev " + +IntToString(iClass5Lev)+" iClass6Lev " + +IntToString(iClass6Lev)+" iClass7Lev " + +IntToString(iClass7Lev)+" iClass8Lev " + +IntToString(iClass8Lev)); + + if (DEBUG) DoDebug("UrPriestCL Total - nTotal "+IntToString(nTotal)); + return nTotal/2; + } + return 0; +} + +int BlighterCL(object oCaster, int nCastingClass) +{ + if (nCastingClass == CLASS_TYPE_BLIGHTER) + { + int nBonus = GetLevelByClass(CLASS_TYPE_DRUID, oCaster); + return nBonus; + } + return 0; +} + +int ReserveFeatCL(object oCaster, int iSpellId) +{ + int nSpellSchool = GetSpellSchool(iSpellId); + int nCLBonus = 0; + + if (GetLocalInt(oCaster, "ReserveFeatsRunning") == TRUE) + { + if (GetHasDescriptor(iSpellId, DESCRIPTOR_ACID) && GetHasFeat(FEAT_ACIDIC_SPLATTER, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_FIRE) && GetHasFeat(FEAT_FIERY_BURST, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_COLD) && GetHasFeat(FEAT_WINTERS_BLAST, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_ELECTRICITY) && GetHasFeat(FEAT_STORM_BOLT, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_SONIC) && GetHasFeat(FEAT_CLAP_OF_THUNDER, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_HEALING) && GetHasFeat(FEAT_TOUCH_OF_HEALING, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_TELEPORTATION) && GetHasFeat(FEAT_DIMENSIONAL_JAUNT, oCaster)) nCLBonus += 1; + if (nSpellSchool == SPELL_SCHOOL_ABJURATION && GetHasFeat(FEAT_MYSTIC_BACKLASH, oCaster)) nCLBonus += 1; + if (nSpellSchool == SPELL_SCHOOL_NECROMANCY && GetHasFeat(FEAT_SICKENING_GRASP, oCaster)) nCLBonus += 1; + if (GetIsFromDomain(iSpellId, "wardom") && GetHasFeat(FEAT_HOLY_WARRIOR, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_EARTH) && GetHasFeat(FEAT_CLUTCH_OF_EARTH, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_AIR) && GetHasFeat(FEAT_BORNE_ALOFT, oCaster)) nCLBonus += 1; + if ((nSpellSchool == SPELL_SCHOOL_ABJURATION) && GetHasFeat(FEAT_PROTECTIVE_WARD, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_DARKNESS) && GetHasFeat(FEAT_SHADOW_VEIL, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_LIGHT) && GetHasFeat(FEAT_SUNLIGHT_EYES, oCaster)) nCLBonus += 1; + if ((nSpellSchool == SPELL_SCHOOL_ENCHANTMENT) && GetHasFeat(FEAT_TOUCH_OF_DISTRACTION, oCaster)) nCLBonus += 1; + if (GetIsFromDomain(iSpellId, "dethdom") && GetHasFeat(FEAT_CHARNEL_MIASMA, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_WATER) && GetHasFeat(FEAT_DROWNING_GLANCE, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_FORCE) && GetHasFeat(FEAT_INVISIBLE_NEEDLE, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_SUMMONING) && GetHasFeat(FEAT_SUMMON_ELEMENTAL, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_SUMMONING) && GetHasFeat(FEAT_DIMENSIONAL_REACH, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_AIR) && GetHasFeat(FEAT_HURRICANE_BREATH, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_POLYMORPH) && GetHasFeat(FEAT_MINOR_SHAPESHIFT, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_GLAMER) && GetHasFeat(FEAT_FACECHANGER, oCaster)) nCLBonus += 1; + return nCLBonus; + } + else return 0; +} + +int GetLevelByTypeArcane(object oCaster = OBJECT_SELF) +{ + int iFirstArcane = GetPrimaryArcaneClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(8, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster); + if (iClass4 == iFirstArcane) iClass4Lev += GetArcanePRCLevels(oCaster); + if (iClass5 == iFirstArcane) iClass5Lev += GetArcanePRCLevels(oCaster); + if (iClass6 == iFirstArcane) iClass6Lev += GetArcanePRCLevels(oCaster); + if (iClass7 == iFirstArcane) iClass7Lev += GetArcanePRCLevels(oCaster); + if (iClass8 == iFirstArcane) iClass8Lev += GetArcanePRCLevels(oCaster); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass5Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass6Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass7Lev); + + if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +int GetLevelByTypeDivine(object oCaster = OBJECT_SELF) +{ + int iFirstDivine = GetPrimaryDivineClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(8, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iClass1 == CLASS_TYPE_PALADIN || iClass1 == CLASS_TYPE_RANGER) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_PALADIN || iClass2 == CLASS_TYPE_RANGER) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_PALADIN || iClass3 == CLASS_TYPE_RANGER) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_PALADIN || iClass4 == CLASS_TYPE_RANGER) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_PALADIN || iClass5 == CLASS_TYPE_RANGER) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_PALADIN || iClass6 == CLASS_TYPE_RANGER) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_PALADIN || iClass7 == CLASS_TYPE_RANGER) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_PALADIN || iClass8 == CLASS_TYPE_RANGER) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + + if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster); + if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster); + if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster); + if (iClass4 == iFirstDivine) iClass4Lev += GetDivinePRCLevels(oCaster); + if (iClass5 == iFirstDivine) iClass5Lev += GetDivinePRCLevels(oCaster); + if (iClass6 == iFirstDivine) iClass6Lev += GetDivinePRCLevels(oCaster); + if (iClass7 == iFirstDivine) iClass7Lev += GetDivinePRCLevels(oCaster); + if (iClass8 == iFirstDivine) iClass8Lev += GetDivinePRCLevels(oCaster); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass5Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass6Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass7Lev); + + if (!GetIsDivineClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsDivineClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsDivineClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsDivineClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsDivineClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsDivineClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsDivineClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsDivineClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +//:: Test Void +//void main (){} \ No newline at end of file diff --git a/trunk/include/prc_inc_clsfunc.nss b/trunk/include/prc_inc_clsfunc.nss new file mode 100644 index 00000000..89c26dc8 --- /dev/null +++ b/trunk/include/prc_inc_clsfunc.nss @@ -0,0 +1,1618 @@ +/* + Class functions. + This scripts holds all functions used for classes in includes. + This prevents us from having one include for each class or set of classes. + + Stratovarius +*/ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////Begin Generic//////////////// + +// Function Definitions: + +// Include Files: +#include "prc_inc_spells" +//#include "prc_alterations" +//#include "prcsp_engine" +//#include "prc_inc_function" +//#include "prc_x2_itemprop" +//#include "prc_class_const" +//#include "prc_feat_const" +//#include "prc_ipfeat_const" +//#include "inc_utility" +// +//#include "pnp_shft_poly" +//#include "x2_inc_spellhook" +//#include "prc_inc_combat" +//#include "prc_inc_sp_tch" + +////////////////End Generic//////////////// + +////////////////Begin Drunken Master////////////////////// + + +// Function Definitions: + +// Searches oPC's inventory and finds the first valid alcoholic beverage container +// (empty) and returns TRUE if a proper container was found. This function takes +// action and returns a boolean. +int UseBottle(object oPC); + +// Searches oPC's inventory for an alcoholic beverage and if one is found it's +// destroyed and replaced by an empty container. This function is only used in +// the Breath of Fire spell script. +int UseAlcohol(object oPC = OBJECT_SELF); + +// Removes all Alcohol effects for oTarget. Used in B o Flame. +void RemoveAlcoholEffects(object oTarget = OBJECT_SELF); + +// Creates an empty bottle on oPC. +// sTag: the tag of the alcoholic beverage used (ale, spirits, wine) +void CreateBottleOnObject(object oPC, string sTag); + + +// Applies Drunk Like a Demno effects +void DrunkLikeDemon(); + +// Add the non-drunken master drinking effects. +void MakeDrunk(int nSpellID); + +// Have the drunken master say one of 6 phrases. +void DrunkenMasterSpeakString(); + +// Creates an empty bottle on oPC. +// nBeverage: the spell id of the alcoholic beverage used (ale, spirits, wine) +void DrunkenMasterCreateEmptyBottle(int nSpellID); + +// Determines the DC needed to save against the cast spell-like ability +// replace PRCGetSaveDC +int GetSpellDCSLA(object oCaster, int iSpelllvl,int iAbi = ABILITY_WISDOM); + +void DoArchmageHeirophantSLA(object oPC, object oTarget, location lTarget, int nSLAID); + +// Functions: +int UseBottle(object oPC) +{ + object oItem = GetFirstItemInInventory(oPC); + //search oPC for a bottle: + string sTag; + while(oItem != OBJECT_INVALID) + { + sTag = GetTag(oItem); + if(sTag == "NW_IT_THNMISC001" + || sTag == "NW_IT_THNMISC002" + || sTag == "NW_IT_THNMISC003" + || sTag == "NW_IT_THNMISC004") + { + SetPlotFlag(oItem, FALSE); + DestroyObject(oItem); + return TRUE; + } + else + oItem = GetNextItemInInventory(); + } + return FALSE; +} + +int UseAlcohol(object oPC = OBJECT_SELF) +{ + object oItem = GetFirstItemInInventory(oPC); + //search oPC for alcohol: + string sTag = GetTag(oItem); + while(oItem != OBJECT_INVALID) + { + if(sTag == "NW_IT_MPOTION021" + || sTag == "NW_IT_MPOTION022" + || sTag == "NW_IT_MPOTION023" + || sTag == "DragonsBreath") + { + SetPlotFlag(oItem, FALSE); + if(GetItemStackSize(oItem) > 1) + { + SetItemStackSize(oItem, GetItemStackSize(oItem) - 1); + // Create an Empty Bottle: + CreateBottleOnObject(oPC, sTag); + return TRUE; + } + else + { + DestroyObject(oItem); + // Create an Empty Bottle: + CreateBottleOnObject(oPC, sTag); + return TRUE; + } + } + else + oItem = GetNextItemInInventory(); + } + return FALSE; +} + +void CreateBottleOnObject(object oPC, string sTag) +{ + if(sTag == "NW_IT_MPOTION021") // Ale + { + CreateItemOnObject("nw_it_thnmisc002", oPC); + } + else if(sTag == "NW_IT_MPOTION022") // Spirits + { + CreateItemOnObject("nw_it_thnmisc003", oPC); + } + else if(sTag == "NW_IT_MPOTION023") // Wine + { + CreateItemOnObject("nw_it_thnmisc004", oPC); + } + else // Other beverage + { + CreateItemOnObject("nw_it_thnmisc001", oPC); + } +} + +int GetIsDrunk(object oTarget = OBJECT_SELF) +{ + return GetHasSpellEffect(406, oTarget) + || GetHasSpellEffect(407, oTarget) + || GetHasSpellEffect(408, oTarget); +} + +void RemoveAlcoholEffects(object oTarget = OBJECT_SELF) +{ + PRCRemoveSpellEffects(406, OBJECT_SELF, oTarget); + PRCRemoveSpellEffects(407, OBJECT_SELF, oTarget); + PRCRemoveSpellEffects(408, OBJECT_SELF, oTarget); +} + +void DrunkenRage() +{ + float fDuration = GetLevelByClass(CLASS_TYPE_DRUNKEN_MASTER) > 9 ? HoursToSeconds(3) : HoursToSeconds(1); + + effect eLink = EffectLinkEffects(EffectAbilityIncrease(ABILITY_STRENGTH, 4), EffectAbilityIncrease(ABILITY_CONSTITUTION, 4)); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_WILL, 2)); + eLink = EffectLinkEffects(eLink, EffectACDecrease(2)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_BLUR)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_AURA_FIRE)); + eLink = ExtraordinaryEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, OBJECT_SELF, fDuration); + + FloatingTextStringOnCreature("Drunken Rage Activated", OBJECT_SELF); +} + +void DrunkLikeDemon() +{ + // A Drunken Master has had a drink. Add effects: + effect eLink = EffectLinkEffects(EffectAbilityIncrease(ABILITY_STRENGTH, 1), EffectAbilityIncrease(ABILITY_CONSTITUTION, 1)); + eLink = EffectLinkEffects(eLink, EffectAbilityDecrease(ABILITY_WISDOM, 1)); + eLink = EffectLinkEffects(eLink, EffectAbilityDecrease(ABILITY_INTELLIGENCE, 1)); + eLink = EffectLinkEffects(eLink, EffectAbilityDecrease(ABILITY_DEXTERITY, 1)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_BLUR)); + + //run checks to see if Dex Modifier will be changed: + if(!(GetAbilityModifier(ABILITY_DEXTERITY) % 2)) + { + //restore AC, Ref save and Tumble to previous values + eLink = EffectLinkEffects(eLink, EffectACIncrease(1)); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_REFLEX, 1)); + eLink = EffectLinkEffects(eLink, EffectSkillIncrease(SKILL_TUMBLE, 1)); + } + eLink = ExtraordinaryEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, OBJECT_SELF, HoursToSeconds(1)); + + FloatingTextStringOnCreature("You are Drunk Like a Demon", OBJECT_SELF); +} + +void MakeDrunk(int nSpellID) +{ + if(Random(100) < 40) + AssignCommand(OBJECT_SELF, ActionPlayAnimation(ANIMATION_LOOPING_TALK_LAUGHING)); + else + AssignCommand(OBJECT_SELF, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_DRUNK)); + + int nPoints; + switch(nSpellID) + { + case 406: nPoints = 1; break;//Ale + case 407: nPoints = 2; break;//Wine + case 408: nPoints = 3; break;//Spirits + } + + //ApplyAbilityDamage(oTarget, ABILITY_INTELLIGENCE, nPoints, DURATION_TYPE_TEMPORARY, TRUE, 60.0); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nPoints)), OBJECT_SELF, 60.0); + AssignCommand(OBJECT_SELF, ActionSpeakStringByStrRef(10499)); +} + +void DrunkenMasterSpeakString() +{ + switch(d6()) + { + case 1: AssignCommand(OBJECT_SELF, ActionSpeakString("Now that's the stuff!")); break; + case 2: AssignCommand(OBJECT_SELF, ActionSpeakString("That one really hit the spot!")); break; + case 3: AssignCommand(OBJECT_SELF, ActionSpeakString("That should keep me warm!")); break; + case 4: AssignCommand(OBJECT_SELF, ActionSpeakString("Good stuff!")); break; + case 5: AssignCommand(OBJECT_SELF, ActionSpeakString("Bless the Wine Gods!")); break; + case 6: AssignCommand(OBJECT_SELF, ActionSpeakString("Just what I needed!")); break; + } +} + +void DrunkenMasterCreateEmptyBottle(int nSpellID) +{ + switch(nSpellID) + { + case 406: CreateItemOnObject("nw_it_thnmisc002", OBJECT_SELF); break;//Ale + case 407: CreateItemOnObject("nw_it_thnmisc004", OBJECT_SELF); break;//Wine + case 408: CreateItemOnObject("nw_it_thnmisc003", OBJECT_SELF); break;//Spirits + default: CreateItemOnObject("nw_it_thnmisc001", OBJECT_SELF); break;//Other + } +} + +////////////////End Drunken Master////////////////// + +////////////////Begin Samurai////////////////// + +// This function is probably utterly broken: the match found variable is not reset in the loop and the returned value will be equal to the last match - Ornedan +int GetPropertyValue(object oWeapon, int iType, int iSubType = -1, int bDebug = FALSE); + +int GetPropertyValue(object oWeapon, int iType, int iSubType = -1, int bDebug = FALSE) +{ + int bReturn = -1; + if(oWeapon == OBJECT_INVALID){return FALSE;} + int bMatch = FALSE; + if (GetItemHasItemProperty(oWeapon, iType)) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("It has the property."));} + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == iType) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Again..."));} + bMatch = TRUE; + if (iSubType > -1) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Subtype Required."));} + if(GetItemPropertySubType(ip) != iSubType) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Subtype wrong."));} + bMatch = FALSE; + } + else + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Subtype Correct."));} + } + } + } + if (bMatch) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Match found."));} + if (GetItemPropertyCostTableValue(ip) > -1) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Cost value found, returning."));} + bReturn = GetItemPropertyCostTableValue(ip); + } + else + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("No cost value for property, returning TRUE."));} + bReturn = 1; + } + } + else + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Match not found."));} + } + ip = GetNextItemProperty(oWeapon); + } + } + return bReturn; +} + + +void WeaponUpgradeVisual(); + +object GetSamuraiToken(object oSamurai); + +void WeaponUpgradeVisual() +{ + object oPC = GetPCSpeaker(); + int iCost = GetLocalInt(oPC, "CODI_SAM_WEAPON_COST"); + object oToken = GetSamuraiToken(oPC); + int iToken = StringToInt(GetTag(oToken)); + int iGold = GetGold(oPC); + if(iGold + iToken < iCost) + { + SendMessageToPC(oPC, "You sense the gods are angered!"); + AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, 25, FALSE); + object oWeapon = GetItemPossessedBy(oPC, "codi_sam_mw"); + DestroyObject(oWeapon); + return; + } + else if(iToken <= iCost) + { + iCost = iCost - iToken; + DestroyObject(oToken); + TakeGoldFromCreature(iCost, oPC, TRUE); + } + else if (iToken > iCost) + { + object oNewToken = CopyObject(oToken, GetLocation(oPC), oPC, IntToString(iToken - iCost)); + DestroyObject(oToken); + } + effect eVis = EffectVisualEffect(VFX_FNF_DISPEL_DISJUNCTION); + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE,1.0,6.0)); + AssignCommand(oPC, ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY2)); + DelayCommand(0.1, SetCommandable(FALSE, oPC)); + DelayCommand(6.5, SetCommandable(TRUE, oPC)); + DelayCommand(5.0,ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetLocation(oPC))); +} + +object GetSamuraiToken(object oSamurai) +{ + object oItem = GetFirstItemInInventory(oSamurai); + while(oItem != OBJECT_INVALID) + { + if(GetResRef(oItem) == "codi_sam_token") + { + return oItem; + } + oItem = GetNextItemInInventory(oSamurai); + } + return OBJECT_INVALID; +} + + + + +////////////////End Samurai////////////////// + +////////////////Begin Vile Feat////////////////// + + +int Vile_Feat(int iTypeWeap) +{ + switch(iTypeWeap) + { + case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_BASTARDSWORD); + case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_VILE_MARTIAL_BATTLEAXE); + case BASE_ITEM_CLUB: return GetHasFeat(FEAT_VILE_MARTIAL_CLUB); + case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_VILE_MARTIAL_DAGGER); + case BASE_ITEM_DART: return GetHasFeat(FEAT_VILE_MARTIAL_DART); + case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_VILE_MARTIAL_DIREMACE); + case BASE_ITEM_DOUBLEAXE: return GetHasFeat(FEAT_VILE_MARTIAL_DOUBLEAXE); + case BASE_ITEM_DWARVENWARAXE: return GetHasFeat(FEAT_VILE_MARTIAL_DWAXE); + case BASE_ITEM_GREATAXE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATAXE); + case BASE_ITEM_GREATSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD); + case BASE_ITEM_HALBERD: return GetHasFeat(FEAT_VILE_MARTIAL_HALBERD); + case BASE_ITEM_HANDAXE: return GetHasFeat(FEAT_VILE_MARTIAL_HANDAXE); + case BASE_ITEM_HEAVYCROSSBOW: return GetHasFeat(FEAT_VILE_MARTIAL_HEAVYCROSSBOW); + case BASE_ITEM_HEAVYFLAIL: return GetHasFeat(FEAT_VILE_MARTIAL_HEAVYFLAIL); + case BASE_ITEM_KAMA: return GetHasFeat(FEAT_VILE_MARTIAL_KAMA); + case BASE_ITEM_KATANA: return GetHasFeat(FEAT_VILE_MARTIAL_KATANA); + case BASE_ITEM_KUKRI: return GetHasFeat(FEAT_VILE_MARTIAL_KUKRI); + case BASE_ITEM_LIGHTCROSSBOW: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHTCROSSBOW); + case BASE_ITEM_LIGHTFLAIL: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHTFLAIL); + case BASE_ITEM_LIGHTHAMMER: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHTHAMMER); + case BASE_ITEM_LIGHTMACE: return GetHasFeat(FEAT_VILE_MARTIAL_MACE); + case BASE_ITEM_LONGBOW: return GetHasFeat(FEAT_VILE_MARTIAL_LONGBOW); + case BASE_ITEM_LONGSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_LONGSWORD); + case BASE_ITEM_MORNINGSTAR: return GetHasFeat(FEAT_VILE_MARTIAL_MORNINGSTAR); + case BASE_ITEM_QUARTERSTAFF: return GetHasFeat(FEAT_VILE_MARTIAL_QUARTERSTAFF); + case BASE_ITEM_RAPIER: return GetHasFeat(FEAT_VILE_MARTIAL_RAPIER); + case BASE_ITEM_SCIMITAR: return GetHasFeat(FEAT_VILE_MARTIAL_SCIMITAR); + case BASE_ITEM_SCYTHE: return GetHasFeat(FEAT_VILE_MARTIAL_SCYTHE); + case BASE_ITEM_SHORTBOW: return GetHasFeat(FEAT_VILE_MARTIAL_SHORTBOW); + case BASE_ITEM_SHORTSPEAR: return GetHasFeat(FEAT_VILE_MARTIAL_SPEAR); + case BASE_ITEM_SHORTSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_SHORTSWORD); + case BASE_ITEM_SHURIKEN: return GetHasFeat(FEAT_VILE_MARTIAL_SHURIKEN); + case BASE_ITEM_SLING: return GetHasFeat(FEAT_VILE_MARTIAL_SLING); + case BASE_ITEM_SICKLE: return GetHasFeat(FEAT_VILE_MARTIAL_SICKLE); + case BASE_ITEM_TWOBLADEDSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_TWOBLADED); + case BASE_ITEM_WARHAMMER: return GetHasFeat(FEAT_VILE_MARTIAL_WARHAMMER); + case BASE_ITEM_WHIP: return GetHasFeat(FEAT_VILE_MARTIAL_WHIP); + case BASE_ITEM_TRIDENT: return GetHasFeat(FEAT_VILE_MARTIAL_TRIDENT); + + //new items + case BASE_ITEM_ELVEN_LIGHTBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_SHORTSWORD) || GetHasFeat(FEAT_VILE_MARTIAL_RAPIER)); + case BASE_ITEM_ELVEN_THINBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_LONGSWORD) || GetHasFeat(FEAT_VILE_MARTIAL_RAPIER)); + case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD); + } + + return FALSE; + +} + +////////////////End Vile Feat////////////////// + +////////////////Begin Soul Inc////////////////// + +const int IPRP_CONST_ONHIT_DURATION_5_PERCENT_1_ROUNDS = 20; + +int Sanctify_Feat(int iTypeWeap) +{ + switch(iTypeWeap) + { + case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BASTARDSWORD); + case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BATTLEAXE); + case BASE_ITEM_CLUB: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_CLUB); + case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DAGGER); + case BASE_ITEM_DART: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DART); + case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DIREMACE); + case BASE_ITEM_DOUBLEAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DOUBLEAXE); + case BASE_ITEM_DWARVENWARAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DWAXE); + case BASE_ITEM_GREATAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GREATAXE); + case BASE_ITEM_GREATSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GREATSWORD); + case BASE_ITEM_HALBERD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HALBERD); + case BASE_ITEM_HANDAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HANDAXE); + case BASE_ITEM_HEAVYCROSSBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW); + case BASE_ITEM_HEAVYFLAIL: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL); + case BASE_ITEM_KAMA: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KAMA); + case BASE_ITEM_KATANA: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KATANA); + case BASE_ITEM_KUKRI: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KUKRI); + case BASE_ITEM_LIGHTCROSSBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW); + case BASE_ITEM_LIGHTFLAIL: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL); + case BASE_ITEM_LIGHTHAMMER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER); + case BASE_ITEM_LIGHTMACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_MACE); + case BASE_ITEM_LONGBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LONGBOW); + case BASE_ITEM_LONGSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LONGSWORD); + case BASE_ITEM_MORNINGSTAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_MORNINGSTAR); + case BASE_ITEM_QUARTERSTAFF: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF); + case BASE_ITEM_RAPIER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_RAPIER); + case BASE_ITEM_SCIMITAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SCIMITAR); + case BASE_ITEM_SCYTHE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SCYTHE); + case BASE_ITEM_SHORTBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHORTBOW); + case BASE_ITEM_SHORTSPEAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SPEAR); + case BASE_ITEM_SHORTSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHORTSWORD); + case BASE_ITEM_SHURIKEN: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHURIKEN); + case BASE_ITEM_SLING: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SLING); + case BASE_ITEM_SICKLE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SICKLE); + case BASE_ITEM_TWOBLADEDSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_TWOBLADED); + case BASE_ITEM_WARHAMMER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_WARHAMMER); + case BASE_ITEM_WHIP: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_WHIP); + case BASE_ITEM_TRIDENT: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_TRIDENT); + + //new items + case BASE_ITEM_ELVEN_LIGHTBLADE: return (GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHORTSWORD) || GetHasFeat(FEAT_SANCTIFY_MARTIAL_RAPIER)); + case BASE_ITEM_ELVEN_THINBLADE: return (GetHasFeat(FEAT_SANCTIFY_MARTIAL_LONGSWORD) || GetHasFeat(FEAT_SANCTIFY_MARTIAL_RAPIER)); + case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GREATSWORD); + } + + return FALSE; +} + +int DamageConv(int iMonsDmg) +{ + + switch(iMonsDmg) + { + case IP_CONST_MONSTERDAMAGE_1d4: return 1; + case IP_CONST_MONSTERDAMAGE_1d6: return 2; + case IP_CONST_MONSTERDAMAGE_1d8: return 3; + case IP_CONST_MONSTERDAMAGE_1d10: return 4; + case IP_CONST_MONSTERDAMAGE_1d12: return 5; + case IP_CONST_MONSTERDAMAGE_1d20: return 6; + + case IP_CONST_MONSTERDAMAGE_2d4: return 10; + case IP_CONST_MONSTERDAMAGE_2d6: return 11; + case IP_CONST_MONSTERDAMAGE_2d8: return 12; + case IP_CONST_MONSTERDAMAGE_2d10: return 13; + case IP_CONST_MONSTERDAMAGE_2d12: return 14; + case IP_CONST_MONSTERDAMAGE_2d20: return 15; + + case IP_CONST_MONSTERDAMAGE_3d4: return 20; + case IP_CONST_MONSTERDAMAGE_3d6: return 21; + case IP_CONST_MONSTERDAMAGE_3d8: return 22; + case IP_CONST_MONSTERDAMAGE_3d10: return 23; + case IP_CONST_MONSTERDAMAGE_3d12: return 24; + case IP_CONST_MONSTERDAMAGE_3d20: return 25; + + + } + + + return 0; +} + +int ConvMonsterDmg(int iMonsDmg) +{ + + switch(iMonsDmg) + { + case 1: return IP_CONST_MONSTERDAMAGE_1d4; + case 2: return IP_CONST_MONSTERDAMAGE_1d6; + case 3: return IP_CONST_MONSTERDAMAGE_1d8; + case 4: return IP_CONST_MONSTERDAMAGE_1d10; + case 5: return IP_CONST_MONSTERDAMAGE_1d12; + case 6: return IP_CONST_MONSTERDAMAGE_1d20; + case 10: return IP_CONST_MONSTERDAMAGE_2d4; + case 11: return IP_CONST_MONSTERDAMAGE_2d6; + case 12: return IP_CONST_MONSTERDAMAGE_2d8; + case 13: return IP_CONST_MONSTERDAMAGE_2d10; + case 14: return IP_CONST_MONSTERDAMAGE_2d12; + case 15: return IP_CONST_MONSTERDAMAGE_2d20; + case 20: return IP_CONST_MONSTERDAMAGE_3d4; + case 21: return IP_CONST_MONSTERDAMAGE_3d6; + case 22: return IP_CONST_MONSTERDAMAGE_3d8; + case 23: return IP_CONST_MONSTERDAMAGE_3d10; + case 24: return IP_CONST_MONSTERDAMAGE_3d12; + case 25: return IP_CONST_MONSTERDAMAGE_3d20; + + } + + return 0; +} + +int MonsterDamage(object oItem) +{ + int iBonus; + int iTemp; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_MONSTER_DAMAGE) + { + iTemp = GetItemPropertyCostTableValue(ip); + iBonus = iTemp > iBonus ? iTemp : iBonus; + } + ip = GetNextItemProperty(oItem); + } + + return iBonus; +} + +int FeatIniDmg(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_BONUS_FEAT) + { + if(GetItemPropertySubType(ip) == IP_CONST_FEAT_WeapFocCreature) + return 1; + } + ip = GetNextItemProperty(oItem); + } + return 0; +} + + +void AddIniDmg(object oPC) +{ + + int bUnarmedDmg = GetHasFeat(FEAT_INCREASE_DAMAGE1, oPC) + + GetHasFeat(FEAT_INCREASE_DAMAGE2, oPC); + + if(!bUnarmedDmg) + return; + + object oCweapB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oCweapL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oCweapR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + + int iDmg; + int iConv; + int iStr = GetAbilityModifier(ABILITY_STRENGTH, oPC); + int iWis = GetAbilityModifier(ABILITY_WISDOM, oPC); + iWis = iWis > iStr ? iWis : 0; + + + /*if(GetHasFeat(FEAT_INTUITIVE_ATTACK, oPC)) + { + SetCompositeBonusT(oCweapB,"",iWis,ITEM_PROPERTY_ATTACK_BONUS); + SetCompositeBonusT(oCweapL,"",iWis,ITEM_PROPERTY_ATTACK_BONUS); + SetCompositeBonusT(oCweapR,"",iWis,ITEM_PROPERTY_ATTACK_BONUS); + } + if (GetHasFeat(FEAT_RAVAGEGOLDENICE, oPC)) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE,2),oCweapB,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE,2),oCweapL,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE,2),oCweapR,9999.0); + }*/ + + + if ( oCweapB != OBJECT_INVALID && !FeatIniDmg(oCweapB)) + { + iDmg = MonsterDamage(oCweapB); + iConv = DamageConv(iDmg) + bUnarmedDmg; + iConv = (iConv > 6 && iConv < 10) ? 6 : iConv; + iConv = (iConv > 15 && iConv < 20) ? 15 : iConv; + iConv = (iConv > 25) ? 25 : iConv; + iConv = ConvMonsterDmg(iConv); + TotalAndRemoveProperty(oCweapB,ITEM_PROPERTY_MONSTER_DAMAGE,-1); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iConv),oCweapB); + //AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oCweapB); + IPSafeAddItemProperty(oCweapB, PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + if ( oCweapL != OBJECT_INVALID && !FeatIniDmg(oCweapL)) + { + iDmg = MonsterDamage(oCweapL); + iConv = DamageConv(iDmg) + bUnarmedDmg; + iConv = (iConv > 6 && iConv < 10) ? 6 : iConv; + iConv = (iConv > 15 && iConv < 20) ? 15 : iConv; + iConv = (iConv > 25) ? 25 : iConv; + iConv = ConvMonsterDmg(iConv); + TotalAndRemoveProperty(oCweapL,ITEM_PROPERTY_MONSTER_DAMAGE,-1); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iConv),oCweapL); + //AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oCweapL); + IPSafeAddItemProperty(oCweapL, PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + if ( oCweapR != OBJECT_INVALID && !FeatIniDmg(oCweapR)) + { + iDmg = MonsterDamage(oCweapR); + iConv = DamageConv(iDmg) + bUnarmedDmg; + iConv = (iConv > 6 && iConv < 10) ? 6 : iConv; + iConv = (iConv > 15 && iConv < 20) ? 15 : iConv; + iConv = (iConv > 25) ? 25 : iConv; + iConv = ConvMonsterDmg(iConv); + TotalAndRemoveProperty(oCweapR,ITEM_PROPERTY_MONSTER_DAMAGE,-1); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iConv),oCweapR); + //AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oCweapR); + IPSafeAddItemProperty(oCweapR, PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } +} + +void AddCriti(object oPC,object oSkin,int ip_feat_crit,int nFeat) +{ + // Do not add multiple instances of the same bonus feat iprop, it lags the game + AddSkinFeat(nFeat, ip_feat_crit, oSkin, oPC); +} + +void ImpCrit(object oPC,object oSkin) +{ + if(GetHasFeat(FEAT_WEAPON_FOCUS_BASTARD_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_BASTARD_SWORD, FEAT_IMPROVED_CRITICAL_BASTARD_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_BATTLE_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_BATTLE_AXE, FEAT_IMPROVED_CRITICAL_BATTLE_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_CLUB, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_CLUB, FEAT_IMPROVED_CRITICAL_CLUB); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DAGGER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DAGGER, FEAT_IMPROVED_CRITICAL_DAGGER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DART, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DART, FEAT_IMPROVED_CRITICAL_DART); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DIRE_MACE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DIRE_MACE, FEAT_IMPROVED_CRITICAL_DIRE_MACE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DOUBLE_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DOUBLE_AXE, FEAT_IMPROVED_CRITICAL_DOUBLE_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DWAXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DWAXE, FEAT_IMPROVED_CRITICAL_DWAXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_GREAT_AXE, FEAT_IMPROVED_CRITICAL_GREAT_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_GREAT_SWORD, FEAT_IMPROVED_CRITICAL_GREAT_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HALBERD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HALBERD, FEAT_IMPROVED_CRITICAL_HALBERD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HAND_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HAND_AXE, FEAT_IMPROVED_CRITICAL_HAND_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW, FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_FLAIL, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL, FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL); + if(GetHasFeat(FEAT_WEAPON_FOCUS_KAMA, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_KAMA, FEAT_IMPROVED_CRITICAL_KAMA); + if(GetHasFeat(FEAT_WEAPON_FOCUS_KATANA, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_KATANA, FEAT_IMPROVED_CRITICAL_KATANA); + if(GetHasFeat(FEAT_WEAPON_FOCUS_KUKRI, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_KUKRI, FEAT_IMPROVED_CRITICAL_KUKRI); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW, FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_FLAIL, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL, FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_HAMMER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER, FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_MACE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_MACE, FEAT_IMPROVED_CRITICAL_LIGHT_MACE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONG_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LONG_SWORD, FEAT_IMPROVED_CRITICAL_LONG_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONGBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LONGBOW, FEAT_IMPROVED_CRITICAL_LONGBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_MORNING_STAR, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_MORNING_STAR, FEAT_IMPROVED_CRITICAL_MORNING_STAR); + if(GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_RAPIER, FEAT_IMPROVED_CRITICAL_RAPIER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SCIMITAR, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SCIMITAR, FEAT_IMPROVED_CRITICAL_SCIMITAR); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SCYTHE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SCYTHE, FEAT_IMPROVED_CRITICAL_SCYTHE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORT_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SHORT_SWORD, FEAT_IMPROVED_CRITICAL_SHORT_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORTBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SHORTBOW, FEAT_IMPROVED_CRITICAL_SHORTBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHURIKEN, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SHURIKEN, FEAT_IMPROVED_CRITICAL_SHURIKEN); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SICKLE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SICKLE, FEAT_IMPROVED_CRITICAL_SICKLE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SLING, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SLING, FEAT_IMPROVED_CRITICAL_SLING); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SPEAR, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SPEAR, FEAT_IMPROVED_CRITICAL_SPEAR); + if(GetHasFeat(FEAT_WEAPON_FOCUS_STAFF, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_STAFF, FEAT_IMPROVED_CRITICAL_STAFF); + if(GetHasFeat(FEAT_WEAPON_FOCUS_THROWING_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_THROWING_AXE, FEAT_IMPROVED_CRITICAL_THROWING_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD, FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_WAR_HAMMER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_WAR_HAMMER, FEAT_IMPROVED_CRITICAL_WAR_HAMMER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_WHIP, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_WHIP, FEAT_IMPROVED_CRITICAL_WHIP); + +} + +////////////////End Soul Inc////////////////// + +////////////////Begin Martial Strike////////////////// + +void MartialStrike() +{ + object oItem; + object oPC = OBJECT_SELF; + + int iEquip=GetLocalInt(oPC,"ONEQUIP"); + int iType; + + if (iEquip==2) + { + + if (!GetHasFeat(FEAT_HOLY_MARTIAL_STRIKE)) return; + + oItem=GetItemLastEquipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_HOLY),oItem,9999.0); + SetLocalInt(oItem,"MartialStrik",1); + } + else if (iEquip==1) + { + oItem=GetItemLastUnequipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if ( GetLocalInt(oItem,"MartialStrik")) + { + RemoveSpecificProperty(oItem,ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP,IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGEBONUS_2d6, 1,"",IP_CONST_DAMAGETYPE_DIVINE,DURATION_TYPE_TEMPORARY); + RemoveSpecificProperty(oItem,ITEM_PROPERTY_VISUALEFFECT,ITEM_VISUAL_HOLY,-1,1,"",-1,DURATION_TYPE_TEMPORARY); + DeleteLocalInt(oItem,"MartialStrik"); + } + + } + else + { + + if (!GetHasFeat(FEAT_HOLY_MARTIAL_STRIKE)) return; + + oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if (!GetLocalInt(oItem,"MartialStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_HOLY),oItem,9999.0); + SetLocalInt(oItem,"MartialStrik",1); + } + oItem=GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + iType= GetBaseItemType(oItem); + if ( !GetLocalInt(oItem,"MartialStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_HOLY),oItem,9999.0); + SetLocalInt(oItem,"MartialStrik",1); + } + } + + +} + + +void UnholyStrike() +{ + object oItem; + object oPC = OBJECT_SELF; + + int iEquip=GetLocalInt(oPC,"ONEQUIP"); + int iType; + + if (iEquip==2) + { + + if (!GetHasFeat(FEAT_UNHOLY_STRIKE)) return; + + oItem=GetItemLastEquipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_EVIL),oItem,9999.0); + SetLocalInt(oItem,"UnholyStrik",1); + } + else if (iEquip==1) + { + oItem=GetItemLastUnequipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if ( GetLocalInt(oItem,"UnholyStrik")) + { + RemoveSpecificProperty(oItem,ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP,IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGEBONUS_2d6, 1,"",IP_CONST_DAMAGETYPE_DIVINE,DURATION_TYPE_TEMPORARY); + RemoveSpecificProperty(oItem,ITEM_PROPERTY_VISUALEFFECT,ITEM_VISUAL_EVIL,-1,1,"",-1,DURATION_TYPE_TEMPORARY); + DeleteLocalInt(oItem,"UnholyStrik"); + } + + } + else + { + + if (!GetHasFeat(FEAT_UNHOLY_STRIKE)) return; + + oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if (!GetLocalInt(oItem,"UnholyStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_EVIL),oItem,9999.0); + SetLocalInt(oItem,"UnholyStrik",1); + } + oItem=GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + iType= GetBaseItemType(oItem); + if ( !GetLocalInt(oItem,"UnholyStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_EVIL),oItem,9999.0); + SetLocalInt(oItem,"UnholyStrik",1); + } + } + + +} + +////////////////End Martial Strike////////////////// + +////////////////Begin Soldier of Light Spells////////////////// +/* As far as I can tell, not used at all - Ornedan +void spellsCureMod(int nCasterLvl ,int nDamage, int nMaxExtraDamage, int nMaximized, int vfx_impactHurt, int vfx_impactHeal, int nSpellID) +{ + //Declare major variables + object oTarget = PRCGetSpellTargetObject(); + int nHeal; + int nMetaMagic = PRCGetMetaMagicFeat(); + effect eVis = EffectVisualEffect(vfx_impactHurt); + effect eVis2 = EffectVisualEffect(vfx_impactHeal); + effect eHeal, eDam; + + int nExtraDamage = nCasterLvl; // * figure out the bonus damage + if (nExtraDamage > nMaxExtraDamage) + { + nExtraDamage = nMaxExtraDamage; + } + // * if low or normal difficulty is treated as MAXIMIZED + if(GetIsPC(oTarget) && GetGameDifficulty() < GAME_DIFFICULTY_CORE_RULES) + { + nDamage = nMaximized + nExtraDamage; + } + else + { + nDamage = nDamage + nExtraDamage; + } + + + //Make metamagic checks + int iBlastFaith = BlastInfidelOrFaithHeal(OBJECT_SELF, oTarget, DAMAGE_TYPE_POSITIVE, TRUE); + if (nMetaMagic & METAMAGIC_MAXIMIZE || iBlastFaith) + { + nDamage = nMaximized + nExtraDamage; + // * if low or normal difficulty then MAXMIZED is doubled. + if(GetIsPC(OBJECT_SELF) && GetGameDifficulty() < GAME_DIFFICULTY_CORE_RULES) + { + nDamage = nDamage + nExtraDamage; + } + } + if (nMetaMagic & METAMAGIC_EMPOWER || GetHasFeat(FEAT_HEALING_DOMAIN_POWER)) + { + nDamage = nDamage + (nDamage/2); + } + + + if (MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) + { + //Figure out the amount of damage to heal + nHeal = nDamage; + //Set the heal effect + eHeal = EffectHeal(nHeal); + //Apply heal effect and VFX impact + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis2, oTarget); + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellID, FALSE)); + + + } + //Check that the target is undead + else + { + int nTouch = PRCDoMeleeTouchAttack(oTarget);; + if (nTouch > 0) + { + if(!GetIsReactionTypeFriendly(oTarget)) + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellID)); + if (!PRCDoResistSpell(OBJECT_SELF, oTarget,nCasterLvl+add_spl_pen(OBJECT_SELF))) + { + eDam = EffectDamage(nDamage,DAMAGE_TYPE_NEGATIVE); + //Apply the VFX impact and effects + DelayCommand(1.0, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + } + } + } + } +} +*/ +////////////////End Soldier of Light Spells////////////////// + +////////////////Begin Master Harper Instruments////////////////// + +void ActiveModeCIMM(object oTarget) +{ + if(!GetLocalInt(oTarget,"use_CIMM") ) + { + string sScript = GetModuleOverrideSpellscript(); + if (sScript != "mh_spell_at_inst") + { + SetLocalString(OBJECT_SELF,"temp_spell_at_inst",sScript); + SetLocalString(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT", "mh_spell_at_inst"); + } + SetLocalInt(OBJECT_SELF,"nb_spell_at_inst",GetLocalInt(OBJECT_SELF,"nb_spell_at_inst")+1); + FloatingTextStrRefOnCreature(16825240,oTarget); + SetLocalInt(oTarget,"use_CIMM",TRUE); + } +} + +void UnactiveModeCIMM(object oTarget) +{ + if(GetLocalInt(oTarget,"use_CIMM") ) + { + string sScript = GetModuleOverrideSpellscript(); + SetLocalInt(OBJECT_SELF,"nb_spell_at_inst",GetLocalInt(OBJECT_SELF,"nb_spell_at_inst")-1); + if (sScript == "mh_spell_at_inst" && GetLocalInt(OBJECT_SELF,"nb_spell_at_inst") == 0) + { + SetLocalString(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT", GetLocalString(OBJECT_SELF,"temp_spell_at_inst")); + GetLocalString(OBJECT_SELF,"temp_spell_at_inst"); + SetLocalString(OBJECT_SELF,"temp_spell_at_inst",""); + } + FloatingTextStrRefOnCreature(16825241,oTarget); + SetLocalInt(oTarget,"use_CIMM",FALSE); + } +} + +////////////////End Master Harper Instruments////////////////// + +////////////////Begin Minstrel of the Edge////////////////// + +// Goes a bit further than RemoveSpellEffects -- makes sure to remove ALL effects +// made by the Singer+Song. +void RemoveSongEffects(int iSong, object oCaster, object oTarget) +{ + effect eCheck = GetFirstEffect(oTarget); + while (GetIsEffectValid(eCheck)) + { + if (GetEffectCreator(eCheck) == oCaster && GetEffectSpellId(eCheck) == iSong) + RemoveEffect(oTarget, eCheck); + eCheck = GetNextEffect(oTarget); + } +} + +// Stores a Song recipient to the PC as a local variable, and creates a list by using +// an index variable. +void StoreSongRecipient(object oRecipient, object oSinger, int iSongID, int iDuration = 0) +{ + int iSlot = GetLocalInt(oSinger, "SONG_SLOT"); + int iIndex = GetLocalInt(oSinger, "SONG_INDEX_" + IntToString(iSlot)) + 1; + string sIndex = "SONG_INDEX_" + IntToString(iSlot); + string sRecip = "SONG_RECIPIENT_" + IntToString(iIndex) + "_" + IntToString(iSlot); + string sSong = "SONG_IN_USE_" + IntToString(iSlot); + + // Store the recipient into the current used slot + SetLocalObject(oSinger, sRecip, oRecipient); + + // Store the song information + SetLocalInt(oSinger, sSong, iSongID); + + // Store the index of creatures we're on + SetLocalInt(oSinger, sIndex, iIndex); +} + +// Removes all effects given by the previous song from all creatures who recieved it. +// Now allows for two "slots", which means you can perform two songs at a time. +void RemoveOldSongEffects(object oSinger, int iSongID) +{ + object oCreature; + int iSlotNow = GetLocalInt(oSinger, "SONG_SLOT"); + int iSlot; + int iNumRecip; + int iSongInUse; + int iIndex; + string sIndex; + string sRecip; + string sSong; + + if (GetHasFeat(FEAT_MINSTREL_GREATER_MINSTREL_SONG, oSinger)) + { + // If you use the same song twice in a row you + // should deal with the same slot again... + if (GetLocalInt(oSinger, "SONG_IN_USE_" + IntToString(iSlotNow)) == iSongID) + iSlot = iSlotNow; + // Otherwise, we should toggle between slot "1" and slot "0" + else + iSlot = (iSlotNow == 1) ? 0 : 1; + } + else + { + iSlot = 0; + } + + // Save the toggle we're on for later. + SetLocalInt(oSinger, "SONG_SLOT", iSlot); + + // Find the proper variable names based on slot + sIndex = "SONG_INDEX_" + IntToString(iSlot); + sSong = "SONG_IN_USE_" + IntToString(iSlot); + + // Store the local variables into script variables + iNumRecip = GetLocalInt(oSinger, sIndex); + iSongInUse = GetLocalInt(oSinger, sSong); + + // Reset the local variables + SetLocalInt(oSinger, sIndex, 0); + SetLocalInt(oSinger, sSong, 0); + + // Removes any effects from the caster first + RemoveSongEffects(iSongInUse, oSinger, oSinger); + + // Removes any effects from the recipients + for (iIndex = 1 ; iIndex <= iNumRecip ; iIndex++) + { + sRecip = "SONG_RECIPIENT_" + IntToString(iIndex) + "_" + IntToString(iSlot); + oCreature = GetLocalObject(oSinger, sRecip); + + RemoveSongEffects(iSongInUse, oSinger, oCreature); + } +} + + +////////////////End Minstrel of the Edge////////////////// + +////////////////Begin Arcane Duelist////////////////// + +void FlurryEffects(object oPC) +{ + effect Effect1 = EffectModifyAttacks(1); + effect Effect2 = EffectAttackDecrease(2, ATTACK_BONUS_MISC); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, Effect1, oPC, RoundsToSeconds(10)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, Effect2, oPC, RoundsToSeconds(10)); + +} + +void CheckCombatDexAttack(object oPC) +{ +//object oPC = GetLocalObject(OBJECT_SELF, "PC_IN_COMBAT_WITH_DEXATTACK_ON"); +int iCombat = GetIsInCombat(oPC); +object oWeapon = GetLocalObject(oPC, "CHOSEN_WEAPON"); + + if(iCombat == TRUE && GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) == oWeapon) + { + DelayCommand(6.0, CheckCombatDexAttack(oPC)); + } + else + { + FloatingTextStringOnCreature("Dexterous Attack Mode Deactivated", oPC, FALSE); + effect eEffects = GetFirstEffect(oPC); + while (GetIsEffectValid(eEffects)) + { + + if (GetEffectType(eEffects) == EFFECT_TYPE_ATTACK_INCREASE && GetEffectSpellId(eEffects) == 1761) // dextrous attack + { + RemoveEffect(oPC, eEffects); + } + + eEffects = GetNextEffect(oPC); + } + DeleteLocalObject(OBJECT_SELF, "PC_IN_COMBAT_WITH_DEXATTACK_ON"); + } +} + +void SPMakeAttack(object oTarget, object oImage) +{ + int iDead = GetIsDead(oTarget); + + if(iDead == FALSE) + { + PrintString("TARGET AINT DEAD"); + DelayCommand(6.0, SPMakeAttack(oTarget, oImage)); + AssignCommand(oImage, ActionAttack(oTarget, FALSE)); + } + if(iDead == TRUE) + { + PrintString("TARGET BE DEAD AS A DOORNAIL"); + DestroyObject(oImage, 0.0); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), GetLocation(oImage), 0.0); + } + +} + +////////////////End Arcane Duelist////////////////// + +////////////////Begin Corpsecrafter////////////// + +void CorpseCrafter(object oPC, object oSummon) +{ + // Hijacking this function because it's already in the right places + if (GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC) >= 8) + { + if (DEBUG) DoDebug("Corpsecrafter: Dread Necro"); + int nHD = GetHitDice(oSummon); + effect eHP = EffectTemporaryHitpoints(nHD * 2); + effect eStr = EffectAbilityIncrease(ABILITY_STRENGTH, 4); + effect eDex = EffectAbilityIncrease(ABILITY_DEXTERITY, 4); + eHP = SupernaturalEffect(eHP); + eStr = SupernaturalEffect(EffectLinkEffects(eStr, eDex)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eHP, oSummon); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eStr, oSummon); + } + if (GetHasFeat(FEAT_CORPSECRAFTER, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Corpsecrafter"); + int nHD = GetHitDice(oSummon); + effect eHP = EffectTemporaryHitpoints(nHD * 2); + effect eStr = EffectAbilityIncrease(ABILITY_STRENGTH, 4); + eHP = SupernaturalEffect(eHP); + eStr = SupernaturalEffect(eStr); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eHP, oSummon); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eStr, oSummon); + } + if (GetHasFeat(FEAT_BOLSTER_RESISTANCE, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Bolster Resistance"); + effect eTurn = EffectTurnResistanceIncrease(4); + eTurn = SupernaturalEffect(eTurn); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eTurn, oSummon); + } + if (GetHasFeat(FEAT_DEADLY_CHILL, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Deadly Chill"); + effect eChill = EffectDamageIncrease(DAMAGE_BONUS_1d6, DAMAGE_TYPE_COLD); + eChill = SupernaturalEffect(eChill); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChill, oSummon); + } + if (GetHasFeat(FEAT_HARDENED_FLESH, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Hardened Flesh"); + effect eAC = EffectACIncrease(2); + eAC = SupernaturalEffect(eAC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAC, oSummon); + } + if (GetHasFeat(FEAT_NIMBLE_BONES, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Nimble Bones"); + object oSkin = GetPCSkin(oPC); + itemproperty iInit = PRCItemPropertyBonusFeat(IP_CONST_FEAT_IMPROVED_INIT); + //AddItemProperty(DURATION_TYPE_PERMANENT, iInit, oSkin); + IPSafeAddItemProperty(oSkin, iInit, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + // Speed boost, average speed is 30 feet, so a 10 foot boost is a 33% boost + effect eSpeed = EffectMovementSpeedIncrease(33); + eSpeed = SupernaturalEffect(eSpeed); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSpeed, oSummon); + } + if (GetHasFeat(FEAT_DESTRUCTION_RETRIBUTION, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Destruction Retribution"); + SetLocalInt(oSummon, "DestructionRetribution", TRUE); + } +} + +////////////////Begin Ninja////////////// + +void Ninja_DecrementKi (object oPC, int iExcept = -1) +{ + if (iExcept != FEAT_KI_POWER) + DecrementRemainingFeatUses(oPC, FEAT_KI_POWER); + if (iExcept != FEAT_GHOST_STEP) + DecrementRemainingFeatUses(oPC, FEAT_GHOST_STEP); + if (iExcept != FEAT_GHOST_STRIKE) + DecrementRemainingFeatUses(oPC, FEAT_GHOST_STRIKE); + if (iExcept != FEAT_GHOST_WALK) + DecrementRemainingFeatUses(oPC, FEAT_GHOST_WALK); + if (iExcept != FEAT_KI_DODGE) + DecrementRemainingFeatUses(oPC, FEAT_KI_DODGE); + // for testing only + SetLocalInt(oPC, "prc_ninja_ki", GetLocalInt(oPC, "prc_ninja_ki") - 1); + ExecuteScript("prc_ninjca", oPC); +} + +int Ninja_AbilitiesEnabled (object oPC) +{ + object oLefthand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + + if (GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC)) > 0 || + GetBaseItemType(oLefthand) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oLefthand) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oLefthand) == BASE_ITEM_TOWERSHIELD) + return FALSE; + // all Ki powers will not function when encumbered + if (GetIsEncumbered(oPC)) + return FALSE; + return TRUE; +} + +////////////////End Ninja////////////// + +////////////////Begin Virtuoso////////////// + +//Decrements the daily uses of Virtuoso Performance by the +// correct amount, returns FALSE if there are insufficient +// uses remaining to use the current feat +int VirtuosoPerformanceDecrement(object oPC, int nSpellID) +{ + int nDecrement = 0; + int nDifference = 1122; //hack, difference in number between feat and spell 2da lines + switch(nSpellID) + { + case SPELL_VIRTUOSO_SUSTAINING_SONG: + case SPELL_VIRTUOSO_CALUMNY: + case SPELL_VIRTUOSO_GREATER_CALUMNY: nDecrement = 1; break; + + case SPELL_VIRTUOSO_MINDBENDING_MELODY: + case SPELL_VIRTUOSO_MAGICAL_MELODY: + case SPELL_VIRTUOSO_REVEALING_MELODY: nDecrement = 2; break; + + case SPELL_VIRTUOSO_SHARP_NOTE: + case SPELL_VIRTUOSO_JARRING_SONG: + case SPELL_VIRTUOSO_SONG_OF_FURY: nDecrement = 3; break; + } + if(!nDecrement) return FALSE; //sanity check + int nUses = GetPersistantLocalInt(oPC, "Virtuoso_Performance_Uses"); + if(nUses >= nDecrement) + { + SetPersistantLocalInt(oPC, "Virtuoso_Performance_Uses", nUses - nDecrement); + int nFeat, nDec; + for(nFeat = FEAT_VIRTUOSO_SUSTAINING_SONG; nFeat <= FEAT_VIRTUOSO_PERFORMANCE; nFeat++) + { + nDec = nDecrement; + if(nFeat == (nSpellID + nDifference)) + nDec--; //already decremented once by being used + for(; nDec > 0; nDec--) + DecrementRemainingFeatUses(oPC, nFeat); + } + return TRUE; + } + else + { //refund feat use :P + IncrementRemainingFeatUses(oPC, nSpellID + nDifference); + return FALSE; + } +} + +////////////////End Virtuoso////////////// + + +///////////////Archmage & Heirophant SLAs /////////// + +void DoArchmageHeirophantSLA(object oPC, object oTarget, location lTarget, int nSLAID) +{ + int nSLAFeatID = -1; //feat ID of the SLA in use + int nSLASpellID = -1;//spell ID of the SLA in use NOT THE SPELL BEING CAST + //get the SLAFeatID + int SLA_ID; + switch(SLA_ID) + { + case 1: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_1; break; + case 2: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_2; break; + case 3: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_3; break; + case 4: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_4; break; + case 5: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_5; break; + } + //get the spellID of the spell your trying to cast + //+1 offset for unassigned + int nSpellID = GetPersistantLocalInt(oPC, "PRC_SLA_SpellID_"+IntToString(nSLAID))-1; + //test if already stored + if(nSpellID == -1) + { + //not stored + FloatingTextStringOnCreature("This SLA has not been stored yet\nThe next spell you cast will be assigned to this SLA", oPC); + SetLocalInt(oPC, "PRC_SLA_Store", nSLAID); + DelayCommand(18.0, + DeleteLocalInt(oPC, "PRC_SLA_Store")); + return; + } + else + { + //stored, recast it + int nSpellClass = GetPersistantLocalInt(oPC, "PRC_SLA_Class_"+IntToString(nSLAID)); + int nMetamagic = GetPersistantLocalInt(oPC, "PRC_SLA_Meta_"+IntToString(nSLAID)); + int nSpellLevel = PRCGetSpellLevelForClass(nSpellID, nSpellClass); + int nBaseDC = 10 + nSpellLevel + GetDCAbilityModForClass(nSpellClass, oPC); + //since this is targetted using a generic feat, + //make sure were within range and target is valid for this spell + //get current distance + /*string sRange = Get2DACache("spells", "Range", nSpellID); + float fDist; + if(GetIsObjectValid(oTarget)) + fDist = GetDistanceToObject(oTarget); + else + fDist = GetDistanceBetweenLocations(GetLocation(oPC), lTarget); + //check distance is allowed + if(fDist < 0.0 + || (sRange == "T" && fDist > 2.25) + || (sRange == "S" && fDist > 8.0 ) + || (sRange == "M" && fDist > 20.0 ) + || (sRange == "L" && fDist > 40.0 ) + ) + { + //out of range + FloatingTextStringOnCreature("You are out of range", oPC); + //replace the useage + IncrementRemainingFeatUses(oPC, nSLAFeatID); + //end the script + return; + }*/ + //check object type + int nTargetType = HexToInt(Get2DACache("spells", "TargetType", nSpellID)); + /* + # 0x01 = 1 = Self + # 0x02 = 2 = Creature + # 0x04 = 4 = Area/Ground + # 0x08 = 8 = Items + # 0x10 = 16 = Doors + # 0x20 = 32 = Placeables + */ + int nCaster = nTargetType & 1; + int nCreature = nTargetType & 2; + int nLocation = nTargetType & 4; + int nItem = nTargetType & 8; + int nDoor = nTargetType & 16; + int nPlaceable = nTargetType & 32; + int nTargetValid = TRUE; + //test targetting self + if(oTarget == OBJECT_SELF) + { + if(!nCaster) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target yourself", oPC); + } + } + //test targetting others + else if(GetIsObjectValid(oTarget)) + { + switch(GetObjectType(oTarget)) + { + case OBJECT_TYPE_CREATURE: + if(!nCreature) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target creatures", oPC); + } + break; + case OBJECT_TYPE_ITEM: + if(!nItem) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target items", oPC); + } + break; + case OBJECT_TYPE_DOOR: + if(!nDoor) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target doors", oPC); + } + break; + case OBJECT_TYPE_PLACEABLE: + if(!nPlaceable) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target placeables", oPC); + } + break; + } + } + //test if can target a location + else if(GetIsObjectValid(GetAreaFromLocation(lTarget))) + { + if(!nLocation) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target locations", oPC); + } + } + //target was not valid, abort + if(!nTargetValid) + { + //replace the useage + IncrementRemainingFeatUses(oPC, nSLAFeatID); + //end the script + return; + } + //actually cast it at this point + //note that these are instant-spells, so we have to add the animation part too + /*if(GetIsObjectValid(oTarget)) + ActionCastFakeSpellAtObject(nSpellID, oTarget); + else + ActionCastFakeSpellAtLocation(nSpellID, lTarget);*/ + ActionDoCommand(ActionCastSpell(nSpellID, 0, nBaseDC, 0, nMetamagic, nSpellClass, 0, 0, OBJECT_INVALID, FALSE)); + } +} + +/////////////// End Archmage & Heirophant SLAs /////////// + +////////////////////////Alienist////////////////////////// +int GetPhobia(object oPC) +{ + int nPhobia = GetPersistantLocalInt(oPC, "Alienist_Phobia"); + if(nPhobia < 1) + { + nPhobia = Random(16) + 1; + SetPersistantLocalInt(oPC, "Alienist_Phobia", nPhobia); + } + return nPhobia; +} + +int GetPhobiaRace(int nPhobia) +{ + switch(nPhobia) + { + case 1: return RACIAL_TYPE_ABERRATION; + case 2: return RACIAL_TYPE_ANIMAL; + case 3: return RACIAL_TYPE_BEAST; + case 4: return RACIAL_TYPE_CONSTRUCT; + case 5: return RACIAL_TYPE_DRAGON; + case 6: return RACIAL_TYPE_HUMANOID_GOBLINOID; + case 7: return RACIAL_TYPE_HUMANOID_MONSTROUS; + case 8: return RACIAL_TYPE_HUMANOID_ORC; + case 9: return RACIAL_TYPE_HUMANOID_REPTILIAN; + case 10: return RACIAL_TYPE_ELEMENTAL; + case 11: return RACIAL_TYPE_FEY; + case 12: return RACIAL_TYPE_GIANT; + case 13: return RACIAL_TYPE_MAGICAL_BEAST; + case 14: return RACIAL_TYPE_SHAPECHANGER; + case 15: return RACIAL_TYPE_UNDEAD; + case 16: return RACIAL_TYPE_VERMIN; + } + return -1;//error +} + +int GetPhobiaFeat(int nPhobia) +{ + switch(nPhobia) + { + case 1: return IP_CONST_PHOBIA_ABERRATION; + case 2: return IP_CONST_PHOBIA_ANIMAL; + case 3: return IP_CONST_PHOBIA_BEAST; + case 4: return IP_CONST_PHOBIA_CONSTRUCT; + case 5: return IP_CONST_PHOBIA_DRAGON; + case 6: return IP_CONST_PHOBIA_GOBLINOID; + case 7: return IP_CONST_PHOBIA_MONSTROUS; + case 8: return IP_CONST_PHOBIA_ORC; + case 9: return IP_CONST_PHOBIA_REPTILIAN; + case 10: return IP_CONST_PHOBIA_ELEMENTAL; + case 11: return IP_CONST_PHOBIA_FEY; + case 12: return IP_CONST_PHOBIA_GIANT; + case 13: return IP_CONST_PHOBIA_MAGICAL_BEAST; + case 14: return IP_CONST_PHOBIA_SHAPECHANGER; + case 15: return IP_CONST_PHOBIA_UNDEAD; + case 16: return IP_CONST_PHOBIA_VERMIN; + } + return -1;//error +} + +/////////////////////DragonSong Lyrist//////////////////////// + +void RemoveOldSongs(object oPC) +{ + if(GetHasSpellEffect(SPELL_DSL_SONG_STRENGTH, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_STRENGTH); + if(GetHasSpellEffect(SPELL_DSL_SONG_COMPULSION, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_COMPULSION); + if(GetHasSpellEffect(SPELL_DSL_SONG_SPEED, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_SPEED); + if(GetHasSpellEffect(SPELL_DSL_SONG_FEAR, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_FEAR); + if(GetHasSpellEffect(SPELL_DSL_SONG_HEALING, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_HEALING); +} + + +// Eldritch Theurge class requires arcane spellcasting and eldritch blast. +// If a character is an Eldritch Theruge we know that she must have levels in Warlock +// since in NWN character can have max 3 classes. We also know that Eldritch Theurge +// is at positon 3 (unless player is cheating). +// So we just need to check the third class. + +//:: [PRC .35] This function will require marker feats +int GetETArcaneClass(object oPC) +{ + int nClass = GetClassByPosition(1, oPC); + if(nClass == CLASS_TYPE_WARLOCK) + nClass = GetClassByPosition(2, oPC); + return nClass; +} + diff --git a/trunk/include/prc_inc_core.nss b/trunk/include/prc_inc_core.nss new file mode 100644 index 00000000..3ab2def1 --- /dev/null +++ b/trunk/include/prc_inc_core.nss @@ -0,0 +1,729 @@ +/* Core functions taken from high up the branch + which are needed lower. */ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Function Prototypes */ +////////////////////////////////////////////////// + +// wrapper for getspelltargetlocation +location PRCGetSpellTargetLocation(object oCaster = OBJECT_SELF); + +// Avoids adding passive spellcasting to the character's action queue by +// creating an object specifically to cast the spell on the character. +// +// NOTE: The spell script must refer to the PC as PRCGetSpellTargetObject() +// otherwise this function WILL NOT WORK. Do not make any assumptions +// about the PC being OBJECT_SELF. +void ActionCastSpellOnSelf(int iSpell, int nMetaMagic = METAMAGIC_NONE, object oTarget = OBJECT_SELF); + +// This is a wrapper function that causes OBJECT_SELF to fire the defined spell +// at the defined level. The target is automatically the object or location +// that the user selects. Useful for SLA's to perform the casting of a true +// spell. This is useful because: +// +// 1) If the original's spell script is updated, so is this one. +// 2) The spells are identified as the true spell. That is, they ARE the true spell. +// 3) Spellhooks (such as item crafting) that can only identify true spells +// will easily work. +// +// This function should only be used when SLA's are meant to simulate true +// spellcasting abilities, such as those seen when using feats with subradials +// to simulate spellbooks. +void ActionCastSpell(int iSpell, int iCasterLev = 0, int iBaseDC = 0, int iTotalDC = 0, + int nMetaMagic = METAMAGIC_NONE, int nClass = CLASS_TYPE_INVALID, + int bUseOverrideTargetLocation=FALSE, int bUseOverrideTargetObject=FALSE, + object oOverrideTarget=OBJECT_INVALID, int bInstantCast=TRUE, int bUseOverrideMetaMagic=FALSE); + +/** + * Checks whether the given creature is committing an action, or + * under such effects that cause a breach of concentration. + * + * @param oConcentrator The creature to test + * @return TRUE if concentration is broken, FALSE otherwise + */ +int GetBreakConcentrationCheck(object oConcentrator); + +/** + * Checks for breaks in concentration for an ongoing effect, and removes + * the effect if concentration is broken. + * + * @param oCaster The creature who cast the effect + * @param SpellID The id of the spell the effect belongs to + * @param oTarget The creature or object that is the target of the effect + * @param nDuration The duration the effect lasts in seconds. + */ +void CheckConcentrationOnEffect(object oCaster, int SpellID, object oTarget, int nDuration); + +// gets the spell level adjustment to the nMetaMagic, including boni from the Improved Metamagic (epic) feat +int GetMetaMagicSpellLevelAdjustment(int nMetaMagic); + +// Returns true if a spellcaster +int GetIsBioSpellCastClass(int nClass); + +// Returns true for spell casters with spellbooks +int GetIsNSBClass(int nClass); + +// returns the spelllevel of nSpell as it can be cast by oCreature +int PRCGetSpellLevel(object oCreature, int nSpell); + +// returns if a character should be using the newspellbook when casting +int UseNewSpellBook(object oCreature); + +// wrapper for GetHasSpell, works for newspellbook 'fake' spells too +// should return 0 if called with a normal spell when a character should be using the newspellbook +int PRCGetHasSpell(int nRealSpellID, object oCreature = OBJECT_SELF); + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +int PRCGetIsRealSpellKnown(int nRealSpellID, object oPC = OBJECT_SELF); + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +// this will only check the spellbook of the class specified +int PRCGetIsRealSpellKnownByClass(int nRealSpellID, int nClass, object oPC = OBJECT_SELF); + +//routes to action cast spell, but puts a wrapper around to tell other functions its a +//SLA, so dont craft etc +//also defaults the totalDC to 10+spellevel+chamod +// moved from prc_inc_racial +void DoRacialSLA(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0, int bInstantCast = FALSE); + +/** + * Deletes a stored manifestation structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalManifestation(object oObject, string sName); + +/** + * Deletes a stored mystery structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalMystery(object oObject, string sName); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// metamagic spell level adjustments for Bioware provided metamagic feats +const int METAMAGIC_EXTEND_LEVEL = 1; +const int METAMAGIC_SILENT_LEVEL = 1; +const int METAMAGIC_STILL_LEVEL = 1; +const int METAMAGIC_EMPOWER_LEVEL = 2; +const int METAMAGIC_MAXIMIZE_LEVEL = 3; +const int METAMAGIC_QUICKEN_LEVEL = 4; + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "lookup_2da_spell" +#include "inc_lookups" +#include "prc_inc_damage" +#include "prc_inc_sb_const" // Spell Book Constants +#include "x0_i0_position" + +/* + access to prc_inc_nwscript via prc_inc_damage + access to PRCGetSpell* via prc_inc_damage +*/ + + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + + +//wrapper for GetSpellTargetLocation() +location PRCGetSpellTargetLocation(object oCaster = OBJECT_SELF) +{ + // check if there is an override location on the module, and return that + // bioware did not define a LOCATION_INVALID const, so we must signal a valid override location by setting a local int on the module + if(GetLocalInt(GetModule(), PRC_SPELL_TARGET_LOCATION_OVERRIDE)) + { + if (DEBUG) DoDebug("PRCGetSpellTargetLocation: found override target location on module"); + return GetLocalLocation(GetModule(), PRC_SPELL_TARGET_LOCATION_OVERRIDE); + } + + + // check if there is an override location on the caster, and return that + // bioware did not define a LOCATION_INVALID const, so we signal a valid override location by setting a local int on oCaster + if (GetLocalInt(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE)) + { + if (DEBUG) DoDebug("PRCGetSpellTargetLocation: found override target location on caster "+GetName(oCaster)); + return GetLocalLocation(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE); + } + + + // The rune/gem/skull always targets the one who activates it. + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && (GetResRef(oItem) == "prc_rune_1" || + GetResRef(oItem) == "prc_skulltalis" || GetTag(oItem) == "prc_attunegem")) + return GetLocation(GetItemPossessor(oItem)); + + if (GetLocalInt(oCaster, "BlackLabyrinth") && d10() < 3) + return GenerateNewLocationFromLocation(GetSpellTargetLocation(), FeetToMeters(5.0*d4()), IntToFloat(Random(360)), IntToFloat(Random(360))); + + // if we made it here, we must use Bioware's function + return GetSpellTargetLocation(); +} + +void ActionCastSpellOnSelf(int iSpell, int nMetaMagic = METAMAGIC_NONE, object oTarget = OBJECT_SELF) +{ + if(!GetIsObjectValid(oTarget)) oTarget = OBJECT_SELF; + object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oTarget)); + + AssignCommand(oCastingObject, ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + if (DEBUG) DoDebug("ActionCastSpellOnSelf: Casting Spell "+IntToString(iSpell)+" on "+GetName(oTarget)); + + DestroyObject(oCastingObject, 6.0); +} + +void ActionCastSpell(int iSpell, int iCasterLev = 0, int iBaseDC = 0, int iTotalDC = 0, + int nMetaMagic = METAMAGIC_NONE, int nClass = CLASS_TYPE_INVALID, + int bUseOverrideTargetLocation=FALSE, int bUseOverrideTargetObject=FALSE, + object oOverrideTarget=OBJECT_INVALID, int bInstantCast=TRUE, int bUseOverrideMetaMagic=FALSE) +{ + + //if its a hostile spell, clear the action queue + //this stops people stacking hostile spells to be instacast + //at the end, for example when coming out of invisibility + // X - hope this is not needed if spells are cast normally + //if(Get2DACache("spells", "HostileSetting", iSpell) == "1" && bInstantCast) + // ClearAllActions(); + + object oTarget = PRCGetSpellTargetObject(); + location lLoc = PRCGetSpellTargetLocation(); + + //set the overriding values + if (iCasterLev != 0) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE, iCasterLev)); + if (iTotalDC != 0) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE, iTotalDC)); + if (iBaseDC != 0) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE, iBaseDC)); + if (nClass != CLASS_TYPE_INVALID) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE, nClass)); + if (bUseOverrideMetaMagic) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE, nMetaMagic)); + else if (nMetaMagic != METAMAGIC_NONE) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_METAMAGIC_ADJUSTMENT, nMetaMagic)); + if (bUseOverrideTargetLocation) + { + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE, TRUE)); + //location must be set outside of this function at the moment + //cant pass a location into a function as an optional parameter + //go bioware for not defining an invalid location constant + } + if (bUseOverrideTargetObject) + { + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE, TRUE)); + ActionDoCommand(SetLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE, oOverrideTarget)); + } + ActionDoCommand(SetLocalInt(OBJECT_SELF, "UsingActionCastSpell", TRUE)); + + if(DEBUG) DoDebug("ActionCastSpell SpellId: " + IntToString(iSpell)); + if(DEBUG) DoDebug("ActionCastSpell Caster Level: " + IntToString(iCasterLev)); + if(DEBUG) DoDebug("ActionCastSpell Base DC: " + IntToString(iBaseDC)); + if(DEBUG) DoDebug("ActionCastSpell Total DC: " + IntToString(iTotalDC)); + if(DEBUG) DoDebug("ActionCastSpell Metamagic: " + IntToString(nMetaMagic)); + if(DEBUG) DoDebug("ActionCastSpell Caster Class: " + IntToString(nClass)); + if(DEBUG) DoDebug("ActionCastSpell Target: " + GetName(oTarget)); + if(DEBUG) DoDebug("ActionCastSpell Override Target: " + GetName(oOverrideTarget)); + + //cast the spell + if (GetIsObjectValid(oOverrideTarget)) + ActionCastSpellAtObject(iSpell, oOverrideTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast); + else if (GetIsObjectValid(oTarget)) + ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast); + else + ActionCastSpellAtLocation(iSpell, lLoc, nMetaMagic, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast); + + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "UsingActionCastSpell")); + + //clean up afterwards + if(bInstantCast)//give scripts time to read the variables + { + if (iCasterLev != 0) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE))); + if (iTotalDC != 0) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE))); + if (iBaseDC != 0) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE))); + if (nClass != CLASS_TYPE_INVALID) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE))); + if (nMetaMagic != METAMAGIC_NONE) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE))); + if (bUseOverrideTargetLocation) + { + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE))); + //location must be set outside of this function at the moment + //cant pass a location into a function as an optional parameter + //go bioware for not defining an invalid location constant + } + if (bUseOverrideTargetObject) + { + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE))); + ActionDoCommand(DelayCommand(1.0, DeleteLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE))); + } + } + else + { + if (iCasterLev != 0) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE)); + if (iTotalDC != 0) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE)); + if (iBaseDC != 0) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE)); + if (nClass != CLASS_TYPE_INVALID) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE)); + if (bUseOverrideMetaMagic) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE)); + else if (nMetaMagic != METAMAGIC_NONE) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_ADJUSTMENT)); + if (bUseOverrideTargetLocation) + { + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE)); + //location must be set outside of this function at the moment + //cant pass a location into a function as an optional parameter + //go bioware for not defining an invalid location constant + } + if (bUseOverrideTargetObject) + { + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE)); + ActionDoCommand(DeleteLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE)); + } + } + + +/* +//The problem with this approace is that the effects are then applies by the original spell, which could go wrong. What to do? + SetLocalInt(OBJECT_SELF, PRC_SPELLID_OVERRIDE, GetSpellId()); + DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELLID_OVERRIDE)); + string sScript = Get2DACache("spells", "ImpactScript", iSpell); + ExecuteScript(sScript, OBJECT_SELF); +*/ +} + +int GetBreakConcentrationCheck(object oConcentrator) +{ + if (GetHasSpellEffect(VESTIGE_DAHLVERNAR, oConcentrator) && !GetLocalInt(oConcentrator, "PactQuality"+IntToString(VESTIGE_DAHLVERNAR))) return TRUE; + + int nAction = GetCurrentAction(oConcentrator); + // creature doing anything that requires attention and breaks concentration + if (nAction == ACTION_DISABLETRAP || nAction == ACTION_TAUNT || + nAction == ACTION_PICKPOCKET || nAction == ACTION_ATTACKOBJECT || + nAction == ACTION_COUNTERSPELL || nAction == ACTION_FLAGTRAP || + nAction == ACTION_CASTSPELL || nAction == ACTION_ITEMCASTSPELL) + { + return TRUE; + } + //suffering a mental effect + effect e1 = GetFirstEffect(oConcentrator); + int nType; + while (GetIsEffectValid(e1)) + { + nType = GetEffectType(e1); + if (nType == EFFECT_TYPE_STUNNED || nType == EFFECT_TYPE_PARALYZE || + nType == EFFECT_TYPE_SLEEP || nType == EFFECT_TYPE_FRIGHTENED || + nType == EFFECT_TYPE_PETRIFY || nType == EFFECT_TYPE_CONFUSED || + nType == EFFECT_TYPE_DOMINATED || nType == EFFECT_TYPE_POLYMORPH) + { + return TRUE; + } + e1 = GetNextEffect(oConcentrator); + } + // add to on damage event + AddEventScript(oConcentrator, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc", FALSE, FALSE); + if(GetLocalInt(oConcentrator, "CONC_BROKEN")) // won't be set first time around regardless + { + DeleteLocalInt(oConcentrator, "CONC_BROKEN"); // reset for next spell + return TRUE; + } + return FALSE; +} + +void CheckConcentrationOnEffect(object oCaster, int SpellID, object oTarget, int nDuration) +{ + int nDur = GetLocalInt(oCaster, "Conc" + IntToString(SpellID)); + if(GetBreakConcentrationCheck(oCaster) == TRUE && nDur < nDuration) + { + FloatingTextStringOnCreature("*Concentration Broken*", oCaster); + DeleteLocalInt(oCaster, "Conc" + IntToString(SpellID)); + PRCRemoveSpellEffects(SpellID, oCaster, oTarget); + } + else if(nDur < nDuration) + { + SetLocalInt(oCaster, "Conc" + IntToString(SpellID), nDur + 3); + DelayCommand(3.0, CheckConcentrationOnEffect(oCaster, SpellID, oTarget, nDuration)); + } + else + { + DeleteLocalInt(oCaster, "Conc" + IntToString(SpellID)); + } +} + +int PRCGetSpellLevelForClass(int nSpell, int nClass) +{ + string sSpellLevel = ""; + if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER) + sSpellLevel = Get2DACache("spells", "Wiz_Sorc", nSpell); + else if (nClass == CLASS_TYPE_RANGER) + sSpellLevel = Get2DACache("spells", "Ranger", nSpell); + else if (nClass == CLASS_TYPE_PALADIN) + sSpellLevel = Get2DACache("spells", "Paladin", nSpell); + else if (nClass == CLASS_TYPE_DRUID) + sSpellLevel = Get2DACache("spells", "Druid", nSpell); + else if (nClass == CLASS_TYPE_CLERIC || nClass == CLASS_TYPE_UR_PRIEST || nClass == CLASS_TYPE_OCULAR) + sSpellLevel = Get2DACache("spells", "Cleric", nSpell); + else if (nClass == CLASS_TYPE_BARD) + sSpellLevel = Get2DACache("spells", "Bard", nSpell); + else if (nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK) + sSpellLevel = Get2DACache("spells", "Cultist", nSpell); + else if (nClass == CLASS_TYPE_NENTYAR_HUNTER) + sSpellLevel = Get2DACache("spells", "Nentyar", nSpell); + else if (nClass == CLASS_TYPE_SHADOWLORD) + sSpellLevel = Get2DACache("spells", "Telflammar", nSpell); + else if (nClass == CLASS_TYPE_SLAYER_OF_DOMIEL) + sSpellLevel = Get2DACache("spells", "Domiel", nSpell); + else if (nClass == CLASS_TYPE_SOHEI) + sSpellLevel = Get2DACache("spells", "Sohei", nSpell); + else if (nClass == CLASS_TYPE_VASSAL) + sSpellLevel = Get2DACache("spells", "Bahamut", nSpell); + else if (nClass == CLASS_TYPE_BLACKGUARD) + sSpellLevel = Get2DACache("spells", "Blackguard", nSpell); + else if (nClass == CLASS_TYPE_KNIGHT_CHALICE) + sSpellLevel = Get2DACache("spells", "Chalice", nSpell); + else if (nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE) + sSpellLevel = Get2DACache("spells", "MiddleCircle", nSpell); + else if (nClass == CLASS_TYPE_SOLDIER_OF_LIGHT) + sSpellLevel = Get2DACache("spells", "SoLight", nSpell); + else if (nClass == CLASS_TYPE_BLIGHTER) + sSpellLevel = Get2DACache("spells", "Blighter", nSpell); + else if (nClass == CLASS_TYPE_HEALER) + sSpellLevel = Get2DACache("spells", "Healer", nSpell); + else if (nClass == CLASS_TYPE_SHAMAN) + sSpellLevel = Get2DACache("spells", "Shaman", nSpell); + else if (nClass == CLASS_TYPE_INVALID) + sSpellLevel = Get2DACache("spells", "Innate", nSpell); + + if (sSpellLevel != "") + return StringToInt(sSpellLevel); + + // 2009-9-21: Support real spell ID's. -N-S + // PRCGetSpellLevel() is called several times in the Bioware spellhooking script. + // That means it will always pass a "real" spell ID to this function, but new-spellbook users won't have the real spell! + // GetSpellLevel() takes the fake spell ID, so this function was always failing. + //int nSpellLevel = GetSpellLevel(nSpell, nClass); + int nSpellLevel = -1; + int nSpellbookID = RealSpellToSpellbookID(nClass, nSpell); + if (nSpellbookID == -1) + nSpellLevel = GetSpellLevel(nSpell, nClass); + else + { + string sFile = GetFileForClass(nClass); + string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookID); + if (sSpellLevel != "") + nSpellLevel = StringToInt(sSpellLevel); + } + + return nSpellLevel; +} + +// returns the spelllevel of nSpell as it can be cast by oCreature +int PRCGetSpellLevel(object oCreature, int nSpell) +{ + /*if (!PRCGetHasSpell(nSpell, oCreature)) + return -1;*/ + + int nClass = PRCGetLastSpellCastClass(); + int nSpellLevel = PRCGetSpellLevelForClass(nSpell, nClass); + if (nSpellLevel != -1) + return nSpellLevel; + + int i; + for (i=1;i<=8;i++) + { + nClass = GetClassByPosition(i, oCreature); + int nCharLevel = GetLevelByClass(nClass, oCreature); + if (nCharLevel) + { + nSpellLevel = PRCGetSpellLevelForClass(nSpell, nClass); + if (nSpellLevel != -1) + return nSpellLevel; + } + } + + //return innate level + return StringToInt(Get2DACache("spells", "Innate", nSpell)); +} + +// gets the spell level adjustment to the nMetaMagic, including boni from the Improved Metamagic (epic) feat +int GetMetaMagicSpellLevelAdjustment(int nMetaMagic) +{ + int nAdj; + if (nMetaMagic == 0) return nAdj; + + if (nMetaMagic & METAMAGIC_EXTEND) nAdj += METAMAGIC_EXTEND_LEVEL; + if (nMetaMagic & METAMAGIC_SILENT) nAdj += METAMAGIC_SILENT_LEVEL; + if (nMetaMagic & METAMAGIC_STILL) nAdj += METAMAGIC_STILL_LEVEL; + if (nMetaMagic & METAMAGIC_EMPOWER) nAdj += METAMAGIC_EMPOWER_LEVEL; + if (nMetaMagic & METAMAGIC_MAXIMIZE) nAdj += METAMAGIC_MAXIMIZE_LEVEL; + if (nMetaMagic & METAMAGIC_QUICKEN) nAdj += METAMAGIC_QUICKEN_LEVEL; + + return nAdj; +} + +int GetIsBioSpellCastClass(int nClass) +{ + return nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_HEALER + || nClass == CLASS_TYPE_BLIGHTER + || nClass == CLASS_TYPE_BLACKGUARD + || nClass == CLASS_TYPE_UR_PRIEST + || nClass == CLASS_TYPE_OCULAR + || nClass == CLASS_TYPE_SLAYER_OF_DOMIEL + || nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK + || nClass == CLASS_TYPE_NENTYAR_HUNTER + || nClass == CLASS_TYPE_SHADOWLORD + || nClass == CLASS_TYPE_SOHEI + || nClass == CLASS_TYPE_SOLDIER_OF_LIGHT + || nClass == CLASS_TYPE_VASSAL + || nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE + || nClass == CLASS_TYPE_KNIGHT_CHALICE + || nClass == CLASS_TYPE_SHAMAN + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER; +} + +int GetIsNSBClass(int nClass) +{ + return !GetIsBioSpellCastClass(nClass) + && GetSpellbookTypeForClass(nClass) != SPELLBOOK_TYPE_INVALID; +} + +// returns if a character should be using the newspellbook when casting +int UseNewSpellBook(object oCreature) +{ + int i; + for (i = 1; i <= 8; i++) + { + int nClass = GetClassByPosition(i, oCreature); + if(GetIsNSBClass(nClass)) + return TRUE; + } + + // Special case + if(GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCreature)) + return TRUE; + + int nPrimaryArcane = GetPrimaryArcaneClass(oCreature); + + //check they have bard/sorc in first arcane slot + if(nPrimaryArcane != CLASS_TYPE_BARD && nPrimaryArcane != CLASS_TYPE_SORCERER) + return FALSE; + //check they have arcane PrC or Draconic Breath/Arcane Grace + if(!GetArcanePRCLevels(oCreature) + && !(GetHasFeat(FEAT_DRACONIC_GRACE, oCreature) || GetHasFeat(FEAT_DRACONIC_BREATH, oCreature))) + return FALSE; + //check if the newspellbooks are disabled + if((GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK) && nPrimaryArcane == CLASS_TYPE_SORCERER) || + (GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK) && nPrimaryArcane == CLASS_TYPE_BARD)) + return FALSE; + //check they have bard/sorc levels + if(!GetLevelByClass(CLASS_TYPE_BARD) && !GetLevelByClass(CLASS_TYPE_SORCERER)) + return FALSE; + + //at this point, they should be using the new spellbook + return TRUE; +} + +// wrapper for GetHasSpell, works for newspellbook 'fake' spells too (and metamagic) +// should return 0 if called with a normal spell when a character should be using the newspellbook +int PRCGetHasSpell(int nRealSpellID, object oCreature = OBJECT_SELF) +{ + if(!PRCGetIsRealSpellKnown(nRealSpellID, oCreature)) + return 0; + int nUses = GetHasSpell(nRealSpellID, oCreature); + + int nClass, nSpellbookID, nCount, nMeta, i, j; + int nSpellbookType, nSpellLevel; + string sFile, sFeat; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oCreature); + sFile = GetFileForClass(nClass); + nSpellbookType = GetSpellbookTypeForClass(nClass); + nSpellbookID = RealSpellToSpellbookID(nClass, nRealSpellID); + nMeta = RealSpellToSpellbookIDCount(nClass, nRealSpellID); + if (nSpellbookID != -1) + { //non-spellbook classes should return -1 + for(j = nSpellbookID; j <= nSpellbookID + nMeta; j++) + { + sFeat = Get2DACache(sFile, "ReqFeat", j); + if(sFeat != "") + { + if(!GetHasFeat(StringToInt(sFeat), oCreature)) + continue; + } + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j); + if(DEBUG) DoDebug("PRCGetHasSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j)); + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(DEBUG) DoDebug("PRCGetHasSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + } + } + } + + if(DEBUG) DoDebug("PRCGetHasSpell: RealSpellID = " + IntToString(nRealSpellID) + ", Uses = " + IntToString(nUses)); + return nUses; +} + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +int PRCGetIsRealSpellKnown(int nRealSpellID, object oPC = OBJECT_SELF) +{ + if(GetHasSpell(nRealSpellID, oPC)) //FUGLY HACK: bioware class having uses of the spell + return TRUE; // means they know the spell (close enough) + int nClass; + int nClassSlot = 1; + while(nClassSlot <= 8) + { + nClass = GetClassByPosition(nClassSlot, oPC); + if(GetIsDivineClass(nClass) || GetIsArcaneClass(nClass)) + if(PRCGetIsRealSpellKnownByClass(nRealSpellID, nClass, oPC)) + return TRUE; + nClassSlot++; + } + // got here means no match + return FALSE; +} + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +// this will only check the spellbook of the class specified +int PRCGetIsRealSpellKnownByClass(int nRealSpellID, int nClass, object oPC = OBJECT_SELF) +{ + // check for whether bard and sorc are using the prc spellbooks + if (nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_SORCERER) + { + if(!UseNewSpellBook(oPC)) + return FALSE; + } + + // get the cls_spell_***.2da index for the real spell + int nSpellbookSpell = RealSpellToSpellbookID(nClass, nRealSpellID); + // if the spell does not exist in the spellbook, return FALSE + if (nSpellbookSpell == -1) + return FALSE; + // next check if the PC is high enough level to know the spell + string sFile = GetFileForClass(nClass); + int nSpellLevel = -1; + string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookSpell); + if (sSpellLevel != "") + nSpellLevel = StringToInt(sSpellLevel); + if ((GetLevelByClass(nClass) < nSpellLevel) || nSpellLevel == -1) + return FALSE; // not high enough level + // at this stage, prepared casters know the spell and only spontaneous classes need checking + // there are exceptions and these need hardcoding: + + if((GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED) && nClass != CLASS_TYPE_ARCHIVIST) + return TRUE; + + // spontaneous casters have all their known spells as hide feats + // get the featID of the spell + int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookSpell)); + if (GetHasFeat(nFeatID, oPC)) + return TRUE; + + return FALSE; +} + +//routes to action cast spell, but puts a wrapper around to tell other functions its a +//SLA, so dont craft etc +//also defaults th totalDC to 10+spellevel+chamod +//this is Base DC, not total DC. SLAs are still spells, so spell focus should still apply. +void DoRacialSLA(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0, int bInstantCast = FALSE) +{ + if(DEBUG) DoDebug("Spell DC passed to DoRacialSLA: " + IntToString(nTotalDC)); + if(nTotalDC == 0) + nTotalDC = 10 + +StringToInt(Get2DACache("spells", "Innate", nSpellID)) + +GetAbilityModifier(ABILITY_CHARISMA); + + ActionDoCommand(SetLocalInt(OBJECT_SELF, "SpellIsSLA", TRUE)); + if(DEBUG) DoDebug("Spell DC entered in ActionCastSpell: " + IntToString(nTotalDC)); + ActionCastSpell(nSpellID, nCasterlevel, 0, nTotalDC, METAMAGIC_NONE, CLASS_TYPE_INVALID, FALSE, FALSE, OBJECT_INVALID, bInstantCast); + //ActionCastSpell(nSpellID, nCasterlevel, 0, nTotalDC); + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "SpellIsSLA")); +} + +void DeleteLocalManifestation(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oManifester"); + + DeleteLocalInt(oObject, sName + "_bCanManifest"); + DeleteLocalInt(oObject, sName + "_nPPCost"); + DeleteLocalInt(oObject, sName + "_nPsiFocUsesRemain"); + DeleteLocalInt(oObject, sName + "_nManifesterLevel"); + DeleteLocalInt(oObject, sName + "_nSpellID"); + + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_1"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_2"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_3"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_4"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_5"); + DeleteLocalInt(oObject, sName + "_nTimesGenericAugUsed"); + + DeleteLocalInt(oObject, sName + "_bChain"); + DeleteLocalInt(oObject, sName + "_bEmpower"); + DeleteLocalInt(oObject, sName + "_bExtend"); + DeleteLocalInt(oObject, sName + "_bMaximize"); + DeleteLocalInt(oObject, sName + "_bSplit"); + DeleteLocalInt(oObject, sName + "_bTwin"); + DeleteLocalInt(oObject, sName + "_bWiden"); + DeleteLocalInt(oObject, sName + "_bQuicken"); +} + +void DeleteLocalMystery(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oShadow"); + + DeleteLocalInt(oObject, sName + "_bCanMyst"); + DeleteLocalInt(oObject, sName + "_nShadowcasterLevel"); + DeleteLocalInt(oObject, sName + "_nMystId"); + DeleteLocalInt(oObject, sName + "_nPen"); + DeleteLocalInt(oObject, sName + "_bIgnoreSR"); + + DeleteLocalInt(oObject, sName + "_bEmpower"); + DeleteLocalInt(oObject, sName + "_bExtend"); + DeleteLocalInt(oObject, sName + "_bMaximize"); + DeleteLocalInt(oObject, sName + "_bQuicken"); + + DeleteLocalInt(oObject, sName + "_nSaveDC"); + DeleteLocalFloat(oObject, sName + "_fDur"); +} + diff --git a/trunk/include/prc_inc_domain.nss b/trunk/include/prc_inc_domain.nss new file mode 100644 index 00000000..d0709086 --- /dev/null +++ b/trunk/include/prc_inc_domain.nss @@ -0,0 +1,585 @@ +//:://///////////////////////////////////////////// +//:: PRC Bonus Domains +//:: prc_inc_domain.nss +//::////////////////////////////////////////////// +//:: Handles all of the code for bonus domains. +//::////////////////////////////////////////////// +//:: Created By: Stratovarius. +//:: Created On: August 31st, 2005 +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + + +// Function returns the domain in the input slot. +// A person can have a maximum of 5 bonus domains. +int GetBonusDomain(object oPC, int nSlot); + +// Function will add a bonus domain to the stored list on the character. +void AddBonusDomain(object oPC, int nDomain); + +// Uses the slot and level to find the appropriate spell, then casts it using ActionCastSpell +// It will also decrement a spell from that level +// If the domain does not have an appropriate spell for that level, an error message appears and nothing happens +void CastDomainSpell(object oPC, int nSlot, int nLevel); + +// Takes the domain and spell level and uses it to find the appropriate spell. +// Right now it uses 2da reads on the domains.2da, although it could be scripted if desired. +int GetDomainSpell(int nDomain, int nLevel, object oPC); + +// Takes the spell level, and returns the radial feat for that level. +// Used in case there is no spell of the appropriate level. +int SpellLevelToFeat(int nLevel); + +// Will return the domain name as a string +// This is used to tell a PC what domains he has in what slot +string GetDomainName(int nDomain); + +// This is the starter function, and fires from Enter and Levelup +// It checks all of the bonus domain feats, and gives the PC the correct domains +void CheckBonusDomains(object oPC); + +// Returns the spell to be burned for CastDomainSpell +int GetBurnableSpell(object oPC, int nLevel); + +// Returns the Domain Power feat +int GetDomainFeat(int nDomain); + +// Returns the Uses per day of the feat entered +int GetDomainFeatUsesPerDay(int nFeat, object oPC); + +// This counts down the number of times a domain has been used in a day +// Returns TRUE if the domain use is valid +// Returns FALSE if the player is out of uses per day +int DecrementDomainUses(int nDomain, object oPC); + +// Used to determine which domain has cast the Turn Undead spell +// Returns the domain constant +int GetTurningDomain(int nSpell); + +// Checks to see if the player has a domain. +// Looks for the domain power constants since every domain has those +int GetHasDomain(object oPC, int nDomain); + +// Cleans the ints that limit the domain spells to being cast 1/day +void BonusDomainRest(object oPC); + +//#include "prc_inc_clsfunc" +#include "prc_alterations" +#include "prc_getbest_inc" +#include "inc_dynconv" + +int GetBonusDomain(object oPC, int nSlot) +{ + /*string sName = "PRCBonusDomain" + IntToString(nSlot); + // Return value in case there is nothing in the slot + int nDomain = 0; + nDomain = GetPersistantLocalInt(oPC, sName);*/ + + return GetPersistantLocalInt(oPC, "PRCBonusDomain" + IntToString(nSlot)); +} + +void AddBonusDomain(object oPC, int nDomain) +{ + //if(DEBUG) DoDebug("AddBonusDomain is running."); + + // Loop through the domain slots to see if there is an open one. + int nSlot = 1; + int nTest = GetBonusDomain(oPC, nSlot); + while(nTest > 0 && 5 >= nSlot) + { + nSlot += 1; + // If the test domain and the domain to be added are the same + // shut down the function, since you don't want to add a domain twice. + if(nTest == nDomain) + { + //FloatingTextStringOnCreature("You already have this domain as a bonus domain.", oPC, FALSE); + return; + } + nTest = GetBonusDomain(oPC, nSlot); + } + // If you run out of slots, display message and end function + if (nSlot > 5) + { + FloatingTextStringOnCreature("You have more than 5 bonus domains, your last domain is lost.", oPC, FALSE); + return; + } + + // If we're here, we know we have an open slot, so we add the domain into it. + string sName = "PRCBonusDomain" + IntToString(nSlot); + SetPersistantLocalInt(oPC, sName, nDomain); + FloatingTextStringOnCreature("You have " + GetStringByStrRef(StringToInt(Get2DACache("prc_domains", "Name", nDomain - 1))) + " as a bonus domain", oPC, FALSE); +} + +int TestSpellTarget(object oPC, object oTarget, int nSpell) +{ + int nTargetType = ~(HexToInt(Get2DACache("spells", "TargetType", nSpell))); + + if(oTarget == oPC && nTargetType & 1) + { + SendMessageToPC(oPC, "You cannot target yourself!"); + return FALSE; + } + else if(GetIsObjectValid(oTarget)) + { + int nObjectType = GetObjectType(oTarget); + if(nObjectType == OBJECT_TYPE_CREATURE && nTargetType & 2) + { + SendMessageToPC(oPC, "You cannot target creatures"); + return FALSE; + } + else if(nObjectType == OBJECT_TYPE_ITEM && nTargetType & 8) + { + SendMessageToPC(oPC, "You cannot target items"); + return FALSE; + } + else if(nObjectType == OBJECT_TYPE_DOOR && nTargetType & 16) + { + SendMessageToPC(oPC, "You cannot target doors"); + return FALSE; + } + else if(nObjectType == OBJECT_TYPE_PLACEABLE && nTargetType & 32) + { + SendMessageToPC(oPC, "You cannot target placeables"); + return FALSE; + } + } + else if(nTargetType & 4) + { + SendMessageToPC(oPC, "You cannot target locations"); + return FALSE; + } + return TRUE; +} + +// Classes using new spellbook systems are handeled separately +int GetIsBioDivineClass(int nClass) +{ + return nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_UR_PRIEST + || nClass == CLASS_TYPE_RANGER; +} + +void CastDomainSpell(object oPC, int nSlot, int nLevel) +{ + if(GetLocalInt(oPC, "DomainCastSpell" + IntToString(nLevel))) //Already cast a spell of this level? + { + FloatingTextStringOnCreature("You have already cast your domain spell for level " + IntToString(nLevel), oPC, FALSE); + return; + } + + int nSpell = GetDomainSpell(GetBonusDomain(oPC, nSlot), nLevel, oPC); + // If there is no spell for that level, you cant cast it. + if(nSpell == -1) + return; + + // Subradial spells are handled through conversation + int bSubRadial = Get2DACache("spells", "SubRadSpell1", nSpell) != ""; + + // Domain casting feats use generic targeting, so check if spell can be cast at selected target + object oTarget = GetSpellTargetObject(); + if(!bSubRadial && !TestSpellTarget(oPC, oTarget, nSpell)) + return; + + int nClass, nCount, nMetamagic = METAMAGIC_NONE; + + // Mystic is a special case - checked first + if(GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) || GetLevelByClass(CLASS_TYPE_NIGHTSTALKER, oPC)) + { + // Mystics can use metamagic with domain spells + nClass = GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) ? CLASS_TYPE_MYSTIC : CLASS_TYPE_NIGHTSTALKER; + nMetamagic = GetLocalInt(oPC, "MetamagicFeatAdjust"); + int nSpellLevel = nLevel; + if(nMetamagic) + { + //Need to check if metamagic can be applied to a spell + int nMetaTest; + int nMetaType = HexToInt(Get2DACache("spells", "MetaMagic", nSpell)); + + switch(nMetamagic) + { + case METAMAGIC_NONE: nMetaTest = 1; break; //no need to change anything + case METAMAGIC_EMPOWER: nMetaTest = nMetaType & 1; nSpellLevel += 2; break; + case METAMAGIC_EXTEND: nMetaTest = nMetaType & 2; nSpellLevel += 1; break; + case METAMAGIC_MAXIMIZE: nMetaTest = nMetaType & 4; nSpellLevel += 3; break; + case METAMAGIC_QUICKEN: nMetaTest = nMetaType & 8; nSpellLevel += 4; break; + case METAMAGIC_SILENT: nMetaTest = nMetaType & 16; nSpellLevel += 1; break; + case METAMAGIC_STILL: nMetaTest = nMetaType & 32; nSpellLevel += 1; break; + } + if(!nMetaTest)//can't use selected metamagic with this spell + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "You can't use "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpell)))+"with selected metamagic.")); + nSpellLevel = nLevel; + } + else if(nLevel > 9)//now test the spell level + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "Modified spell level is to high! Casting spell without metamagic")); + nSpellLevel = nLevel; + } + else if(GetLocalInt(oPC, "PRC_metamagic_state") == 1) + SetLocalInt(oPC, "MetamagicFeatAdjust", 0); + } + + nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(CLASS_TYPE_MYSTIC), nSpellLevel); + // we can't cast metamagiced version of the spell - assuming that player want to cast the spell anyway + if(!nCount) + nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(CLASS_TYPE_MYSTIC), nLevel); + // Do we have slots available? + if(nCount) + { + // Prepare to cast the spell + nLevel = nSpellLevel;//correct the spell level if we're using metamagic + SetLocalInt(oPC, "NSB_Class", nClass); + SetLocalInt(oPC, "NSB_SpellLevel", nLevel); + } + } + + // checking 'newspellbook' classes is much faster than checking bioware spellbooks + if(!nCount) + { + int n; + for(n = 1; n < 8; n++) + { + nClass = GetClassByPosition(n, oPC); + + // Check to see if you can burn a spell of that slot or if the person has already + // cast all of their level X spells for the day + if(!GetIsBioDivineClass(nClass)) + { + int nSpellbook = GetSpellbookTypeForClass(nClass); + if(nSpellbook == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nLevel); + if(nCount) + {// Prepare to cast the spell + SetLocalInt(oPC, "NSB_Class", nClass); + SetLocalInt(oPC, "NSB_SpellLevel", nLevel); + } + } + else if(nSpellbook == SPELLBOOK_TYPE_PREPARED) + { + string sArray = "NewSpellbookMem_"+IntToString(nClass); + string sIDX = "SpellbookIDX" + IntToString(nLevel) + "_" + IntToString(nClass); + int i, nSpellbookID, nMax = persistant_array_get_size(oPC, sIDX); + for(i = 0; i < nMax; i++) + { + nSpellbookID = persistant_array_get_int(oPC, sIDX, i); + nCount = persistant_array_get_int(oPC, sArray, nSpellbookID); + if(nCount) + { + SetLocalInt(oPC, "NSB_Class", nClass); + SetLocalInt(oPC, "NSB_SpellbookID", nSpellbookID); + break; + } + } + } + } + if(nCount) + //we have found valid spell slot, no point in running this loop again + break; + } + } + + // test bioware spellbooks + if(!nCount) + { + nCount = GetBurnableSpell(oPC, nLevel) + 1;//fix for Acid Fog spell + if(nCount) + { + SetLocalInt(oPC, "Domain_BurnableSpell", nCount); + nClass = GetPrimaryDivineClass(oPC); + } + } + + //No spell left to burn? Tell the player that. + if(!nCount) + { + FloatingTextStringOnCreature("You have no spells left to trade for a domain spell.", oPC, FALSE); + return; + } + + SetLocalInt(oPC, "DomainCast", nLevel); + if(bSubRadial) + { + SetLocalInt(oPC, "DomainOrigSpell", nSpell); + SetLocalInt(oPC, "DomainCastClass", nClass); + SetLocalObject(oPC, "DomainTarget", oTarget); + SetLocalLocation(oPC, "DomainTarget", GetSpellTargetLocation()); + StartDynamicConversation("prc_domain_conv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC); + } + else + { + if(nMetamagic & METAMAGIC_QUICKEN) + { + //Adding Auto-Quicken III for one round - deleted after casting is finished. + object oSkin = GetPCSkin(oPC); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpell)) + StringToInt(Get2DACache("spells", "CastTime", nSpell)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + } + int nDC = 10 + nLevel + GetDCAbilityModForClass(nClass, oPC); + ActionCastSpell(nSpell, 0, nDC, 0, nMetamagic, nClass, FALSE, FALSE, OBJECT_INVALID, FALSE); + ActionDoCommand(DeleteLocalInt(oPC, "DomainCast")); + } +} + +int GetDomainSpell(int nDomain, int nLevel, object oPC) +{ + // The -1 on nDomains is to adjust from a base 1 to a base 0 system. + string sSpell = Get2DACache("prc_domains", "Level_" + IntToString(nLevel), (nDomain - 1)); + if (DEBUG) DoDebug("Domain Spell: " + sSpell); + //if (DEBUG) DoDebug("GetDomainSpell has fired"); + int nSpell = -1; + if(sSpell == "") + { + FloatingTextStringOnCreature("You do not have a domain spell of that level.", oPC, FALSE); + //int nFeat = SpellLevelToFeat(nLevel); + //IncrementRemainingFeatUses(oPC, nFeat); + } + else + { + nSpell = StringToInt(sSpell); + } + + return nSpell; +} + +int SpellLevelToFeat(int nLevel) +{ + switch(nLevel) + { + case 1: return FEAT_CAST_DOMAIN_LEVEL_ONE; + case 2: return FEAT_CAST_DOMAIN_LEVEL_TWO; + case 3: return FEAT_CAST_DOMAIN_LEVEL_THREE; + case 4: return FEAT_CAST_DOMAIN_LEVEL_FOUR; + case 5: return FEAT_CAST_DOMAIN_LEVEL_FIVE; + case 6: return FEAT_CAST_DOMAIN_LEVEL_SIX; + case 7: return FEAT_CAST_DOMAIN_LEVEL_SEVEN; + case 8: return FEAT_CAST_DOMAIN_LEVEL_EIGHT; + case 9: return FEAT_CAST_DOMAIN_LEVEL_NINE; + } + + return -1; +} + +string GetDomainName(int nDomain) +{ + string sName; + // Check that the domain slot is not empty + if(nDomain) + { + sName = Get2DACache("prc_domains", "Name", (nDomain - 1)); + sName = GetStringByStrRef(StringToInt(sName)); + } + else + sName = GetStringByStrRef(6497); // "Empty Slot" + + return sName; +} + +void CheckBonusDomains(object oPC) +{ + int nBonusDomain, nDomainFeat; + int nSlot = 1; + while(nSlot < 6) + { + nBonusDomain = GetBonusDomain(oPC, nSlot); + nDomainFeat = GetDomainFeat(nBonusDomain); + if(!GetHasFeat(nDomainFeat, oPC)) SetPersistantLocalInt(oPC, "PRCBonusDomain" + IntToString(nSlot), 0); + //SendMessageToPC(oPC, "PRCBonusDomain"+IntToString(nSlot)" = "+IntToString(nBonusDomain)); + //SendMessageToPC(oPC, "PRCBonusDomain"+IntToString(nSlot)" feat = "+IntToString(GetDomainFeat(nDomainFeat))); + nSlot += 1; + } + + if (GetHasFeat(FEAT_BONUS_DOMAIN_AIR, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_AIR); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ANIMAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ANIMAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DEATH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DEATH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DESTRUCTION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DESTRUCTION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_EARTH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_EARTH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_EVIL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_EVIL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FIRE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FIRE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_GOOD, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_GOOD); + if (GetHasFeat(FEAT_BONUS_DOMAIN_HEALING, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_HEALING); + if (GetHasFeat(FEAT_BONUS_DOMAIN_KNOWLEDGE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_KNOWLEDGE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_MAGIC, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_MAGIC); + if (GetHasFeat(FEAT_BONUS_DOMAIN_PLANT, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_PLANT); + if (GetHasFeat(FEAT_BONUS_DOMAIN_PROTECTION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_PROTECTION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_STRENGTH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_STRENGTH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SUN); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TRAVEL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TRAVEL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TRICKERY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TRICKERY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_WAR, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_WAR); + if (GetHasFeat(FEAT_BONUS_DOMAIN_WATER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_WATER); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DARKNESS, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DARKNESS); + if (GetHasFeat(FEAT_BONUS_DOMAIN_STORM, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_STORM); + if (GetHasFeat(FEAT_BONUS_DOMAIN_METAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_METAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_PORTAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_PORTAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FORCE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FORCE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SLIME, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SLIME); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TYRANNY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TYRANNY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DOMINATION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DOMINATION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SPIDER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SPIDER); + if (GetHasFeat(FEAT_BONUS_DOMAIN_UNDEATH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_UNDEATH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TIME, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TIME); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DWARF, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DWARF); + if (GetHasFeat(FEAT_BONUS_DOMAIN_CHARM, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_CHARM); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ELF, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ELF); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FAMILY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FAMILY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FATE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FATE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_GNOME, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_GNOME); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ILLUSION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ILLUSION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_HATRED, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_HATRED); + if (GetHasFeat(FEAT_BONUS_DOMAIN_HALFLING, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_HALFLING); + if (GetHasFeat(FEAT_BONUS_DOMAIN_NOBILITY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_NOBILITY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_OCEAN, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_OCEAN); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ORC, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ORC); + if (GetHasFeat(FEAT_BONUS_DOMAIN_RENEWAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_RENEWAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_RETRIBUTION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_RETRIBUTION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_RUNE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_RUNE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SPELLS, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SPELLS); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SCALEYKIND, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SCALEYKIND); + if (GetHasFeat(FEAT_BONUS_DOMAIN_BLIGHTBRINGER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_BLIGHTBRINGER); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DRAGON, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DRAGON); + if (GetHasFeat(FEAT_BONUS_DOMAIN_COLD, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_COLD); + if (GetHasFeat(FEAT_BONUS_DOMAIN_WINTER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_WINTER); + + //if (DEBUG) FloatingTextStringOnCreature("Check Bonus Domains is running", oPC, FALSE); +} + +int GetBurnableSpell(object oPC, int nLevel) +{ + int nBurnableSpell = -1; + + if (nLevel == 1) nBurnableSpell = GetBestL1Spell(oPC, nBurnableSpell); + else if (nLevel == 2) nBurnableSpell = GetBestL2Spell(oPC, nBurnableSpell); + else if (nLevel == 3) nBurnableSpell = GetBestL3Spell(oPC, nBurnableSpell); + else if (nLevel == 4) nBurnableSpell = GetBestL4Spell(oPC, nBurnableSpell); + else if (nLevel == 5) nBurnableSpell = GetBestL5Spell(oPC, nBurnableSpell); + else if (nLevel == 6) nBurnableSpell = GetBestL6Spell(oPC, nBurnableSpell); + else if (nLevel == 7) nBurnableSpell = GetBestL7Spell(oPC, nBurnableSpell); + else if (nLevel == 8) nBurnableSpell = GetBestL8Spell(oPC, nBurnableSpell); + else if (nLevel == 9) nBurnableSpell = GetBestL9Spell(oPC, nBurnableSpell); + + return nBurnableSpell; +} + +int GetDomainFeat(int nDomain) +{ + // The -1 on nDomain is to adjust from a base 1 to a base 0 system. + // Returns the domain power feat + return StringToInt(Get2DACache("domains", "GrantedFeat", nDomain - 1)); +} + +int GetDomainFeatUsesPerDay(int nFeat, object oPC) +{ + int nUses = StringToInt(Get2DACache("feat", "USESPERDAY", nFeat)); + // These are the domains that have ability based uses per day + if (nUses == 33) + { + // The Strength domain, which uses Strength when the Cleric has Kord levels + // Without Kord levels, its 1 use per day + if(nFeat == FEAT_STRENGTH_DOMAIN_POWER) + { + nUses = 1; + if(GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oPC)) nUses = GetAbilityModifier(ABILITY_STRENGTH, oPC); + // Catching exceptions + if(nUses < 1) nUses = 1; + } + if(nFeat == FEAT_SUN_DOMAIN_POWER) + { + if(GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC) && GetLevelByClass(CLASS_TYPE_MYSTIC, oPC)) + { + nUses = GetHasFeat(FEAT_EXTRA_TURNING, oPC) ? 7 : 3; + nUses += GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + else + nUses = 1; + } + + // All other ones so far are the Charisma based turning domains + nUses = 3 + GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + + return nUses; +} + +int DecrementDomainUses(int nDomain, object oPC) +{ + int nReturn = TRUE; + int nUses = GetLocalInt(oPC, "BonusDomainUsesPerDay" + GetDomainName(nDomain)); + // If there is still a valid use left, remove it + if (nUses >= 1) SetLocalInt(oPC, "BonusDomainUsesPerDay" + GetDomainName(nDomain), (nUses - 1)); + // Tell the player how many uses he has left + else // He has no more uses for the day + { + nReturn = FALSE; + } + + FloatingTextStringOnCreature("You have " + IntToString(nUses - 1) + " uses per day left of the " + GetDomainName(nDomain) + " power.", oPC, FALSE); + + return nReturn; +} + +int GetTurningDomain(int nSpell) +{ + switch(nSpell) + { + case SPELL_TURN_REPTILE: return PRC_DOMAIN_SCALEYKIND; + case SPELL_TURN_OOZE: return PRC_DOMAIN_SLIME; + case SPELL_TURN_SPIDER: return PRC_DOMAIN_SPIDER; + case SPELL_TURN_PLANT: return PRC_DOMAIN_PLANT; + case SPELL_TURN_AIR: return PRC_DOMAIN_AIR; + case SPELL_TURN_EARTH: return PRC_DOMAIN_EARTH; + case SPELL_TURN_FIRE: return PRC_DOMAIN_FIRE; + case SPELL_TURN_WATER: return PRC_DOMAIN_WATER; + case SPELL_TURN_BLIGHTSPAWNED: return PRC_DOMAIN_BLIGHTBRINGER; + } + + return -1; +} + +int GetHasDomain(object oPC, int nDomain) +{ + // Get the domain power feat for the appropriate domain + int nFeat = GetDomainFeat(nDomain); + + return GetHasFeat(nFeat, oPC); +} + +void BonusDomainRest(object oPC) +{ + // Bonus Domain ints that limit you to casting 1/day per level + int i; + for (i = 1; i < 10; i++) + { + DeleteLocalInt(oPC, "DomainCastSpell" + IntToString(i)); + } + + // This is code to stop you from using the Domain per day abilities more than you should be able to + int i2; + // Highest domain constant is 59 + for (i2 = 1; i2 < 60; i2++) + { + // This is to ensure they only get the ints set for the domains they do have + if (GetHasDomain(oPC, i2)) + { + // Store the number of uses a day here + SetLocalInt(oPC, "BonusDomainUsesPerDay" + GetDomainName(i2), GetDomainFeatUsesPerDay(GetDomainFeat(i2), oPC)); + } + } +} + +int GetDomainCasterLevel(object oPC) +{ + return GetLevelByClass(CLASS_TYPE_CLERIC, oPC) + + GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) + + GetLevelByClass(CLASS_TYPE_SHAMAN, oPC) + + GetLevelByClass(CLASS_TYPE_TEMPLAR, oPC) + + GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oPC) + + GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oPC) + + GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oPC); +} \ No newline at end of file diff --git a/trunk/include/prc_inc_function.nss b/trunk/include/prc_inc_function.nss new file mode 100644 index 00000000..2fa5d15c --- /dev/null +++ b/trunk/include/prc_inc_function.nss @@ -0,0 +1,1837 @@ +//:://///////////////////////////////////////////// +//:: [PRC Feat Router] +//:: [inc_prc_function.nss] +//::////////////////////////////////////////////// +//:: This file serves as a hub for the various +//:: PRC passive feat functions. If you need to +//:: add passive feats for a new PRC, link them here. +//:: +//:: This file also contains a few multi-purpose +//:: PRC functions that need to be included in several +//:: places, ON DIFFERENT PRCS. Make local include files +//:: for any functions you use ONLY on ONE PRC. +//::////////////////////////////////////////////// +//:: Created By: Aaon Graywolf +//:: Created On: Dec 19, 2003 +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + +//-------------------------------------------------------------------------- +// This is the "event" that is called to re-evalutate PRC bonuses. Currently +// it is fired by OnEquip, OnUnequip and OnLevel. If you want to move any +// classes into this event, just copy the format below. Basically, this function +// is meant to keep the code looking nice and clean by routing each class's +// feats to their own self-contained script +//-------------------------------------------------------------------------- + +//:: Test Void +//void main (){} + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int TEMPLATE_SLA_START = 16304; +const int TEMPLATE_SLA_END = 16400; + +const string PRC_ScrubPCSkin_Generation = "PRC_ScrubPCSkin_Generation"; +const string PRC_EvalPRCFeats_Generation = "PRC_EvalPRCFeats_Generation"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +void EvalPRCFeats(object oPC); + +int BlastInfidelOrFaithHeal(object oCaster, object oTarget, int iEnergyType, int iDisplayFeedback); + +void ScrubPCSkin(object oPC, object oSkin); + +void DeletePRCLocalInts(object oSkin); + +void DelayedAddIPFeats(int nExpectedGeneration, object oPC); + +void DelayedReApplyUnhealableAbilityDamage(int nExpectedGeneration, object oPC); + +int nbWeaponFocus(object oPC); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +// Minimalist includes +#include "prc_inc_util" +#include "prc_inc_spells" +#include "prc_inc_stunfist" +#include "inc_nwnx_funcs" +#include "prc_template_con" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void DeleteCharacterData(object oPC) +{ + DeletePersistantLocalString(oPC, "PRC_Class_Script1"); + DeletePersistantLocalString(oPC, "PRC_Class_Script2"); + DeletePersistantLocalString(oPC, "PRC_Class_Script3"); + DeletePersistantLocalString(oPC, "PRC_Class_Script4"); + DeletePersistantLocalString(oPC, "PRC_Class_Script5"); + DeletePersistantLocalString(oPC, "PRC_Class_Script6"); + DeletePersistantLocalString(oPC, "PRC_Class_Script7"); + DeletePersistantLocalString(oPC, "PRC_Class_Script8"); + DeletePersistantLocalInt(oPC, "PRC_Class_Data"); +} + +void SetupCharacterData(object oPC) +{ + // iData format: + // 0x01 - run alternate magic system setup + // 0x02 - add new metamagic feats for spontaneous casters + // 0x04 - run reduced arcane spell failure check + // use bitwise to combine flags + + int i, iData, iShifting; + for(i = 1; i <= 8; i++) + { + int nClassType = GetClassByPosition(i, oPC); + if(nClassType != CLASS_TYPE_INVALID) + { + string sScript; + switch(nClassType) + { + case CLASS_TYPE_ABJURANT_CHAMPION: sScript = "prc_abchamp"; break; + case CLASS_TYPE_ACOLYTE: sScript = "prc_acolyte"; break; + case CLASS_TYPE_ALIENIST: sScript = "prc_alienist"; break; + case CLASS_TYPE_ARCANE_DUELIST: sScript = "prc_arcduel"; break; + case CLASS_TYPE_ARCHIVIST: sScript = "prc_archivist"; iData |= 0x01; break; + case CLASS_TYPE_ASSASSIN: iData |= 0x03; break; + case CLASS_TYPE_BAELNORN: sScript = "prc_baelnorn"; break; + case CLASS_TYPE_BARD: iData |= 0x07; break; + case CLASS_TYPE_BATTLESMITH: sScript = "prc_battlesmith"; break; + case CLASS_TYPE_BEGUILER: iData |= 0x03; break; + case CLASS_TYPE_BINDER: sScript = "bnd_binder"; break; + case CLASS_TYPE_BFZ: sScript = "prc_bfz"; break; + case CLASS_TYPE_BLACK_BLOOD_CULTIST: sScript = "prc_bbc"; break; + case CLASS_TYPE_BLACKGUARD: sScript = "prc_blackguard"; break; + case CLASS_TYPE_BLADESINGER: sScript = "prc_bladesinger"; iData |= 0x04; break; + case CLASS_TYPE_BLIGHTLORD: sScript = "prc_blightlord"; break; + case CLASS_TYPE_BLOODCLAW_MASTER: sScript = "tob_bloodclaw"; break; + case CLASS_TYPE_BONDED_SUMMONNER: sScript = "prc_bondedsumm"; break; + case CLASS_TYPE_CELEBRANT_SHARESS: iData |= 0x03; break; + case CLASS_TYPE_CHILD_OF_NIGHT: sScript = "shd_childnight"; break; + case CLASS_TYPE_COC: sScript = "prc_coc"; break; + case CLASS_TYPE_COMBAT_MEDIC: sScript = "prc_cbtmed"; break; + case CLASS_TYPE_CONTEMPLATIVE: sScript = "prc_contemplate"; break; + case CLASS_TYPE_CRUSADER: sScript = "tob_crusader"; iData |= 0x01; break; + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: sScript = "prc_shatterdpeak"; break; + case CLASS_TYPE_CW_SAMURAI: sScript = "prc_cwsamurai"; break; + case CLASS_TYPE_DEEPSTONE_SENTINEL: sScript = "tob_deepstone"; break; + case CLASS_TYPE_DIAMOND_DRAGON: sScript = "psi_diadra"; break; + case CLASS_TYPE_DISC_BAALZEBUL: sScript = "prc_baalzebul"; break; + case CLASS_TYPE_DISCIPLE_OF_ASMODEUS: sScript = "prc_discasmodeus"; break; + case CLASS_TYPE_DISCIPLE_OF_MEPH: sScript = "prc_discmeph"; break; + case CLASS_TYPE_DISPATER: sScript = "prc_dispater"; break; + case CLASS_TYPE_DRAGON_DEVOTEE: sScript = "prc_dragdev"; break; + case CLASS_TYPE_DRAGON_DISCIPLE: sScript = "prc_dradis"; break; + case CLASS_TYPE_DRAGON_SHAMAN: sScript = "prc_dragonshaman"; break; + case CLASS_TYPE_DRAGONFIRE_ADEPT: sScript = "inv_drgnfireadpt"; iData |= 0x01; break; + case CLASS_TYPE_DREAD_NECROMANCER: sScript = "prc_dreadnecro"; iData |= 0x03; break; + case CLASS_TYPE_DRUID: iShifting = TRUE; break; + case CLASS_TYPE_DRUNKEN_MASTER: sScript = "prc_drunk"; break; + case CLASS_TYPE_DUELIST: sScript = "prc_duelist"; break; + case CLASS_TYPE_DUSKBLADE: sScript = "prc_duskblade"; iData |= 0x03; break; + case CLASS_TYPE_ENLIGHTENEDFIST: sScript = "prc_enlfis"; break; + case CLASS_TYPE_ELEMENTAL_SAVANT: sScript = "prc_elemsavant"; break; + case CLASS_TYPE_ETERNAL_BLADE: sScript = "tob_eternalblade"; break; + case CLASS_TYPE_FACTOTUM: sScript = "prc_factotum"; break; + case CLASS_TYPE_FAVOURED_SOUL: sScript = "prc_favouredsoul"; iData |= 0x03; break; + case CLASS_TYPE_FIST_OF_ZUOKEN: sScript = "psi_zuoken"; iData |= 0x01; break; + case CLASS_TYPE_FOE_HUNTER: sScript = "prc_foe_hntr"; break; + case CLASS_TYPE_FORESTMASTER: sScript = "prc_forestmaster"; break; + case CLASS_TYPE_FORSAKER: sScript = "prc_forsaker"; break; + case CLASS_TYPE_FRE_BERSERKER: sScript = "prc_frebzk"; break; + case CLASS_TYPE_FROSTRAGER: sScript = "prc_frostrager"; break; + case CLASS_TYPE_FROST_MAGE: sScript = "prc_frostmage"; break; + case CLASS_TYPE_HALFLING_WARSLINGER: sScript = "prc_warsling"; break; + case CLASS_TYPE_HARPER: iData |= 0x03; break; + case CLASS_TYPE_HEARTWARDER: sScript = "prc_heartwarder"; break; + case CLASS_TYPE_HENSHIN_MYSTIC: sScript = "prc_henshin"; break; + case CLASS_TYPE_HEXBLADE: iData |= 0x03; break; + case CLASS_TYPE_HEXTOR: sScript = "prc_hextor"; break; + case CLASS_TYPE_IAIJUTSU_MASTER: sScript = "prc_iaijutsu_mst"; break; + case CLASS_TYPE_INCARNATE: sScript = "moi_incarnate"; break; + case CLASS_TYPE_INCARNUM_BLADE: sScript = "moi_iblade"; break; + case CLASS_TYPE_INITIATE_DRACONIC: sScript = "prc_initdraconic"; break; + case CLASS_TYPE_IRONMIND: sScript = "psi_ironmind"; break; + case CLASS_TYPE_IRONSOUL_FORGEMASTER: sScript = "moi_ironsoul"; break; + case CLASS_TYPE_JADE_PHOENIX_MAGE: sScript = "tob_jadephoenix"; break; + case CLASS_TYPE_JUDICATOR: sScript = "prc_judicator"; break; + case CLASS_TYPE_JUSTICEWW: iData |= 0x03; break; + case CLASS_TYPE_KNIGHT: sScript = "prc_knight"; break; + case CLASS_TYPE_KNIGHT_CHALICE: sScript = "prc_knghtch"; break; + case CLASS_TYPE_KNIGHT_WEAVE: iData |= 0x03; break; + case CLASS_TYPE_KNIGHT_SACRED_SEAL: sScript = "bnd_kss"; break; + case CLASS_TYPE_LASHER: sScript = "prc_lasher"; break; + case CLASS_TYPE_LEGENDARY_DREADNOUGHT: sScript = "prc_legendread"; break; + case CLASS_TYPE_LICH: sScript = "pnp_lich_level"; break; + case CLASS_TYPE_MAGEKILLER: sScript = "prc_magekill"; break; + case CLASS_TYPE_MASTER_HARPER: sScript = "prc_masterh"; break; + case CLASS_TYPE_MASTER_OF_NINE: sScript = "tob_masterofnine"; break; + case CLASS_TYPE_MASTER_OF_SHADOW: sScript = "shd_mastershadow"; break; + case CLASS_TYPE_MIGHTY_CONTENDER_KORD: sScript = "prc_contendkord"; break; + case CLASS_TYPE_MORNINGLORD: sScript = "prc_morninglord"; break; + case CLASS_TYPE_NIGHTSHADE: sScript = "prc_nightshade"; break; + case CLASS_TYPE_NINJA: sScript = "prc_ninjca"; break; + case CLASS_TYPE_OLLAM: sScript = "prc_ollam"; break; + case CLASS_TYPE_OOZEMASTER: sScript = "prc_oozemstr"; break; + case CLASS_TYPE_ORDER_BOW_INITIATE: sScript = "prc_ootbi"; break; + case CLASS_TYPE_PEERLESS: sScript = "prc_peerless"; break; + case CLASS_TYPE_PNP_SHIFTER : iShifting = TRUE; break; + case CLASS_TYPE_PRC_EYE_OF_GRUUMSH: sScript = "prc_eog"; break; + case CLASS_TYPE_PSION: iData |= 0x01; break; + case CLASS_TYPE_PSYWAR: iData |= 0x01; break; + case CLASS_TYPE_PSYCHIC_ROGUE: sScript = "psi_psyrogue"; iData |= 0x01; break; + case CLASS_TYPE_PYROKINETICIST: sScript = "psi_pyro"; break; + case CLASS_TYPE_RAGE_MAGE: iData |= 0x04; break; + case CLASS_TYPE_REAPING_MAULER : sScript = "prc_reapmauler"; break; + case CLASS_TYPE_RUBY_VINDICATOR: sScript = "tob_rubyknight"; break; + case CLASS_TYPE_RUNESCARRED: sScript = "prc_runescarred"; break; + case CLASS_TYPE_SACREDFIST: sScript = "prc_sacredfist"; break; + case CLASS_TYPE_SANCTIFIED_MIND: sScript = "psi_sancmind"; break; + case CLASS_TYPE_SAPPHIRE_HIERARCH: sScript = "moi_sapphire"; break; + case CLASS_TYPE_SCOUT: sScript = "prc_scout"; break; + case CLASS_TYPE_SERENE_GUARDIAN: sScript = "prc_sereneguard"; break; + case CLASS_TYPE_SHADOW_ADEPT: sScript = "prc_shadowadept"; break; + case CLASS_TYPE_SHADOWCASTER: sScript = "shd_shadowcaster"; iData |= 0x01; break; + case CLASS_TYPE_SHADOWSMITH: iData |= 0x01; break; + case CLASS_TYPE_SHADOW_SUN_NINJA: sScript = "tob_shadowsun"; break; + case CLASS_TYPE_SHADOWBLADE: sScript = "prc_sb_shdstlth"; break; + case CLASS_TYPE_SHADOWMIND: sScript = "psi_shadowmind"; break; + case CLASS_TYPE_SHADOWBANE_STALKER: sScript = "prc_shadstalker"; break; + case CLASS_TYPE_SHADOW_THIEF_AMN: sScript = "prc_amn"; break; + case CLASS_TYPE_SHAMAN: sScript = "prc_shaman"; break; + case CLASS_TYPE_SHINING_BLADE: sScript = "prc_sbheir"; break; + case CLASS_TYPE_SHOU: sScript = "prc_shou"; break; + case CLASS_TYPE_SKULLCLAN_HUNTER: sScript = "prc_skullclan"; break; + case CLASS_TYPE_SLAYER_OF_DOMIEL: sScript = "prc_slayerdomiel"; break; + case CLASS_TYPE_SOHEI: sScript = "prc_sohei"; break; + case CLASS_TYPE_SOLDIER_OF_LIGHT: sScript = "prc_soldoflight"; break; + case CLASS_TYPE_SORCERER: iData |= 0x03; break; + case CLASS_TYPE_SOULBORN: sScript = "moi_soulborn"; break; + case CLASS_TYPE_SOUL_EATER: iShifting = TRUE; break; + case CLASS_TYPE_SOULKNIFE: sScript = "psi_sk_clseval"; break; + case CLASS_TYPE_SPELLSWORD: sScript = "prc_spellswd"; iData |= 0x04; break; + case CLASS_TYPE_SPINEMELD_WARRIOR: sScript = "moi_spinemeld"; break; + case CLASS_TYPE_STORMLORD: sScript = "prc_stormlord"; break; + case CLASS_TYPE_SUBLIME_CHORD: sScript = "prc_schord"; iData |= 0x03; break; + case CLASS_TYPE_SUEL_ARCHANAMACH: iData |= 0x03; break; + case CLASS_TYPE_SWASHBUCKLER: sScript = "prc_swashbuckler"; break; + case CLASS_TYPE_SWIFT_WING: sScript = "prc_swiftwing"; break; + case CLASS_TYPE_SWORDSAGE: sScript = "tob_swordsage"; iData |= 0x01; break; + case CLASS_TYPE_TALON_OF_TIAMAT: sScript = "prc_talontiamat"; break; + case CLASS_TYPE_TEMPEST: sScript = "prc_tempest"; break; + case CLASS_TYPE_TEMPUS: sScript = "prc_battletempus"; break; + case CLASS_TYPE_TENEBROUS_APOSTATE: sScript = "bnd_tenebrous"; break; + case CLASS_TYPE_THAYAN_KNIGHT: sScript = "prc_thayknight"; break; + case CLASS_TYPE_THRALL_OF_GRAZZT_A: sScript = "tog"; break; + case CLASS_TYPE_THRALLHERD: sScript = "psi_thrallherd"; break; + case CLASS_TYPE_TOTEMIST: sScript = "moi_totemist"; break; + case CLASS_TYPE_TOTEM_RAGER: sScript = "moi_totemrager"; break; + case CLASS_TYPE_TRUENAMER: sScript = "true_truenamer"; iData |= 0x01; break; + case CLASS_TYPE_VASSAL: sScript = "prc_vassal"; break; + case CLASS_TYPE_VIGILANT: sScript = "prc_vigilant"; break; + case CLASS_TYPE_WARBLADE: sScript = "tob_warblade"; iData |= 0x01; break; + case CLASS_TYPE_WARCHIEF: sScript = "prc_warchief"; break; + case CLASS_TYPE_WARFORGED_JUGGERNAUT: sScript = "prc_juggernaut"; break; + case CLASS_TYPE_WARLOCK: sScript = "inv_warlock"; iData |= 0x01; break; + case CLASS_TYPE_WARMAGE: iData |= 0x03; break; + case CLASS_TYPE_WARMIND: sScript = "psi_warmind"; iData |= 0x01; break; + case CLASS_TYPE_WEREWOLF: sScript = "prc_werewolf"; break; + case CLASS_TYPE_WILDER: iData |= 0x01; break; + + // Races that can cast spells + case CLASS_TYPE_DRAGON: iData |= 0x03; break; + case CLASS_TYPE_SHAPECHANGER: iData |= 0x03; break; + case CLASS_TYPE_OUTSIDER: iData |= 0x03; break; + case CLASS_TYPE_ABERRATION: iData |= 0x03; break; + case CLASS_TYPE_MONSTROUS: iData |= 0x03; break; + case CLASS_TYPE_FEY: iData |= 0x03; break; + } + if(sScript != "") + SetPersistantLocalString(oPC, "PRC_Class_Script"+IntToString(i), sScript); + } + if (DEBUG) DoDebug("SetupCharacterData Class: " + IntToString(nClassType)); + if (DEBUG) DoDebug("SetupCharacterData Data: " + IntToString(iData)); + } + if(iData) + SetPersistantLocalInt(oPC, "PRC_Class_Data", iData); + + if(iShifting) + SetPersistantLocalInt(oPC, "PRC_UNI_SHIFT_SCRIPT", 1); + + //Setup class info for onleveldown script + int nCharData = ((GetClassByPosition(8, oPC) & 0xFF) << 56) | + ((GetClassByPosition(7, oPC) & 0xFF) << 48) | + ((GetClassByPosition(5, oPC) & 0xFF) << 40) | + ((GetClassByPosition(5, oPC) & 0xFF) << 32) | + ((GetClassByPosition(4, oPC) & 0xFF) << 24) | + ((GetClassByPosition(3, oPC) & 0xFF) << 16) | + ((GetClassByPosition(2, oPC) & 0xFF) << 8) | + (GetClassByPosition(1, oPC) & 0xFF); + + SetPersistantLocalInt(oPC, "PRC_Character_Data", nCharData); +} + +void DelayedExecuteScript(int nExpectedGeneration, string sScriptName, object oPC) +{ + if (nExpectedGeneration != GetLocalInt(oPC, PRC_EvalPRCFeats_Generation)) + { + //Generation has changed, so don't apply the effect + return; + } + ExecuteScript(sScriptName, oPC); +} + +void DelayedReApplyUnhealableAbilityDamage(int nExpectedGeneration, object oPC) +{ + if (nExpectedGeneration != GetLocalInt(oPC, PRC_ScrubPCSkin_Generation)) + { + //Generation has changed, so don't apply the effect + return; + } + ReApplyUnhealableAbilityDamage(oPC); +} + +int ToBFeats(object oPC) +{ + if(GetHasFeat(FEAT_SHADOW_BLADE, oPC) || + GetHasFeat(FEAT_RAPID_ASSAULT, oPC) || + GetHasFeat(FEAT_DESERT_WIND_DODGE, oPC) || + GetHasFeat(FEAT_DESERT_FIRE, oPC) || + GetHasFeat(FEAT_IRONHEART_AURA, oPC) || + GetHasFeat(FEAT_SHADOW_TRICKSTER, oPC) || + GetHasFeat(FEAT_WHITE_RAVEN_DEFENSE, oPC) || + GetHasFeat(FEAT_DEVOTED_BULWARK, oPC) || + GetHasFeat(FEAT_SNAP_KICK, oPC) || + GetHasFeat(FEAT_THREE_MOUNTAINS, oPC) || + GetHasFeat(FEAT_VAE_SCHOOL, oPC) || + GetHasFeat(FEAT_INLINDL_SCHOOL, oPC) || + GetHasFeat(FEAT_XANIQOS_SCHOOL, oPC) || + GetHasFeat(FEAT_SHIELD_WALL, oPC) || + GetHasFeat(FEAT_CROSSBOW_SNIPER, oPC) || + GetHasFeat(FEAT_CRESCENT_MOON, oPC) || + GetHasFeat(FEAT_QUICK_STAFF, oPC) || + GetHasFeat(FEAT_BEAR_FANG, oPC) || + GetHasFeat(FEAT_IMPROVED_RAPID_SHOT, oPC) || + GetHasFeat(FEAT_DIRE_FLAIL_SMASH, oPC) || + GetHasFeat(FEAT_SHIELD_SPECIALIZATION_LIGHT, oPC) || + GetHasFeat(FEAT_SHIELD_SPECIALIZATION_HEAVY, oPC) || + GetHasFeat(FEAT_FOCUSED_SHIELD, oPC) || + GetHasFeat(FEAT_SHIELDMATE, oPC) || + GetHasFeat(FEAT_IMPROVED_SHIELDMATE, oPC) || + GetHasFeat(FEAT_SHIELDED_CASTING, oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_DESERT_WIND , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_DEVOTED_SPIRIT, oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_DIAMOND_MIND , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_IRON_HEART , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_SETTING_SUN , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_SHADOW_HAND , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_STONE_DRAGON , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_TIGER_CLAW , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_WHITE_RAVEN , oPC) || + GetRacialType(oPC) == RACIAL_TYPE_RETH_DEKALA || + GetRacialType(oPC) == RACIAL_TYPE_HADRIMOI) + return TRUE; + + return FALSE; +} + +void EvalPRCFeats(object oPC) +{ + // Player is currently making character in ConvoCC + // don't run EvalPRCFeats() yet + if(oPC == GetLocalObject(GetModule(), "ccc_active_pc")) + return; + + int nGeneration = PRC_NextGeneration(GetLocalInt(oPC, PRC_EvalPRCFeats_Generation)); + if (DEBUG > 1) DoDebug("EvalPRCFeats Generation: " + IntToString(nGeneration)); + SetLocalInt(oPC, PRC_EvalPRCFeats_Generation, nGeneration); + + //permanent ability changes + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + ExecuteScript("prc_nwnx_funcs", oPC); + + //Add IP Feats to the hide + DelayCommand(0.0f, DelayedAddIPFeats(nGeneration, oPC)); + + // If there is a bonus domain, it will always be in the first slot, so just check that. + // It also runs things that clerics with those domains need + if (GetPersistantLocalInt(oPC, "PRCBonusDomain1") > 0 || GetLevelByClass(CLASS_TYPE_CLERIC, oPC)) + DelayCommand(0.1f, ExecuteScript("prc_domain_skin", oPC)); + + // special add atk bonus equal to Enhancement + ExecuteScript("ft_sanctmartial", oPC); + + //hook in the weapon size restrictions script + //ExecuteScript("prc_restwpnsize", oPC); + + //Route the event to the appropriate class specific scripts + int i, iData; + string sScript; + for (i = 1; i <= 3; i++) + { + sScript = GetPersistantLocalString(oPC, "PRC_Class_Script"+IntToString(i)); + if(sScript != "") + { + if (DEBUG) DoDebug("PRC_Class_Script: "+sScript); + ExecuteScript(sScript, oPC); + } + } + + iData = GetPersistantLocalInt(oPC, "PRC_Class_Data"); + + // Handle alternate caster types gaining new stuff + if(iData & 0x01) + ExecuteScript("prc_amagsys_gain", oPC); + + // Add ip feats for spontaneous casters using metamagic + if(iData & 0x02) + ExecuteScript("prc_metamagic", oPC); + + // Handle classes with reduced arcane spell failure + if(iData & 0x04) + ExecuteScript("prc_reducedasf", oPC); + + if(GetPersistantLocalInt(oPC, "PRC_UNI_SHIFT_SCRIPT")) + //Executing shifter-related stuff like this has these advantages: + //1) All races and classes that need it can get it without a different script needing to be created for each. + //2) PCs with shifter-related stuff from multiple classes or races don't run the same functions multiple times. + ExecuteScript("prc_uni_shift", oPC); + + // Templates + //these go here so feats can be reused + ExecuteScript("prc_templates", oPC); + + // Feats + //these are here so if templates add them the if check runs after the template was applied + ExecuteScript("prc_feats", oPC); + + if (ToBFeats(oPC)) + ExecuteScript("tob_feats", oPC); + + if (GetIsIncarnumUser(oPC)) + ExecuteScript("moi_events", oPC); + + if (GetIsBinder(oPC)) + ExecuteScript("bnd_events", oPC); + + // check if character with crafting feat has appropriate base item in her inventory + // x - moved from prc_onhb_indiv.nss + if(GetPRCSwitch(PRC_CRAFTING_BASE_ITEMS)) + DelayCommand(0.5f, ExecuteScript("prc_crftbaseitms", oPC)); + + // Add the teleport management feats. + // 2005.11.03: Now added to all base classes on 1st level - Ornedan +// ExecuteScript("prc_tp_mgmt_eval", oPC); + + // Size changes. Removed due to double-dipping most size adjustments + //ExecuteScript("prc_size", oPC); + + // Speed changes + // The local int is for when items are requipped too quickly and this script bugs out + if (!GetLocalInt(oPC, "PRCSpeedDelay")) + { + ExecuteScript("prc_speed", oPC); + SetLocalInt(oPC, "PRCSpeedDelay", TRUE); + DelayCommand(0.15, DeleteLocalInt(oPC, "PRCSpeedDelay")); + } + + // ACP system + if((GetIsPC(oPC) && + (GetPRCSwitch(PRC_ACP_MANUAL) || + GetPRCSwitch(PRC_ACP_AUTOMATIC) + ) + ) || + (!GetIsPC(oPC) && + GetPRCSwitch(PRC_ACP_NPC_AUTOMATIC) + ) + ) + ExecuteScript("acp_auto", oPC); + +// this is handled inside the PRC Options conversation now. +/* // Epic spells + if((GetCasterLvl(CLASS_TYPE_CLERIC, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_DRUID, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_SORCERER, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_FAVOURED_SOUL, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_HEALER, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_WIZARD, oPC) >= 21 + ) && + !GetHasFeat(FEAT_EPIC_SPELLCASTING_REST, oPC) + ) + { + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_REST), 0.0f, + X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + }*/ + + // Miscellaneous + ExecuteScript("prc_sneak_att", oPC); + ExecuteScript("race_skin", oPC); + ExecuteScript("prc_mithral", oPC); + if(GetPRCSwitch(PRC_ENFORCE_RACIAL_APPEARANCE) && GetIsPC(oPC)) + ExecuteScript("race_appear", oPC); + + //Reserve Feats + if(!GetLocalInt(oPC, "ReserveFeatsRunning")) + { + if(GetHasFeat(FEAT_HOLY_WARRIOR, oPC) || + GetHasFeat(FEAT_MYSTIC_BACKLASH, oPC) || + GetHasFeat(FEAT_ACIDIC_SPLATTER, oPC) || + GetHasFeat(FEAT_FIERY_BURST, oPC) || + GetHasFeat(FEAT_STORM_BOLT, oPC) || + GetHasFeat(FEAT_WINTERS_BLAST, oPC) || + GetHasFeat(FEAT_CLAP_OF_THUNDER, oPC) || + GetHasFeat(FEAT_SICKENING_GRASP, oPC) || + GetHasFeat(FEAT_TOUCH_OF_HEALING, oPC) || + GetHasFeat(FEAT_DIMENSIONAL_JAUNT, oPC) || + GetHasFeat(FEAT_CLUTCH_OF_EARTH, oPC) || + GetHasFeat(FEAT_BORNE_ALOFT, oPC) || + GetHasFeat(FEAT_PROTECTIVE_WARD, oPC) || + GetHasFeat(FEAT_SHADOW_VEIL, oPC) || + GetHasFeat(FEAT_SUNLIGHT_EYES, oPC) || + GetHasFeat(FEAT_TOUCH_OF_DISTRACTION, oPC) || + GetHasFeat(FEAT_UMBRAL_SHROUD, oPC) || + GetHasFeat(FEAT_CHARNEL_MIASMA, oPC) || + GetHasFeat(FEAT_DROWNING_GLANCE, oPC) || + GetHasFeat(FEAT_INVISIBLE_NEEDLE, oPC) || + GetHasFeat(FEAT_SUMMON_ELEMENTAL, oPC) || + GetHasFeat(FEAT_DIMENSIONAL_REACH, oPC) || + GetHasFeat(FEAT_HURRICANE_BREATH, oPC) || + GetHasFeat(FEAT_MINOR_SHAPESHIFT, oPC) || + GetHasFeat(FEAT_FACECHANGER, oPC)) + { + ExecuteScript("prc_reservefeat", oPC); + } + } + + /*if(// Psionics + GetLevelByClass(CLASS_TYPE_PSION, oPC) || + GetLevelByClass(CLASS_TYPE_WILDER, oPC) || + GetLevelByClass(CLASS_TYPE_PSYWAR, oPC) || + GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oPC) || + GetLevelByClass(CLASS_TYPE_WARMIND, oPC) || + // New spellbooks + GetLevelByClass(CLASS_TYPE_BARD, oPC) || + GetLevelByClass(CLASS_TYPE_SORCERER, oPC) || + GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oPC) || + GetLevelByClass(CLASS_TYPE_FAVOURED_SOUL, oPC) || + GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) || + GetLevelByClass(CLASS_TYPE_HEXBLADE, oPC) || + GetLevelByClass(CLASS_TYPE_DUSKBLADE, oPC) || + GetLevelByClass(CLASS_TYPE_WARMAGE, oPC) || + GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER,oPC) || + GetLevelByClass(CLASS_TYPE_JUSTICEWW, oPC) || + GetLevelByClass(CLASS_TYPE_WITCH, oPC) || + GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oPC) || + GetLevelByClass(CLASS_TYPE_ARCHIVIST, oPC) || + GetLevelByClass(CLASS_TYPE_BEGUILER, oPC) || + GetLevelByClass(CLASS_TYPE_HARPER, oPC) || + GetLevelByClass(CLASS_TYPE_TEMPLAR, oPC) || + // Truenaming + GetLevelByClass(CLASS_TYPE_TRUENAMER, oPC) || + // Tome of Battle + GetLevelByClass(CLASS_TYPE_CRUSADER, oPC) || + GetLevelByClass(CLASS_TYPE_SWORDSAGE, oPC) || + GetLevelByClass(CLASS_TYPE_WARBLADE, oPC) || + // Invocations + GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oPC) || + GetLevelByClass(CLASS_TYPE_WARLOCK, oPC) || + // Racial casters + (GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + ) + { + DelayCommand(1.0, DelayedExecuteScript(nGeneration, "prc_amagsys_gain", oPC)); + + //add selectable metamagic feats for spontaneous spellcasters + ExecuteScript("prc_metamagic", oPC); + }*/ + + // Gathers all the calls to UnarmedFists & Feats to one place. + // Must be after all evaluationscripts that need said functions. + //if(GetLocalInt(oPC, "CALL_UNARMED_FEATS") || GetLocalInt(oPC, "CALL_UNARMED_FISTS")) // ExecuteScript() is pretty expensive, do not run it needlessly - 20060702, Ornedan + ExecuteScript("unarmed_caller", oPC); + + // Gathers all the calls to SetBaseAttackBonus() to one place + // Must be after all evaluationscripts that need said function. + ExecuteScript("prc_bab_caller", oPC); + +//:: [PRC .35] Needs marker feats + // Classes an invoker can take + if(GetLevelByClass(CLASS_TYPE_MAESTER, oPC) || + GetLevelByClass(CLASS_TYPE_ACOLYTE, oPC) || + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oPC) || + GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oPC)) + { + //Set arcane or invocation bonus caster levels + + //Arcane caster first class position, take arcane + if(GetFirstArcaneClassPosition(oPC) == 1) + SetLocalInt(oPC, "INV_Caster", 1); + //Invoker first class position. take invoker + else if(GetClassByPosition(1, oPC) == CLASS_TYPE_WARLOCK || GetClassByPosition(1, oPC) == CLASS_TYPE_DRAGONFIRE_ADEPT) + SetLocalInt(oPC, "INV_Caster", 2); + //Non arcane first class position, invoker second. Take invoker + else if(GetFirstArcaneClassPosition(oPC) ==0 && (GetClassByPosition(2, oPC) == CLASS_TYPE_WARLOCK || GetClassByPosition(2, oPC) == CLASS_TYPE_DRAGONFIRE_ADEPT)) + SetLocalInt(oPC, "INV_Caster", 2); + //last cas would be Non-invoker first class position, arcane second position. take arcane. + else + SetLocalInt(oPC, "INV_Caster", 1); + } +} + +void DelayedAddIPFeats(int nExpectedGeneration, object oPC) +{ + if (nExpectedGeneration != GetLocalInt(oPC, PRC_EvalPRCFeats_Generation)) + { + //Generation has changed, so don't apply the effect + return; + } + + object oSkin = GetPCSkin(oPC); + + //Horse menu + AddSkinFeat(FEAT_HORSE_MENU, 40, oSkin, oPC); + + // Switch convo feat + //Now everyone gets it at level 1, but just to be on the safe side + AddSkinFeat(FEAT_OPTIONS_CONVERSATION, 229, oSkin, oPC); + + //PnP familiars + if(GetHasFeat(FEAT_SUMMON_FAMILIAR, oPC) && GetPRCSwitch(PRC_PNP_FAMILIARS)) + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_PNP_FAMILIAR), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + else if(!GetHasFeat(FEAT_SUMMON_FAMILIAR, oPC) || !GetPRCSwitch(PRC_PNP_FAMILIARS)) + RemoveItemProperty(oSkin, ItemPropertyBonusFeat(IP_CONST_PNP_FAMILIAR)); + + //PnP Spell Schools + if(GetPRCSwitch(PRC_PNP_SPELL_SCHOOLS) + && GetLevelByClass(CLASS_TYPE_WIZARD, oPC) + && !GetIsPolyMorphedOrShifted(oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_GENERAL, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_ABJURATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_CONJURATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_DIVINATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_ENCHANTMENT, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_EVOCATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_ILLUSION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_NECROMANCY, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_TRANSMUTATION, oPC) + //&& !PRCGetHasEffect(EFFECT_TYPE_POLYMORPH, oPC) //so it doesnt pop up on polymorphing + //&& !GetLocalInt(oSkin, "nPCShifted") //so it doenst pop up on shifting + ) + { + if(GetXP(oPC))// ConvoCC compatibility fix + ExecuteScript("prc_pnp_shcc_s", oPC); + } + + /*//Arcane Archer old imbue arrow + if(GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER, oPC) >= 2 + && !GetHasFeat(FEAT_PRESTIGE_IMBUE_ARROW, oPC) + && GetPRCSwitch(PRC_PNP_SPELL_SCHOOLS)) + { + //add the old feat to the hide + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_FEAT_FEAT_PRESTIGE_IMBUE_ARROW), 0.0f, + X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + }*/ + + //handle PnP sling switch + if(GetPRCSwitch(PRC_PNP_SLINGS)) + { + if(GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)) == BASE_ITEM_SLING) + IPSafeAddItemProperty(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC), + ItemPropertyMaxRangeStrengthMod(20), + 999999.9); + } +} + +void TemplateSLAs(object oPC) +{ + int i; + for(i = TEMPLATE_SLA_START; i <= TEMPLATE_SLA_END; i++) + { + DeleteLocalInt(oPC, "TemplateSLA_"+IntToString(i)); + } +} + +void DeletePRCLocalInts(object oSkin) +{ + // This will get rid of any SetCompositeAttackBonus LocalInts: + object oPC = GetItemPossessor(oSkin); + DeleteLocalInt(oPC, "CompositeAttackBonusR"); + DeleteLocalInt(oPC, "CompositeAttackBonusL"); + + //Do not use DelayCommand for this--it's too dangerous: + //if SetCompositeAttackBonus is called at the wrong time the result will be incorrect. + DeleteNamedComposites(oPC, "PRC_ComAttBon"); + + // PRCGetClassByPosition and PRCGetLevelByPosition cleanup + // not needed now that GetClassByPosition() works for custom classes + // DeleteLocalInt(oPC, "PRC_ClassInPos1"); + // DeleteLocalInt(oPC, "PRC_ClassInPos2"); + // DeleteLocalInt(oPC, "PRC_ClassInPos3"); + // DeleteLocalInt(oPC, "PRC_ClassLevelInPos1"); + // DeleteLocalInt(oPC, "PRC_ClassLevelInPos2"); + // DeleteLocalInt(oPC, "PRC_ClassLevelInPos3"); + + //persistant local token object cache + //looks like logging off then back on without the server rebooting breaks it + //I guess because the token gets a new ID, but the local still points to the old one + DeleteLocalObject(oPC, "PRC_HideTokenCache"); + + DeleteLocalInt(oSkin, "PRC_ArcaneSpellFailure"); + + DeleteLocalInt(oPC, "PRC_SwiftActionUsed"); + DeleteLocalInt(oPC, "PRC_MoveActionUsed"); + + // In order to work with the PRC system we need to delete some locals for each + // PRC that has a hide + + //Do not use DelayCommand for this--it's too dangerous: + //if SetCompositeBonus is called between the time EvalPRCFeats removes item properties + //and the time this delayed command is executed, the result will be incorrect. + //Since this error case actually happens frequently with any delay here, just don't do it. + DeleteNamedComposites(oSkin, "PRC_CBon"); + + if (DEBUG) DoDebug("Clearing class flags"); + + // Elemental Savants + DeleteLocalInt(oSkin,"ElemSavantResist"); + DeleteLocalInt(oSkin,"ElemSavantPerfection"); + DeleteLocalInt(oSkin,"ElemSavantImmMind"); + DeleteLocalInt(oSkin,"ElemSavantImmParal"); + DeleteLocalInt(oSkin,"ElemSavantImmSleep"); + // HeartWarder + DeleteLocalInt(oSkin,"FeyType"); + // OozeMaster + DeleteLocalInt(oSkin,"IndiscernibleCrit"); + DeleteLocalInt(oSkin,"IndiscernibleBS"); + DeleteLocalInt(oSkin,"OneOozeMind"); + DeleteLocalInt(oSkin,"OneOozePoison"); + // Storm lord + DeleteLocalInt(oSkin,"StormLResElec"); + // Spell sword + DeleteLocalInt(oSkin,"SpellswordSFBonusNormal"); + DeleteLocalInt(oSkin,"SpellswordSFBonusEpic"); + // Acolyte of the skin + DeleteLocalInt(oSkin,"AcolyteSymbBonus"); + DeleteLocalInt(oSkin,"AcolyteResistanceCold"); + DeleteLocalInt(oSkin,"AcolyteResistanceFire"); + DeleteLocalInt(oSkin,"AcolyteResistanceAcid"); + DeleteLocalInt(oSkin,"AcolyteResistanceElectric"); + // Battleguard of Tempus + DeleteLocalInt(oSkin,"FEAT_WEAP_TEMPUS"); + // Bonded Summoner + DeleteLocalInt(oSkin,"BondResEle"); + DeleteLocalInt(oSkin,"BondSubType"); + // Disciple of Meph + DeleteLocalInt(oSkin,"DiscMephResist"); + DeleteLocalInt(oSkin,"DiscMephGlove"); + // Initiate of Draconic Mysteries + DeleteLocalInt(oSkin,"IniSR"); + DeleteLocalInt(oSkin,"IniStunStrk"); + // Man at Arms + DeleteLocalInt(oSkin,"ManArmsCore"); + // Telflammar Shadowlord + DeleteLocalInt(oSkin,"ShaDiscorp"); + // Vile Feats + DeleteLocalInt(oSkin,"DeformGaunt"); + DeleteLocalInt(oSkin,"DeformObese"); + // Sneak Attack + DeleteLocalInt(oSkin,"RogueSneakDice"); + DeleteLocalInt(oSkin,"BlackguardSneakDice"); + // Sacred Fist + DeleteLocalInt(oSkin,"SacFisMv"); + // Minstrel + DeleteLocalInt(oSkin,"MinstrelSFBonus"); /// @todo Make ASF reduction compositable + // Nightshade + DeleteLocalInt(oSkin,"ImmuNSWeb"); + DeleteLocalInt(oSkin,"ImmuNSPoison"); + // Soldier of Light + DeleteLocalInt(oSkin,"ImmuPF"); + // Ultimate Ranger + DeleteLocalInt(oSkin,"URImmu"); + // Thayan Knight + DeleteLocalInt(oSkin,"ThayHorror"); + DeleteLocalInt(oSkin,"ThayZulkFave"); + DeleteLocalInt(oSkin,"ThayZulkChamp"); + // Black Flame Zealot + DeleteLocalInt(oSkin,"BFZHeart"); + // Henshin Mystic + DeleteLocalInt(oSkin,"Happo"); + DeleteLocalInt(oSkin,"HMInvul"); + //Blightlord + DeleteLocalInt(oSkin, "WntrHeart"); + DeleteLocalInt(oSkin, "BlightBlood"); + // Contemplative + DeleteLocalInt(oSkin, "ContempDisease"); + DeleteLocalInt(oSkin, "ContempPoison"); + DeleteLocalInt(oSkin, "ContemplativeDR"); + DeleteLocalInt(oSkin, "ContemplativeSR"); + // Dread Necromancer + DeleteLocalInt(oSkin, "DNDamageResist"); + // Warsling Sniper + DeleteLocalInt(oPC, "CanRicochet"); + // Blood Magus + DeleteLocalInt(oSkin, "ThickerThanWater"); + + // Feats + DeleteLocalInt(oPC, "ForceOfPersonalityWis"); + DeleteLocalInt(oPC, "ForceOfPersonalityCha"); + DeleteLocalInt(oPC, "InsightfulReflexesInt"); + DeleteLocalInt(oPC, "InsightfulReflexesDex"); + DeleteLocalInt(oSkin, "TactileTrapsmithSearchIncrease"); + DeleteLocalInt(oSkin, "TactileTrapsmithDisableIncrease"); + DeleteLocalInt(oSkin, "TactileTrapsmithSearchDecrease"); + DeleteLocalInt(oSkin, "TactileTrapsmithDisableDecrease"); + + // Warmind + DeleteLocalInt(oSkin, "EnduringBody"); + + // Ironmind + DeleteLocalInt(oSkin, "IronMind_DR"); + + // Suel Archanamach + DeleteLocalInt(oSkin, "SuelArchanamachSpellFailure"); + + // Favoured Soul + DeleteLocalInt(oSkin, "FavouredSoulResistElementAcid"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementCold"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementElec"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementFire"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementSonic"); + DeleteLocalInt(oSkin, "FavouredSoulDR"); + + // Domains + DeleteLocalInt(oSkin, "StormDomainPower"); + + // Skullclan Hunter + DeleteLocalInt(oSkin, "SkullClanFear"); + DeleteLocalInt(oSkin, "SkullClanDisease"); + DeleteLocalInt(oSkin, "SkullClanProtectionEvil"); + DeleteLocalInt(oSkin, "SkullClanSwordLight"); + DeleteLocalInt(oSkin, "SkullClanParalysis"); + DeleteLocalInt(oSkin, "SkullClanAbilityDrain"); + DeleteLocalInt(oSkin, "SkullClanLevelDrain"); + + // Sohei + DeleteLocalInt(oSkin, "SoheiDamageResist"); + + // Dragon Disciple + DeleteLocalInt(oPC, "DragonDiscipleBreathWeaponUses"); + + //Dragon Shaman + DeleteLocalInt(oPC, "DragonShamanTotem"); + + //Warblade + DeleteLocalInt(oSkin, "PRC_WEAPON_APTITUDE_APPLIED"); + + //Shifter(PnP) + DeleteLocalInt(oSkin, "PRC_SHIFTER_TEMPLATE_APPLIED"); + + DeleteLocalInt(oPC, "ScoutFreeMove"); + DeleteLocalInt(oPC, "ScoutFastMove"); + DeleteLocalInt(oPC, "ScoutBlindsight"); + + //Truenamer + // Called elsewhere now + /*int UtterID; + for(UtterID = 3526; UtterID <= 3639; UtterID++) // All utterances + DeleteLocalInt(oPC, "PRC_LawOfResistance" + IntToString(UtterID)); + for(UtterID = 3418; UtterID <= 3431; UtterID++) // Syllable of Detachment to Word of Heaven, Greater + DeleteLocalInt(oPC, "PRC_LawOfResistance" + IntToString(UtterID));*/ + + //Invocations + DeleteLocalInt(oPC, "ChillingFogLock"); + //Endure Exposure wearing off + array_delete(oPC, "BreathProtected"); + DeleteLocalInt(oPC, "DragonWard"); + + //Scry on Familiar + DeleteLocalInt(oPC, "Scry_Familiar"); + + // Undead HD + DeleteLocalInt(oPC, "PRCUndeadHD"); + DeleteLocalInt(oPC, "PRCUndeadFSPen"); + + //Template Spell-Like Abilities + DelayCommand(0.5f, TemplateSLAs(oPC)); + + // future PRCs Go below here +} + +void ScrubPCSkin(object oPC, object oSkin) +{ + int nGeneration = PRC_NextGeneration(GetLocalInt(oPC, PRC_ScrubPCSkin_Generation)); + if (DEBUG > 1) DoDebug("ScrubPCSkin Generation: " + IntToString(nGeneration)); + SetLocalInt(oPC, PRC_ScrubPCSkin_Generation, nGeneration); + + int iCode = GetHasFeat(FEAT_SF_CODE,oPC); + int ipType, st; + if(!(/*GetIsPolyMorphedOrShifted(oPC) || */GetIsObjectValid(GetMaster(oPC)))) + { + itemproperty ip = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ip)) + { + // Insert Logic here to determine if we spare a property + ipType = GetItemPropertyType(ip); + if(ipType == ITEM_PROPERTY_BONUS_FEAT) + { + // Check for specific Bonus Feats + // Reference iprp_feats.2da + st = GetItemPropertySubType(ip); + + // Spare 400 through 570 and 398 -- epic spells & spell effects + //also spare the new spellbook feats (1000-12000 & 17701-24704 + //also spare the psionic, trunaming, tob, invocation feats (12000-16000) + // spare template, tob stuff (16300-17700) + // changed by fluffyamoeba so that iprp weapon specialization, dev crit, epic weapon focus, epic weapon spec + // overwhelming crit and weapon of choice are no longer skipped. + if ((st < 400 || st > 570) + && st != 398 + && (st < 1000 || st > 13520) + //&& (st < 1000 || st > 13999) + //&& (st < 14501 || st > 15999) + && (st < 16300 || st > 24704) + && (st < 223 || st > 226) // draconic feats + && (st < 229 || st > 249) // Pnp spellschool feats and PRC options feat (231-249 & 229) + && st != 259 // 259 - psionic focus + && (st < 141 || st > 200) // 141 - shadowmaster shades, 142-151 bonus domains casting feats, 152 - 200 bonus domain powers + && ( (st == IP_CONST_FEAT_PRC_POWER_ATTACK_QUICKS_RADIAL || + st == IP_CONST_FEAT_POWER_ATTACK_SINGLE_RADIAL || + st == IP_CONST_FEAT_POWER_ATTACK_FIVES_RADIAL) ? // Remove the PRC Power Attack radials if the character no longer has Power Attack + !GetHasFeat(FEAT_POWER_ATTACK, oPC) : + TRUE // If the feat is not relevant to this clause, always pass + ) + ) + RemoveItemProperty(oSkin, ip); + } + else if(ipType == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + //bonus spellslots code here + //st = GetItemPropertySubType(ip); + RemoveItemProperty(oSkin, ip); + } + else + RemoveItemProperty(oSkin, ip); + + // Get the next property + ip = GetNextItemProperty(oSkin); + } + } + if(iCode) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(381),oSkin); + + // Schedule restoring the unhealable ability damage + DelayCommand(0.1f, DelayedReApplyUnhealableAbilityDamage(nGeneration, oPC)); + + // Remove all natural weapons too + // ClearNaturalWeapons(oPC); + // Done this way to remove prc_inc_natweap and prc_inc_combat from the include + // Should help with compile speeds and the like + //array_delete(oPC, "ARRAY_NAT_SEC_WEAP_RESREF"); + //array_delete(oPC, "ARRAY_NAT_PRI_WEAP_RESREF"); + //array_delete(oPC, "ARRAY_NAT_PRI_WEAP_ATTACKS"); +} + +int BlastInfidelOrFaithHeal(object oCaster, object oTarget, int iEnergyType, int iDisplayFeedback) +{ + //Don't bother doing anything if iEnergyType isn't either positive/negative energy + if(iEnergyType != DAMAGE_TYPE_POSITIVE && iEnergyType != DAMAGE_TYPE_NEGATIVE) + return FALSE; + + //If the target is undead and damage type is negative + //or if the target is living and damage type is positive + //then we're healing. Otherwise, we're harming. + int iTombTainted = GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD; + int iHeal = ( iEnergyType == DAMAGE_TYPE_NEGATIVE && (MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || iTombTainted)) || + ( iEnergyType == DAMAGE_TYPE_POSITIVE && MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD && !iTombTainted); + int iRetVal = FALSE; + int iAlignDif = CompareAlignment(oCaster, oTarget); + string sFeedback = ""; + + if(iHeal){ + if((GetHasFeat(FEAT_FAITH_HEALING, oCaster) && iAlignDif < 2)){ + iRetVal = TRUE; + sFeedback = "Faith Healing"; + } + } + else{ + if((GetHasFeat(FEAT_BLAST_INFIDEL, oCaster) && iAlignDif >= 2)){ + iRetVal = TRUE; + sFeedback = "Blast Infidel"; + } + } + + if(iDisplayFeedback) FloatingTextStringOnCreature(sFeedback, oCaster); + return iRetVal; +} + +int GetShiftingFeats(object oPC) +{ + int nNumFeats; + nNumFeats = GetHasFeat(FEAT_BEASTHIDE_ELITE, oPC) + + GetHasFeat(FEAT_DREAMSIGHT_ELITE, oPC) + + GetHasFeat(FEAT_GOREBRUTE_ELITE, oPC) + + GetHasFeat(FEAT_LONGSTRIDE_ELITE, oPC) + + GetHasFeat(FEAT_LONGTOOTH_ELITE, oPC) + + GetHasFeat(FEAT_RAZORCLAW_ELITE, oPC) + + GetHasFeat(FEAT_WILDHUNT_ELITE, oPC) + + GetHasFeat(FEAT_EXTRA_SHIFTER_TRAIT, oPC) + + GetHasFeat(FEAT_HEALING_FACTOR, oPC) + + GetHasFeat(FEAT_SHIFTER_AGILITY, oPC) + + GetHasFeat(FEAT_SHIFTER_DEFENSE, oPC) + + GetHasFeat(FEAT_GREATER_SHIFTER_DEFENSE, oPC) + + GetHasFeat(FEAT_SHIFTER_FEROCITY, oPC) + + GetHasFeat(FEAT_SHIFTER_INSTINCTS, oPC) + + GetHasFeat(FEAT_SHIFTER_SAVAGERY, oPC); + + return nNumFeats; +} + +//Including DelayedApplyEffectToObject here because it is often used in conjunction with EvalPRCFeats and I don't know a better place to put it +void DelayedApplyEffectToObject(int nExpectedGeneration, int nCurrentGeneration, int nDuration, effect eEffect, object oTarget, float fDuration) +{ + if (nExpectedGeneration != nCurrentGeneration) + { + //Generation has changed, so don't apply the effect + return; + } + ApplyEffectToObject(nDuration, eEffect, oTarget, fDuration); +} + +//Including DelayedApplyEffectToObject here because it is often used in conjunction with EvalPRCFeats and I don't know a better place to put it +void DelayApplyEffectToObject(float fDelay, string sGenerationName, int nDuration, effect eEffect, object oTarget, float fDuration = 0.0f) +{ + /* + There are a couple of problems that can arise in code that removes and reapplies effects; + this function helps deal with those problems. One example of a typical place where these problems + frequently arise is in the class scripts called by the EvalPRCFeats function. + + The first problem is that when code removes and immediately reapplies a series of effects, + some of those effects may not actually be reapplied. This is because the RemoveEffect() function + doesn't actually remove an effect, it marks it to be removed later--when the currently running + script finishes. If any of the effects we reapply matches one of the effects marked to be + removed, that reapplied effect will be removed when the currently running script finishes + and so will be unexpectedly missing. To illustrate: + 1) We start with effect A and B. + 2) The application function is called; it removes all effects and reapplies effects B and C. + 3) The actual removal happens when the script ends: effect A and B are removed. + End result: we have only effect C instead of the expected B and C. + The solution to this is to reapply the effects later using DelayCommand(). + + This introduces a new problem. If the function that removes and reapplies the effects is called + multiple times quickly, it can queue up a series of delayed applications. This causes two problems: + if the data on which the effects are calculated changes, the earlier delayed applications can + apply effects that should no longer be used, but they are anyway because the delayed code doesn't + know this. To illustrate: + 1) The application function is called; it removes all effects, schedules delayed application of effect A. + 2) The application function is called again; it removes all effects, schedules delayed application of effect B. + 3) Delayed application of effect A occurs. + 4) Delayed application of effect B occurs. + End result: we have both effect A and B instead of the expected result, B alone. + Another problem is that we can end up with multiple copies of the same effect. + If this happens enough, it can cause "Effect List overflow" errors. Also, if the effect stacks + with itself, this gives a greater bonus or penalty than it should. To illustrate: + 1) The application function is called; it removes all effects, schedules delayed application of effect C. + 2) The application function is called; it removes all effects, schedules delayed application of effect C. + 3) Delayed application of effect C occurs. + 4) Delayed application of effect C occurs. + End result: we have effect C twice instead of just once. + The solution is to both these problems is for the application function to increment an integer each time it + is called and to pass this to the delayed application function. The delayed application actually happens only + if the generation when it runs is the same as the generation when it was scheduled. To illustrate: + 1) We start with effect A and B applied. + 2) The application function is called: it increments generation to 2, schedules delayed application of effect B and C. + 3) The application function is called: it increments generation to 3, schedules delayed application of effect C. + 4) The generation 2 delayed application function executes: it sees that the current generation is 3 and simply exits, doing nothing. + 5) The generation 3 delayed application function executes: it sees that the current generation is 3, so it applies effect C. + End result: we have one copy of effect C, which is what we wanted. + */ + + if (fDelay < 0.0f || GetStringLength(sGenerationName) == 0) + { + ApplyEffectToObject(nDuration, eEffect, oTarget, fDuration); + } + else + { + int nExpectedGeneration = GetLocalInt(oTarget, sGenerationName); //This gets the generation now + DelayCommand( + fDelay, + DelayedApplyEffectToObject( + nExpectedGeneration, + GetLocalInt(oTarget, sGenerationName), //This is delayed by the DelayCommand, so it gets the generation when DelayedApplyEffectToObject is actually executed + nDuration, + eEffect, + oTarget, + fDuration + ) + ); + } +} + +//Including DelayedAddItemProperty here to keep it with DelayedApplyEffectToObject, though more properly it should probably be in inc_item_props.nss +void DelayedAddItemProperty(int nExpectedGeneration, int nCurrentGeneration, int nDurationType, itemproperty ipProperty, object oItem, float fDuration) +{ + if (nExpectedGeneration != nCurrentGeneration) + { + //Generation has changed, so don't apply the effect + return; + } + AddItemProperty(nDurationType, ipProperty, oItem, fDuration); +} + +//Including DelayAddItemProperty here to keep it with DelayApplyEffectToObject, though more properly it should probably be in inc_item_props.nss +void DelayAddItemProperty(float fDelay, object oGenerationHolder, string sGenerationName, int nDurationType, itemproperty ipProperty, object oItem, float fDuration = 0.0f) +{ + /* + There are a couple of problems that can arise in code that removes and reapplies item properties; + this function helps deal with those problems. One example of a typical place where these problems + frequently arise is in the class scripts called by the EvalPRCFeats function. + + The first problem is that when code removes and immediately reapplies a series of item properties, + some of those properties may not actually be reapplied. This is because the RemoveItemProperty() function + doesn't actually remove a property, it marks it to be removed later--when the currently running + script finishes. If any of the properties we reapply matches one of the properties marked to be + removed, that reapplied property will be removed when the currently running script finishes + and so will be unexpectedly missing. To illustrate: + 1) We start with properties A and B. + 2) The application function is called; it removes all properties and reapplies properties B and C. + 3) The actual removal happens when the script ends: property A and B are removed. + End result: we have only property C instead of the expected B and C. + The solution to this is to reapply the properties later using DelayCommand(). + + This introduces a new problem. If the function that removes and reapplies the properties is called + multiple times quickly, it can queue up a series of delayed applications. This causes two problems: + if the data on which the properties are calculated changes, the earlier delayed applications can + apply properties that should no longer be used, but they are anyway because the delayed code doesn't + know this. To illustrate: + 1) The application function is called; it removes all properties, schedules delayed application of property A. + 2) The application function is called again; it removes all properties, schedules delayed application of property B. + 3) Delayed application of property A occurs. + 4) Delayed application of property B occurs. + End result: we have both property A and B instead of the expected result, B alone. + Another problem is that we can end up with multiple copies of the same property. + If this happens enough, it can cause "Effect List overflow" errors. Also, if the property stacks + with itself, this gives a greater bonus or penalty than it should. To illustrate: + 1) The application function is called; it removes all properties, schedules delayed application of property C. + 2) The application function is called; it removes all properties, schedules delayed application of property C. + 3) Delayed application of property C occurs. + 4) Delayed application of property C occurs. + End result: we have property C twice instead of just once. + The solution is to both these problems is for the application function to increment an integer each time it + is called and to pass this to the delayed application function. The delayed application actually happens only + if the generation when it runs is the same as the generation when it was scheduled. To illustrate: + 1) We start with property A and B applied. + 2) The application function is called: it increments generation to 2, schedules delayed application of property B and C. + 3) The application function is called: it increments generation to 3, schedules delayed application of property C. + 4) The generation 2 delayed application function executes: it sees that the current generation is 3 and simply exits, doing nothing. + 5) The generation 3 delayed application function executes: it sees that the current generation is 3, so it applies property C. + End result: we have one copy of property C, which is what we wanted. + */ + + if (fDelay < 0.0f || GetStringLength(sGenerationName) == 0) + { + AddItemProperty(nDurationType, ipProperty, oItem, fDuration); + } + else + { + int nExpectedGeneration = GetLocalInt(oGenerationHolder, sGenerationName); //This gets the generation now + DelayCommand( + fDelay, + DelayedAddItemProperty( + nExpectedGeneration, + GetLocalInt(oGenerationHolder, sGenerationName), //This is delayed by the DelayCommand, so it gets the generation when DelayedAddItemProperty is actually executed + nDurationType, + ipProperty, + oItem, + fDuration + ) + ); + } +} + +void FeatUsePerDay(object oPC, int iFeat, int iAbiMod = ABILITY_CHARISMA, int iMod = 0, int iMin = 1) +{ + if(!GetHasFeat(iFeat,oPC)) + return; + + int iAbi = GetAbilityModifier(iAbiMod, oPC); + iAbi = (iAbi > 0) ? iAbi : 0; + + if (iAbiMod == -1) iAbi = 0; + iAbi += iMod; + + if(iAbi < iMin) + iAbi = iMin; + + while(GetHasFeat(iFeat, oPC)) + DecrementRemainingFeatUses(oPC, iFeat); + + while(iAbi) + { + IncrementRemainingFeatUses(oPC, iFeat); + iAbi--; + } +} + +void DomainUses(object oPC) +{ + int nUses; + if(!GetHasFeat(FEAT_BONUS_DOMAIN_STRENGTH, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_STRENGTH_DOMAIN_POWER)) + { + nUses = GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oPC) ? GetAbilityModifier(ABILITY_STRENGTH, oPC) : 1; + FeatUsePerDay(oPC, FEAT_STRENGTH_DOMAIN_POWER, -1, nUses); + } + if(GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC) && GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) && PRC_Funcs_GetFeatKnown(oPC, FEAT_SUN_DOMAIN_POWER)) + { + nUses = GetHasFeat(FEAT_EXTRA_TURNING, oPC) ? 7 : 3; + FeatUsePerDay(oPC, FEAT_SUN_DOMAIN_POWER, ABILITY_CHARISMA, nUses); + } + else if(!GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_SUN_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_SUN_DOMAIN_POWER, -1, 1); + + if(!GetHasFeat(FEAT_BONUS_DOMAIN_BLIGHTBRINGER, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_BLIGHTBRINGER)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_BLIGHTBRINGER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_AIR, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_AIR_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_AIR_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_EARTH, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_EARTH_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_EARTH_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_FIRE, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_FIRE_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_FIRE_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_WATER, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_WATER_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_WATER_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_SLIME, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_SLIME)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_SLIME, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_SPIDER, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_SPIDER)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_SPIDER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_SCALEYKIND, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_SCALEYKIND)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_SCALEYKIND, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_PLANT, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_PLANT_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_PLANT_DOMAIN_POWER, ABILITY_CHARISMA, 3); +} + +void MephlingBreath(object oPC) //:: Mephlings +{ + if(!GetHasFeat(FEAT_MEPHLING_BREATH, oPC)) + return; + + int nMephBreath = ((1 + GetHitDice(oPC)) / 4); + + FeatUsePerDay(oPC, FEAT_MEPHLING_BREATH, -1, nMephBreath); +} + +void FeatAlaghar(object oPC) +{ + int iAlagharLevel = GetLevelByClass(CLASS_TYPE_ALAGHAR, oPC); + + if (!iAlagharLevel) return; + + int iClangStrike = iAlagharLevel/3; + int iClangMight = (iAlagharLevel - 1)/3; + int iRockburst = (iAlagharLevel + 2)/4; + + FeatUsePerDay(oPC, FEAT_CLANGEDDINS_STRIKE, -1, iClangStrike); + FeatUsePerDay(oPC, FEAT_CLANGEDDINS_MIGHT, -1, iClangMight); + FeatUsePerDay(oPC, FEAT_ALAG_ROCKBURST, -1, iRockburst); +} + + +void FeatDiabolist(object oPC) +{ + int Diabol = GetLevelByClass(CLASS_TYPE_DIABOLIST, oPC); + + if (!Diabol) return; + + int iUse = (Diabol + 3)/3; + + FeatUsePerDay(oPC,FEAT_DIABOL_DIABOLISM_1,-1,iUse); + FeatUsePerDay(oPC,FEAT_DIABOL_DIABOLISM_2,-1,iUse); + FeatUsePerDay(oPC,FEAT_DIABOL_DIABOLISM_3,-1,iUse); +} + +void FeatNinja (object oPC) +{ + int iNinjaLevel = GetLevelByClass(CLASS_TYPE_NINJA, oPC); + // Ascetic Stalker + if (GetHasFeat(FEAT_ASCETIC_STALKER, oPC)) + iNinjaLevel += GetLevelByClass(CLASS_TYPE_MONK, oPC);; + + if (!iNinjaLevel) return; + + int nUsesLeft = iNinjaLevel/2; + if (nUsesLeft < 1) + nUsesLeft = 1; + + // Expanded Ki Pool + if (GetHasFeat(FEAT_EXPANDED_KI_POOL, oPC)) nUsesLeft += 3; + + FeatUsePerDay(oPC, FEAT_KI_POWER, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_GHOST_STEP, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_GHOST_STRIKE, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_GHOST_WALK, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_KI_DODGE, ABILITY_WISDOM, nUsesLeft); + + SetLocalInt(oPC, "prc_ninja_ki", nUsesLeft); +} + +void BarbarianRage(object oPC) +{ + if(!GetHasFeat(FEAT_BARBARIAN_RAGE, oPC)) return; + + int nUses = (GetLevelByClass(CLASS_TYPE_BARBARIAN, oPC) + GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oPC) + GetLevelByClass(CLASS_TYPE_PRC_EYE_OF_GRUUMSH, oPC)) / 4 + 1; + nUses += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) + 2) / 5; + nUses += (GetLevelByClass(CLASS_TYPE_BATTLERAGER, oPC) + 1) / 2; + nUses += (GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oPC) + 2) / 4; + nUses += GetLevelByClass(CLASS_TYPE_RUNESCARRED, oPC) ? ((GetLevelByClass(CLASS_TYPE_RUNESCARRED, oPC) / 4) + 1) : 0; + nUses += (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oPC) + 4) / 6; + + if(GetHasFeat(FEAT_EXTRA_RAGE, oPC)) nUses += 2; + + FeatUsePerDay(oPC, FEAT_BARBARIAN_RAGE, -1, nUses); + FeatUsePerDay(oPC, FEAT_GREATER_RAGE, -1, nUses); + + if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) > 0) + { + if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) > 9) + nUses = 3; + else if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) > 4) + nUses = 2; + else + nUses = 1; + + FeatUsePerDay(oPC, FEAT_SPELL_RAGE, -1, nUses); + } +} + +void BardSong(object oPC) +{ + // This is used to set the number of bardic song uses per day, as bardic PrCs can increase it + // or other classes can grant it on their own + if(!GetHasFeat(FEAT_BARD_SONGS, oPC)) return; + + int nTotal = GetLevelByClass(CLASS_TYPE_BARD, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_DIRGESINGER, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oPC) / 2; + + if(GetHasFeat(FEAT_EXTRA_MUSIC, oPC)) nTotal += 4; + + FeatUsePerDay(oPC, FEAT_BARD_SONGS, -1, nTotal); +} + +void FeatVirtuoso(object oPC) +{ + int iVirtuosoLevel = GetLevelByClass(CLASS_TYPE_VIRTUOSO, oPC); + if (!iVirtuosoLevel) return; + + int nUses = GetLevelByClass(CLASS_TYPE_BARD, oPC) + iVirtuosoLevel; + if(GetHasFeat(FEAT_EXTRA_MUSIC, oPC)) nUses += 4; + SetPersistantLocalInt(oPC, "Virtuoso_Performance_Uses", nUses); + int nFeat; + for(nFeat = FEAT_VIRTUOSO_SUSTAINING_SONG; nFeat <= FEAT_VIRTUOSO_PERFORMANCE; nFeat++) + { + FeatUsePerDay(oPC, nFeat, -1, nUses); + } +} + +void HexCurse(object oPC) +{ + int iHexLevel = GetLevelByClass(CLASS_TYPE_HEXBLADE, oPC); + + if (!iHexLevel) return; + + //Hexblade's Curse + int nUses = (iHexLevel + 3) / 4; // every 4 levels get 1 more use + FeatUsePerDay(oPC, FEAT_HEXCURSE, ABILITY_CHARISMA, nUses); + + //Swift Cast + if(iHexLevel > 13) + nUses = (iHexLevel + 2) / 4; + else if(iHexLevel > 10) + nUses = 3; + else if(iHexLevel > 7) + nUses = 2; + else if(iHexLevel > 5) + nUses = 1; + else + nUses = 0; + FeatUsePerDay(oPC, FEAT_SWIFT_CAST, -1, nUses); +} + +void FeatShadowblade(object oPC) +{ + int iShadowLevel = GetLevelByClass(CLASS_TYPE_SHADOWBLADE, oPC); + if (!iShadowLevel) return; + + FeatUsePerDay(oPC, FEAT_UNERRING_STRIKE, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_UNEXPECTED_STRIKE, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_EPHEMERAL_WEAPON, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_SHADOWY_STRIKE, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_FAR_SHADOW, -1, iShadowLevel); +} + +void FeatNoble(object oPC) +{ + int iNobleLevel = GetLevelByClass(CLASS_TYPE_NOBLE, oPC); + if (!iNobleLevel) return; + + int nBonus = 0; + if (iNobleLevel >= 17) nBonus = 5; + else if (iNobleLevel >= 13) nBonus = 4; + else if (iNobleLevel >= 9) nBonus = 3; + else if (iNobleLevel >= 5) nBonus = 2; + else if (iNobleLevel >= 2) nBonus = 1; + + FeatUsePerDay(oPC, FEAT_NOBLE_CONFIDENCE, -1, nBonus); + + nBonus = (iNobleLevel - 11) / 3 + 1; + + FeatUsePerDay(oPC, FEAT_NOBLE_GREATNESS, -1, nBonus); +} + +void DarkKnowledge(object oPC) +{ + int iArchivistLevel = GetLevelByClass(CLASS_TYPE_ARCHIVIST, oPC); + if(!iArchivistLevel) return; + + int nUses = (iArchivistLevel / 3) + 3; + FeatUsePerDay(oPC, FEAT_DK_TACTICS, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_PUISSANCE, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_FOE, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_DREADSECRET, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_FOREKNOWLEDGE, -1, nUses); +} + +void FeatImbueArrow(object oPC) +{ + if(GetPRCSwitch(PRC_USE_NEW_IMBUE_ARROW)) + FeatUsePerDay(oPC, FEAT_PRESTIGE_IMBUE_ARROW, -1, 0, 0); +} + +void DragonDisciple(object oPC) +{ + if(!GetHasFeat(FEAT_DRAGON_DIS_BREATH, oPC)) + return; + + //Dragon Disciples that do not possess any breath weapon + if(GetHasFeat(FEAT_CHIANG_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_PAN_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_SHEN_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_TUN_MI_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_YU_LUNG_DRAGON, oPC)) + DecrementRemainingFeatUses(oPC, FEAT_DRAGON_DIS_BREATH); +} + +void Warlock(object oPC) +{ + if(GetHasFeat(FEAT_FIENDISH_RESILIENCE, oPC)) + { + //Add daily Uses of Fiendish Resilience for epic warlock + if(GetHasFeat(FEAT_EPIC_FIENDISH_RESILIENCE_I, oPC)) + { + int nFeatAmt = 0; + int bDone = FALSE; + while(!bDone) + { + if(nFeatAmt >= 9) + bDone = TRUE; + else if(GetHasFeat(FEAT_EPIC_FIENDISH_RESILIENCE_II + nFeatAmt, oPC)) + nFeatAmt++; + else + bDone = TRUE; + } + nFeatAmt++; + FeatUsePerDay(oPC, FEAT_FIENDISH_RESILIENCE, -1, nFeatAmt); + } + else + FeatUsePerDay(oPC, FEAT_FIENDISH_RESILIENCE, -1, 1); + } + + //Hellfire infusion + int nCha = GetAbilityModifier(ABILITY_CHARISMA, oPC); + FeatUsePerDay(oPC, FEAT_HELLFIRE_INFUSION, -1, nCha); + + //Eldritch Spellweave + nCha += 3; + FeatUsePerDay(oPC, FEAT_ELDRITCH_SPELLWEAVE, -1, nCha); +} + +void KotMC(object oPC) +{ + int iKotMCLevel = GetLevelByClass(CLASS_TYPE_KNIGHT_MIDDLECIRCLE, oPC); + if(!iKotMCLevel) return; + + int nUses = iKotMCLevel / 3; + FeatUsePerDay(oPC, FEAT_KOTMC_TRUE_STRIKE, -1, nUses); +} + +void Templar(object oPC) +{ + if(!GetHasFeat(FEAT_SECULAR_AUTHORITY, oPC)) return; + + int nTemplar = GetLevelByClass(CLASS_TYPE_TEMPLAR, oPC); + int nUses = nTemplar + ((GetHitDice(oPC) - nTemplar) / 4); + FeatUsePerDay(oPC, FEAT_SECULAR_AUTHORITY, -1, nUses); +} + +void FeatRacial(object oPC) +{ + //Shifter bonus shifting uses + int nRace = GetRacialType(oPC); + if(nRace == RACIAL_TYPE_SHIFTER) + { + int nShiftFeats = GetShiftingFeats(oPC); + int nBonusShiftUses = (nShiftFeats / 2) + 1; + FeatUsePerDay(oPC, FEAT_SHIFTER_SHIFTING, -1, nBonusShiftUses); + } + else if(nRace == RACIAL_TYPE_FORESTLORD_ELF) + { + int nUses = GetHitDice(oPC) / 5 + 1; + FeatUsePerDay(oPC, FEAT_FORESTLORD_TREEWALK, -1, nUses); + } +} + +void CombatMedic(object oPC) +{ + int iCombMed = GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oPC); + if(!iCombMed) return; + + FeatUsePerDay(oPC, FEAT_HEALING_KICKER_1, ABILITY_WISDOM, iCombMed); + FeatUsePerDay(oPC, FEAT_HEALING_KICKER_2, ABILITY_WISDOM, iCombMed); + FeatUsePerDay(oPC, FEAT_HEALING_KICKER_3, ABILITY_WISDOM, iCombMed); +} + +void MasterOfShrouds(object oPC) +{ + if(!GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oPC)) return; + + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_1, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_2, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_3, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_4, ABILITY_CHARISMA, 3); +} + +void SLAUses(object oPC) +{ + if(!GetHasFeat(FEAT_SPELL_LIKE_ABILITY_1, oPC)) return; + + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_1, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_1")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_2, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_2")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_3, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_3")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_4, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_4")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_5, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_5")); +} + +void ShadowShieldUses(object oPC) +{ + if(!GetHasFeat(FEAT_SA_SHIELDSHADOW, oPC)) return; + FeatUsePerDay(oPC, FEAT_SA_SHIELDSHADOW, -1, GetPrCAdjustedCasterLevelByType(TYPE_ARCANE, oPC)); +} + +void BlighterFeats(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_BLIGHTER, oPC); + if(0 >= nClass) return; + + if (nClass == 3) + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 1); + else if (nClass == 4) + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 2); + else if (nClass == 5) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 2); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 1); + } + else if (nClass == 6) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 3); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 1); + } + else if (nClass == 7) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 3); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 2); + } + else if (nClass == 8) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 4); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 2); + } + else if (nClass == 9) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 4); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 3); + } + else + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 5); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 3); + } +} + +void MysteryFeats(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oPC); + if(nClass > 0) + { + if (nClass >= 10) + FeatUsePerDay(oPC, FEAT_CLOAK_SHADOWS, -1, 2); + else if (nClass >= 6) + FeatUsePerDay(oPC, FEAT_CLOAK_SHADOWS, -1, 3); + else + FeatUsePerDay(oPC, FEAT_CLOAK_SHADOWS, -1, 1); + + if (nClass >= 7) + FeatUsePerDay(oPC, FEAT_DANCING_SHADOWS, -1, 2); + else + FeatUsePerDay(oPC, FEAT_DANCING_SHADOWS, -1, 1); + } + nClass = GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oPC); + if(nClass > 0) + { + if (nClass >= 8) + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, 3); + else if (nClass >= 5) + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, 2); + else + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, 1); + } + nClass = GetLevelByClass(CLASS_TYPE_SHADOWSMITH, oPC); + if(nClass > 0) + { + FeatUsePerDay(oPC, FEAT_TOUCH_SHADOW , -1, nClass); + FeatUsePerDay(oPC, FEAT_SHROUD_SHADOW , -1, nClass); + FeatUsePerDay(oPC, FEAT_SHADOW_CRAFT , -1, nClass/2); + FeatUsePerDay(oPC, FEAT_ARMOR_SHADOW , -1, nClass/2); + FeatUsePerDay(oPC, FEAT_ARMOR_SHADOW_Q, -1, nClass/2); + } +} + +void WildMage(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oPC); + if(nClass > 0) + { + if (nClass >= 8) + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, 2); + else if (nClass >= 5) + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, 3); + else + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, 1); + } +} + +void Factotum(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + if(nClass > 0) + { + if (nClass >= 20) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 6); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 6); + } + else if (nClass >= 15) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 5); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 5); + } + else if (nClass >= 10) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 4); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 4); + } + else + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 3); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 3); + } + } +} + +void Sharess(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oPC); + if(nClass > 0) + { + FeatUsePerDay(oPC, FEAT_CELEBRANT_SHARESS_FASCINATE , -1, 0, nClass); + FeatUsePerDay(oPC, FEAT_CELEBRANT_SHARESS_CONFUSE , -1, 0, nClass); + FeatUsePerDay(oPC, FEAT_CELEBRANT_SHARESS_DOMINATE , -1, 0, nClass); + } +} + +void SoulbornDefense(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_SOULBORN, oPC); + if(nClass > 0) + { + if (nClass >= 37) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 8); + else if (nClass >= 33) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 7); + else if (nClass >= 29) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 6); + else if (nClass >= 25) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 5); + else if (nClass >= 21) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 4); + else if (nClass >= 17) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 3); + else if (nClass >= 13) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 1); + } +} + +void TotemistReshape(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_TOTEMIST, oPC); + if(nClass > 0) + { + if (nClass >= 40) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 9); + else if (nClass >= 36) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 8); + else if (nClass >= 32) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 7); + else if (nClass >= 28) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 6); + else if (nClass >= 24) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 5); + else if (nClass >= 20) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 4); + else if (nClass >= 16) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 3); + else if (nClass >= 12) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 1); + } +} + +void CWSamurai(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CW_SAMURAI, oPC); + if(nClass > 0) + { + if (nClass >= 17) + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 4); + else if (nClass >= 12) + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 3); + else if (nClass >= 7) + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 1); + } +} + +void CrusaderSmite(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC); + if(nClass > 0) + { + if (nClass >= 18) + FeatUsePerDay(oPC, FEAT_CRUSADER_SMITE, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_CRUSADER_SMITE, -1, 0, 1); + } +} + +void AnimaMage(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oPC); + if(nClass > 0) + { + if (nClass >= 9) + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, 3); + else if (nClass >= 7) + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, 1); + } +} + +void FeatSpecialUsePerDay(object oPC) +{ + FeatUsePerDay(oPC, FEAT_WWOC_WIDEN_SPELL, ABILITY_CHARISMA, GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oPC)); + FeatUsePerDay(oPC, FEAT_FIST_DAL_QUOR_STUNNING_STRIKE, -1, GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oPC)); + FeatUsePerDay(oPC, FEAT_AD_FALSE_KEENNESS, -1, GetLevelByClass(CLASS_TYPE_ARCANE_DUELIST, oPC)); + FeatUsePerDay(oPC, FEAT_LASHER_STUNNING_SNAP, -1, GetLevelByClass(CLASS_TYPE_LASHER, oPC)); + FeatUsePerDay(oPC, FEAT_AD_BLUR, -1, GetLevelByClass(CLASS_TYPE_ARCANE_DUELIST, oPC)); + FeatUsePerDay(oPC, FEAT_SHADOW_RIDE, -1, GetLevelByClass(CLASS_TYPE_CRINTI_SHADOW_MARAUDER, oPC)); + FeatUsePerDay(oPC, FEAT_SHADOWJUMP, -1, GetLevelByClass(CLASS_TYPE_SHADOWLORD, oPC)); + FeatUsePerDay(oPC, FEAT_SHADOWBANE_SMITE, -1, (GetLevelByClass(CLASS_TYPE_SHADOWBANE_INQUISITOR, oPC)+2)/4); + FeatUsePerDay(oPC, FEAT_INCARNUM_RADIANCE, -1, (GetLevelByClass(CLASS_TYPE_INCARNATE, oPC)+2)/5); + FeatUsePerDay(oPC, FEAT_RAPID_MELDSHAPING, -1, (GetLevelByClass(CLASS_TYPE_INCARNATE, oPC)+1)/6); + FeatUsePerDay(oPC, FEAT_SMITE_OPPOSITION, -1, (GetLevelByClass(CLASS_TYPE_SOULBORN, oPC)+5)/5); + FeatUsePerDay(oPC, FEAT_SMITE_CHAOS, -1, (GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oPC)+2)/3); + FeatUsePerDay(oPC, FEAT_INCANDESCENT_OVERLOAD, -1, (GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oPC)-1)/3); + FeatUsePerDay(oPC, FEAT_NECROCARNATE_SOULSHIELD, -1, GetLevelByClass(CLASS_TYPE_NECROCARNATE, oPC)/2); + FeatUsePerDay(oPC, FEAT_SCION_DANTALION_SCHOLARSHIP, -1, GetLevelByClass(CLASS_TYPE_SCION_DANTALION, oPC)); + FeatUsePerDay(oPC, FEAT_SMITE_GOOD_ALIGN, -1, (GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oPC)+1)/2); + FeatUsePerDay(oPC, FEAT_FIST_OF_IRON, ABILITY_WISDOM, 3); + FeatUsePerDay(oPC, FEAT_SMITE_UNDEAD, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_COC_WRATH, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_KILLOREN_ASPECT_D, ABILITY_CHARISMA); + FeatUsePerDay(oPC, FEAT_AVENGING_STRIKE, ABILITY_CHARISMA, 0, 1); + FeatUsePerDay(oPC, FEAT_INCARNUM_BLADE_REBIND, ABILITY_CONSTITUTION, 1); + FeatUsePerDay(oPC, FEAT_WITCHBORN_INTEGUMENT, ABILITY_CONSTITUTION, 1); + FeatUsePerDay(oPC, FEAT_LIPS_RAPTUR); + FeatUsePerDay(oPC, FEAT_COMMAND_SPIDERS, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_FM_FOREST_DOMINION, ABILITY_CHARISMA, 3); + FeatDiabolist(oPC); + FeatAlaghar(oPC); + ShadowShieldUses(oPC); + CombatMedic(oPC); + FeatNinja(oPC); + FeatNoble(oPC); + MasterOfShrouds(oPC); + HexCurse(oPC); + FeatRacial(oPC); + FeatShadowblade(oPC); + SoulbornDefense(oPC); + + int nDread = GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC); + if (nDread >= 17) + FeatUsePerDay(oPC, FEAT_DN_ENERVATING_TOUCH, -1, nDread); + else + FeatUsePerDay(oPC, FEAT_DN_ENERVATING_TOUCH, -1, nDread/2); + + SLAUses(oPC); + DomainUses(oPC); + BardSong(oPC); + BarbarianRage(oPC); + FeatVirtuoso(oPC); + ResetExtraStunfistUses(oPC); + DarkKnowledge(oPC); + FeatImbueArrow(oPC); + DragonDisciple(oPC); + Warlock(oPC); + KotMC(oPC); + Templar(oPC); + BlighterFeats(oPC); + MysteryFeats(oPC); + WildMage(oPC); + Factotum(oPC); + Sharess(oPC); + TotemistReshape(oPC); + CWSamurai(oPC); + CrusaderSmite(oPC); + AnimaMage(oPC); + MephlingBreath(oPC); +} + diff --git a/trunk/include/prc_inc_itmrstr.nss b/trunk/include/prc_inc_itmrstr.nss new file mode 100644 index 00000000..7f2ca808 --- /dev/null +++ b/trunk/include/prc_inc_itmrstr.nss @@ -0,0 +1,561 @@ +/* + + This include governs all the new itemproperties + Both restrictions and features + + + +*/ +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string PLAYER_SPEED_INCREASE = "player_speed_increase"; +const string PLAYER_SPEED_DECREASE = "player_speed_decrease"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int DoUMDCheck(object oItem, object oPC, int nDCMod); + +int CheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID); + +/** + * Non-returning wrapper for CheckPRCLimitations. + */ +void VoidCheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID); + +void CheckForPnPHolyAvenger(object oItem); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_utility" +#include "prc_inc_newip" +#include "prc_inc_castlvl" +#include "inc_newspellbook" + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/*void _prc_inc_itmrstr_ApplySpeedModification(object oPC, int nEffectType, int nSpeedMod) +{ + if(DEBUG) DoDebug("_prc_inc_itmrstr_ApplySpeedModification(" + DebugObject2Str(oPC) + ", " + IntToString(nEffectType) + ", " + IntToString(nSpeedMod) + ")"); + // The skin object should be OBJECT_SELF here + // Clean up existing speed modification + effect eTest = GetFirstEffect(oPC); + while(GetIsEffectValid(eTest)) + { + if(GetEffectCreator(eTest) == OBJECT_SELF && + GetEffectType(eTest) == nEffectType && + GetEffectSubType(eTest) == SUBTYPE_SUPERNATURAL + ) + RemoveEffect(oPC, eTest); + eTest = GetNextEffect(oPC); + } + + // Apply speed mod if there is any + if(nSpeedMod > 0) + { + effect eSpeedMod = SupernaturalEffect(nEffectType == EFFECT_TYPE_MOVEMENT_SPEED_INCREASE ? + EffectMovementSpeedIncrease(nSpeedMod) : + EffectMovementSpeedDecrease(nSpeedMod) + ); + /// @todo Determine if the delay is actually needed here + DelayCommand(0.5, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSpeedMod, oPC)); + } +} + +void _prc_inc_itmrstr_ApplySpeedIncrease(object oPC) +{ + // Get target speed modification value. Limit to 99, since that's the effect constructor maximum value + int nSpeedMod = min(99, GetLocalInt(oPC, PLAYER_SPEED_INCREASE)); + object oSkin = GetPCSkin(oPC); + + AssignCommand(oSkin, _prc_inc_itmrstr_ApplySpeedModification(oPC, EFFECT_TYPE_MOVEMENT_SPEED_INCREASE, nSpeedMod)); +} + + +void _prc_inc_itmrstr_ApplySpeedDecrease(object oPC) +{ + // Get target speed modification value. Limit to 99, since that's the effect constructor maximum value + int nSpeedMod = GetLocalInt(oPC, PLAYER_SPEED_DECREASE); + object oSkin = GetPCSkin(oPC); + + AssignCommand(oSkin, _prc_inc_itmrstr_ApplySpeedModification(oPC, EFFECT_TYPE_MOVEMENT_SPEED_DECREASE, nSpeedMod)); +}*/ + +void _prc_inc_itmrstr_ApplyAoE(object oPC, object oItem, int nSubType, int nCost) +{ + int nAoEID = StringToInt(Get2DACache("iprp_aoe", "AoEID", nSubType)); + string sTag = Get2DACache("vfx_persistent", "LABEL", nAoEID); + effect eAoE = EffectAreaOfEffect(nAoEID, + Get2DACache("iprp_aoe", "EnterScript", nSubType), + Get2DACache("iprp_aoe", "HBScript", nSubType), + Get2DACache("iprp_aoe", "ExitScript", nSubType)); + + // The item applies the AoE effect + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAoE, oPC); + + // Get an object reference to the newly created AoE + location lLoc = GetLocation(oPC); + object oAoE = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lLoc, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + while(GetIsObjectValid(oAoE)) + { + // Test if we found the correct AoE + if(GetTag(oAoE) == sTag && + !GetLocalInt(oAoE, "PRC_AoE_IPRP_Init") + ) + { + SetLocalInt(oAoE, "PRC_AoE_IPRP_Init", TRUE); + break; + } + // Didn't find, get next + oAoE = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lLoc, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + } + if(!GetIsObjectValid(oAoE)) DoDebug("ERROR: _prc_inc_itmrstr_ApplyAoE: Can't find AoE created by " + DebugObject2Str(oItem)); + + // Set caster level override on the AoE + SetLocalInt(oAoE, PRC_CASTERLEVEL_OVERRIDE, nCost); + //if(DEBUG) DoDebug("_prc_inc_itmrstr_ApplyAoE: AoE level: " + IntToString(nCost)); +} + +void _prc_inc_itmrstr_ApplyWizardry(object oPC, object oItem, int nSpellLevel, string sType) +{ + //properties were already applied - happens when loading a saved game + if(GetLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel))) + return; + + SetLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel), TRUE); + int nClass, nSlots, i; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oPC); + if((sType == "A" && GetIsArcaneClass(nClass)) || (sType == "D" && GetIsDivineClass(nClass))) + { + if(GetAbilityScoreForClass(nClass, oPC) < nSpellLevel + 10) + continue; + + int nSpellSlotLevel = GetSpellslotLevel(nClass, oPC) - 1; + string sFile = Get2DACache("classes", "SpellGainTable", nClass); + nSlots = StringToInt(Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nSpellSlotLevel)); + //if(DEBUG) DoDebug("Adding "+IntToString(nSlots)" bonus slots for "+IntToString(nClass)" class."); + + if(nSlots) + { + string sVar = "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); + int j = 0; + while(j < nSlots) + { + //DoDebug(IntToString(j)); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusLevelSpell(nClass, nSpellLevel), oItem); + //nsb compatibility + SetLocalInt(oPC, sVar, (GetLocalInt(oPC, sVar) + 1)); + j++; + } + } + } + } + SetPlotFlag(oItem, TRUE); +} + +void _prc_inc_itmrstr_RemoveWizardry(object oPC, object oItem, int nSpellLevel, string sType) +{ + DeleteLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel)); + SetPlotFlag(oItem, FALSE); + itemproperty ipTest = GetFirstItemProperty(oItem); + string sVar; + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + if(GetItemPropertyCostTableValue(ipTest) == nSpellLevel) + { + int nClass = GetItemPropertySubType(ipTest); + if((sType == "A" && GetIsArcaneClass(nClass)) || (sType == "D" && GetIsDivineClass(nClass))) + { + RemoveItemProperty(oItem, ipTest); + //remove bonus slots from nsb classes + sVar = "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); + SetLocalInt(oPC, sVar, (GetLocalInt(oPC, sVar) - 1)); + int nCount, nSpellbook = GetSpellbookTypeForClass(nClass); + string sArray = "NewSpellbookMem_"+IntToString(nClass); + if(nSpellbook == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oPC, sArray, nSpellLevel); + if(nCount) + { + nCount--; + persistant_array_set_int(oPC, sArray, nSpellLevel, nCount); + } + } + else if(nSpellbook == SPELLBOOK_TYPE_PREPARED) + { + string sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + IntToString(nClass); + int i, nSpellbookID, nMax = persistant_array_get_size(oPC, sIDX) - 1; + for(i = nMax; i >= 0; i--) + { + nSpellbookID = persistant_array_get_int(oPC, sIDX, i); + nCount = persistant_array_get_int(oPC, sArray, nSpellbookID); + if(nCount) + { + nCount--; + persistant_array_set_int(oPC, sArray, nSpellbookID, nCount); + break; + } + } + } + } + } + } + ipTest = GetNextItemProperty(oItem); + } +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetUMDForItemCost(object oItem) +{ + string s2DAEntry; + int nValue = GetGoldPieceValue(oItem); + int n2DAValue = StringToInt(s2DAEntry); + int i; + while(n2DAValue < nValue) + { + s2DAEntry = Get2DACache("skillvsitemcost", "DeviceCostMax", i); + n2DAValue = StringToInt(s2DAEntry); + i++; + } + i--; + string s2DAReqSkill = Get2DACache("skillvsitemcost", "SkillReq_Class", i); + if(s2DAReqSkill == "") + return -1; + return StringToInt(s2DAReqSkill); +} + +//this is a scripted version of the bioware UMD check for using restricted items +//this also applies effects relating to new itemproperties +int DoUMDCheck(object oItem, object oPC, int nDCMod) +{ + + //doesnt have UMD + if(!GetHasSkill(SKILL_USE_MAGIC_DEVICE, oPC)) + return FALSE; + + int nSkill = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oPC); + int nReqSkill = GetUMDForItemCost(oItem); + //class is a dc20 test + nReqSkill = nReqSkill - 20 + nDCMod; + if(nReqSkill > nSkill) + return FALSE; + else + return TRUE; +} + +void VoidCheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID) +{ + CheckPRCLimitations(oItem, oPC); +} + +//tests for use restrictions +//also appies effects for those IPs tat need them +/// @todo Rename. It's not just limitations anymore +int CheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID) +{ + // Sanity check - the item needs to be valid + if(!GetIsObjectValid(oItem)) + return FALSE; /// @todo Might be better to auto-pass the limitation aspect in case of invalid item + + // In case no item owner was given, find it out + if(!GetIsObjectValid(oPC)) + oPC = GetItemPossessor(oItem); + + // Sanity check - the item needs to be in some creature's possession for this function to make sense + if(!GetIsObjectValid(oPC)) + return FALSE; + + // Equip and Unequip events need some special handling + int bUnequip = GetItemLastUnequipped() == oItem && GetLocalInt(oPC, "ONEQUIP") == 1; + int bEquip = GetItemLastEquipped() == oItem && GetLocalInt(oPC, "ONEQUIP") == 2; + + // Use restriction and UMD use + int bPass = TRUE; + int nUMDDC = 0; + + // Speed modification. Used to determine if effects need to be applied + int nSpeedIncrease = GetLocalInt(oPC, PLAYER_SPEED_INCREASE); + int nSpeedDecrease = GetLocalInt(oPC, PLAYER_SPEED_DECREASE); + + // Loop over all itemproperties on the item + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + int ipType = GetItemPropertyType(ipTest); + /* Use restrictions. All of these can be skipped when unequipping */ + if(!bUnequip) + { + if (ipType == ITEM_PROPERTY_USE_LIMITATION_ABILITY_SCORE) + { + int nValue = GetItemPropertyCostTableValue(ipTest); + if(GetAbilityScore(oPC, GetItemPropertySubType(ipTest), TRUE) < nValue) + bPass = FALSE; + nUMDDC += nValue - 15; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SKILL_RANKS) + { + int nValue = GetItemPropertyCostTableValue(ipTest); + if(GetSkillRank(GetItemPropertySubType(ipTest), oPC) < nValue) + bPass = FALSE; + nUMDDC += nValue - 10; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SPELL_LEVEL) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_AllSpell" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_ARCANE_SPELL_LEVEL) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_ArcSpell" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_DIVINE_SPELL_LEVEL) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_DivSpell" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SNEAK_ATTACK) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_SneakLevel" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_GENDER) + { + if(GetGender(oPC) != GetItemPropertySubType(ipTest)) + bPass = FALSE; + nUMDDC += 5; + } + } + + /* Properties that apply effects. Unequip should cause cleanup here */ + if(ipType == ITEM_PROPERTY_SPEED_INCREASE) + { + int iItemAdjust; + switch(GetItemPropertyCostTableValue(ipTest)) + { + case 0: iItemAdjust = 10; break; + case 1: iItemAdjust = 20; break; + case 2: iItemAdjust = 30; break; + case 3: iItemAdjust = 40; break; + case 4: iItemAdjust = 50; break; + case 5: iItemAdjust = 60; break; + case 6: iItemAdjust = 70; break; + case 7: iItemAdjust = 80; break; + case 8: iItemAdjust = 90; break; + case 9: iItemAdjust = 100; break; + } + if(bUnequip) + nSpeedIncrease -= iItemAdjust; + else if(bEquip) + nSpeedIncrease += iItemAdjust; + } + else if(ipType == ITEM_PROPERTY_SPEED_DECREASE) + { + int iItemAdjust; + switch(GetItemPropertyCostTableValue(ipTest)) + { + case 0: iItemAdjust = 10; break; + case 1: iItemAdjust = 20; break; + case 2: iItemAdjust = 30; break; + case 3: iItemAdjust = 40; break; + case 4: iItemAdjust = 50; break; + case 5: iItemAdjust = 60; break; + case 6: iItemAdjust = 70; break; + case 7: iItemAdjust = 80; break; + case 8: iItemAdjust = 90; break; + case 9: iItemAdjust = 99; break; + } + if(bUnequip) + nSpeedDecrease -= iItemAdjust; + else if(bEquip) + nSpeedDecrease += iItemAdjust; + } + else if(ipType == ITEM_PROPERTY_PNP_HOLY_AVENGER) + { + if(bEquip) + { + int nPaladinLevels = GetLevelByClass(CLASS_TYPE_PALADIN, oPC); + if(!nPaladinLevels) + { + //not a paladin? fake it + //not really a true PnP test + //instead it sets the paladin level + //to the UMD ranks minus the amount required + //to use a class restricted item of that value + int nSkill = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oPC); + if(nSkill) + { + int nReqSkill = GetUMDForItemCost(oItem); + nSkill -= nReqSkill; + if(nSkill > 0) + nPaladinLevels = nSkill; + } + } + + // Add Holy Avenger specials for Paladins (or successfull fake-Paladins) + if(nPaladinLevels) + { + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyEnhancementBonus(5), 99999.9)); + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, + IP_CONST_DAMAGETYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), 99999.9)); + //this is a normal dispel magic useage, should be specific + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyCastSpell(IP_CONST_CASTSPELL_DISPEL_MAGIC_5, + IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE), 99999.9)); + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyCastSpellCasterLevel(SPELL_DISPEL_MAGIC, + nPaladinLevels), 99999.9)); + } + // Non-Paladin's get +2 enhancement bonus + else + { + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyEnhancementBonus(2), 99999.9)); + + // Remove Paladin specials + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, DURATION_TYPE_TEMPORARY, -1); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP, DURATION_TYPE_TEMPORARY, IP_CONST_ALIGNMENTGROUP_EVIL); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL, DURATION_TYPE_TEMPORARY); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL, DURATION_TYPE_TEMPORARY); + } + } + else if(bUnequip) + { + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, + DURATION_TYPE_TEMPORARY, -1); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP, + DURATION_TYPE_TEMPORARY, IP_CONST_ALIGNMENTGROUP_EVIL); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL, + DURATION_TYPE_TEMPORARY); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL, + DURATION_TYPE_TEMPORARY); + } + } + else if(ipType == ITEM_PROPERTY_AREA_OF_EFFECT) + { + + // This should only happen on equip or unequip + if(bEquip || bUnequip) + { + // Remove existing AoE + effect eTest = GetFirstEffect(oPC); + while(GetIsEffectValid(eTest)) + { + if(GetEffectCreator(eTest) == oItem + && GetEffectType(eTest) == EFFECT_TYPE_AREA_OF_EFFECT) + { + RemoveEffect(oPC, eTest); + if(DEBUG) DoDebug("CheckPRCLimitations: Removing old AoE effect"); + } + eTest = GetNextEffect(oPC); + } + + // Create new AoE - Only when equipping + if(bEquip) + { + AssignCommand(oItem, _prc_inc_itmrstr_ApplyAoE(oPC, oItem, GetItemPropertySubType(ipTest), GetItemPropertyCostTable(ipTest))); + }// end if - Equip event + }// end if - Equip or Unequip event + }// end if - AoE iprp + else if(ipType == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + // Only equippable items can provide bonus spell slots + if(bEquip || bUnequip) + { + int nSubType = GetItemPropertySubType(ipTest); + int nCost = GetItemPropertyCostTable(ipTest); + SetLocalInt(oPC, + "PRC_IPRPBonSpellSlots_" + IntToString(nSubType) + "_" + IntToString(nCost), + GetLocalInt(oPC, + "PRC_IPRPBonSpellSlots_" + IntToString(nSubType) + "_" + IntToString(nCost) + ) + + (bEquip ? 1 : -1) + ); + } + } + else if(ipType == ITEM_PROPERTY_WIZARDRY) + { + int nCost = GetItemPropertyCostTableValue(ipTest); + if(bEquip) + AssignCommand(oItem, _prc_inc_itmrstr_ApplyWizardry(oPC, oItem, nCost, "A")); + else if(bUnequip) + AssignCommand(oItem, _prc_inc_itmrstr_RemoveWizardry(oPC, oItem, nCost, "A")); + } + else if(ipType == ITEM_PROPERTY_DIVINITY) + { + int nCost = GetItemPropertyCostTableValue(ipTest); + if(bEquip) + AssignCommand(oItem, _prc_inc_itmrstr_ApplyWizardry(oPC, oItem, nCost, "D")); + else if(bUnequip) + AssignCommand(oItem, _prc_inc_itmrstr_RemoveWizardry(oPC, oItem, nCost, "D")); + } + + ipTest = GetNextItemProperty(oItem); + }// end while - Loop over all itemproperties + + // Determine if speed modification totals had changed + if(nSpeedDecrease != GetLocalInt(oPC, PLAYER_SPEED_DECREASE)) + { + SetLocalInt(oPC, PLAYER_SPEED_DECREASE, nSpeedDecrease); + //_prc_inc_itmrstr_ApplySpeedDecrease(oPC); + } + if(nSpeedIncrease != GetLocalInt(oPC, PLAYER_SPEED_INCREASE)) + { + SetLocalInt(oPC, PLAYER_SPEED_INCREASE, nSpeedIncrease); + //_prc_inc_itmrstr_ApplySpeedIncrease(oPC); + } + + // If some restriction would prevent item use, perform UMD skill check + // Skip in case of unequip + if(!bUnequip && !bPass) + bPass = DoUMDCheck(oItem, oPC, nUMDDC); + + return bPass; +} + +void CheckForPnPHolyAvenger(object oItem) +{ + if(!GetPRCSwitch(PRC_PNP_HOLY_AVENGER_IPROP)) + return; + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_HOLY_AVENGER) + { + DelayCommand(0.1, RemoveItemProperty(oItem, ipTest)); + DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyPnPHolyAvenger())); + } + ipTest = GetNextItemProperty(oItem); + } +} \ No newline at end of file diff --git a/trunk/include/prc_inc_leadersh.nss b/trunk/include/prc_inc_leadersh.nss new file mode 100644 index 00000000..d305b67a --- /dev/null +++ b/trunk/include/prc_inc_leadersh.nss @@ -0,0 +1,1043 @@ +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string COHORT_DATABASE = "PRCCOHORTS"; +const string COHORT_TAG = "prc_cohort"; + +//in the database there is the folloxing data structures: +/* + int CohortCount (total number of cohorts) + object Cohort_X_obj (cohort itself) + string Cohort_X_name (cohort name) + int Cohort_X_race (cohort race) + int Cohort_X_class1 (cohort class pos1) + int Cohort_X_class2 (cohort class pos2) + int Cohort_X_class3 (cohort class pos3) + int Cohort_X_order (cohort law/chaos measure) + int Cohort_X_moral (cohort good/evil measure) + int Cohort_X_ethran (cohort has ethran feat) + string Cohort_X_cdkey (cdkey of owning player) +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int GetMaximumCohortCount(object oPC); +object GetCohort(int nID, object oPC); +int GetCurrentCohortCount(object oPC); +int GetCohortMaxLevel(int nLeadership, object oPC); +void RegisterAsCohort(object oPC); +object AddCohortToPlayer(int nCohortID, object oPC); +void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE); +void RemoveCohortFromPlayer(object oCohort, object oPC); +int GetLeadershipScore(object oPC = OBJECT_SELF); +void CheckHB(object oPC); +void AddPremadeCohortsToDB(); +void StoreCohort(object oCohort); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_feat_const" +#include "nw_o2_coninclude" +//#include "inc_utility" +#include "inc_ecl" +#include "inc_nwnx_funcs" +//#include "pnp_shft_poly" //for DoRandomAppearance + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +object AddCohortToPlayer(int nCohortID, object oPC) +{ + object oCohort = RetrieveCampaignObject(COHORT_DATABASE, "Cohort_"+IntToString(nCohortID)+"_obj", GetLocation(oPC)); + //give it a tag + AssignCommand(oCohort, SetIsDestroyable(TRUE, FALSE, FALSE)); + DestroyObject(oCohort); + oCohort = CopyObject(oCohort, GetLocation(oPC), OBJECT_INVALID, COHORT_TAG); + SetLocalInt(oCohort, "CohortID", nCohortID); + //pass it to the next function + AddCohortToPlayerByObject(oCohort, oPC); + return oCohort; +} + +//changes portrait, head, and appearance +//based on the target race with a degree of randomization. +//This should only be used on NPCs, not players. +void DoRandomAppearance(int nRace, object oTarget = OBJECT_SELF) +{ + //store current appearance to be safe + int nAppearance; //appearance to change into + int nHeadMax; //max head ID, changed to random 1-max + int nGender = GetGender(oTarget); + int nPortraitMin;//minimum row in portraits.2da + int nPortraitMax;//maximum row in portraits.2da + switch(nRace) + { + case RACIAL_TYPE_DWARF: + nAppearance = APPEARANCE_TYPE_DWARF; + if(nGender == GENDER_MALE) + { nHeadMax = 10; nPortraitMin = 9; nPortraitMax = 17; } + else + { nHeadMax = 12; nPortraitMin = 1; nPortraitMax = 8; } + break; + case RACIAL_TYPE_ELF: + nAppearance = APPEARANCE_TYPE_ELF; + if(nGender == GENDER_MALE) + { nHeadMax = 10; nPortraitMin = 31; nPortraitMax = 40; } + else + { nHeadMax = 16; nPortraitMin = 18; nPortraitMax = 30; } + break; + case RACIAL_TYPE_HALFELF: + nAppearance = APPEARANCE_TYPE_HALF_ELF; + if(nGender == GENDER_MALE) + { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } + else + { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } + break; + case RACIAL_TYPE_HALFORC: + nAppearance = APPEARANCE_TYPE_HALF_ORC; + if(nGender == GENDER_MALE) + { nHeadMax = 11; nPortraitMin = 134; nPortraitMax = 139; } + else + { nHeadMax = 1; nPortraitMin = 130; nPortraitMax = 133; } + break; + case RACIAL_TYPE_HUMAN: + nAppearance = APPEARANCE_TYPE_HUMAN; + if(nGender == GENDER_MALE) + { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } + else + { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } + break; + case RACIAL_TYPE_HALFLING: + nAppearance = APPEARANCE_TYPE_HALFLING; + if(nGender == GENDER_MALE) + { nHeadMax = 8; nPortraitMin = 61; nPortraitMax = 66; } + else + { nHeadMax = 11; nPortraitMin = 54; nPortraitMax = 59; } + break; + case RACIAL_TYPE_GNOME: + nAppearance = APPEARANCE_TYPE_GNOME; + if(nGender == GENDER_MALE) + { nHeadMax = 11; nPortraitMin = 47; nPortraitMax = 53; } + else + { nHeadMax = 9; nPortraitMin = 41; nPortraitMax = 46; } + break; + default: //not a normal race, abort + return; + } + //change the appearance + SetCreatureAppearanceType(oTarget, nAppearance); + + //need to be delayed a bit otherwise you get "supergnome" and "exploded elf" effects + DelayCommand(1.1, SetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN, d2(), oTarget)); + DelayCommand(1.2, SetCreatureBodyPart(CREATURE_PART_LEFT_SHIN, d2(), oTarget)); + DelayCommand(1.3, SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, d2(), oTarget)); + DelayCommand(1.4, SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, d2(), oTarget)); + DelayCommand(1.5, SetCreatureBodyPart(CREATURE_PART_TORSO, d2(), oTarget)); + DelayCommand(1.6, SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, d2(), oTarget)); + DelayCommand(1.7, SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, d2(), oTarget)); + DelayCommand(1.8, SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, d2(), oTarget)); + DelayCommand(1.9, SetCreatureBodyPart(CREATURE_PART_LEFT_BICEP, d2(), oTarget)); + + //dont do these body parts, they dont have tattoos and weird things could happen + //SetCreatureBodyPart(CREATURE_PART_BELT, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_NECK, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_PELVIS, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, d2(), oTarget); + //randomise the head + DelayCommand(2.0, SetCreatureBodyPart(CREATURE_PART_HEAD, Random(nHeadMax)+1, oTarget)); + + //remove any wings/tails + SetCreatureWingType(CREATURE_WING_TYPE_NONE, oTarget); + SetCreatureTailType(CREATURE_TAIL_TYPE_NONE, oTarget); + + int nPortraitID = Random(nPortraitMax-nPortraitMin+1)+nPortraitMin; + string sPortraitResRef = Get2DACache("portraits", "BaseResRef", nPortraitID); + sPortraitResRef = GetStringLeft(sPortraitResRef, GetStringLength(sPortraitResRef)-1); //trim the trailing _ + SetPortraitResRef(oTarget, sPortraitResRef); + SetPortraitId(oTarget, nPortraitID); +} + +void CancelGreatFeats(object oSpawn) +{ + //store how many Great X feats they have + //this is to fix a bioware bug where de-leveling doesnt remove the stat bonus + int nGreatStr; + int nGreatDex; + int nGreatCon; + int nGreatInt; + int nGreatWis; + int nGreatCha; + if (GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_10, oSpawn)) nGreatStr = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_9, oSpawn)) nGreatStr = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_8, oSpawn)) nGreatStr = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_7, oSpawn)) nGreatStr = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_6, oSpawn)) nGreatStr = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_5, oSpawn)) nGreatStr = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_4, oSpawn)) nGreatStr = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_3, oSpawn)) nGreatStr = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_2, oSpawn)) nGreatStr = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_1, oSpawn)) nGreatStr = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_10, oSpawn)) nGreatDex = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_9, oSpawn)) nGreatDex = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_8, oSpawn)) nGreatDex = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_7, oSpawn)) nGreatDex = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_6, oSpawn)) nGreatDex = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_5, oSpawn)) nGreatDex = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_4, oSpawn)) nGreatDex = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_3, oSpawn)) nGreatDex = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_2, oSpawn)) nGreatDex = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_1, oSpawn)) nGreatDex = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_10, oSpawn)) nGreatCon = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_9, oSpawn)) nGreatCon = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_8, oSpawn)) nGreatCon = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_7, oSpawn)) nGreatCon = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_6, oSpawn)) nGreatCon = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_5, oSpawn)) nGreatCon = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_4, oSpawn)) nGreatCon = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_3, oSpawn)) nGreatCon = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_2, oSpawn)) nGreatCon = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_1, oSpawn)) nGreatCon = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_10, oSpawn)) nGreatInt = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_9, oSpawn)) nGreatInt = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_8, oSpawn)) nGreatInt = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_7, oSpawn)) nGreatInt = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_6, oSpawn)) nGreatInt = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_5, oSpawn)) nGreatInt = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_4, oSpawn)) nGreatInt = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_3, oSpawn)) nGreatInt = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_2, oSpawn)) nGreatInt = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_1, oSpawn)) nGreatInt = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_WISDOM_10, oSpawn)) nGreatWis = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_9, oSpawn)) nGreatWis = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_8, oSpawn)) nGreatWis = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_7, oSpawn)) nGreatWis = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_6, oSpawn)) nGreatWis = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_5, oSpawn)) nGreatWis = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_4, oSpawn)) nGreatWis = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_3, oSpawn)) nGreatWis = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_2, oSpawn)) nGreatWis = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_1, oSpawn)) nGreatWis = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_10, oSpawn)) nGreatCha = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_9, oSpawn)) nGreatCha = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_8, oSpawn)) nGreatCha = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_7, oSpawn)) nGreatCha = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_6, oSpawn)) nGreatCha = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_5, oSpawn)) nGreatCha = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_4, oSpawn)) nGreatCha = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_3, oSpawn)) nGreatCha = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_2, oSpawn)) nGreatCha = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_1, oSpawn)) nGreatCha = 1; + + //apply penalties to counter the GreatX feats + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + { + if(nGreatStr) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_STRENGTH, -nGreatStr); + if(nGreatDex) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_DEXTERITY, -nGreatDex); + if(nGreatCon) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_CONSTITUTION, -nGreatCon); + if(nGreatInt) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_INTELLIGENCE, -nGreatInt); + if(nGreatWis) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_WISDOM, -nGreatWis); + if(nGreatCha) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_CHARISMA, -nGreatCha); + } + else + { + if(nGreatStr) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_STRENGTH, nGreatStr)), + oSpawn); + if(nGreatDex) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_DEXTERITY, nGreatDex)), + oSpawn); + if(nGreatCon) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_CONSTITUTION, nGreatCon)), + oSpawn); + if(nGreatInt) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nGreatInt)), + oSpawn); + if(nGreatWis) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_WISDOM, nGreatWis)), + oSpawn); + if(nGreatCha) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_CHARISMA, nGreatCha)), + oSpawn); + } +} + +void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE) +{ + //add it to the pc + int nMaxHenchmen = GetMaxHenchmen(); + SetMaxHenchmen(99); + AddHenchman(oPC, oCohort); + SetMaxHenchmen(nMaxHenchmen); + object oSkin = GetPCSkin(oCohort); + + if(bDoSetup) + { + //if it was a premade one, give it a random name + //randomize its appearance using DoRandomAppearance + if(GetResRef(oCohort) != "") + { + string sName; + //first name + switch(MyPRCGetRacialType(oCohort)) + { + case RACIAL_TYPE_DWARF: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_DWARF_FEMALE); + else + sName += RandomName(NAME_FIRST_DWARF_MALE); + break; + case RACIAL_TYPE_ELF: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_ELF_FEMALE); + else + sName += RandomName(NAME_FIRST_ELF_MALE); + break; + case RACIAL_TYPE_GNOME: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_GNOME_FEMALE); + else + sName += RandomName(NAME_FIRST_GNOME_MALE); + break; + case RACIAL_TYPE_HUMAN: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HUMAN_FEMALE); + else + sName += RandomName(NAME_FIRST_HUMAN_MALE); + break; + case RACIAL_TYPE_HALFELF: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HALFELF_FEMALE); + else + sName += RandomName(NAME_FIRST_HALFELF_MALE); + break; + case RACIAL_TYPE_HALFORC: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HALFORC_FEMALE); + else + sName += RandomName(NAME_FIRST_HALFORC_MALE); + break; + case RACIAL_TYPE_HALFLING: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HALFLING_FEMALE); + else + sName += RandomName(NAME_FIRST_HALFLING_MALE); + break; + } + sName += " "; + //surname + switch(MyPRCGetRacialType(oCohort)) + { + case RACIAL_TYPE_DWARF: + sName += RandomName(NAME_LAST_DWARF); + break; + case RACIAL_TYPE_ELF: + sName += RandomName(NAME_LAST_ELF); + break; + case RACIAL_TYPE_GNOME: + sName += RandomName(NAME_LAST_GNOME); + break; + case RACIAL_TYPE_HUMAN: + sName += RandomName(NAME_LAST_HUMAN); + break; + case RACIAL_TYPE_HALFELF: + sName += RandomName(NAME_LAST_HALFELF); + break; + case RACIAL_TYPE_HALFORC: + sName += RandomName(NAME_LAST_HALFORC); + break; + case RACIAL_TYPE_HALFLING: + sName += RandomName(NAME_LAST_HALFLING); + break; + } + //sanity check + if(sName == " ") + sName = ""; + //change the name + AssignCommand(oCohort, SetName(oCohort, sName)); + + //use disguise code to alter head etc + DoRandomAppearance(MyPRCGetRacialType(oCohort), oCohort); + + //DoRandomAppearance removed wings/tails need to re-add + if(GetRacialType(oCohort) == RACIAL_TYPE_FEYRI) + SetCreatureWingType(CREATURE_WING_TYPE_DEMON, oCohort); + else if(GetRacialType(oCohort) == RACIAL_TYPE_AVARIEL) + SetCreatureWingType(CREATURE_WING_TYPE_BIRD, oCohort); + } + //if its a custom made cohort, need to cancel GreatX feats + else + CancelGreatFeats(oCohort); + + //set it to the pcs level + int nLevel = GetCohortMaxLevel(GetLeadershipScore(oPC), oPC); + SetXP(oCohort, nLevel*(nLevel-1)*500); + SetLocalInt(oCohort, "MastersXP", GetXP(oPC)); + DelayCommand(1.0, AssignCommand(oCohort, SetIsDestroyable(FALSE, TRUE, TRUE))); + DelayCommand(1.0, AssignCommand(oCohort, SetLootable(oCohort, TRUE))); + //set its maximum level lag + if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)) + { + //bonus cohort, no cap + } + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1) + { + //thrallherd with switch, 1 level lag + SetLocalInt(oCohort, "CohortLevelLag", 1); + } + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10 + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2) + { + //twofold master with switch, 2 level lag + SetLocalInt(oCohort, "CohortLevelLag", 2); + } + else + { + //other cohort have a 2 level lag + SetLocalInt(oCohort, "CohortLevelLag", 2); + } + + //strip its equipment & inventory + object oTest = GetFirstItemInInventory(oCohort); + object oToken = GetHideToken(oCohort); + while(GetIsObjectValid(oTest)) + { + if(GetHasInventory(oTest)) + { + object oTest2 = GetFirstItemInInventory(oTest); + while(GetIsObjectValid(oTest2)) + { + // Avoid blowing up the hide and token that just had the eventscripts stored on them + if(oTest2 != oSkin && oTest2 != oToken) + DestroyObject(oTest2); + oTest2 = GetNextItemInInventory(oTest); + } + } + // Avoid blowing up the hide and token that just had the eventscripts stored on them + if(oTest != oSkin && oTest != oToken) + DestroyObject(oTest); + oTest = GetNextItemInInventory(oCohort); + } + int nSlot; + for(nSlot = 0;nSlot<14;nSlot++) + { + oTest = GetItemInSlot(nSlot, oCohort); + DestroyObject(oTest); + } + //get rid of any gold it has + TakeGoldFromCreature(GetGold(oCohort), oCohort, TRUE); + } + //clean up any leftovers on the skin + ScrubPCSkin(oCohort, oSkin); + DeletePRCLocalInts(oSkin); + + //turn on its scripts + //normal MoB set + AddEventScript(oCohort, EVENT_VIRTUAL_ONPHYSICALATTACKED, "prc_ai_mob_attck", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONBLOCKED, "prc_ai_mob_block", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONCOMBATROUNDEND, "prc_ai_mob_combt", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONDAMAGED, "prc_ai_mob_damag", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONDISTURBED, "prc_ai_mob_distb", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONPERCEPTION, "prc_ai_mob_percp", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONSPAWNED, "prc_ai_mob_spawn", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONSPELLCASTAT, "prc_ai_mob_spell", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONDEATH, "prc_ai_mob_death", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONRESTED, "prc_ai_mob_rest", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONUSERDEFINED, "prc_ai_mob_userd", TRUE, FALSE); + //dont run this, cohort-specific script replaces it + //AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_mob_conv", TRUE, TRUE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_mob_heart", TRUE, FALSE); + //cohort specific ones + AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_coh_conv", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_coh_hb", TRUE, FALSE); + + //mark the master on the cohort + SetLocalObject(oCohort, "MasterObject", oPC); + + //DEBUG + //various tests + if (DEBUG) DoDebug("Cohort Name="+GetName(oCohort)); + if (DEBUG) DoDebug("Cohort HD="+IntToString(GetHitDice(oCohort))); + if (DEBUG) DoDebug("Cohort XP="+IntToString(GetXP(oCohort))); + if (DEBUG) DoDebug("Cohort GetIsPC="+IntToString(GetIsPC(oCohort))); + + // And now gear it up + if (!GetPRCSwitch(PRC_DISABLE_COHORT_STARTING_GEAR)) + { + int i; + int nHD = GetHitDice(oCohort); + for(i = 0;i= 3) nLeadership += GetLevelByClass(CLASS_TYPE_SHADOW_THIEF_AMN, oPC) - 2; + //without epic leadership its capped at 25 + if(!GetHasFeat(FEAT_EPIC_LEADERSHIP, oPC) && nLeadership > 25) + nLeadership = 25; + + return nLeadership; +} + +void StoreCohort(object oCohort) +{ + int nCohortCount = GetCampaignInt(COHORT_DATABASE, "CohortCount"); + int i; + for(i=0;i 1) + return; + SetLocalInt(oPC, "CohortCheckHB", GetLocalInt(oPC, "CohortCheckHB")+1); + DelayCommand(0.99, + SetLocalInt(oPC, "CohortCheckHB", GetLocalInt(oPC, "CohortCheckHB")-1)); + SetCommandable(FALSE, oPC); + if(GetHitDice(oPC) == 40) + { + StoreCohort(oPC); + //restore previous xp amound + SetXP(oPC, GetLocalInt(oPC, "OriginalXP")); + //tell the player what was done + SendMessageToPC(oPC, "Character registered as cohort."); + //remove the non-commandabiltiy + SetCommandable(TRUE, oPC); + // Clean up + DeletePersistantLocalInt(oPC, "RegisteringAsCohort"); + DeleteLocalInt(oPC, "OriginalXP"); + //stop the psuedoHB + return; + } + DelayCommand(1.0, CheckHB(oPC)); +} + +void RegisterAsCohort(object oPC) +{ + string sMessage; + sMessage += "This will register you character to be selected as a cohort.\n"; + sMessage += "As part of this process, you have to levelup to level 40.\n"; + sMessage += "Once you reach level 40, your character will be stored.\n"; + sMessage += "Then when the character is used as a cohort, it will follow that levelup path.\n"; + sMessage += "Any changes to the cohort will not apply to the original character.\n"; + //SendMessageToPC(oPC, sMessage); + FloatingTextStringOnCreature(sMessage, oPC); + + SetLocalInt(oPC, "OriginalXP", GetXP(oPC)); + SetXP(oPC, 40*(40-1)*500); + SetPersistantLocalInt(oPC, "RegisteringAsCohort", TRUE); + AssignCommand(GetModule(), CheckHB(oPC)); +} + +int LeadershipScore2CohortLevel(int nLeadership) +{ + switch(nLeadership) + { + case 1: return 0; + case 2: return 1; + case 3: return 2; + case 4: + case 5: return 3; + case 6: return 4; + case 7: + case 8: return 5; + case 9: return 6; + case 10: + case 11: return 7; + case 12: return 8; + case 13: return 9; + case 14: + case 15: return 10; + case 16: return 11; + case 17: + case 18: return 12; + case 19: return 13; + case 20: return 14; + case 21: + case 22: return 15; + case 23: return 16; + case 24: + case 25: return 17; + case 26: + case 27: return 18; + case 28: + case 29: return 19; + case 30: + case 31: return 20; + case 32: + case 33: return 21; + case 34: + case 35: return 22; + case 36: + case 37: return 23; + case 38: + case 39: return 24; + case 40: + case 41: return 25; + case 42: + case 43: return 26; + case 44: + case 45: return 27; + case 46: + case 47: return 28; + case 48: + case 49: return 29; + case 50: + case 51: return 30; + case 52: + case 53: return 31; + case 54: + case 55: return 32; + case 56: + case 57: return 33; + case 58: + case 59: return 34; + case 60: return 35; + case 61: + case 62: return 36; + case 63: + case 64: return 37; + case 65: + case 66: return 38; + case 67: + case 68: return 39; + case 69: + case 70: return 40; + } + return 0; +} + +int GetCohortMaxLevel(int nLeadership, object oPC) +{ + //if its a bonus cohort, use the players ECL + int nMasterLevel = GetECL(oPC); + if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)) + return nMasterLevel; + int nLevel = LeadershipScore2CohortLevel(nLeadership); + //apply a level lag + if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1) + { + //thrallherd with switch, 1 level lag + if(nLevel > nMasterLevel-1) + nLevel = nMasterLevel-1; + } + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10 + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2) + { + //twofold master with switch, 2 level lag + if(nLevel > nMasterLevel-2) + nLevel = nMasterLevel-2; + } + else + { + //other cohort have a 2 level lag + if(nLevel > nMasterLevel-2) + nLevel = nMasterLevel-2; + if (GetHasFeat(FEAT_IMPROVED_COHORT, oPC)) nLevel += 1; + } + //really, leadership should be capped at 25 / 17HD + //but this is a sanity check + if(nLevel > 20 + && !GetHasFeat(FEAT_EPIC_LEADERSHIP, oPC)) + nLevel = 20; + return nLevel; +} + +int GetCurrentCohortCount(object oPC) +{ + int nCount; + object oTest; + object oOldTest; + int i = 1; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + while(GetIsObjectValid(oTest) && oTest != oOldTest) + { + if(GetTag(oTest) == COHORT_TAG) + nCount++; + i++; + oOldTest = oTest; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + } + return nCount; +} + +object GetCohort(int nID, object oPC) +{ + int nCount; + object oTest; + object oOldTest; + int i = 1; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + while(GetIsObjectValid(oTest) && oTest != oOldTest) + { + if(GetTag(oTest) == COHORT_TAG) + nCount++; + if(nCount == nID) + return oTest; + i++; + oOldTest = oTest; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + } + return OBJECT_INVALID; +} + +int GetMaximumCohortCount(object oPC) +{ + int nCount; + if(!GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC)) + { + if(GetHasFeat(FEAT_LEADERSHIP, oPC)) + nCount++; + if(GetHasFeat(FEAT_LEGENDARY_COMMANDER, oPC)) + nCount++; + } + //thrallherd with switch + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP)) + { + nCount++; + //twofold masteer + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) > 9) + nCount++; + } + //hathran class + if(GetHasFeat(FEAT_HATH_COHORT, oPC)) + nCount++; + //orc warlord with switch + if(GetHasFeat(FEAT_GATHER_HORDE_I, oPC) + && GetPRCSwitch(PRC_ORC_WARLORD_COHORT)) + nCount++; + nCount += GetPRCSwitch(PRC_BONUS_COHORTS); + return nCount; +} + +int GetIsCohortChoiceValid(string sName, int nRace, int nClass1, int nClass2, int nClass3, int nOrder, int nMoral, int nEthran, string sKey, int nDeleted, object oPC) +{ + //has been deleted + if(nDeleted) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because cohort had been deleted"); + return FALSE; + } + + int bIsValid = TRUE; + int nCohortCount = GetMaximumCohortCount(oPC); + int i; + //another players cohort + if(GetPCPublicCDKey(oPC) != "" + && GetPCPublicCDKey(oPC) != sKey) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because cdkey is incorrect"); + bIsValid = FALSE; + } + //is character + if(bIsValid + && GetName(oPC) == sName) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because name is in use"); + bIsValid = FALSE; + } + //is already a cohort + if(bIsValid && sName != "") + { + for(i=1;i<=nCohortCount;i++) + { + object oCohort = GetCohort(i, oPC); + if(GetName(oCohort) == sName) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because cohort is already in use."); + bIsValid = FALSE; + } + } + } + //hathran + if(bIsValid + && GetHasFeat(FEAT_HATH_COHORT, oPC)) + { + int nEthranBarbarianCount = 0; + for(i=1;i<=nCohortCount;i++) + { + object oCohort = GetCohort(i, oPC); + if(GetIsObjectValid(oCohort) + &&(GetHasFeat(FEAT_HATH_COHORT, oCohort) + || GetLevelByClass(CLASS_TYPE_BARBARIAN, oCohort))) + nEthranBarbarianCount++; + } + //must have at least one ethran or barbarian + if(!nEthranBarbarianCount + && GetCurrentCohortCount(oPC) >= GetMaximumCohortCount(oPC)-1 + && !nEthran + && nClass1 != CLASS_TYPE_BARBARIAN + && nClass2 != CLASS_TYPE_BARBARIAN + && nClass3 != CLASS_TYPE_BARBARIAN) + bIsValid = FALSE; + } + //OrcWarlord + if(bIsValid + && GetHasFeat(FEAT_GATHER_HORDE_I, oPC) + && GetPRCSwitch(PRC_ORC_WARLORD_COHORT)) + { + int nOrcCount = 0; + for(i=1;i<=nCohortCount;i++) + { + object oCohort = GetCohort(i, oPC); + if(GetIsObjectValid(oCohort) + && (MyPRCGetRacialType(oCohort) == RACIAL_TYPE_HUMANOID_ORC + || MyPRCGetRacialType(oCohort) == RACIAL_TYPE_HALFORC)) + nOrcCount++; + } + //must have at least one orc + if(!nOrcCount + && GetCurrentCohortCount(oPC) >= GetMaximumCohortCount(oPC)-1 + && nRace != RACIAL_TYPE_HUMANOID_ORC + && nRace != RACIAL_TYPE_HALFORC + && nRace != RACIAL_TYPE_GRAYORC + && nRace != RACIAL_TYPE_OROG + && nRace != RACIAL_TYPE_TANARUKK + ) + bIsValid = FALSE; + } + //Undead Leadership + //Wild Cohort + //not implemented yet + //return result + return bIsValid; +} + +int GetIsCohortChoiceValidByID(int nID, object oPC) +{ + string sID = IntToString(nID); + string sName = GetCampaignString( COHORT_DATABASE, "Cohort_"+sID+"_name"); + int nRace = GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_race"); + int nClass1=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class1"); + int nClass2=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class2"); + int nClass3=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class3"); + int nOrder= GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_order"); + int nMoral= GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_moral"); + int nEthran=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_ethran"); + string sKey = GetCampaignString( COHORT_DATABASE, "Cohort_"+sID+"_cdkey"); + int nDeleted = GetCampaignInt(COHORT_DATABASE, "Cohort_"+sID+"_deleted"); + return GetIsCohortChoiceValid(sName, nRace, nClass1, nClass2, nClass3, nOrder, nMoral, nEthran, sKey, nDeleted, oPC); +} + +int GetCanRegister(object oPC) +{ + int bReturn = TRUE; + int i; + int nCohortCount = GetCampaignInt(COHORT_DATABASE, "CohortCount"); + for(i=0;i 0)) + { + if (GetIsArcaneClass(nClass1, oCaster)) nClass1Lvl = GetPrCAdjustedCasterLevel(nClass1, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass2, oCaster)) nClass2Lvl = GetPrCAdjustedCasterLevel(nClass2, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass3, oCaster)) nClass3Lvl = GetPrCAdjustedCasterLevel(nClass3, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass4, oCaster)) nClass4Lvl = GetPrCAdjustedCasterLevel(nClass4, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass5, oCaster)) nClass5Lvl = GetPrCAdjustedCasterLevel(nClass5, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass6, oCaster)) nClass6Lvl = GetPrCAdjustedCasterLevel(nClass6, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass7, oCaster)) nClass7Lvl = GetPrCAdjustedCasterLevel(nClass7, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass8, oCaster)) nClass8Lvl = GetPrCAdjustedCasterLevel(nClass8, oCaster, bAdjustForPractisedSpellcaster); + } + else if (nClassType == TYPE_DIVINE && (GetFirstDivineClassPosition(oCaster) > 0)) + { + if (GetIsDivineClass(nClass1, oCaster)) nClass1Lvl = GetPrCAdjustedCasterLevel(nClass1, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass2, oCaster)) nClass2Lvl = GetPrCAdjustedCasterLevel(nClass2, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass3, oCaster)) nClass3Lvl = GetPrCAdjustedCasterLevel(nClass3, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass4, oCaster)) nClass4Lvl = GetPrCAdjustedCasterLevel(nClass4, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass5, oCaster)) nClass5Lvl = GetPrCAdjustedCasterLevel(nClass5, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass6, oCaster)) nClass6Lvl = GetPrCAdjustedCasterLevel(nClass6, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass7, oCaster)) nClass7Lvl = GetPrCAdjustedCasterLevel(nClass7, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass8, oCaster)) nClass8Lvl = GetPrCAdjustedCasterLevel(nClass8, oCaster, bAdjustForPractisedSpellcaster); + } + int nHighest = nClass1Lvl; + if (nClass2Lvl > nHighest) nHighest = nClass2Lvl; + if (nClass3Lvl > nHighest) nHighest = nClass3Lvl; + if (nClass4Lvl > nHighest) nHighest = nClass4Lvl; + if (nClass5Lvl > nHighest) nHighest = nClass5Lvl; + if (nClass6Lvl > nHighest) nHighest = nClass6Lvl; + if (nClass7Lvl > nHighest) nHighest = nClass7Lvl; + if (nClass8Lvl > nHighest) nHighest = nClass8Lvl; + return nHighest; +} + +int GetLevelByTypeArcaneFeats(object oCaster = OBJECT_SELF, int iSpellID = -1) +{ + int iFirstArcane = GetPrimaryArcaneClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iSpellID = -1) iSpellID = PRCGetSpellId(oCaster); + + int iBoost = ShadowWeave(oCaster, iSpellID) + + FireAdept(oCaster, iSpellID) + + DomainPower(oCaster, iSpellID) + + StormMagic(oCaster) + + CormanthyranMoonMagic(oCaster) + + DraconicPower(oCaster); + + if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster); + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster); + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster); + + iClass1Lev += iBoost; + iClass2Lev += iBoost; + iClass3Lev += iBoost; + iClass4Lev += iBoost; + iClass5Lev += iBoost; + iClass6Lev += iBoost; + iClass7Lev += iBoost; + iClass8Lev += iBoost; + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass1Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass2Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass3Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass1Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass2Lev); + + if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +int GetLevelByTypeDivineFeats(object oCaster = OBJECT_SELF, int iSpellID = -1) +{ + int iFirstDivine = GetPrimaryDivineClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iSpellID = -1) iSpellID = PRCGetSpellId(oCaster); + + int iBoost = ShadowWeave(oCaster, iSpellID) + + FireAdept(oCaster, iSpellID) + + DomainPower(oCaster, iSpellID) + + CormanthyranMoonMagic(oCaster) + + StormMagic(oCaster); + + if (iClass1 == CLASS_TYPE_PALADIN + || iClass1 == CLASS_TYPE_RANGER + || iClass1 == CLASS_TYPE_ANTI_PALADIN) + iClass1Lev = iClass1Lev / 2; + if (iClass2 == CLASS_TYPE_PALADIN + || iClass2 == CLASS_TYPE_RANGER + || iClass2 == CLASS_TYPE_ANTI_PALADIN) + iClass2Lev = iClass2Lev / 2; + if (iClass3 == CLASS_TYPE_PALADIN + || iClass3 == CLASS_TYPE_RANGER + || iClass3 == CLASS_TYPE_ANTI_PALADIN) + iClass3Lev = iClass3Lev / 2; + if (iClass4 == CLASS_TYPE_PALADIN + || iClass4 == CLASS_TYPE_RANGER + || iClass4 == CLASS_TYPE_ANTI_PALADIN) + iClass4Lev = iClass4Lev / 2; + if (iClass5 == CLASS_TYPE_PALADIN + || iClass5 == CLASS_TYPE_RANGER + || iClass5 == CLASS_TYPE_ANTI_PALADIN) + iClass5Lev = iClass5Lev / 2; + if (iClass6 == CLASS_TYPE_PALADIN + || iClass6 == CLASS_TYPE_RANGER + || iClass6 == CLASS_TYPE_ANTI_PALADIN) + iClass6Lev = iClass6Lev / 2; + if (iClass7 == CLASS_TYPE_PALADIN + || iClass7 == CLASS_TYPE_RANGER + || iClass7 == CLASS_TYPE_ANTI_PALADIN) + iClass7Lev = iClass7Lev / 2; + if (iClass8 == CLASS_TYPE_PALADIN + || iClass8 == CLASS_TYPE_RANGER + || iClass8 == CLASS_TYPE_ANTI_PALADIN) + iClass8Lev = iClass7Lev / 2; + + if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster); + if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster); + if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster); + if (iClass4 == iFirstDivine) iClass4Lev += GetDivinePRCLevels(oCaster); + if (iClass5 == iFirstDivine) iClass5Lev += GetDivinePRCLevels(oCaster); + if (iClass6 == iFirstDivine) iClass6Lev += GetDivinePRCLevels(oCaster); + if (iClass7 == iFirstDivine) iClass7Lev += GetDivinePRCLevels(oCaster); + if (iClass8 == iFirstDivine) iClass8Lev += GetDivinePRCLevels(oCaster); + + iClass1Lev += iBoost; + iClass2Lev += iBoost; + iClass3Lev += iBoost; + iClass4Lev += iBoost; + iClass5Lev += iBoost; + iClass6Lev += iBoost; + iClass7Lev += iBoost; + iClass8Lev += iBoost; + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass1, iClass7Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass2, iClass8Lev); + + if (!GetIsDivineClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsDivineClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsDivineClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsDivineClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsDivineClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsDivineClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsDivineClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsDivineClass(iClass8, oCaster)) iClass3Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +// looks up the spell level for the arcane caster classes (Wiz_Sorc, Bard) in spells.2da. +// Caveat: some onhitcast spells don't have any spell-levels listed for any class +int GetIsArcaneSpell (int iSpellId) +{ + return Get2DACache("spells", "Wiz_Sorc", iSpellId) != "" + || Get2DACache("spells", "Bard", iSpellId) != ""; +} + +// looks up the spell level for the divine caster classes (Cleric, Druid, Ranger, Paladin) in spells.2da. +// Caveat: some onhitcast spells don't have any spell-levels listed for any class +int GetIsDivineSpell (int iSpellId) +{ + return Get2DACache("spells", "Cleric", iSpellId) != "" + || Get2DACache("spells", "Druid", iSpellId) != "" + || Get2DACache("spells", "Ranger", iSpellId) != "" + || Get2DACache("spells", "Paladin", iSpellId) != ""; +} + +int BWSavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0) +{ + // GZ: sanity checks to prevent wrapping around + if (nDC < 1) nDC = 1; + else if (nDC > 255) nDC = 255; + + int bValid = 0; + int nEff; + + //some maneuvers allow people to use skill check instead of a save + if(nSavingThrow == SAVING_THROW_FORT) + { + bValid = GetLocalInt(oTarget, "MindOverBody") ? + GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) : + FortitudeSave(oTarget, nDC, nSaveType, oSaveVersus); + if(bValid == 1) nEff = VFX_IMP_FORTITUDE_SAVING_THROW_USE; + } + else if(nSavingThrow == SAVING_THROW_REFLEX) + { + bValid = GetLocalInt(oTarget, "ActionBeforeThought") ? + GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) : + ReflexSave(oTarget, nDC, nSaveType, oSaveVersus); + if(bValid == 1) nEff = VFX_IMP_REFLEX_SAVE_THROW_USE; + } + else if(nSavingThrow == SAVING_THROW_WILL) + { + bValid = GetLocalInt(oTarget, "MomentOfPerfectMind") ? + GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) : + WillSave(oTarget, nDC, nSaveType, oSaveVersus); + if(bValid == 1) nEff = VFX_IMP_WILL_SAVING_THROW_USE; + } + + /* + return 0 = FAILED SAVE + return 1 = SAVE SUCCESSFUL + return 2 = IMMUNE TO WHAT WAS BEING SAVED AGAINST + */ + if(bValid == 0) + { + int nSpellID = PRCGetSpellId(oSaveVersus); + if(nSaveType == SAVING_THROW_TYPE_DEATH + || nSpellID == SPELL_WEIRD + || nSpellID == SPELL_FINGER_OF_DEATH) + //&& nSpellID != SPELL_HORRID_WILTING) + { + if(fDelay > 0.0f) DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oTarget)); + else ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oTarget); + } + } + else //if(bValid == 1 || bValid == 2) + { + if(bValid == 2) nEff = VFX_IMP_MAGIC_RESISTANCE_USE; + + if(fDelay > 0.0f) DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nEff), oTarget)); + else ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nEff), oTarget); + } + return bValid; +} + +void PRCBonusDamage(object oTarget, object oCaster = OBJECT_SELF) +{ + // Does nothing, left here so files don't need to be edited. +} + +// Bonus damage to a spell for Spell Betrayal Ability +int SpellBetrayalDamage(object oTarget, object oCaster) +{ + int iDam = 0; + int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster); + + if(ThrallLevel >= 2) + { + if( GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE) ) + { + ThrallLevel /= 2; + iDam = d6(ThrallLevel); + } + } + + return iDam; +} + +// Bonus damage to a spell for Spellstrike Ability +int SpellStrikeDamage(object oTarget, object oCaster) +{ + int iDam = 0; + int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster); + + if(ThrallLevel >= 6) + { + if( GetIsAOEFlanked(oTarget, oCaster) ) + { + ThrallLevel /= 4; + iDam = d6(ThrallLevel); + } + } + + return iDam; +} + +// Adds the bonus damage from both Spell Betrayal and Spellstrike together +int ApplySpellBetrayalStrikeDamage(object oTarget, object oCaster, int bShowTextString = TRUE) +{ + int iDam = 0; + int iBetrayalDam = SpellBetrayalDamage(oTarget, oCaster); + int iStrikeDam = SpellStrikeDamage(oTarget, oCaster); + string sMes = ""; + + if(iStrikeDam > 0 && iBetrayalDam > 0) sMes ="*Spellstrike Betrayal Sucess*"; + else if(iBetrayalDam > 0) sMes ="*Spell Betrayal Sucess*"; + else if(iStrikeDam > 0) sMes ="*Spellstrike Sucess*"; + + if(bShowTextString) FloatingTextStringOnCreature(sMes, oCaster, TRUE); + + iDam = iBetrayalDam + iStrikeDam; + + // debug code + //sMes = "Spell Betrayal / Spellstrike Bonus Damage: " + IntToString(iBetrayalDam) + " + " + IntToString(iStrikeDam) + " = " + IntToString(iDam); + //DelayCommand( 1.0, FloatingTextStringOnCreature(sMes, oCaster, TRUE) ); + + return iDam; +} + +int SpellDamagePerDice(object oCaster, int nDice) +{ + // Arcane only + if (!GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster))) + return 0; + + int nDam = 0; + nDam += GetLocalInt(oCaster, "StrengthFromMagic") * nDice * 2; + + if (DEBUG) DoDebug("SpellDamagePerDice returning "+IntToString(nDam)+" for "+GetName(oCaster)); + + return nDam; +} + +int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0, int bImmunityCheck = FALSE) +{ + int nSpell = PRCGetSpellId(oSaveVersus); + int nSpellSchool = GetSpellSchool(nSpell); + + //If bImmunityCheck == FALSE: returns 0 if failure or immune, 1 if success - same as MySavingThrow + //If bImmunityCheck == TRUE: returns 0 if failure, 1 if success, 2 if immune + + // Enigma Helm Crown Bind + if (GetIsMeldBound(oTarget, MELD_ENIGMA_HELM) == CHAKRA_CROWN && GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM)) + return 2; + + // Planar Ward + if (GetHasSpellEffect(MELD_PLANAR_WARD, oTarget) && (GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM) || GetIsOfSubschool(nSpell, SUBSCHOOL_COMPULSION))) + return 2; + + // Chucoclops influence makes you autofail fear saves + if (GetHasSpellEffect(VESTIGE_CHUPOCLOPS, oTarget) && !GetLocalInt(oTarget, "PactQuality"+IntToString(VESTIGE_CHUPOCLOPS)) && nSaveType == SAVING_THROW_TYPE_FEAR) + return 1; + + // Iron Mind's Mind Over Body, allows them to treat other saves as will saves up to 3/day. + // No point in having it trigger when its a will save. + if(nSavingThrow != SAVING_THROW_WILL && GetLocalInt(oTarget, "IronMind_MindOverBody")) + { + nSavingThrow = SAVING_THROW_WILL; + DeleteLocalInt(oTarget, "IronMind_MindOverBody"); + } + + // Handle the target having Force of Will and being targeted by a psionic power + if(nSavingThrow != SAVING_THROW_WILL && + ((nSpell > 14000 && nSpell < 14360) || + (nSpell > 15350 && nSpell < 15470) + ) && + GetHasFeat(FEAT_FORCE_OF_WILL, oTarget) && + !GetLocalInt(oTarget, "ForceOfWillUsed") && + // Only use will save if it's better + ((nSavingThrow == SAVING_THROW_FORT ? GetFortitudeSavingThrow(oTarget) : GetReflexSavingThrow(oTarget)) > GetWillSavingThrow(oTarget)) + ) + { + nSavingThrow = SAVING_THROW_WILL; + SetLocalInt(oTarget, "ForceOfWillUsed", TRUE); + DelayCommand(6.0f, DeleteLocalInt(oTarget, "ForceOfWillUsed")); + // "Force of Will used for this round." + FloatingTextStrRefOnCreature(16826670, oTarget, FALSE); + } + + //TouchOfDistraction + if (GetLocalInt(oTarget, "HasTouchOfDistraction") && (nSavingThrow = SAVING_THROW_REFLEX)) + { + nDC += 2; + DeleteLocalInt(oTarget, "HasTouchOfDistraction"); + } + + //Diamond Defense + if(GetLocalInt(oTarget, "PRC_TOB_DIAMOND_DEFENSE")) + nDC -= GetLocalInt(oTarget, "PRC_TOB_DIAMOND_DEFENSE"); + + // Master of Nine + if(GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oSaveVersus) > 2) + { + int nLastClass = GetLocalInt(oSaveVersus, "PRC_CurrentManeuver_InitiatingClass") - 1; + if(nLastClass == CLASS_TYPE_WARBLADE + || nLastClass == CLASS_TYPE_SWORDSAGE + || nLastClass == CLASS_TYPE_CRUSADER) + { + // Increases maneuver DCs by 1 + nDC += 1; + } + } + + // Incarnum Resistance + if(GetHasFeat(FEAT_INCARNUM_RESISTANCE, oTarget) && nSpell > 18900 && nSpell < 18969) + nDC -= 2; + + // This is done here because it is possible to tell the saving throw type here + // it works by lowering the DC rather than adding to the save + // same net effect but slightly different numbers + if(nSaveType == SAVING_THROW_TYPE_MIND_SPELLS) + { + //Thayan Knights auto-fail mind spells cast by red wizards + if(GetLevelByClass(CLASS_TYPE_RED_WIZARD, oSaveVersus) + && GetLevelByClass(CLASS_TYPE_THAYAN_KNIGHT, oTarget)) + return 0; + // Tyranny Domain increases the DC of mind spells by +2. + if(GetHasFeat(FEAT_DOMAIN_POWER_TYRANNY, oSaveVersus)) + nDC += 2; + // +2 bonus on saves against mind affecting, done here + if(GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oTarget) > 1) + nDC -= 2; + // Scorpion's Resolve gives a +4 bonus on mind affecting saves + if(GetHasFeat(FEAT_SCORPIONS_RESOLVE, oTarget)) + nDC -= 4; + // Warped Mind gives a bonus on mind affecting equal to half aberrant + if(GetHasFeat(FEAT_ABERRANT_WARPED_MIND, oTarget)) + nDC -= GetAberrantFeatCount(oTarget)/2; + } + else if(nSaveType == SAVING_THROW_TYPE_FEAR) + { + // Unnatural Will adds Charisma to saves against fear + if(GetHasFeat(FEAT_UNNATURAL_WILL, oTarget)) + nDC -= GetAbilityModifier(ABILITY_CHARISMA, oTarget); + // Krinth have +4 saves against Fear + if(GetRacialType(oTarget) == RACIAL_TYPE_KRINTH) + nDC -= 4; + // Turlemoi/Lashemoi have -4 saves against Fear + if(GetRacialType(oTarget) == RACIAL_TYPE_LASHEMOI || GetRacialType(oTarget) == RACIAL_TYPE_TURLEMOI || GetLocalInt(oTarget, "SpeedFromPain") >= 3) + nDC += 4; + } + else if(nSaveType == SAVING_THROW_TYPE_NEGATIVE) + { + // +4 bonus on saves against negative energy, done here + if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 8) + nDC -= 4; + // Jergal's Pact gives a +2 bonus on negative energy saves + if(GetHasFeat(FEAT_JERGALS_PACT, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_DISEASE) + { + // Plague Resistant gives a +4 bonus on disease saves + if(GetHasFeat(FEAT_PLAGUE_RESISTANT, oTarget)) + nDC -= 4; + // +4/+2 bonus on saves against disease, done here + if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13) + nDC -= 4; + else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_POISON) + { + // +4/+2 bonus on saves against poison, done here + if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13) + nDC -= 4; + else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3) + nDC -= 2; + if(GetHasFeat(FEAT_POISON_4, oTarget)) + nDC -= 4; + if(GetHasFeat(FEAT_POISON_3, oTarget)) + nDC -= 3; + if(GetRacialType(oTarget) == RACIAL_TYPE_EXTAMINAAR && nSavingThrow == SAVING_THROW_FORT) + nDC -= 2; + if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK) + nDC -= 1; + } + else if(nSaveType == SAVING_THROW_TYPE_FIRE) + { + // Bloodline of Fire gives a +4 bonus on fire saves + if(GetHasFeat(FEAT_BLOODLINE_OF_FIRE, oTarget)) + nDC -= 4; + if(GetHasFeat(FEAT_HARD_FIRE, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + // +2 vs fire for Halfgiant + if(GetHasFeat(FEAT_ACCLIMATED_FIRE, oTarget)) + nDC -= 2; + // +2 vs fire for Heat Endurance feat + if(GetHasFeat(FEAT_HEAT_ENDURANCE, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_COLD) + { + if(GetHasFeat(FEAT_HARD_WATER, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + // +2 vs cold for Cold Endurance feat + if(GetHasFeat(FEAT_COLD_ENDURANCE, oTarget)) + nDC -= 2; + // +2 vs cold for Winterhide Shifter trait + if(GetHasFeat(FEAT_SHIFTER_WINTERHIDE, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_ELECTRICITY) + { + if(GetHasFeat(FEAT_HARD_AIR, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + else if(GetHasFeat(FEAT_HARD_ELEC, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_SONIC) + { + if(GetHasFeat(FEAT_HARD_AIR, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + } + else if(nSaveType == SAVING_THROW_TYPE_ACID) + { + if(GetHasFeat(FEAT_HARD_EARTH, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + } + + // This is done here because it is possible to tell the spell school here + // it works by lowering the DC rather than adding to the save + // same net effect but slightly different numbers + if(nSpellSchool == SPELL_SCHOOL_TRANSMUTATION) + { + // Full Moon's Trick - +2 vs Transmutation spels + if(GetLocalInt(oTarget, "FullMoon_Trans")) + nDC -= 2; + if(GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oTarget) >= 2) + nDC -= 4; + } + + // Sapphire Heirarch gets +4 against Chaos and Transmutation effects + if(GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oTarget) >= 2 && GetHasDescriptor(nSpell, DESCRIPTOR_CHAOTIC)) + nDC -= 4; + + // Charming Veil Brow bind grants Essentia bonus on saves against charm and compulsion + if(GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM) || GetIsOfSubschool(nSpell, SUBSCHOOL_COMPULSION)) + { + if (GetIsMeldBound(oTarget, MELD_CHARMING_VEIL) == CHAKRA_BROW) + nDC -= GetEssentiaInvested(oTarget, MELD_CHARMING_VEIL); + } + + // Krinth get +1 save against Shadow spells + if (GetIsOfSubschool(nSpell, SUBSCHOOL_SHADOW) && GetRacialType(oTarget) == RACIAL_TYPE_KRINTH) + nDC -= 1; + + //Psionic race save boosts - +1 vs all spells for Xeph + if(GetHasFeat(FEAT_XEPH_SPELLHARD, oTarget)) + nDC -= 1; + + // Apostate - 1/2 HD bonus to resist divine spells + if(GetHasFeat(FEAT_APOSTATE, oTarget)) + { + //if divine + if(GetIsDivineClass(PRCGetLastSpellCastClass(oSaveVersus))) + nDC -= GetHitDice(oTarget) / 2; + } + + // Hammer of Witches - +1 bonus against Arcane spells + if(GetIsObjectValid(GetItemPossessedBy(oTarget, "WOL_HammerWitches"))) + { + if(GetIsArcaneClass(PRCGetLastSpellCastClass(oSaveVersus))) + nDC -= 1; + } + + // Cultist of the Shattered Peak - +1 bonus against Arcane spells + if(GetLevelByClass(CLASS_TYPE_CULTIST_SHATTERED_PEAK, oTarget)) + { + if(GetIsArcaneClass(PRCGetLastSpellCastClass(oSaveVersus))) + nDC -= 1; + } + + //Insightful Divination + if(GetLocalInt(oTarget, "Insightful Divination")) + { + nDC -= GetLocalInt(oTarget, "Insightful Divination"); + DeleteLocalInt(oTarget, "Insightful Divination"); + } + + // Phierans Resolve - +4 bonus vs spells with evil descriptor + if(GetHasSpellEffect(SPELL_PHIERANS_RESOLVE, oTarget) && GetHasDescriptor(nSpell, DESCRIPTOR_EVIL)) + nDC -= 4; + + //spell school modificators + int nSchool = GetLocalInt(oSaveVersus, "X2_L_LAST_SPELLSCHOOL_VAR"); + if(nSchool == SPELL_SCHOOL_NECROMANCY) + { + // Necrotic Cyst penalty on Necromancy spells + if(GetPersistantLocalInt(oTarget, NECROTIC_CYST_MARKER)) + nDC += 2; + } + else if(nSchool == SPELL_SCHOOL_ILLUSION) + { + // Piercing Sight gives a +4 bonus on illusion saves + if(GetHasFeat(FEAT_PIERCING_SIGHT, oTarget)) + nDC -= 4; + // Adds +2 to Illusion DCs + if(GetLocalInt(oSaveVersus, "ShadowTrickster")) + nDC += 2; + // Illusion Veil Meld + if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oSaveVersus)) + nDC += 1; + // Illusion Veil Meld + if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oTarget)) + nDC -= GetEssentiaInvested(oTarget, MELD_ILLUSION_VEIL); + if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK) + nDC -= 1; + } + else if(nSchool == SPELL_SCHOOL_ENCHANTMENT) + { + //check if Unsettling Enchantment applies + if(GetHasFeat(FEAT_UNSETTLING_ENCHANTMENT, oSaveVersus) && !GetIsImmune(oTarget, IMMUNITY_TYPE_MIND_SPELLS)) + { + effect eLink = EffectLinkEffects(EffectACDecrease(2), EffectAttackDecrease(2)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, 6.0); + } + if(GetRacialType(oTarget) == RACIAL_TYPE_KILLOREN) + nDC -= 2; + if(GetLocalInt(oTarget, "KillorenAncient")) + nDC -= 2; + if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK) + nDC -= 1; + } + + // Hexblade gets a bonus against spells equal to his Charisma (Min +1) + if(nSchool && GetLevelByClass(CLASS_TYPE_HEXBLADE, oTarget)) + { + int nHexCha = GetAbilityModifier(ABILITY_CHARISMA, oTarget); + if (nHexCha < 1) nHexCha = 1; + nDC -= nHexCha; + } + + // Totemist gets a save v magical beasts + if(MyPRCGetRacialType(oSaveVersus) == RACIAL_TYPE_MAGICAL_BEAST && GetLevelByClass(CLASS_TYPE_TOTEMIST, oTarget) >= 3) + nDC -= 3; + + int nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // If the spell is save immune then the link must be applied in order to get the true immunity + // to be resisted. That is the reason for returing false and not true. True blocks the + // application of effects. + if(nSaveRoll == 2 && !bImmunityCheck) + return 0; + + // Failed the save - check if we can reroll + if(!nSaveRoll) + { + if(nSaveType == SAVING_THROW_TYPE_MIND_SPELLS) + { + // Bond Of Loyalty + if(GetLocalInt(oTarget, "BondOfLoyalty")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + DeleteLocalInt(oTarget, "BondOfLoyalty"); + } + } + else if(nSaveType == SAVING_THROW_TYPE_FEAR) + { + // Call To Battle Reroll + if(GetLocalInt(oTarget, "CallToBattle")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + DeleteLocalInt(oTarget, "CallToBattle"); + } + } + + // Second Chance power reroll + if(!nSaveRoll && GetLocalInt(oTarget, "PRC_Power_SecondChance_Active") // Second chance is active + && !GetLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound")) // And hasn't yet been used for this round + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // Can't use this ability again for a round + SetLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound", TRUE); + DelayCommand(6.0f, DeleteLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound")); + } + + // Zealous Surge Reroll + if(!nSaveRoll && GetLocalInt(oTarget, "ZealousSurge")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // Ability Used + DeleteLocalInt(oTarget, "ZealousSurge"); + } + + // Balam's Cunning Reroll + if(!nSaveRoll && GetLocalInt(oTarget, "BalamCunning")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // Ability Used + DeleteLocalInt(oTarget, "BalamCunning"); + } + + if(!nSaveRoll) + { + if(nSavingThrow == SAVING_THROW_REFLEX) + { + // Dive for Cover reroll + if(GetHasFeat(FEAT_DIVE_FOR_COVER, oTarget)) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 6.0); + } + } + // Impetuous Endurance - fortitude or will save + else if(GetLevelByClass(CLASS_TYPE_KNIGHT, oTarget) > 16) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + } + } + } + //Serene Guardian Unclouded Mind + int nSerene = GetLevelByClass(CLASS_TYPE_SERENE_GUARDIAN, oTarget); + //Applies on failed will saves from 9th level on + if (nSaveRoll == 1 && nSavingThrow == SAVING_THROW_WILL && nSerene >= 9) + { + if (GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC)) // Concentration check to beat the DC + nSaveRoll = 0; + } + + // Iron Mind Barbed Mind ability + if(nSaveRoll == 1 && nSaveType == SAVING_THROW_TYPE_MIND_SPELLS) + { + // Only works in Heavy Armour + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oTarget) > 9 + && GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget)) > 5) + { + // Spell/Power caster takes 1d6 damage and 1 Wisdom damage + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(), DAMAGE_TYPE_MAGICAL), oSaveVersus); + ApplyAbilityDamage(oSaveVersus, ABILITY_WISDOM, 1, DURATION_TYPE_TEMPORARY, TRUE, -1.0); + } + } + + return nSaveRoll; +} + + +int PRCGetReflexAdjustedDamage(int nDamage, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF) +{ + int nEvasion; + if(GetHasFeat(FEAT_IMPROVED_EVASION, oTarget)) + nEvasion = 2; + else if(GetHasFeat(FEAT_EVASION, oTarget)) + nEvasion = 1; + + // Grants evasion against dragons, don't override if they've already gotit + object oWOL = GetItemPossessedBy(oTarget, "WOL_CrimsonRuination"); + if(oWOL != GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget) && MyPRCGetRacialType(oSaveVersus) == RACIAL_TYPE_DRAGON && 1 > nEvasion) + nEvasion = 1; + + // This ability removes evasion from the target + if(GetLocalInt(oTarget, "TrueConfoundingResistance")) + { + if(nEvasion) + nEvasion -= 1; + else + nDC += 2; + } + + //Get saving throw result + int nSaveRoll = PRCMySavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, nSaveType, oSaveVersus); + + //save success + if(nSaveRoll) + nDamage = nEvasion ? 0 : nDamage / 2; + //save failed - check improved evasion + else if(nEvasion == 2) + nDamage = nDamage / 2; + + return nDamage; +} + +// function for internal use in GetCasterLvl + +// caster level for arcane base classes (with practiced spellcaster feats) +int GetCasterLvlArcane(int iClassType, object oCaster) +{ + if (GetPrimaryArcaneClass(oCaster) == iClassType) + return GetLevelByTypeArcane(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster); + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +// caster level for classes with half progression +int GetCasterLvlArcaneSemi(int iClassType, object oCaster) +{ + if (GetPrimaryArcaneClass(oCaster) == iClassType) + return GetLevelByTypeArcane(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster) / 2; + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +// caster level for divine base classes (with practiced spellcaster feats) +int GetCasterLvlDivine(int iClassType, object oCaster) +{ + if (GetPrimaryDivineClass(oCaster) == iClassType) + return GetLevelByTypeDivine(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster); + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +// caster level for classes with half progression +int GetCasterLvlDivineSemi(int iClassType, object oCaster) +{ + if (GetPrimaryDivineClass(oCaster) == iClassType) + return GetLevelByTypeDivine(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster) / 2; + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +int GetCasterLvl(int iTypeSpell, object oCaster = OBJECT_SELF) +{ + switch (iTypeSpell) + { + case TYPE_ARCANE: + return GetLevelByTypeArcane(oCaster); + + case TYPE_DIVINE: + return GetLevelByTypeDivine(oCaster); + + case CLASS_TYPE_SORCERER: + { + if (GetPrimaryArcaneClass(oCaster) == CLASS_TYPE_SORCERER) + return GetLevelByTypeArcane(oCaster); + + if(!GetLevelByClass(CLASS_TYPE_SORCERER, oCaster)) + { + int iTemp; + int nRace = GetRacialType(oCaster); + + //Aranea include outsider HD as sorc + if(nRace == RACIAL_TYPE_ARANEA) + iTemp = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oCaster); + + //Rakshasa include outsider HD as sorc + if(nRace == RACIAL_TYPE_RAKSHASA) + iTemp = GetLevelByClass(CLASS_TYPE_OUTSIDER, oCaster); + + //Drider include aberration HD as sorc + if(nRace == RACIAL_TYPE_DRIDER) + iTemp = GetLevelByClass(CLASS_TYPE_ABERRATION, oCaster); + + //Arkamoi + Redspawn include MH HD as sorc + if(nRace == RACIAL_TYPE_ARKAMOI) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster); + if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster)*3/4; + if(nRace == RACIAL_TYPE_MARRUTACT) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster)*6/7; + + iTemp += PracticedSpellcasting(oCaster, CLASS_TYPE_SORCERER, iTemp); + iTemp += DraconicPower(oCaster); + return iTemp; + } + } + + case CLASS_TYPE_BARD: + { + if (GetPrimaryArcaneClass(oCaster) == CLASS_TYPE_BARD) + return GetLevelByTypeArcane(oCaster); + + if(!GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + int iTemp; + int nRace = GetRacialType(oCaster); + + //Gloura include fey HD as bard + if(nRace == RACIAL_TYPE_GLOURA) + iTemp = GetLevelByClass(CLASS_TYPE_FEY, oCaster); + + iTemp += PracticedSpellcasting(oCaster, CLASS_TYPE_BARD, iTemp); + iTemp += DraconicPower(oCaster); + return iTemp; + } + } + + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + case CLASS_TYPE_WIZARD: + return GetCasterLvlArcane(iTypeSpell, oCaster); + + case CLASS_TYPE_HEXBLADE: + return GetCasterLvlArcaneSemi(iTypeSpell, oCaster); + + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + return GetCasterLvlDivine(iTypeSpell, oCaster); + + case CLASS_TYPE_ANTI_PALADIN: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SOHEI: + return GetCasterLvlDivineSemi(iTypeSpell, oCaster); + } + return 0; +} + + +////////////////Begin Spellsword////////////////// + +void SetAllAoEInts(int SpellID, object oAoE, int nBaseSaveDC, int SpecDispel = 0, int nCasterLevel = 0) +{ + if(!GetLocalInt(oAoE, "X2_AoE_BaseSaveDC"))//DC should always be > 0 + { + SetLocalInt(oAoE, "X2_AoE_SpellID", SpellID); + SetLocalInt(oAoE, "X2_AoE_BaseSaveDC", nBaseSaveDC); + if(SpecDispel) SetLocalInt(oAoE, "X2_AoE_SpecDispel", SpecDispel); + if(!nCasterLevel) nCasterLevel = PRCGetCasterLevel(); + SetLocalInt(oAoE, "X2_AoE_Caster_Level", nCasterLevel); + if(GetHasFeat(FEAT_SHADOWWEAVE)) SetLocalInt(oAoE, "X2_AoE_Weave", TRUE); + } +} + +//GetNextObjectInShape wrapper for changing the AOE of the channeled spells +object MyNextObjectInShape(int nShape, + float fSize, location lTarget, + int bLineOfSight = FALSE, + int nObjectFilter = OBJECT_TYPE_CREATURE, + vector vOrigin=[0.0, 0.0, 0.0]) +{ + object oCaster = OBJECT_SELF; + + // War Wizard of Cormyr's Widen Spell ability + float fMulti = GetLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER); + //if(DEBUG) DoDebug("Original Spell Size: " + FloatToString(fSize)+"\n" + "Widened Multiplier: " + FloatToString(fMulti)); + + float fHFInfusion = GetLocalFloat(oCaster, "PRC_HF_Infusion_Wid"); + if(fHFInfusion > 0.0f) + { + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster) + { + //Hellfire Infusion - doesn't work on scrolls and potions + int nType = GetBaseItemType(oItem); + if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION + && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL) + { + fMulti = fHFInfusion; + DelayCommand(1.0, DeleteLocalFloat(oCaster, "PRC_HF_Infusion_Wid")); + } + } + } + + if(fMulti > 0.0) + { + fSize *= fMulti; + if(DEBUG) DoDebug("New Spell Size: " + FloatToString(fSize)); + } + + if(GetLocalInt(oCaster, "spellswd_aoe") == 1) + { + return OBJECT_INVALID; + } + + return GetNextObjectInShape(nShape,fSize,lTarget,bLineOfSight,nObjectFilter,vOrigin); +} + +//GetFirstObjectInShape wrapper for changing the AOE of the channeled spells +object MyFirstObjectInShape(int nShape, + float fSize, location lTarget, + int bLineOfSight = FALSE, + int nObjectFilter = OBJECT_TYPE_CREATURE, + vector vOrigin=[0.0, 0.0, 0.0]) +{ + object oCaster = OBJECT_SELF; + + //int on caster for the benefit of spellfire wielder resistance + // string sName = "IsAOE_" + IntToString(GetSpellId()); + string sName = "IsAOE_" + IntToString(PRCGetSpellId(oCaster)); + + SetLocalInt(oCaster, sName, 1); + DelayCommand(0.1, DeleteLocalInt(oCaster, sName)); + + // War Wizard of Cormyr's Widen Spell ability + float fMulti = GetLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER); + //if(DEBUG) DoDebug("Original Spell Size: " + FloatToString(fSize)+"\n" + "Widened Multiplier: " + FloatToString(fMulti)); + + float fHFInfusion = GetLocalFloat(oCaster, "PRC_HF_Infusion_Wid"); + if(fHFInfusion > 0.0f) + { + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster) + { + //Hellfire Infusion - doesn't work on scrolls and potions + int nType = GetBaseItemType(oItem); + if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION + && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL) + { + fMulti = fHFInfusion; + DelayCommand(1.0, DeleteLocalFloat(oCaster, "PRC_HF_Infusion_Wid")); + } + } + } + + if(fMulti > 0.0) + { + fSize *= fMulti; + if(DEBUG) DoDebug("New Spell Size: " + FloatToString(fSize)); + + // This allows it to affect the entire casting + DelayCommand(1.0, DeleteLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER)); + } + + if(GetLocalInt(oCaster, "spellswd_aoe") == 1) + { + return PRCGetSpellTargetObject(oCaster); + } + + return GetFirstObjectInShape(nShape,fSize,lTarget,bLineOfSight,nObjectFilter,vOrigin); +} + + +//This checks if the spell is channeled and if there are multiple spells +//channeled, which one is it. Then it checks in either case if the spell +//has the metamagic feat the function gets and returns TRUE or FALSE accordingly +//Also used by the new spellbooks for the same purpose +/* replaced by wrapper for GetMetaMagicFeat instead + Not necessarily. This may still be a usefule level of abstraction - Ornedan + */ +int CheckMetaMagic(int nMeta,int nMMagic) +{ + return nMeta & nMMagic; +} + +int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE) +{ + int nOverride = GetLocalInt(oCaster, PRC_METAMAGIC_OVERRIDE); + if(nOverride) + { + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found override metamagic = "+IntToString(nOverride)+", original = "+IntToString(GetMetaMagicFeat())); + return nOverride; + } + + object oItem = PRCGetSpellCastItem(oCaster); + + // we assume that we are casting from an item, if the item is valid and the item belongs to oCaster + // however, we cannot be sure because of Bioware's inadequate implementation of GetSpellCastItem + if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster) + { + int nFeat; + + //check item for metamagic + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_METAMAGIC) + { + int nSubType = GetItemPropertySubType(ipTest); + nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType)); + if(nSubType == PRCGetSpellId(oCaster)) + { + int nCostValue = GetItemPropertyCostTableValue(ipTest); + if(nCostValue == -1 && DEBUG) + DoDebug("Problem examining itemproperty"); + switch(nCostValue) + { + //bitwise "addition" equivalent to nFeat = (nFeat | nSSFeat) + case 1: nFeat |= METAMAGIC_QUICKEN; break; + case 2: nFeat |= METAMAGIC_EMPOWER; break; + case 3: nFeat |= METAMAGIC_EXTEND; break; + case 4: nFeat |= METAMAGIC_MAXIMIZE; break; + case 5: nFeat |= METAMAGIC_SILENT; break; + case 6: nFeat |= METAMAGIC_STILL; break; + } + } + } + ipTest = GetNextItemProperty(oItem); + } + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: item casting with item = "+GetName(oItem)+", found metamagic = "+IntToString(nFeat)); + + //Hellfire Infusion - doesn't work on scrolls and potions + int nType = GetBaseItemType(oItem); + if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION + && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL) + { + nFeat |= GetLocalInt(oCaster, "PRC_HF_Infusion"); + if(bClearFeatFlags) DeleteLocalInt(oCaster, "PRC_HF_Infusion"); + } + + //apply metamagic adjustment (chanell spell) + nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT); + return nFeat; + } + + if(GetLocalInt(oCaster, "PRC_SPELL_EVENT")) + { + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found spell event metamagic = "+IntToString(GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC"))+", original = "+IntToString(GetMetaMagicFeat())); + return GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC"); + } + + int nFeat = GetMetaMagicFeat(); + if(nFeat == METAMAGIC_ANY) + // work around for spontaneous casters (bard or sorcerer) having all metamagic turned on when using ActionCastSpell* + nFeat = METAMAGIC_NONE; + + nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT); + + int nClass = PRCGetLastSpellCastClass(oCaster); + // Suel Archanamach's Extend spells they cast on themselves. + // Only works for Suel Spells, and not any other caster type they might have + // Since this is a spellscript, it assumes OBJECT_SELF is the caster + if(nClass == CLASS_TYPE_SUEL_ARCHANAMACH + && GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH) >= 3) + { + // Check that they cast on themselves + // if (oCaster == PRCGetSpellTargetObject()) + if(oCaster == PRCGetSpellTargetObject(oCaster)) + { + // Add extend to the metamagic feat using bitwise math + nFeat |= METAMAGIC_EXTEND; + } + } + //Code for the Abjurant Champion. Works similar to the Suel Archamamach but + //their extend only works on abjuration spells they cast. + if(GetHasFeat(FEAT_EXTENDED_ABJURATION, oCaster) + && GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION) >= 1) + { + int nSpellSchool = GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR"); + // Check that they cast an abjuration spell + if(nSpellSchool == SPELL_SCHOOL_ABJURATION) + { + // Add extend to the metamagic feat using bitwise math + nFeat |= METAMAGIC_EXTEND; + if (DEBUG) FloatingTextStringOnCreature("Extended Abjuration Applied", oCaster, FALSE); + } + } + // Magical Contraction, Truenaming Utterance + if(GetHasSpellEffect(UTTER_MAGICAL_CONTRACTION_R, oCaster)) + //(GetLocalInt(oCaster, "TrueMagicalContraction")) + { + nFeat |= METAMAGIC_EMPOWER; + } + // Sudden Metamagic + int nSuddenMeta = GetLocalInt(oCaster, "SuddenMeta"); + if(nSuddenMeta) + { + nFeat |= nSuddenMeta; + if(bClearFeatFlags) + DeleteLocalInt(oCaster, "SuddenMeta"); + } + + int nDivMeta = GetLocalInt(oCaster, "DivineMeta"); + if(nDivMeta) + { + if(GetIsDivineClass(nClass, oCaster)) + { + nFeat |= nDivMeta; + if(bClearFeatFlags) + DeleteLocalInt(oCaster, "DivineMeta"); + } + } + + int nSpelldance = GetLocalInt(oCaster, "Spelldance"); + if(nSpelldance) + { + nFeat |= nSpelldance; + if (DEBUG) FloatingTextStringOnCreature("Metamagic Spelldances applied", oCaster, FALSE); + } + + int nSpellLevel = PRCGetSpellLevel(oCaster, PRCGetSpellId()); + if (GetLocalInt(oCaster, "Aradros_Extend")/* && GetIsArcaneClass(nClass, oCaster)*/) + { + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: GetIsArcaneClass is TRUE"); + int nHD = GetHitDice(oCaster); + if (nHD >= 12 && 6 >= nSpellLevel) + { + nFeat |= METAMAGIC_EXTEND; + DeleteLocalInt(oCaster, "Aradros_Extend"); + if (DEBUG) FloatingTextStringOnCreature("Aradros Extend Applied", oCaster, FALSE); + } + else if (3 >= nSpellLevel) + { + nFeat |= METAMAGIC_EXTEND; + DeleteLocalInt(oCaster, "Aradros_Extend"); + if (DEBUG) FloatingTextStringOnCreature("Aradros Extend Applied", oCaster, FALSE); + } + } + + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: nSpellLevel " +IntToString(nSpellLevel)+", PRCGetSpellId " +IntToString(PRCGetSpellId())+", nClass " +IntToString(nClass)); + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: returning " +IntToString(nFeat)); + return nFeat; +} + +//Wrapper for The MaximizeOrEmpower function +int PRCMaximizeOrEmpower(int nDice, int nNumberOfDice, int nMeta, int nBonus = 0) +{ + int i, nDamage; + for (i = 1; i <= nNumberOfDice; i++) + { + nDamage = nDamage + Random(nDice) + 1; + } + + //Resolve metamagic + if(nMeta & METAMAGIC_MAXIMIZE) + nDamage = nDice * nNumberOfDice; + if(nMeta & METAMAGIC_EMPOWER) + nDamage = nDamage + nDamage / 2; + + return nDamage + nBonus; +} + +float PRCGetMetaMagicDuration(float fDuration, int nMeta = -1) +{ + if(nMeta == -1) // no metamagic value was passed, so get it here + nMeta = PRCGetMetaMagicFeat(); + + if(nMeta & METAMAGIC_EXTEND) + fDuration *= 2; + + return fDuration; +} + +int PRCGetMetaMagicDamage(int nDamageType, int nDice, int nDieSize, + int nBonusPerDie = 0, int nBonus = 0, int nMetaMagic = -1) +{ + // If the metamagic argument wasn't given get it. + if (-1 == nMetaMagic) nMetaMagic = PRCGetMetaMagicFeat(); + + // Roll the damage, applying metamagic. + int nDamage = PRCMaximizeOrEmpower(nDieSize, nDice, nMetaMagic, (nBonusPerDie * nDice) + nBonus); + return nDamage; +} + +void PRCSetSchool(int nSchool = SPELL_SCHOOL_GENERAL) +{ + // Remove the last value in case it is there and set the new value if it is NOT general. + if(SPELL_SCHOOL_GENERAL != nSchool) + SetLocalInt(OBJECT_SELF, "X2_L_LAST_SPELLSCHOOL_VAR", nSchool); + else + DeleteLocalInt(OBJECT_SELF, "X2_L_LAST_SPELLSCHOOL_VAR"); +} + +void PRCSignalSpellEvent(object oTarget, int bHostile = TRUE, int nSpellID = -1, object oCaster = OBJECT_SELF) +{ + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(oCaster, nSpellID, bHostile)); +} +/* +void SPEvilShift(object oPC) +{ + // Check for alignment shift switch being active + if(GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT)) + { + // Amount of adjustment is equal to the square root of your distance from pure evil. + // In other words, the amount of shift is higher the farther you are from pure evil, with the + // extremes being 10 points of shift at pure good and 0 points of shift at pure evil. + AdjustAlignment(oPC, ALIGNMENT_EVIL, FloatToInt(sqrt(IntToFloat(GetGoodEvilValue(oPC)))), FALSE); + } +} + +void SPGoodShift(object oPC) +{ + // Check for alignment shift switch being active + if(GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT)) + { + // Amount of adjustment is equal to the square root of your distance from pure good. + // In other words, the amount of shift is higher the farther you are from pure good, with the + // extremes being 10 points of shift at pure evil and 0 points of shift at pure good. + AdjustAlignment(oPC, ALIGNMENT_GOOD, FloatToInt(sqrt(IntToFloat(100 - GetGoodEvilValue(oPC)))), FALSE); + } +}*/ + +void DoCorruptionCost(object oPC, int nAbility, int nCost, int bDrain) +{ + // Undead redirect all damage & drain to Charisma, sez http://www.wizards.com/dnd/files/BookVileFAQ12102002.zip + if(MyPRCGetRacialType(oPC) == RACIAL_TYPE_UNDEAD) + nAbility = ABILITY_CHARISMA; + + //Exalted Raiment + if(GetHasSpellEffect(SPELL_EXALTED_RAIMENT, GetItemInSlot(INVENTORY_SLOT_CHEST, oPC))) + { + nCost -= 1; + + if(nCost < 1) + nCost = 1; + } + + if (GetHasFeat(FEAT_FAVORED_ZULKIRS, oPC)) nCost -= 1; + + // Is it ability drain? + if(bDrain) + ApplyAbilityDamage(oPC, nAbility, nCost, DURATION_TYPE_PERMANENT, TRUE); + // Or damage + else + ApplyAbilityDamage(oPC, nAbility, nCost, DURATION_TYPE_TEMPORARY, TRUE, -1.0f); +} + +void MultisummonPreSummon(object oPC = OBJECT_SELF, int bOverride = FALSE) +{ + if(!GetPRCSwitch(PRC_MULTISUMMON) && !bOverride) + return; + int i=1; + int nCount = GetPRCSwitch(PRC_MULTISUMMON); + if(bOverride) + nCount = bOverride; + if(nCount < 0 + || nCount == 1) + nCount = 99; + if(nCount > 99) + nCount = 99; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummon) && !GetLocalInt(oSummon, "RFSummonedElemental") && i < nCount) + { + AssignCommand(oSummon, SetIsDestroyable(FALSE, FALSE, FALSE)); + AssignCommand(oSummon, DelayCommand(0.3, SetIsDestroyable(TRUE, FALSE, FALSE))); + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } +} + + +//This function returns 1 only if the object oTarget is the object +//the weapon hit when it channeled the spell sSpell or if there is no +//channeling at all +int ChannelChecker(string sSpell, object oTarget) +{ + int nSpell = GetLocalInt(GetAreaOfEffectCreator(), sSpell+"channeled"); + int nTarget = GetLocalInt(oTarget, sSpell+"target"); + if(nSpell == 1 && nTarget == 1) + { + return 1; + } + else if(nSpell != 1 && nTarget != 1) + { + return 1; + } + else + { + return 0; + } +} + +//If a spell is being channeled, we store its target and its name +void StoreSpellVariables(string sString,int nDuration) +{ + object oCaster = OBJECT_SELF; + object oTarget = GetSpellTargetObject(); //using prc version could cause problems + + if(GetLocalInt(oCaster,"spellswd_aoe") == 1) + { + SetLocalInt(oCaster, sString+"channeled",1); + SetLocalInt(oTarget, sString+"target",1); + } + DelayCommand(RoundsToSeconds(nDuration),DeleteLocalInt(oTarget, sString+"target")); + DelayCommand(RoundsToSeconds(nDuration),DeleteLocalInt(oCaster, sString+"channeled")); +} + +effect ChannelingVisual() +{ + return EffectVisualEffect(VFX_DUR_SPELLTURNING); +} + +////////////////End Spellsword////////////////// + + +int GetHasMettle(object oTarget, int nSavingThrow = SAVING_THROW_WILL) +{ + if(GetLevelByClass(CLASS_TYPE_HEXBLADE, oTarget) > 2) return TRUE; + if(GetLevelByClass(CLASS_TYPE_SOHEI, oTarget) > 8) return TRUE; + if(GetLevelByClass(CLASS_TYPE_CRUSADER, oTarget) > 12) return TRUE; + if(GetLocalInt(oTarget, "FactotumMettle")) return TRUE; + + if(nSavingThrow = SAVING_THROW_WILL) + { + // Iron Mind's ability only functions in Heavy Armour + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oTarget) > 4 + && GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget)) > 5) + return TRUE; + // Fill out the line below to add another class with Will mettle + // if (GetLevelByClass(CLASS_TYPE_X, oTarget) >= X) return TRUE; + } + /*else if(nSavingThrow = SAVING_THROW_FORT) + { + // Add Classes with Fort mettle here + // if (GetLevelByClass(CLASS_TYPE_X, oTarget) >= X) return TRUE; + }*/ + + return FALSE; +} + +void DoCommandSpell(object oCaster, object oTarget, int nSpellId, int nDuration, int nCaster) +{ + if(DEBUG) DoDebug("DoCommandSpell: SpellId: " + IntToString(nSpellId)); + if(DEBUG) DoDebug("Command Spell: Begin"); + if(nSpellId == SPELL_COMMAND_APPROACH + || nSpellId == SPELL_GREATER_COMMAND_APPROACH + || nSpellId == 18359 //MYST_VOICE_SHADOW_APPROACH + || nSpellId == SPELL_DOA_COMMAND_APPROACH + || nSpellId == SPELL_DOA_GREATER_COMMAND_APPROACH) + { + // Force the target to approach the caster + if(DEBUG) DoDebug("Command Spell: Approach"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + AssignCommand(oTarget, ActionForceMoveToObject(oCaster, TRUE)); + } + // Creatures that can't be disarmed ignore this + else if(nSpellId == SPELL_COMMAND_DROP + || nSpellId == SPELL_GREATER_COMMAND_DROP + || nSpellId == 18360 //MYST_VOICE_SHADOW_DROP + || nSpellId == SPELL_DOA_COMMAND_DROP + || nSpellId == SPELL_DOA_GREATER_COMMAND_DROP) + { + if(GetIsCreatureDisarmable(oTarget) && !GetPRCSwitch(PRC_PNP_DISARM)) + { + // Force the target to drop what its holding + if(DEBUG) DoDebug("Command Spell: Drop"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + object oTargetWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + SetDroppableFlag(oTargetWep, TRUE); + SetStolenFlag(oTargetWep, FALSE); + AssignCommand(oTarget, ActionPutDownItem(oTargetWep)); + /*oTargetWep = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + SetDroppableFlag(oTargetWep, TRUE); + SetStolenFlag(oTargetWep, FALSE); + AssignCommand(oTarget, ActionPutDownItem(oTargetWep)); */ + } + else + { + FloatingTextStringOnCreature(GetName(oTarget) + " is not disarmable.", oCaster, FALSE); + } + } + else if(nSpellId == SPELL_COMMAND_FALL + || nSpellId == SPELL_GREATER_COMMAND_FALL + || nSpellId == 18361 //MYST_VOICE_SHADOW_FALL + || nSpellId == SPELL_DOA_COMMAND_FALL + || nSpellId == SPELL_DOA_GREATER_COMMAND_FALL) + { + // Force the target to fall down + if(DEBUG) DoDebug("Command Spell: Fall"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, RoundsToSeconds(nDuration),TRUE,-1,nCaster); + } + else if(nSpellId == SPELL_COMMAND_FLEE + || nSpellId == SPELL_GREATER_COMMAND_FLEE + || nSpellId == 18362 //MYST_VOICE_SHADOW_FLEE + || nSpellId == SPELL_DOA_COMMAND_FLEE + || nSpellId == SPELL_DOA_GREATER_COMMAND_FLEE) + { + // Force the target to flee the caster + if(DEBUG) DoDebug("Command Spell: Flee"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + AssignCommand(oTarget, ActionMoveAwayFromObject(oCaster, TRUE)); + } + else if(nSpellId == SPELL_COMMAND_HALT + || nSpellId == SPELL_GREATER_COMMAND_HALT + || nSpellId == 18363 //MYST_VOICE_SHADOW_HALT + || nSpellId == SPELL_DOA_COMMAND_HALT + || nSpellId == SPELL_DOA_GREATER_COMMAND_HALT) + { + // Force the target to stand still + if(DEBUG) DoDebug("Command Spell: Stand"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectCutsceneParalyze(), oTarget, RoundsToSeconds(nDuration),TRUE,-1,nCaster); + } + else // Catch errors here + { + FloatingTextStringOnCreature("sp_command/sp_greatcommand: Error, Unknown SpellId", oCaster, FALSE); + } + if(DEBUG) DoDebug("Command Spell: End"); +} + +void SetIncorporeal(object oTarget, float fDuration, int nSuperOrEx, int nPermanent = FALSE) +{ + if (!GetIsObjectValid(oTarget)) + return; //No point working + + // Immune to non-magical weapons, ignore physical objects + effect eIncorporeal = EffectLinkEffects(EffectDamageReduction(100, DAMAGE_POWER_PLUS_ONE, 0), EffectCutsceneGhost()); + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_BLUDGEONING, 50)); // 50% chance of magical weapons not working. Done as 50% Damage Immunity + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_SLASHING, 50)); + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_PIERCING, 50)); + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectMissChance(50, MISS_CHANCE_TYPE_VS_MELEE)); // 50% melee miss chance to hit non-incorporeal targets. That's going to be everything + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectSkillIncrease(SKILL_MOVE_SILENTLY, 99)); // Cannot be heard + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE)); // Visual effect + + // No Strength Score, use Dex for melee attacks too + int nStr = GetAbilityScore(oTarget, ABILITY_STRENGTH); + int nDex = GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + int nPen; + + if (nStr>10) + { + nPen = nStr - 10; + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectAbilityDecrease(ABILITY_STRENGTH, nPen)); // Reduce Strength to 10 + + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if (GetIsObjectValid(oWeapon) && IPGetIsMeleeWeapon(oWeapon)) + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (GetIsObjectValid(oWeapon) && IPGetIsMeleeWeapon(oWeapon)) + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget); + if (GetIsObjectValid(oWeapon)) // We know these are melee weapons + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget); + if (GetIsObjectValid(oWeapon)) // We know these are melee weapons + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget); + if (GetIsObjectValid(oWeapon)) // We know these are melee weapons + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + } + + SetLocalInt(oTarget, "Incorporeal", TRUE); + if (!nPermanent) DelayCommand(fDuration, DeleteLocalInt(oTarget, "Incorporeal")); + + if (nSuperOrEx == 1) + eIncorporeal = SupernaturalEffect(eIncorporeal); + else if (nSuperOrEx == 2) + eIncorporeal = ExtraordinaryEffect(eIncorporeal); + + if (nPermanent) + SPApplyEffectToObject(DURATION_TYPE_PERMANENT, eIncorporeal, oTarget); + else + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eIncorporeal, oTarget, fDuration, FALSE, -1, -1); +} + +int GetIsIncorporeal(object oTarget) +{ + //Check for local int + if(GetPersistantLocalInt(oTarget, "Is_Incorporeal")) + return TRUE; + + //Check for local int + if(GetLocalInt(oTarget, "Incorporeal")) + return TRUE; + + //check for feat + if(GetHasFeat(FEAT_INCORPOREAL, oTarget)) + return TRUE; + + //base it on appearance + int nAppear = GetAppearanceType(oTarget); + if(nAppear == APPEARANCE_TYPE_ALLIP + || nAppear == APPEARANCE_TYPE_SHADOW + || nAppear == APPEARANCE_TYPE_SHADOW_FIEND + || nAppear == APPEARANCE_TYPE_SPECTRE + || nAppear == APPEARANCE_TYPE_WRAITH) + return TRUE; + + //Return value + return FALSE; +} + +int GetIsEthereal(object oTarget) +{ + return GetPersistantLocalInt(oTarget, "Is_Ethereal") + || GetHasFeat(FEAT_ETHEREAL, oTarget); +} + +int PRCGetIsAliveCreature(object oTarget) +{ + int bAlive = TRUE; + // non-creatures aren't alive + if (GetObjectType(oTarget) != OBJECT_TYPE_CREATURE) + return FALSE; // night of the living waypoints :p + + int nType = MyPRCGetRacialType(oTarget); + + //Non-living races, excluding warforged + if(nType == RACIAL_TYPE_UNDEAD || + (nType == RACIAL_TYPE_CONSTRUCT && !GetIsWarforged(oTarget))) bAlive = FALSE; + + //If they're dead :P + if(GetIsDead(oTarget)) bAlive = FALSE; + + //return + return bAlive; +} + +int GetControlledUndeadTotalHD(object oPC = OBJECT_SELF) +{ + int nTotalHD; + int i = 1; + object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummonTest)) + { + if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_UNDEAD) + nTotalHD += GetHitDice(oSummonTest); + if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC); + i++; + oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } + return nTotalHD; +} + + +int GetControlledFiendTotalHD(object oPC = OBJECT_SELF) +{ + int nTotalHD; + int i = 1; + object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummonTest)) + { + if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_OUTSIDER + && GetAlignmentGoodEvil(oSummonTest) == ALIGNMENT_EVIL) + nTotalHD += GetHitDice(oSummonTest); + if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC); + i++; + oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } + return nTotalHD; +} + +int GetControlledCelestialTotalHD(object oPC = OBJECT_SELF) +{ + int nTotalHD; + int i = 1; + object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummonTest)) + { + if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_OUTSIDER + && GetAlignmentGoodEvil(oSummonTest) == ALIGNMENT_GOOD) + nTotalHD += GetHitDice(oSummonTest); + if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC); + i++; + oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } + return nTotalHD; +} + + + +// wrapper for DecrementRemainingSpellUses, works for newspellbook 'fake' spells too +// should also find and decrement metamagics for newspellbooks +void PRCDecrementRemainingSpellUses(object oCreature, int nSpell) +{ + if (!UseNewSpellBook(oCreature) && GetHasSpell(nSpell, oCreature)) + { + DecrementRemainingSpellUses(oCreature, nSpell); + return; + } + + int nClass, nSpellbookID, nCount, nMeta, i, j; + int nSpellbookType, nSpellLevel; + string sFile, sFeat; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oCreature); + sFile = GetFileForClass(nClass); + nSpellbookType = GetSpellbookTypeForClass(nClass); + nSpellbookID = RealSpellToSpellbookID(nClass, nSpell); + nMeta = RealSpellToSpellbookIDCount(nClass, nSpell); + if (nSpellbookID != -1) + { //non-spellbook classes should return -1 + for(j = nSpellbookID; j <= nSpellbookID + nMeta; j++) + { + sFeat = Get2DACache(sFile, "ReqFeat", j); + if(sFeat != "") + { + if(!GetHasFeat(StringToInt(sFeat), oCreature)) + continue; + } + nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j)); + + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j); + if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + persistant_array_set_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j, nCount - 1); + return; + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + persistant_array_set_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel, nCount - 1); + return; + } + } + } + } + } + if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: Spell " + IntToString(nSpell) + " not found"); +} + +// +// This function determines if spell damage is elemental +// +int IsSpellDamageElemental(int nDamageType) +{ + return nDamageType & 2480;// 2480 = (DAMAGE_TYPE_ACID | DAMAGE_TYPE_COLD | DAMAGE_TYPE_ELECTRICAL | DAMAGE_TYPE_FIRE | DAMAGE_TYPE_SONIC) +} + +// +// This function converts spell damage into the correct type +// TODO: Change the name to consistent (large churn project). +// +int ChangedElementalDamage(object oCaster, int nDamageType) +{ + + // None of the stuff here works when items are involved + if (GetIsObjectValid(PRCGetSpellCastItem())) return nDamageType; + + int nNewType; + + //eldritch spellweave + if(GetIsObjectValid(GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))) + { + int nEssence = GetLocalInt(oCaster, "BlastEssence"); + + if(nEssence == INVOKE_BRIMSTONE_BLAST) + nNewType = DAMAGE_TYPE_FIRE; + + else if(nEssence == INVOKE_HELLRIME_BLAST) + nNewType = DAMAGE_TYPE_COLD; + + else if(nEssence == INVOKE_UTTERDARK_BLAST) + nNewType = DAMAGE_TYPE_NEGATIVE; + + else if(nEssence == INVOKE_VITRIOLIC_BLAST) + nNewType = DAMAGE_TYPE_ACID; + + //save new type for other functions + if(nNewType) + SetLocalInt(oCaster, "archmage_mastery_elements", nNewType); + } + else + // Check if an override is set + nNewType = GetLocalInt(oCaster, "archmage_mastery_elements"); + + // If so, check if the spell qualifies for a change + if (!nNewType || !IsSpellDamageElemental(nDamageType)) + nNewType = nDamageType; + + return nNewType; +} + +//used in scripts after ChangedElementalDamage() to determine saving throw type +int ChangedSaveType(int nDamageType) +{ + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: return SAVING_THROW_TYPE_ACID; + case DAMAGE_TYPE_COLD: return SAVING_THROW_TYPE_COLD; + case DAMAGE_TYPE_ELECTRICAL: return SAVING_THROW_TYPE_ELECTRICITY; + case DAMAGE_TYPE_FIRE: return SAVING_THROW_TYPE_FIRE; + case DAMAGE_TYPE_SONIC: return SAVING_THROW_TYPE_SONIC; + case DAMAGE_TYPE_DIVINE: return SAVING_THROW_TYPE_DIVINE; + case DAMAGE_TYPE_NEGATIVE: return SAVING_THROW_TYPE_NEGATIVE; + case DAMAGE_TYPE_POSITIVE: return SAVING_THROW_TYPE_POSITIVE; + } + return SAVING_THROW_TYPE_NONE;//if it ever gets here, than the function was used incorrectly +} + +// this is possibly used in variations elsewhere +int PRCGetElementalDamageType(int nDamageType, object oCaster = OBJECT_SELF) +{ + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + case DAMAGE_TYPE_COLD: + case DAMAGE_TYPE_ELECTRICAL: + case DAMAGE_TYPE_FIRE: + case DAMAGE_TYPE_SONIC: + nDamageType = ChangedElementalDamage(oCaster, nDamageType); + } + return nDamageType; +} + +int GetHasBaneMagic(int nRace) +{ + switch(nRace) + { + case RACIAL_TYPE_ABERRATION: return GetHasFeat(FEAT_BANE_MAGIC_ABERRATION); + case RACIAL_TYPE_ANIMAL: return GetHasFeat(FEAT_BANE_MAGIC_ANIMAL); + case RACIAL_TYPE_BEAST: return GetHasFeat(FEAT_BANE_MAGIC_BEAST); + case RACIAL_TYPE_CONSTRUCT: return GetHasFeat(FEAT_BANE_MAGIC_CONSTRUCT); + case RACIAL_TYPE_DRAGON: return GetHasFeat(FEAT_BANE_MAGIC_DRAGON); + case RACIAL_TYPE_DWARF: return GetHasFeat(FEAT_BANE_MAGIC_DWARF); + case RACIAL_TYPE_ELEMENTAL: return GetHasFeat(FEAT_BANE_MAGIC_ELEMENTAL); + case RACIAL_TYPE_ELF: return GetHasFeat(FEAT_BANE_MAGIC_ELF); + case RACIAL_TYPE_FEY: return GetHasFeat(FEAT_BANE_MAGIC_FEY); + case RACIAL_TYPE_GIANT: return GetHasFeat(FEAT_BANE_MAGIC_GIANT); + case RACIAL_TYPE_GNOME: return GetHasFeat(FEAT_BANE_MAGIC_GNOME); + case RACIAL_TYPE_HALFELF: return GetHasFeat(FEAT_BANE_MAGIC_HALFELF); + case RACIAL_TYPE_HALFLING: return GetHasFeat(FEAT_BANE_MAGIC_HALFLING); + case RACIAL_TYPE_HALFORC: return GetHasFeat(FEAT_BANE_MAGIC_HALFORC); + case RACIAL_TYPE_HUMAN: return GetHasFeat(FEAT_BANE_MAGIC_HUMAN); + case RACIAL_TYPE_HUMANOID_GOBLINOID: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_GOBLINOID); + case RACIAL_TYPE_HUMANOID_MONSTROUS: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_MONSTROUS); + case RACIAL_TYPE_HUMANOID_ORC: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_ORC); + case RACIAL_TYPE_HUMANOID_REPTILIAN: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_REPTILIAN); + case RACIAL_TYPE_MAGICAL_BEAST: return GetHasFeat(FEAT_BANE_MAGIC_MAGICAL_BEAST); + case RACIAL_TYPE_OUTSIDER: return GetHasFeat(FEAT_BANE_MAGIC_OUTSIDER); + case RACIAL_TYPE_SHAPECHANGER: return GetHasFeat(FEAT_BANE_MAGIC_SHAPECHANGER); + case RACIAL_TYPE_UNDEAD: return GetHasFeat(FEAT_BANE_MAGIC_UNDEAD); + case RACIAL_TYPE_VERMIN: return GetHasFeat(FEAT_BANE_MAGIC_VERMIN); + } + return FALSE; +} + +void DoPiercingCold(object oCaster, object oTarget, int nDamageAmount, int nCurrentHP) +{ + // Get change in HP from spell damage being applied + int nTest = nCurrentHP - GetCurrentHitPoints(oTarget); + // If there's no damage resistance, nTest and nDamageAmount should be equal + if (nDamageAmount > nTest) + { + // Apply the difference to ignore resist + effect eDam = EffectDamage(nDamageAmount - nTest, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + effect eVis = EffectVisualEffect(VFX_IMP_FROST_L); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + //FloatingTextStringOnCreature("Piercing Cold Triggered", oCaster, FALSE); + } +} + +void BreakDR(object oCaster, object oTarget, int nDamageAmount, int nCurrentHP) +{ + // Get change in HP from spell damage being applied + int nTest = nCurrentHP - GetCurrentHitPoints(oTarget); + // If there's no damage resistance, nTest and nDamageAmount should be equal + if (nDamageAmount > nTest) + { + // Apply the difference to ignore resist + effect eDam = EffectDamage(nDamageAmount - nTest, DAMAGE_TYPE_POSITIVE, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + } +} + +effect PRCEffectDamage(object oTarget, int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL, int nMetaMagic=METAMAGIC_NONE) +{ + object oCaster = OBJECT_SELF; + + // Incorporeal creatures have a 50% chance of not being hurt by damage other than Magical/Divine/Negative + if (GetLocalInt(oTarget, "Incorporeal") && nDamageType != DAMAGE_TYPE_MAGICAL && nDamageType != DAMAGE_TYPE_NEGATIVE && nDamageType != DAMAGE_TYPE_DIVINE) + { + if (d2() == 1) // 50% chance + { + if (GetIsPC(oTarget)) + FloatingTextStringOnCreature("Spell missed due to Incorporeality", oTarget, FALSE); + effect eEffect; + return eEffect; // Null return + } + } + + // None of the stuff here works when items are involved + if (!GetIsObjectValid(PRCGetSpellCastItem())) + { + if(PRCGetLastSpellCastClass(oCaster) == CLASS_TYPE_WARMAGE && !GetLocalInt(oTarget, "WarmageEdgeDelay")) + { + // Warmage Edge + nDamageAmount += GetAbilityModifier(ABILITY_INTELLIGENCE); + if(GetHasFeat(FEAT_TYPE_EXTRA_EDGE)) + { + // Extra Edge feat. + nDamageAmount += (GetLevelByClass(CLASS_TYPE_WARMAGE) / 4) + 1; + } + SetLocalInt(oTarget, "WarmageEdgeDelay", TRUE); + DelayCommand(0.25, DeleteLocalInt(oTarget, "WarmageEdgeDelay")); + } + + if(GetHasSpellEffect(MELD_ARCANE_FOCUS, oCaster) && !GetLocalInt(oTarget, "ArcaneFocusDelay")) + { + nDamageAmount += 1 + GetEssentiaInvested(oCaster, MELD_ARCANE_FOCUS); + SetLocalInt(oTarget, "ArcaneFocusDelay", TRUE); + DelayCommand(0.25, DeleteLocalInt(oTarget, "ArcaneFocusDelay")); + if (DEBUG) DoDebug("ArcaneFocus Damage Applied"); + } + + // Thrall of Grazzt damage + nDamageAmount += SpellBetrayalDamage(oTarget, oCaster); + + int nRace = MyPRCGetRacialType(oTarget); + + //Bane Magic + if(GetHasBaneMagic(nRace)) + nDamageAmount += d6(2); + + //Eldritch Spellweave + if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET")) + { + //Bane blast essence is active ;) + if(nRace == ((GetLocalInt(oCaster, "EssenceData") >>> 16) & 0xFF) - 1) + { + DeleteLocalObject(oCaster, "SPELLWEAVE_TARGET"); + nDamageAmount += d6(2); + } + } + + // Piercing Evocation + if(GetHasFeat(FEAT_PIERCING_EVOCATION) && GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR") == SPELL_SCHOOL_EVOCATION) + { + // Elemental damage only + if(IsSpellDamageElemental(nDamageType)) + { + // Damage magical, max 10 to magical + if(nDamageAmount > 10) + { + nDamageAmount -= 10; + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(10), oTarget); + } + else + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamageAmount), oTarget); + effect eEffect; + return eEffect; // Null return + } + } + } + + // This is done here so it affects all spells + if(GetLocalInt(oCaster, "Diabolism")) + { + //FloatingTextStringOnCreature("Diabolism is active", oCaster, FALSE); + int iDiabolistLevel = GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + DelayCommand(3.0, DeleteLocalInt(oCaster, "Diabolism")); + + if(iDiabolistLevel) + { + int nDice = (iDiabolistLevel + 5) / 5; + int nDamage = d6(nDice); + int nDamageType = DAMAGE_TYPE_DIVINE; + + if(GetLocalInt(oCaster, "VileDiabolism")) + { + //FloatingTextStringOnCreature("Vile Diabolism is active", oCaster, FALSE); + nDamage /= 2; + nDamageType = DAMAGE_TYPE_POSITIVE; + DeleteLocalInt(oCaster, "VileDiabolism"); + } + + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage, nDamageType), oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_LOS_EVIL_10), oTarget); + } + } + + // Piercing Cold for the Frost Mage + if (GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) >= 4 && nDamageType == DAMAGE_TYPE_COLD) + { + int nCurrentHP = GetCurrentHitPoints(oTarget); + DelayCommand(0.1, DoPiercingCold(oCaster, oTarget, nDamageAmount, nCurrentHP)); + } + + // Die DR die + if (GetLocalInt(oCaster, "MoveIgnoreDR")) + { + int nCurrentHP = GetCurrentHitPoints(oTarget); + DelayCommand(0.1, BreakDR(oCaster, oTarget, nDamageAmount, nCurrentHP)); + } + } + + // Frostrager heals on cold damage when raging. 1 heal for every 2 cold damage. + if (GetLocalInt(oTarget, "Frostrage") && nDamageType == DAMAGE_TYPE_COLD) + { + nDamageAmount /= 2; + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nDamageAmount), oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FROST_L), oTarget); + + FloatingTextStringOnCreature("Absorb Cold healed " + IntToString(nDamageAmount), oTarget, FALSE); + effect eEffect; + return eEffect; //Doesn't hurt him + } + // Phoenix Belt gains fast healing when hit by fire damage + if (GetHasSpellEffect(MELD_PHOENIX_BELT, oTarget) && nDamageType == DAMAGE_TYPE_FIRE) + { + int nEssentia = GetEssentiaInvested(oTarget, MELD_PHOENIX_BELT); + int nResist = nEssentia * 5; + int nDur; + if (nDamageAmount >= nResist) nDur = nResist; + else nDur = nDamageAmount; + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectRegenerate(1, 6.0), oTarget, RoundsToSeconds(nDur+1)); + } + + return EffectDamage(nDamageAmount, nDamageType, nDamagePower); +} + +// * Kovi. removes any effects from this type of spell +// * i.e., used in Mage Armor to remove any previous +// * mage armors +void PRCRemoveEffectsFromSpell(object oTarget, int SpellID) +{ + effect eLook = GetFirstEffect(oTarget); + while(GetIsEffectValid(eLook)) + { + if(GetEffectSpellId(eLook) == SpellID) + RemoveEffect(oTarget, eLook); + + eLook = GetNextEffect(oTarget); + } +} + +void PRCRemoveSpecificEffect(int nEffectTypeID, object oTarget) +{ + //Search through the valid effects on the target. + effect eAOE = GetFirstEffect(oTarget); + while (GetIsEffectValid(eAOE)) + { + if (GetEffectType(eAOE) == nEffectTypeID) + { + //If the effect was created by the spell then remove it + RemoveEffect(oTarget, eAOE); + } + //Get next effect on the target + eAOE = GetNextEffect(oTarget); + } +} + +effect PRCGetScaledEffect(effect eStandard, object oTarget) +{ + int nDiff = GetGameDifficulty(); + int nEffType = GetEffectType(eStandard); + + if(GetIsPC(oTarget) || GetIsPC(GetMaster(oTarget))) + { + if(nEffType == EFFECT_TYPE_FRIGHTENED) + { + if(nDiff == GAME_DIFFICULTY_VERY_EASY) + { + return EffectAttackDecrease(-2); + } + else if(nDiff == GAME_DIFFICULTY_EASY) + { + return EffectAttackDecrease(-4); + } + } + if(nDiff == GAME_DIFFICULTY_VERY_EASY && + (nEffType == EFFECT_TYPE_PARALYZE || + nEffType == EFFECT_TYPE_STUNNED || + nEffType == EFFECT_TYPE_CONFUSED)) + { + return EffectDazed(); + } + if(nEffType == EFFECT_TYPE_CHARMED + || nEffType == EFFECT_TYPE_DOMINATED) + { + return EffectDazed(); + } + } + return eStandard; +} + +int PRCAmIAHumanoid(object oTarget) +{ + int nRacial = MyPRCGetRacialType(oTarget); + if(nRacial == RACIAL_TYPE_DWARF + || nRacial == RACIAL_TYPE_HALFELF + || nRacial == RACIAL_TYPE_HALFORC + || nRacial == RACIAL_TYPE_ELF + || nRacial == RACIAL_TYPE_GNOME + || nRacial == RACIAL_TYPE_HUMANOID_GOBLINOID + || nRacial == RACIAL_TYPE_HALFLING + || nRacial == RACIAL_TYPE_HUMAN + || nRacial == RACIAL_TYPE_HUMANOID_MONSTROUS + || nRacial == RACIAL_TYPE_HUMANOID_ORC + || nRacial == RACIAL_TYPE_HUMANOID_REPTILIAN) + { + return TRUE; + } + return FALSE; +} + +int PRCGetScaledDuration(int nActualDuration, object oTarget) +{ + int nDiff = GetGameDifficulty(); + int nNew = nActualDuration; + if(GetIsPC(oTarget) && nActualDuration > 3) + { + if(nDiff == GAME_DIFFICULTY_VERY_EASY + || nDiff == GAME_DIFFICULTY_EASY) + { + nNew = nActualDuration / 4; + } + else if(nDiff == GAME_DIFFICULTY_NORMAL) + { + nNew = nActualDuration / 2; + } + if(nNew == 0) + { + nNew = 1; + } + } + return nNew; +} + +effect PRCCreateProtectionFromAlignmentLink(int nAlignment, int nPower = 1) +{ + int nFinal = nPower * 2; + int nAlignmentLC; + int nAlignmentGE; + effect eDur; + + switch(nAlignment) + { + case ALIGNMENT_LAWFUL:{ + nAlignmentLC = ALIGNMENT_LAWFUL; + nAlignmentGE = ALIGNMENT_ALL; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR); + break;} + case ALIGNMENT_CHAOTIC:{ + nAlignmentLC = ALIGNMENT_CHAOTIC; + nAlignmentGE = ALIGNMENT_ALL; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR); + break;} + case ALIGNMENT_GOOD:{ + nAlignmentLC = ALIGNMENT_ALL; + nAlignmentGE = ALIGNMENT_GOOD; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR); + break;} + case ALIGNMENT_EVIL:{ + nAlignmentLC = ALIGNMENT_ALL; + nAlignmentGE = ALIGNMENT_EVIL; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR); + break;} + } + + //construct final effect + effect eLink = EffectACIncrease(nFinal, AC_DEFLECTION_BONUS); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_ALL, nFinal)); + eLink = EffectLinkEffects(eLink, EffectImmunity(IMMUNITY_TYPE_MIND_SPELLS)); + //make it vs alignment + eLink = VersusAlignmentEffect(eLink, nAlignmentLC, nAlignmentGE); + //add duration vfx + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE)); + eLink = EffectLinkEffects(eLink, eDur); + + return eLink; +} + +float PRCGetSpellEffectDelay(location SpellTargetLocation, object oTarget) +{ + float fDelay = GetDistanceBetweenLocations(SpellTargetLocation, GetLocation(oTarget))/20; + return fDelay; +} + +// * returns true if the creature has flesh +int PRCIsImmuneToPetrification(object oCreature) +{ + int nAppearance = GetAppearanceType(oCreature); + int bImmune = FALSE; + + if (GetHasSpellEffect(VESTIGE_HAAGENTI, oCreature) && GetLocalInt(oCreature, "ExploitVestige") != VESTIGE_HAAGENTI_IMMUNE_TRANS) bImmune = TRUE; + + switch (nAppearance) + { + case APPEARANCE_TYPE_BASILISK: + case APPEARANCE_TYPE_COCKATRICE: + case APPEARANCE_TYPE_MEDUSA: + case APPEARANCE_TYPE_ALLIP: + case APPEARANCE_TYPE_ELEMENTAL_AIR: + case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_EARTH: + case APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_FIRE: + case APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_WATER: + case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER: + case APPEARANCE_TYPE_GOLEM_STONE: + case APPEARANCE_TYPE_GOLEM_IRON: + case APPEARANCE_TYPE_GOLEM_CLAY: + case APPEARANCE_TYPE_GOLEM_BONE: + case APPEARANCE_TYPE_GORGON: + case APPEARANCE_TYPE_HEURODIS_LICH: + case APPEARANCE_TYPE_LANTERN_ARCHON: + case APPEARANCE_TYPE_SHADOW: + case APPEARANCE_TYPE_SHADOW_FIEND: + case APPEARANCE_TYPE_SHIELD_GUARDIAN: + case APPEARANCE_TYPE_SKELETAL_DEVOURER: + case APPEARANCE_TYPE_SKELETON_CHIEFTAIN: + case APPEARANCE_TYPE_SKELETON_COMMON: + case APPEARANCE_TYPE_SKELETON_MAGE: + case APPEARANCE_TYPE_SKELETON_PRIEST: + case APPEARANCE_TYPE_SKELETON_WARRIOR: + case APPEARANCE_TYPE_SKELETON_WARRIOR_1: + case APPEARANCE_TYPE_SPECTRE: + case APPEARANCE_TYPE_WILL_O_WISP: + case APPEARANCE_TYPE_WRAITH: + case APPEARANCE_TYPE_BAT_HORROR: + case 405: // Dracolich: + case 415: // Alhoon + case 418: // shadow dragon + case 420: // mithral golem + case 421: // admantium golem + case 430: // Demi Lich + case 469: // animated chest + case 474: // golems + case 475: // golems + bImmune = TRUE; + } + + int nRacialType = MyPRCGetRacialType(oCreature); + switch(nRacialType) + { + case RACIAL_TYPE_ELEMENTAL: + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_OOZE: + case RACIAL_TYPE_UNDEAD: + bImmune = TRUE; + } + + // 01/08/07 Racial feat for petrification immunity + if(GetHasFeat(FEAT_IMMUNE_PETRIFICATION)) bImmune = TRUE; + + // 03/07/2005 CraigW - Petrification immunity can also be granted as an item property. + if ( ResistSpell(OBJECT_SELF,oCreature) == 2 ) + { + bImmune = TRUE; + } + + // * GZ: Sept 2003 - Prevent people from petrifying DM, resulting in GUI even when + // effect is not successful. + if (!GetPlotFlag(oCreature) && GetIsDM(oCreature)) + { + bImmune = FALSE; + } + return bImmune; +} + +// * This is a wrapper for how Petrify will work in Expansion Pack 1 +// * Scripts affected: flesh to stone, breath petrification, gaze petrification, touch petrification +// * nPower : This is the Hit Dice of a Monster using Gaze, Breath or Touch OR it is the Caster Spell of +// * a spellcaster +// * nFortSaveDC: pass in this number from the spell script +void PRCDoPetrification(int nPower, object oSource, object oTarget, int nSpellID, int nFortSaveDC) +{ + + if(!GetIsReactionTypeFriendly(oTarget)) + { + // * exit if creature is immune to petrification + if(PRCIsImmuneToPetrification(oTarget)) + return; + + float fDifficulty = 0.0; + int bIsPC = GetIsPC(oTarget); + int bShowPopup = FALSE; + + // * calculate Duration based on difficulty settings + int nGameDiff = GetGameDifficulty(); + switch (nGameDiff) + { + case GAME_DIFFICULTY_VERY_EASY: + case GAME_DIFFICULTY_EASY: + case GAME_DIFFICULTY_NORMAL: + fDifficulty = RoundsToSeconds(nPower); // One Round per hit-die or caster level + break; + case GAME_DIFFICULTY_CORE_RULES: + case GAME_DIFFICULTY_DIFFICULT: + bShowPopup = TRUE; + break; + } + + int nSaveDC = nFortSaveDC; + effect ePetrify = EffectPetrify(); + + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + + effect eLink = EffectLinkEffects(eDur, ePetrify); + + // Let target know the negative spell has been cast + SignalEvent(oTarget, + EventSpellCastAt(OBJECT_SELF, nSpellID)); + //SpeakString(IntToString(nSpellID)); + + // Do a fortitude save check + if (!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC)) + { + // Save failed; apply paralyze effect and VFX impact + + /// * The duration is permanent against NPCs but only temporary against PCs + if (bIsPC == TRUE) + { + if (bShowPopup == TRUE) + { + // * under hardcore rules or higher, this is an instant death + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget); + //only pop up death panel if switch is not set + if(!GetPRCSwitch(PRC_NO_PETRIFY_GUI)) + DelayCommand(2.75, PopUpDeathGUIPanel(oTarget, FALSE , TRUE, 40579)); + //run the PRC Ondeath code + //no way to run the normal module ondeath code too + //so a execute script has been added for builders to take advantage of + DelayCommand(2.75, ExecuteScript("prc_ondeath", oTarget)); + DelayCommand(2.75, ExecuteScript("prc_pw_petrific", oTarget)); + // if in hardcore, treat the player as an NPC + bIsPC = FALSE; + //fDifficulty = TurnsToSeconds(nPower); // One turn per hit-die + } + else + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDifficulty); + } + else + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget); + } + // April 2003: Clearing actions to kick them out of conversation when petrified + AssignCommand(oTarget, ClearAllActions(TRUE)); + } + } + +} + +//------------------------------------------------------------------------------ +// GZ: 2003-Oct-15 +// A different approach for timing these spells that has the positive side +// effects of making the spell dispellable as well. +// I am using the VFX applied by the spell to track the remaining duration +// instead of adding the remaining runtime on the stack +// +// This function returns FALSE if a delayed Spell effect from nSpell_ID has +// expired. See x2_s0_bigby4.nss for details +//------------------------------------------------------------------------------ +int PRCGetDelayedSpellEffectsExpired(int nSpell_ID, object oTarget, object oCaster) +{ + + if (!GetHasSpellEffect(nSpell_ID,oTarget) ) + { + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID)); + return TRUE; + } + + //-------------------------------------------------------------------------- + // GZ: 2003-Oct-15 + // If the caster is dead or no longer there, cancel the spell, as it is + // directed + //-------------------------------------------------------------------------- + if( !GetIsObjectValid(oCaster)) + { + GZPRCRemoveSpellEffects(nSpell_ID, oTarget); + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID)); + return TRUE; + } + + if (GetIsDead(oCaster)) + { + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID)); + GZPRCRemoveSpellEffects(nSpell_ID, oTarget); + return TRUE; + } + + return FALSE; + +} + +// Much similar to PRCGetHasSpell, but used for JPM to get spells left not counting metamagic +int PRCGetSpellUsesLeft(int nRealSpellID, object oCreature = OBJECT_SELF) +{ + if(!PRCGetIsRealSpellKnown(nRealSpellID, oCreature)) + return 0; + int nUses = GetHasSpell(nRealSpellID, oCreature); + + int nClass, nSpellbookID, nCount, i, j; + int nSpellbookType, nSpellLevel; + string sFile, sFeat; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oCreature); + sFile = GetFileForClass(nClass); + nSpellbookType = GetSpellbookTypeForClass(nClass); + nSpellbookID = RealSpellToSpellbookID(nClass, nRealSpellID); + if (nSpellbookID != -1) + { //non-spellbook classes should return -1 + sFeat = Get2DACache(sFile, "ReqFeat", j); + if(sFeat != "") + { + if(!GetHasFeat(StringToInt(sFeat), oCreature)) + continue; + } + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j); + if(DEBUG) DoDebug("PRCGetHasSpell(Prepared Caster): NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j)); + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(DEBUG) DoDebug("PRCGetHasSpell(Spontaneous Caster): NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + } + } + + if(DEBUG) DoDebug("PRCGetHasSpell: RealSpellID = " + IntToString(nRealSpellID) + ", Uses = " + IntToString(nUses)); + return nUses; +} + +// * Applies the effects of FEAT_AUGMENT_SUMMON to summoned creatures. +void AugmentSummonedCreature(string sResRef) +{ + if(GetHasFeat(FEAT_AUGMENT_SUMMON)) + { + int i = 1; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF); + while(GetIsObjectValid(oSummon)) + { + if(GetResRef(oSummon) == sResRef && !GetLocalInt(oSummon, "Augmented")) + { + effect eLink = EffectAbilityIncrease(ABILITY_STRENGTH, 4); + eLink = EffectLinkEffects(eLink, EffectAbilityIncrease(ABILITY_CONSTITUTION, 4)); + eLink = SupernaturalEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oSummon); + SetLocalInt(oSummon, "Augmented", TRUE); + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i); + } + } + if(sResRef == "prc_sum_treant") + { + int i = 1; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF); + while(GetIsObjectValid(oSummon)) + { + if(GetResRef(oSummon) == sResRef) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_PROT_BARKSKIN), oSummon); + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i); + } + } + if(GetHasFeat(FEAT_BECKON_THE_FROZEN)) + { + int i = 1; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF); + while(GetIsObjectValid(oSummon)) + { + if(GetResRef(oSummon) == sResRef && !GetLocalInt(oSummon, "BeckonTheFrozen")) + { + effect eLink = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD); + eLink = EffectLinkEffects(eLink, EffectDamageImmunityDecrease(DAMAGE_TYPE_FIRE, 50)); + eLink = EffectLinkEffects(eLink, EffectDamageImmunityIncrease(DAMAGE_TYPE_COLD, 100)); + eLink = SupernaturalEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oSummon); + SetLocalInt(oSummon, "BeckonTheFrozen", TRUE); + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i); + } + } +} + +object GetAreaOfEffectObject(location lTarget, string sTag, object oCaster = OBJECT_SELF) +{ + object oAoE = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + while(GetIsObjectValid(oAoE)) + { + if((GetAreaOfEffectCreator(oAoE) == oCaster) //was created by oCaster + && GetTag(oAoE) == sTag //has required tag + && !GetLocalInt(oAoE, "X2_AoE_BaseSaveDC")) //and wasn't setup before + { + return oAoE; + } + oAoE = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + } + return OBJECT_INVALID; +} + +string GetAreaOfEffectTag(int nAoE) +{ + return Get2DACache("vfx_persistent", "LABEL", nAoE); +} + +int CheckTurnUndeadUses(object oPC, int nUses) +{ + int i; + while(i < nUses) + { + if(GetHasFeat(FEAT_TURN_UNDEAD, oPC)) + { + DecrementRemainingFeatUses(oPC, FEAT_TURN_UNDEAD); + i++; + } + else + break; + } + if(i < nUses) + { + while(i) + { + IncrementRemainingFeatUses(oPC, FEAT_TURN_UNDEAD); + i--; + } + return FALSE; + } + return TRUE; +} + +// this will execute the prespellcastcode, whose full functionality is incoded in X2PreSpellCastCode2(), +// as a script, to save loading time for spells scripts and reduce memory usage of NWN +// the prespellcode takes up roughly 250 kByte compiled code, meaning that every spell script that +// calls it directly as a function (e.g.: X2PreSpellCastCode2) will be between 100 kByte to 250 kByte +// larger, than a spell script calling the prespellcode via ExecuteScript (e.g. X2PreSpellCastCode) +// Although ExecuteScript is slightly slower than a direct function call, quite likely overall performance is +// increased, because for every new spell 100-250 kByte less code need to be loaded into memory +// and NWN has more free memory available to keep more spells scripts (and other crucial scripts) +//in RAM + +int X2PreSpellCastCode() +{ + object oCaster = OBJECT_SELF; + + // SetLocalInt(oCaster, "PSCC_Ret", 0); + ExecuteScript("prc_prespell", oCaster); + + int nReturn = GetLocalInt(oCaster, "PSCC_Ret"); + // DeleteLocalInt(oCaster, "PSCC_Ret"); + + return nReturn; +} + +//:: Test Void +//void main (){} \ No newline at end of file diff --git a/trunk/include/prc_shifter_info.nss b/trunk/include/prc_shifter_info.nss new file mode 100644 index 00000000..e5f715d3 --- /dev/null +++ b/trunk/include/prc_shifter_info.nss @@ -0,0 +1,1436 @@ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +#include "prc_inc_function" +#include "inc_nwnx_funcs" + +//:: Test Void +//void main (){} + + +const int DEBUG_NATURAL_AC_CALCULATION = FALSE; +int MAX_BONUS = GetPRCSwitch(PRC_PNP_SHIFTER_BONUS); +const int MAX_PENALTY = 10; +int NWNX_STR_LIMIT = 100 - MAX_BONUS; + +struct _prc_inc_ability_info_struct{ + int nTemplateSTR; + int nTemplateDEX; + int nTemplateCON; + + int nShifterSTR; + int nShifterDEX; + int nShifterCON; + + int nDeltaSTR; + int nDeltaDEX; + int nDeltaCON; + + int nItemSTR; + int nItemDEX; + int nItemCON; + + int nExtraSTR; + int nExtraDEX; + int nExtraCON; + + int nItemDeltaSTR; + int nItemDeltaDEX; + int nItemDeltaCON; +}; + +//TODO: also count item penalties? +struct _prc_inc_ability_info_struct _prc_inc_CountItemAbilities(object oCreature) +{ + struct _prc_inc_ability_info_struct rInfoStruct; + rInfoStruct.nItemSTR = 0; + rInfoStruct.nItemDEX = 0; + rInfoStruct.nItemCON = 0; + + object oItem; + itemproperty iProperty; + int nSlot; + for(nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++) + { + switch (nSlot) + { + case INVENTORY_SLOT_CARMOUR: + case INVENTORY_SLOT_CWEAPON_R: + case INVENTORY_SLOT_CWEAPON_L: + case INVENTORY_SLOT_CWEAPON_B: + break; + + default: + { + oItem = GetItemInSlot(nSlot, oCreature); + if (GetIsObjectValid(oItem)) + { + iProperty = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(iProperty)) + { + if (GetItemPropertyType(iProperty) == ITEM_PROPERTY_ABILITY_BONUS && + GetItemPropertyDurationType(iProperty) == DURATION_TYPE_PERMANENT + ) + { + int nSubType = GetItemPropertySubType(iProperty); + int nCostTableValue = GetItemPropertyCostTableValue(iProperty); + if (nSubType == IP_CONST_ABILITY_STR) + rInfoStruct.nItemSTR += nCostTableValue; + else if (nSubType == IP_CONST_ABILITY_DEX) + rInfoStruct.nItemDEX += nCostTableValue; + else if (nSubType == IP_CONST_ABILITY_CON) + rInfoStruct.nItemCON += nCostTableValue; + } + // Next item property. + iProperty = GetNextItemProperty(oItem); + } + } + } + } + } + return rInfoStruct; +} + +struct _prc_inc_ability_info_struct _prc_inc_shifter_GetAbilityInfo(object oTemplate, object oShifter) +{ + int bFuncs = GetPRCSwitch(PRC_NWNX_FUNCS); + + //Initialize with item ability bonuses + + struct _prc_inc_ability_info_struct rInfoStruct = _prc_inc_CountItemAbilities(oShifter); + + //Get template creature abilities + + rInfoStruct.nTemplateSTR = GetAbilityScore(oTemplate, ABILITY_STRENGTH, TRUE); + rInfoStruct.nTemplateDEX = GetAbilityScore(oTemplate, ABILITY_DEXTERITY, TRUE); + rInfoStruct.nTemplateCON = GetAbilityScore(oTemplate, ABILITY_CONSTITUTION, TRUE); + //TODO: merge in "Ability Bonus: Strength" from item property from template hide here (not too important, as not many templates use this) + //TODO: merge in "Ability Bonus: Dexterity" from item property from template hide here (not too important, as not many templates use this) + //TODO: merge in "Ability Bonus: Constitution" from item property from template hide here (not too important, as not many templates use this) + + //Calculate how they compare to the shifter's abilities + + rInfoStruct.nShifterSTR = GetAbilityScore(oShifter, ABILITY_STRENGTH, TRUE); + rInfoStruct.nShifterDEX = GetAbilityScore(oShifter, ABILITY_DEXTERITY, TRUE); + rInfoStruct.nShifterCON = GetAbilityScore(oShifter, ABILITY_CONSTITUTION, TRUE); + + rInfoStruct.nDeltaSTR = rInfoStruct.nTemplateSTR - rInfoStruct.nShifterSTR; + rInfoStruct.nDeltaDEX = rInfoStruct.nTemplateDEX - rInfoStruct.nShifterDEX; + rInfoStruct.nDeltaCON = rInfoStruct.nTemplateCON - rInfoStruct.nShifterCON; + + //Handle stat boosting items + if (rInfoStruct.nItemSTR > MAX_BONUS) + rInfoStruct.nItemSTR = MAX_BONUS; + else if (rInfoStruct.nItemSTR < -MAX_PENALTY) + rInfoStruct.nItemSTR = -MAX_PENALTY; + + if (rInfoStruct.nItemDEX > MAX_BONUS) + rInfoStruct.nItemDEX = MAX_BONUS; + else if (rInfoStruct.nItemDEX < -MAX_PENALTY) + rInfoStruct.nItemDEX = -MAX_PENALTY; + + if (rInfoStruct.nItemCON > MAX_BONUS) + rInfoStruct.nItemCON = MAX_BONUS; + else if (rInfoStruct.nItemCON < -MAX_PENALTY) + rInfoStruct.nItemCON = -MAX_PENALTY; + + //Handle changes that exceed bonus or penalty caps + + rInfoStruct.nItemDeltaSTR = rInfoStruct.nDeltaSTR + rInfoStruct.nItemSTR; + if (bFuncs) + { + //NWNX boosts aren't capped, so we don't need to handle caps, generally speaking. + rInfoStruct.nExtraSTR = 0; + + //However, due to a Bioware issue, if STR, including bonuses, goes greater than 100, + //the amount of weight the PC can carry drops to 0. So, cap STR to make sure this doesn't happen. + + if (rInfoStruct.nTemplateSTR > NWNX_STR_LIMIT) + { + rInfoStruct.nExtraSTR = rInfoStruct.nTemplateSTR - NWNX_STR_LIMIT; + rInfoStruct.nTemplateSTR = NWNX_STR_LIMIT; + rInfoStruct.nDeltaSTR = rInfoStruct.nTemplateSTR - rInfoStruct.nShifterSTR; + } + } + else if (rInfoStruct.nItemDeltaSTR > MAX_BONUS) + rInfoStruct.nExtraSTR = rInfoStruct.nItemDeltaSTR - MAX_BONUS; + else if(rInfoStruct.nItemDeltaSTR < -MAX_PENALTY) + rInfoStruct.nExtraSTR = rInfoStruct.nItemDeltaSTR + MAX_PENALTY; + + rInfoStruct.nItemDeltaDEX = rInfoStruct.nDeltaDEX + rInfoStruct.nItemDEX; + if (bFuncs) + rInfoStruct.nExtraDEX = 0; //NWNX boosts aren't capped, so we don't need to handle caps + else if (rInfoStruct.nItemDeltaDEX > MAX_BONUS) + rInfoStruct.nExtraDEX = rInfoStruct.nItemDeltaDEX - MAX_BONUS; + else if(rInfoStruct.nItemDeltaDEX < -MAX_PENALTY) + rInfoStruct.nExtraDEX = rInfoStruct.nItemDeltaDEX + MAX_PENALTY; + + rInfoStruct.nItemDeltaCON = rInfoStruct.nDeltaCON + rInfoStruct.nItemCON; + if (bFuncs) + rInfoStruct.nExtraCON = 0; //NWNX boosts aren't capped, so we don't need to handle caps + else if (rInfoStruct.nItemDeltaCON > MAX_BONUS) + rInfoStruct.nExtraCON = rInfoStruct.nItemDeltaCON - MAX_BONUS; + else if(rInfoStruct.nItemDeltaCON < -MAX_PENALTY) + rInfoStruct.nExtraCON = rInfoStruct.nItemDeltaCON + MAX_PENALTY; + + return rInfoStruct; +} + +int _prc_inc_GetItemACBonus(object oItem) +{ + int nArmorBonus = 0; + itemproperty iProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProp)) + { + if(GetItemPropertyType(iProp) == ITEM_PROPERTY_AC_BONUS && GetItemPropertyDurationType(iProp) == DURATION_TYPE_PERMANENT) + nArmorBonus = max(nArmorBonus, GetItemPropertyCostTableValue(iProp)); //TODO: pick the biggest? the first? stack them? + iProp = GetNextItemProperty(oItem); + } + return nArmorBonus; +} + +int _prc_inc_GetArmorMaxDEXBonus(object oArmor, int nMaxDexACBonus = 100) +{ + if (GetIsObjectValid(oArmor)) + { + int nArmorAC = GetItemACValue(oArmor) - _prc_inc_GetItemACBonus(oArmor); //Exclude magical AC bonus to figure out armor type + switch(nArmorAC) + { + //TODO: CAN THESE BE LOOKED UP IN A 2DA OR SOMEWHERE? + case 8: case 7: case 6: + nMaxDexACBonus = 1; break; + case 5: + nMaxDexACBonus = 2; break; + case 4: case 3: + nMaxDexACBonus = 4; break; + case 2: + nMaxDexACBonus = 6; break; + case 1: + nMaxDexACBonus = 8; break; + } + } + return nMaxDexACBonus; +} + +struct _prc_inc_ac_info_struct{ + int nArmorBase; + int nArmorBonus; + + int nShieldBase; + int nShieldBonus; + + int nDodgeBonus; + int nNaturalBonus; + int nDeflectionBonus; + + int nDEXBonus; +}; + +struct _prc_inc_ac_info_struct _prc_inc_ACInfo(object oTemplate) +{ + struct _prc_inc_ac_info_struct ac_info; + + object oArmorItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTemplate); + ac_info.nArmorBonus = _prc_inc_GetItemACBonus(oArmorItem); + ac_info.nArmorBase = GetItemACValue(oArmorItem) - ac_info.nArmorBonus; + + ac_info.nDodgeBonus = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BOOTS, oTemplate)); + ac_info.nNaturalBonus = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_NECK, oTemplate)); + + ac_info.nDeflectionBonus = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_HEAD, oTemplate)); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CLOAK, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTRING, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BELT, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_ARROWS, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BULLETS, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BOLTS, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTemplate))); + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate))); + + object oOffHandItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTemplate); + ac_info.nShieldBase = 0; + ac_info.nShieldBonus = 0; + switch (GetBaseItemType(oOffHandItem)) + { + case BASE_ITEM_SMALLSHIELD: + ac_info.nShieldBase = 1; + ac_info.nShieldBonus = GetItemACValue(oOffHandItem) - ac_info.nShieldBase; + break; + case BASE_ITEM_LARGESHIELD: + ac_info.nShieldBase = 2; + ac_info.nShieldBonus = GetItemACValue(oOffHandItem) - ac_info.nShieldBase; + break; + case BASE_ITEM_TOWERSHIELD: + ac_info.nShieldBase = 3; + ac_info.nShieldBonus = GetItemACValue(oOffHandItem) - ac_info.nShieldBase; + break; + default: //A weapon + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(oOffHandItem)); + break; + } + + object oArmsItem = GetItemInSlot(INVENTORY_SLOT_ARMS, oTemplate); + switch (GetBaseItemType(oArmsItem)) + { + case BASE_ITEM_BRACER: + ac_info.nShieldBonus = max(ac_info.nShieldBonus, GetItemACValue(oArmsItem)); + break; + case BASE_ITEM_GLOVES: + default: + ac_info.nDeflectionBonus = max(ac_info.nDeflectionBonus, GetItemACValue(oArmsItem)); + break; + } + + if (ac_info.nArmorBonus > 20) + ac_info.nArmorBonus = 20; + if (ac_info.nDodgeBonus > 20) + ac_info.nDodgeBonus = 20; + if (ac_info.nNaturalBonus > 20) + ac_info.nNaturalBonus = 20; + if (ac_info.nDeflectionBonus > 20) + ac_info.nDeflectionBonus = 20; + if (ac_info.nShieldBonus > 20) + ac_info.nShieldBonus = 20; + + ac_info.nDEXBonus = min(GetAbilityModifier(ABILITY_DEXTERITY, oTemplate), _prc_inc_GetArmorMaxDEXBonus(oArmorItem)); + //TODO: make sure this isn't < 0? + + return ac_info; +} + +//Estimate natural AC of the creature oTemplate +int _prc_inc_CreatureNaturalAC(object oTemplate) +{ + struct _prc_inc_ac_info_struct ac_info = _prc_inc_ACInfo(oTemplate); + + //TODO: GetAC(oTemplate) often returns an AC different (usually higher) than the combat debugging log indicates it should be. + //Note that combat debugging doesn't report DEX bonus, Monk WIS bonus, etc.; where does this come in? + int nNaturalAC = GetAC(oTemplate) + - 10 // Adjust for base AC + - ac_info.nDEXBonus // And Dex bonus + - ac_info.nArmorBase // Etc... + - ac_info.nArmorBonus + - ac_info.nDodgeBonus + - ac_info.nNaturalBonus + - ac_info.nDeflectionBonus + - ac_info.nShieldBase + - ac_info.nShieldBonus; + + //TODO: + //Subtract +4 Dodge bonus if template has Haste? + //Subtract +1 AC / each 5 points of the Tumble skill? + //Subtract Monk AC from level progression? + //Subtract WIS AC if Monk/Ninja, etc.? + //Make sure nNaturalAC is not < 0 (it was for me once using the old method of calculation, which is why I created this new one) + + if (DEBUG_NATURAL_AC_CALCULATION || DEBUG) + { + DoDebug("_prc_inc_CreatureNaturalAC: total ac: " + IntToString(GetAC(oTemplate))); + DoDebug("_prc_inc_CreatureNaturalAC: base ac: " + IntToString(10)); + DoDebug("_prc_inc_CreatureNaturalAC: armor base ac: " + IntToString(ac_info.nArmorBase)); + DoDebug("_prc_inc_CreatureNaturalAC: armor bonus ac: " + IntToString(ac_info.nArmorBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: shield base ac: " + IntToString(ac_info.nShieldBase)); + DoDebug("_prc_inc_CreatureNaturalAC: shield bonus ac: " + IntToString(ac_info.nShieldBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: dodge bonus ac: " + IntToString(ac_info.nDodgeBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: natural bonus ac: " + IntToString(ac_info.nNaturalBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: deflection bonus ac: " + IntToString(ac_info.nDeflectionBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: dex ac: " + IntToString(ac_info.nDEXBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: calculated natural ac: " + IntToString(nNaturalAC)); + } + + //TODO: combat debugging shows actual natural AC (as well as other type); compare with that to debug. + + return nNaturalAC; +} + +int _prc_inc_GetFeatDeathAttackLevel(int nFeat) +{ + switch(nFeat) + { + case FEAT_PRESTIGE_DEATH_ATTACK_1: return 1; + case FEAT_PRESTIGE_DEATH_ATTACK_2: return 2; + case FEAT_PRESTIGE_DEATH_ATTACK_3: return 3; + case FEAT_PRESTIGE_DEATH_ATTACK_4: return 4; + case FEAT_PRESTIGE_DEATH_ATTACK_5: return 5; + case FEAT_PRESTIGE_DEATH_ATTACK_6: return 6; + case FEAT_PRESTIGE_DEATH_ATTACK_7: return 7; + case FEAT_PRESTIGE_DEATH_ATTACK_8: return 8; + case FEAT_PRESTIGE_DEATH_ATTACK_9: return 9; + case FEAT_PRESTIGE_DEATH_ATTACK_10: return 10; + case FEAT_PRESTIGE_DEATH_ATTACK_11: return 11; + case FEAT_PRESTIGE_DEATH_ATTACK_12: return 12; + case FEAT_PRESTIGE_DEATH_ATTACK_13: return 13; + case FEAT_PRESTIGE_DEATH_ATTACK_14: return 14; + case FEAT_PRESTIGE_DEATH_ATTACK_15: return 15; + case FEAT_PRESTIGE_DEATH_ATTACK_16: return 16; + case FEAT_PRESTIGE_DEATH_ATTACK_17: return 17; + case FEAT_PRESTIGE_DEATH_ATTACK_18: return 18; + case FEAT_PRESTIGE_DEATH_ATTACK_19: return 19; + case FEAT_PRESTIGE_DEATH_ATTACK_20: return 20; + } + return 0; +} + +int _prc_inc_GetHasFeat(object oTemplate, int nFeat) +{ + //If oTemplate has the feat FEAT_SNEAK_ATTACK_10, GetHasFeat() always says + //it has FEAT_PRESTIGE_DEATH_ATTACK_1 through FEAT_PRESTIGE_DEATH_ATTACK_20, + //whether it actually does or not. Work around this as follows: + int nSuppress=0; + int FEAT_SNEAK_ATTACK_10 = 353; + if(GetHasFeat(FEAT_SNEAK_ATTACK_10, oTemplate)) + { + int nFeatDeathAttackLevel = _prc_inc_GetFeatDeathAttackLevel(nFeat); + if(nFeatDeathAttackLevel) + { + int nActualDeathAttackLevel = 0; + nActualDeathAttackLevel += (GetLevelByClass(CLASS_TYPE_ASSASSIN, oTemplate) + 1) / 2; + //TODO: Add other classes here? OR use GetTotalSneakAttackDice(), etc. from prc_inc_sneak instead? + if(nFeatDeathAttackLevel > nActualDeathAttackLevel) + nSuppress = 1; + } + } + + return GetHasFeat(nFeat, oTemplate) && !nSuppress; +} + +int _prc_inc_shifting_GetIsCreatureHarmless(object oTemplate) +{ + return GetChallengeRating(oTemplate) < 1.0; +} + +int _prc_inc_shifting_CharacterLevelRequirement(object oTemplate) +{ + return GetPRCSwitch(PNP_SHFT_USECR) ? FloatToInt(GetChallengeRating(oTemplate)) : GetHitDice(oTemplate); +} + +int _prc_inc_shifting_ShifterLevelRequirement(object oTemplate) +{ + int nRacialType = MyPRCGetRacialType(oTemplate); + int nSize = PRCGetCreatureSize(oTemplate); + int nLevelRequired = 0; + + // Size tests + if(nSize >= CREATURE_SIZE_HUGE) + nLevelRequired = max(nLevelRequired, 7); + if(nSize == CREATURE_SIZE_LARGE) + nLevelRequired = max(nLevelRequired, 3); + if(nSize == CREATURE_SIZE_MEDIUM) + nLevelRequired = max(nLevelRequired, 1); + if(nSize == CREATURE_SIZE_SMALL) + nLevelRequired = max(nLevelRequired, 1); + if(nSize <= CREATURE_SIZE_TINY) + nLevelRequired = max(nLevelRequired, 3); + + // Type tests + if(nRacialType == RACIAL_TYPE_OUTSIDER) + nLevelRequired = max(nLevelRequired, 9); + if(nRacialType == RACIAL_TYPE_ELEMENTAL) + nLevelRequired = max(nLevelRequired, 9); + if(nRacialType == RACIAL_TYPE_CONSTRUCT) + nLevelRequired = max(nLevelRequired, 8); + if(nRacialType == RACIAL_TYPE_UNDEAD) + nLevelRequired = max(nLevelRequired, 8); + if(nRacialType == RACIAL_TYPE_DRAGON) + nLevelRequired = max(nLevelRequired, 7); + if(nRacialType == RACIAL_TYPE_ABERRATION) + nLevelRequired = max(nLevelRequired, 6); + if(nRacialType == RACIAL_TYPE_OOZE) + nLevelRequired = max(nLevelRequired, 6); + if(nRacialType == RACIAL_TYPE_MAGICAL_BEAST) + nLevelRequired = max(nLevelRequired, 5); + if(nRacialType == RACIAL_TYPE_GIANT) + nLevelRequired = max(nLevelRequired, 4); + if(nRacialType == RACIAL_TYPE_VERMIN) + nLevelRequired = max(nLevelRequired, 4); + if(nRacialType == RACIAL_TYPE_BEAST) + nLevelRequired = max(nLevelRequired, 3); + if(nRacialType == RACIAL_TYPE_ANIMAL) + nLevelRequired = max(nLevelRequired, 2); + if(nRacialType == RACIAL_TYPE_HUMANOID_MONSTROUS) + nLevelRequired = max(nLevelRequired, 2); + if(nRacialType == RACIAL_TYPE_DWARF || + nRacialType == RACIAL_TYPE_ELF || + nRacialType == RACIAL_TYPE_GNOME || + nRacialType == RACIAL_TYPE_HUMAN || + nRacialType == RACIAL_TYPE_HALFORC || + nRacialType == RACIAL_TYPE_HALFELF || + nRacialType == RACIAL_TYPE_HALFLING || + nRacialType == RACIAL_TYPE_HUMANOID_ORC || + nRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + ) + nLevelRequired = max(nLevelRequired, 1); + + return nLevelRequired; +} + +int _prc_inc_shifting_GetCanFormCast(object oTemplate) +{ + int nRacialType = MyPRCGetRacialType(oTemplate); + + // Need to have hands, and the ability to speak + + switch (nRacialType) + { + case RACIAL_TYPE_ABERRATION: + case RACIAL_TYPE_ANIMAL: + case RACIAL_TYPE_BEAST: + case RACIAL_TYPE_MAGICAL_BEAST: + case RACIAL_TYPE_VERMIN: + case RACIAL_TYPE_OOZE: +// case RACIAL_TYPE_PLANT: + // These forms can't cast spells + return FALSE; + case RACIAL_TYPE_DWARF: + case RACIAL_TYPE_ELF: + case RACIAL_TYPE_GNOME: + case RACIAL_TYPE_HALFLING: + case RACIAL_TYPE_HALFELF: + case RACIAL_TYPE_HALFORC: + case RACIAL_TYPE_HUMAN: + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_DRAGON: + case RACIAL_TYPE_HUMANOID_GOBLINOID: + case RACIAL_TYPE_HUMANOID_MONSTROUS: + case RACIAL_TYPE_HUMANOID_ORC: + case RACIAL_TYPE_HUMANOID_REPTILIAN: + case RACIAL_TYPE_ELEMENTAL: + case RACIAL_TYPE_FEY: + case RACIAL_TYPE_GIANT: + case RACIAL_TYPE_OUTSIDER: + case RACIAL_TYPE_SHAPECHANGER: + case RACIAL_TYPE_UNDEAD: + // Break and go return TRUE at the end of the function + break; + + default:{ + if(DEBUG) DoDebug("prc_inc_shifting: _GetCanFormCast(): Unknown racial type: " + IntToString(nRacialType)); + } + } + + return TRUE; +} + +string _prc_inc_AbilityTypeString(int nAbilityType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_abilities", "Name", nAbilityType))); +} + +string _prc_inc_AlignmentGroupString(int nAlignmentGroup) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_aligngrp", "Name", nAlignmentGroup))); +} + +string _prc_inc_BonusFeatTypeString(int nBonusFeatType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_feats", "Name", nBonusFeatType))); +} + +string _prc_inc_ClassTypeString(int nClassType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", nClassType))); +} + +string _prc_inc_CostTableEntryString(int nCostTable, int nCostTableValue) +{ + string sCostTableName = Get2DACache("iprp_costtable", "Name", nCostTable); + if(sCostTableName == "" || sCostTableName == "****") + return "??? (" + IntToString(nCostTable) + " / " + IntToString(nCostTableValue) + ")"; + string sCostTableEntry = Get2DACache(sCostTableName, "Name", nCostTableValue); + if(sCostTableEntry == "" || sCostTableEntry == "****") + return "??? (" + sCostTableName + " / " + IntToString(nCostTableValue) + ")"; + return GetStringByStrRef(StringToInt(sCostTableEntry)); +} + +string _prc_inc_DamageTypeString(int nDamageType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_damagetype", "Name", nDamageType))); +} + +string _prc_inc_ImmunityTypeString(int nImmunityType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_immunity", "Name", nImmunityType))); +} + +string _prc_inc_OnHitSpellTypeString(int nOnHitSpellType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_onhitspell", "Name", nOnHitSpellType))); +} + +string _prc_inc_OnHitTypeString(int nOnHitSpellType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_onhit", "Name", nOnHitSpellType))); +} + +string _prc_inc_OnMonsterHitTypeString(int nOnMonsterHitType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_monsterhit", "Name", nOnMonsterHitType))); +} + +string _prc_inc_SavingThrowElementTypeString(int nSavingThrowType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_saveelement", "Name", nSavingThrowType))); +} + +string _prc_inc_SavingThrowTypeString(int nSavingThrowType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_savingthrow", "Name", nSavingThrowType))); +} + +string _prc_inc_SkillTypeString(int nSkillType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("skills", "Name", nSkillType))); +} + +string _prc_inc_SpecialWalkTypeString(int nWalkType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_walk", "Name", nWalkType))); +} + +string _prc_inc_SpellSchoolTypeString(int nSpellSchoolType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_spellshl", "Name", nSpellSchoolType))); +} + +string _prc_inc_SpellTypeString(int nOnHitSpellType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_spells", "Name", nOnHitSpellType))); +} + +string _prc_inc_VisualEffectString(int nVisualEffect) +{ + //TODO: Look up in 2da (which one?) + switch(nVisualEffect) + { + case ITEM_VISUAL_ACID: + return "Acid"; + case ITEM_VISUAL_COLD: + return "Cold"; + case ITEM_VISUAL_ELECTRICAL: + return "Electrical"; + case ITEM_VISUAL_FIRE: + return "Fire"; + case ITEM_VISUAL_SONIC: + return "Sonic"; + case ITEM_VISUAL_HOLY: + return "Holy"; + case ITEM_VISUAL_EVIL: + return "Evil"; + } + return "???"; +} + +string _prc_inc_ItemPropertyString(itemproperty iprop) +{ + int nType = GetItemPropertyType(iprop); + int nSubType = GetItemPropertySubType(iprop); + int nDurationType = GetItemPropertyDurationType(iprop); + int nParam1 = GetItemPropertyParam1(iprop); + int nParam1Value = GetItemPropertyParam1Value(iprop); + int nCostTable = GetItemPropertyCostTable(iprop); + int nCostTableValue = GetItemPropertyCostTableValue(iprop); + string sType = IntToString(nType); + string sSubType = IntToString(nSubType); + string sDurationType = IntToString(nDurationType); + string sParam1 = IntToString(nParam1); + string sParam1Value = IntToString(nParam1Value); + string sCostTable = IntToString(nCostTable); + string sCostTableValue = IntToString(nCostTableValue); + string sResult = + "Typ: " + sType + "; " + + "SubTyp: " + sSubType + "; " + + "DurTyp: " + sDurationType + "; " + + "Parm: " + sParam1 + "; " + + "ParmVal: " + sParam1Value + "; " + + "CTab: " + sCostTable + "; " + + "CVal: " + sCostTableValue; + string sTypeName = GetStringByStrRef(StringToInt(Get2DACache("itempropdef", "Name", nType))); + switch (nType) + { + //TODO: these are all the possible cases; need to handle more of them. + //DONE case ITEM_PROPERTY_ABILITY_BONUS: + //DONE case ITEM_PROPERTY_AC_BONUS: + // case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE: + // case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP: + // case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT: + //DONE case ITEM_PROPERTY_ENHANCEMENT_BONUS: + //DONE case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: + // case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER + //DONE case ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION + //DONE case ITEM_PROPERTY_BONUS_FEAT: + // case ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N: + //DONE case ITEM_PROPERTY_CAST_SPELL: + //DONE case ITEM_PROPERTY_DAMAGE_BONUS: + //DONE case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + //case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + //case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT: + //DONE case ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE: + // case ITEM_PROPERTY_DECREASED_DAMAGE: + //DONE case ITEM_PROPERTY_DAMAGE_REDUCTION: + //DONE case ITEM_PROPERTY_DAMAGE_RESISTANCE: + //DONE case ITEM_PROPERTY_DAMAGE_VULNERABILITY: + //DONE case ITEM_PROPERTY_DARKVISION: + //DONE case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: + // case ITEM_PROPERTY_DECREASED_AC: + // case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER: //TODO: e.g. S1-Tomb of Horrors: DesertDragon + // case ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT: + //DONE case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: + // case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE: + //DONE case ITEM_PROPERTY_HASTE: + // case ITEM_PROPERTY_HOLY_AVENGER: + //DONE case ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS: + //DONE case ITEM_PROPERTY_IMPROVED_EVASION: + //DONE case ITEM_PROPERTY_SPELL_RESISTANCE: + //DONE case ITEM_PROPERTY_SAVING_THROW_BONUS: + //DONE case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: + //DONE case ITEM_PROPERTY_KEEN: + //DONE case ITEM_PROPERTY_LIGHT: + // case ITEM_PROPERTY_MIGHTY: + // case ITEM_PROPERTY_MIND_BLANK: + // case ITEM_PROPERTY_NO_DAMAGE: + //DONE case ITEM_PROPERTY_ON_HIT_PROPERTIES: + //DONE case ITEM_PROPERTY_DECREASED_SAVING_THROWS: + //DONE case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: + //DONE case ITEM_PROPERTY_REGENERATION: + //DONE case ITEM_PROPERTY_SKILL_BONUS: + //DONE case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL: + //DONE case ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL: + // case ITEM_PROPERTY_THIEVES_TOOLS: + //DONE case ITEM_PROPERTY_ATTACK_BONUS: + //DONE case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: + // case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: + // case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: + // case ITEM_PROPERTY_UNLIMITED_AMMUNITION: + // case ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_USE_LIMITATION_CLASS: + // case ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE: + // case ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT: + // case ITEM_PROPERTY_USE_LIMITATION_TILESET: + //DONE case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + // case ITEM_PROPERTY_TRAP: + //DONE case ITEM_PROPERTY_TRUE_SEEING: + //DONE case ITEM_PROPERTY_ON_MONSTER_HIT: + //DONE case ITEM_PROPERTY_TURN_RESISTANCE: + //DONE case ITEM_PROPERTY_MASSIVE_CRITICALS: + //DONE case ITEM_PROPERTY_FREEDOM_OF_MOVEMENT: + //DONE case ITEM_PROPERTY_MONSTER_DAMAGE: + //DONE case ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL: + //DONE case ITEM_PROPERTY_SPECIAL_WALK: + // case ITEM_PROPERTY_HEALERS_KIT: + // case ITEM_PROPERTY_WEIGHT_INCREASE: + //DONE case ITEM_PROPERTY_ONHITCASTSPELL: + //DONE case ITEM_PROPERTY_VISUALEFFECT: + // case ITEM_PROPERTY_ARCANE_SPELL_FAILURE: + + //Completely ignore + case ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION: + return ""; + + //Property name only + case ITEM_PROPERTY_DARKVISION: + case ITEM_PROPERTY_FREEDOM_OF_MOVEMENT: + case ITEM_PROPERTY_HASTE: + case ITEM_PROPERTY_IMPROVED_EVASION: + case ITEM_PROPERTY_KEEN: + case ITEM_PROPERTY_TRUE_SEEING: + return sTypeName; + + //Interpret cost table information + case ITEM_PROPERTY_AC_BONUS: + case ITEM_PROPERTY_ATTACK_BONUS: + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL: + case ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL: + case ITEM_PROPERTY_LIGHT: + case ITEM_PROPERTY_MASSIVE_CRITICALS: + case ITEM_PROPERTY_MONSTER_DAMAGE: + case ITEM_PROPERTY_REGENERATION: + case ITEM_PROPERTY_SPELL_RESISTANCE: + case ITEM_PROPERTY_TURN_RESISTANCE: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + + //Interpret cost table information; interpret subtype as damage type + case ITEM_PROPERTY_DAMAGE_BONUS: + case ITEM_PROPERTY_DAMAGE_RESISTANCE: + case ITEM_PROPERTY_DAMAGE_VULNERABILITY: + case ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue) + " " + _prc_inc_DamageTypeString(nSubType); + + //Interpret cost table information; interpret subtype as racial group + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + return sTypeName + ": " + _prc_inc_AlignmentGroupString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + + //Special handling + case ITEM_PROPERTY_ABILITY_BONUS: + case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: + return sTypeName + ": " + _prc_inc_AbilityTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_BONUS_FEAT: + return sTypeName + ": " + _prc_inc_BonusFeatTypeString(nSubType); + case ITEM_PROPERTY_CAST_SPELL: + return sTypeName + ": " + _prc_inc_SpellTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + return sTypeName + ": " + _prc_inc_AlignmentGroupString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue) + " " + _prc_inc_DamageTypeString(nParam1Value); + case ITEM_PROPERTY_DAMAGE_REDUCTION: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue) + " / " + IntToString(StringToInt(sSubType)+1); + case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: + case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE: + return sTypeName + ": " + _prc_inc_DamageTypeString(nSubType); + case ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS: + return sTypeName + ": " + _prc_inc_ImmunityTypeString(nSubType); + case ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL: + return sTypeName + ": " + _prc_inc_SpellSchoolTypeString(nSubType); + case ITEM_PROPERTY_ON_HIT_PROPERTIES: + return sTypeName + ": " + _prc_inc_OnHitTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_ONHITCASTSPELL: + return sTypeName + ": " + _prc_inc_OnHitSpellTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_ON_MONSTER_HIT: + return sTypeName + ": " + _prc_inc_OnMonsterHitTypeString(nSubType) + " " + IntToString(nCostTableValue+1); + case ITEM_PROPERTY_SKILL_BONUS: + return sTypeName + ": " + _prc_inc_SkillTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_SPECIAL_WALK: + return sTypeName + ": " + _prc_inc_SpecialWalkTypeString(nSubType); + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_VISUALEFFECT: + return sTypeName + ": " + _prc_inc_VisualEffectString(nSubType); + + case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: + case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: + return sTypeName + ": " + _prc_inc_SavingThrowTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + + case ITEM_PROPERTY_DECREASED_SAVING_THROWS: + case ITEM_PROPERTY_SAVING_THROW_BONUS: + return sTypeName + ": " + _prc_inc_SavingThrowElementTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + } + return sTypeName + " (" + sResult + ")"; +} + +string _prc_inc_EffectString(effect eEffect) +{ + int nType = GetEffectType(eEffect); + int nSubType = GetEffectSubType(eEffect); + int nDurationType = GetEffectDurationType(eEffect); + int nSpellId = GetEffectSpellId(eEffect); + string sType = IntToString(nType); + string sSubType = IntToString(nSubType); + string sDurationType = IntToString(nDurationType); + string sSpellId = IntToString(nSpellId); + + //Decode type if possible + //TODO: look up in 2da (which one?) instead of having a big switch statement + switch (nType) + { + case EFFECT_TYPE_INVALIDEFFECT : sType = "EFFECT_TYPE_INVALIDEFFECT"; break; + case EFFECT_TYPE_DAMAGE_RESISTANCE : sType = "EFFECT_TYPE_DAMAGE_RESISTANCE"; break; + //case EFFECT_TYPE_ABILITY_BONUS : sType = "EFFECT_TYPE_ABILITY_BONUS"; break; + case EFFECT_TYPE_REGENERATE : sType = "EFFECT_TYPE_REGENERATE"; break; + //case EFFECT_TYPE_SAVING_THROW_BONUS : sType = "EFFECT_TYPE_SAVING_THROW_BONUS"; break; + //case EFFECT_TYPE_MODIFY_AC : sType = "EFFECT_TYPE_MODIFY_AC"; break; + //case EFFECT_TYPE_ATTACK_BONUS : sType = "EFFECT_TYPE_ATTACK_BONUS"; break; + case EFFECT_TYPE_DAMAGE_REDUCTION : sType = "EFFECT_TYPE_DAMAGE_REDUCTION"; break; + //case EFFECT_TYPE_DAMAGE_BONUS : sType = "EFFECT_TYPE_DAMAGE_BONUS"; break; + case EFFECT_TYPE_TEMPORARY_HITPOINTS : sType = "EFFECT_TYPE_TEMPORARY_HITPOINTS"; break; + //case EFFECT_TYPE_DAMAGE_IMMUNITY : sType = "EFFECT_TYPE_DAMAGE_IMMUNITY"; break; + case EFFECT_TYPE_ENTANGLE : sType = "EFFECT_TYPE_ENTANGLE"; break; + case EFFECT_TYPE_INVULNERABLE : sType = "EFFECT_TYPE_INVULNERABLE"; break; + case EFFECT_TYPE_DEAF : sType = "EFFECT_TYPE_DEAF"; break; + case EFFECT_TYPE_RESURRECTION : sType = "EFFECT_TYPE_RESURRECTION"; break; + case EFFECT_TYPE_IMMUNITY : sType = "EFFECT_TYPE_IMMUNITY"; break; + //case EFFECT_TYPE_BLIND : sType = "EFFECT_TYPE_BLIND"; break; + case EFFECT_TYPE_ENEMY_ATTACK_BONUS : sType = "EFFECT_TYPE_ENEMY_ATTACK_BONUS"; break; + case EFFECT_TYPE_ARCANE_SPELL_FAILURE : sType = "EFFECT_TYPE_ARCANE_SPELL_FAILURE"; break; + //case EFFECT_TYPE_MOVEMENT_SPEED : sType = "EFFECT_TYPE_MOVEMENT_SPEED"; break; + case EFFECT_TYPE_AREA_OF_EFFECT : sType = "EFFECT_TYPE_AREA_OF_EFFECT"; break; + case EFFECT_TYPE_BEAM : sType = "EFFECT_TYPE_BEAM"; break; + //case EFFECT_TYPE_SPELL_RESISTANCE : sType = "EFFECT_TYPE_SPELL_RESISTANCE"; break; + case EFFECT_TYPE_CHARMED : sType = "EFFECT_TYPE_CHARMED"; break; + case EFFECT_TYPE_CONFUSED : sType = "EFFECT_TYPE_CONFUSED"; break; + case EFFECT_TYPE_FRIGHTENED : sType = "EFFECT_TYPE_FRIGHTENED"; break; + case EFFECT_TYPE_DOMINATED : sType = "EFFECT_TYPE_DOMINATED"; break; + case EFFECT_TYPE_PARALYZE : sType = "EFFECT_TYPE_PARALYZE"; break; + case EFFECT_TYPE_DAZED : sType = "EFFECT_TYPE_DAZED"; break; + case EFFECT_TYPE_STUNNED : sType = "EFFECT_TYPE_STUNNED"; break; + case EFFECT_TYPE_SLEEP : sType = "EFFECT_TYPE_SLEEP"; break; + case EFFECT_TYPE_POISON : sType = "EFFECT_TYPE_POISON"; break; + case EFFECT_TYPE_DISEASE : sType = "EFFECT_TYPE_DISEASE"; break; + case EFFECT_TYPE_CURSE : sType = "EFFECT_TYPE_CURSE"; break; + case EFFECT_TYPE_SILENCE : sType = "EFFECT_TYPE_SILENCE"; break; + case EFFECT_TYPE_TURNED : sType = "EFFECT_TYPE_TURNED"; break; + case EFFECT_TYPE_HASTE : sType = "EFFECT_TYPE_HASTE"; break; + case EFFECT_TYPE_SLOW : sType = "EFFECT_TYPE_SLOW"; break; + case EFFECT_TYPE_ABILITY_INCREASE : sType = "EFFECT_TYPE_ABILITY_INCREASE"; break; + case EFFECT_TYPE_ABILITY_DECREASE : sType = "EFFECT_TYPE_ABILITY_DECREASE"; break; + case EFFECT_TYPE_ATTACK_INCREASE : sType = "EFFECT_TYPE_ATTACK_INCREASE"; break; + case EFFECT_TYPE_ATTACK_DECREASE : sType = "EFFECT_TYPE_ATTACK_DECREASE"; break; + case EFFECT_TYPE_DAMAGE_INCREASE : sType = "EFFECT_TYPE_DAMAGE_INCREASE"; break; + case EFFECT_TYPE_DAMAGE_DECREASE : sType = "EFFECT_TYPE_DAMAGE_DECREASE"; break; + case EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE : sType = "EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE"; break; + case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE : sType = "EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE"; break; + case EFFECT_TYPE_AC_INCREASE : sType = "EFFECT_TYPE_AC_INCREASE"; break; + case EFFECT_TYPE_AC_DECREASE : sType = "EFFECT_TYPE_AC_DECREASE"; break; + case EFFECT_TYPE_MOVEMENT_SPEED_INCREASE : sType = "EFFECT_TYPE_MOVEMENT_SPEED_INCREASE"; break; + case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE : sType = "EFFECT_TYPE_MOVEMENT_SPEED_DECREASE"; break; + case EFFECT_TYPE_SAVING_THROW_INCREASE : sType = "EFFECT_TYPE_SAVING_THROW_INCREASE"; break; + case EFFECT_TYPE_SAVING_THROW_DECREASE : sType = "EFFECT_TYPE_SAVING_THROW_DECREASE"; break; + case EFFECT_TYPE_SPELL_RESISTANCE_INCREASE : sType = "EFFECT_TYPE_SPELL_RESISTANCE_INCREASE"; break; + case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE : sType = "EFFECT_TYPE_SPELL_RESISTANCE_DECREASE"; break; + case EFFECT_TYPE_SKILL_INCREASE : sType = "EFFECT_TYPE_SKILL_INCREASE"; break; + case EFFECT_TYPE_SKILL_DECREASE : sType = "EFFECT_TYPE_SKILL_DECREASE"; break; + case EFFECT_TYPE_INVISIBILITY : sType = "EFFECT_TYPE_INVISIBILITY"; break; + case EFFECT_TYPE_IMPROVEDINVISIBILITY : sType = "EFFECT_TYPE_IMPROVEDINVISIBILITY"; break; + case EFFECT_TYPE_DARKNESS : sType = "EFFECT_TYPE_DARKNESS"; break; + case EFFECT_TYPE_DISPELMAGICALL : sType = "EFFECT_TYPE_DISPELMAGICALL"; break; + case EFFECT_TYPE_ELEMENTALSHIELD : sType = "EFFECT_TYPE_ELEMENTALSHIELD"; break; + case EFFECT_TYPE_NEGATIVELEVEL : sType = "EFFECT_TYPE_NEGATIVELEVEL"; break; + case EFFECT_TYPE_POLYMORPH : sType = "EFFECT_TYPE_POLYMORPH"; break; + case EFFECT_TYPE_SANCTUARY : sType = "EFFECT_TYPE_SANCTUARY"; break; + case EFFECT_TYPE_TRUESEEING : sType = "EFFECT_TYPE_TRUESEEING"; break; + case EFFECT_TYPE_SEEINVISIBLE : sType = "EFFECT_TYPE_SEEINVISIBLE"; break; + case EFFECT_TYPE_TIMESTOP : sType = "EFFECT_TYPE_TIMESTOP"; break; + case EFFECT_TYPE_BLINDNESS : sType = "EFFECT_TYPE_BLINDNESS"; break; + case EFFECT_TYPE_SPELLLEVELABSORPTION : sType = "EFFECT_TYPE_SPELLLEVELABSORPTION"; break; + case EFFECT_TYPE_DISPELMAGICBEST : sType = "EFFECT_TYPE_DISPELMAGICBEST"; break; + case EFFECT_TYPE_ULTRAVISION : sType = "EFFECT_TYPE_ULTRAVISION"; break; + case EFFECT_TYPE_MISS_CHANCE : sType = "EFFECT_TYPE_MISS_CHANCE"; break; + case EFFECT_TYPE_CONCEALMENT : sType = "EFFECT_TYPE_CONCEALMENT"; break; + case EFFECT_TYPE_SPELL_IMMUNITY : sType = "EFFECT_TYPE_SPELL_IMMUNITY"; break; + case EFFECT_TYPE_VISUALEFFECT : sType = "EFFECT_TYPE_VISUALEFFECT"; break; + case EFFECT_TYPE_DISAPPEARAPPEAR : sType = "EFFECT_TYPE_DISAPPEARAPPEAR"; break; + case EFFECT_TYPE_SWARM : sType = "EFFECT_TYPE_SWARM"; break; + case EFFECT_TYPE_TURN_RESISTANCE_DECREASE : sType = "EFFECT_TYPE_TURN_RESISTANCE_DECREASE"; break; + case EFFECT_TYPE_TURN_RESISTANCE_INCREASE : sType = "EFFECT_TYPE_TURN_RESISTANCE_INCREASE"; break; + case EFFECT_TYPE_PETRIFY : sType = "EFFECT_TYPE_PETRIFY"; break; + case EFFECT_TYPE_CUTSCENE_PARALYZE : sType = "EFFECT_TYPE_CUTSCENE_PARALYZE"; break; + case EFFECT_TYPE_ETHEREAL : sType = "EFFECT_TYPE_ETHEREAL"; break; + case EFFECT_TYPE_SPELL_FAILURE : sType = "EFFECT_TYPE_SPELL_FAILURE"; break; + case EFFECT_TYPE_CUTSCENEGHOST : sType = "EFFECT_TYPE_CUTSCENEGHOST"; break; + case EFFECT_TYPE_CUTSCENEIMMOBILIZE : sType = "EFFECT_TYPE_CUTSCENEIMMOBILIZE"; break; + } + + //Decode subtype if possible + //TODO: look up in 2da (which one?) instead of having a switch statement + switch (nSubType) + { + case SUBTYPE_MAGICAL : sSubType = "SUBTYPE_MAGICAL"; break; + case SUBTYPE_SUPERNATURAL : sSubType = "SUBTYPE_SUPERNATURAL"; break; + case SUBTYPE_EXTRAORDINARY : sSubType = "SUBTYPE_EXTRAORDINARY"; break; + } + + //Decode duration type if possible + //TODO: look up in 2da (which one?) instead of having a switch statement + switch (nDurationType) + { + case DURATION_TYPE_INSTANT : sDurationType = "DURATION_TYPE_INSTANT"; break; + case DURATION_TYPE_TEMPORARY : sDurationType = "DURATION_TYPE_TEMPORARY"; break; + case DURATION_TYPE_PERMANENT : sDurationType = "DURATION_TYPE_PERMANENT"; break; + } + + string sResult = + "EFFECT Type: " + sType + "; " + + "SubType: " + sSubType + "; " + + "DurationType: " + sDurationType + "; " + + "SpellId: " + sSpellId; + + return sResult; +} + +void _prc_inc_PrintShapeInfo(object oPC, string sMessage) +{ + if(!GetLocalInt(oPC, "PRC_SuppressChatPrint")) + SendMessageToPC(oPC, sMessage); //Send to chat window in game + if(GetLocalInt(oPC, "PRC_EnableLogPrint")) + PrintString(sMessage); //Write to log file for reference +} + +void _prc_inc_PrintClassInfo(string sPrefix, object oPC, object oTemplate, int nClassType) +{ + if (nClassType != CLASS_TYPE_INVALID) + { + int nLevel = GetLevelByClass(nClassType, oTemplate); + string sClassName = _prc_inc_ClassTypeString(nClassType); + _prc_inc_PrintShapeInfo(oPC, sPrefix + sClassName + " (" + IntToString(nLevel) + ")"); + } +} + +void _prc_inc_PrintItemProperty(string sPrefix, object oPC, itemproperty iProp, int bIncludeTemp = FALSE) +{ + int nDurationType = GetItemPropertyDurationType(iProp); + if(nDurationType == DURATION_TYPE_PERMANENT || (bIncludeTemp && nDurationType == DURATION_TYPE_TEMPORARY)) + { + string sPropString = _prc_inc_ItemPropertyString(iProp); + if(sPropString != "") + { + if (nDurationType == DURATION_TYPE_TEMPORARY) + sPropString = GetStringByStrRef(57473+0x01000000) + sPropString; //"TEMPORARY: " + _prc_inc_PrintShapeInfo(oPC, sPrefix + sPropString); + } + } +} + +void _prc_inc_PrintAllItemProperties(string sPrefix, object oPC, object oItem, int bIncludeTemp = FALSE) +{ + itemproperty iProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProp)) + { + _prc_inc_PrintItemProperty(sPrefix, oPC, iProp, bIncludeTemp); + iProp = GetNextItemProperty(oItem); + } +} + +void _prc_inc_PrintEffect(string sPrefix, object oPC, effect eEffect) +{ + if (GetEffectType(eEffect) == EFFECT_TYPE_INVALIDEFFECT) + { + //An effect with type EFFECT_TYPE_INVALID is added for each item property + //They are also added for a couple of other things (Knockdown, summons, etc.) + //Just skip these + } + else + { + string sEffectString = _prc_inc_EffectString(eEffect); + if(sEffectString != "") + _prc_inc_PrintShapeInfo(oPC, sPrefix + sEffectString); + } +} + +void _prc_inc_PrintAllEffects(string sPrefix, object oPC, object oItem) +{ + effect eEffect = GetFirstEffect(oItem); + while(GetIsEffectValid(eEffect)) + { + _prc_inc_PrintEffect(sPrefix, oPC, eEffect); + eEffect = GetNextEffect(oItem); + } +} + +//NOTE: THIS FUNCTION HAS A LOT OF CODE IN COMMON WITH _prc_inc_shifting_CreateShifterActiveAbilitiesItem +//TODO: PUT SOME OF IT IN A SHARED FUNCTION THAT THEY BOTH CALL +void _prc_inc_shifting_PrintShifterActiveAbilities(object oPC, object oTemplate) +{ + string sPrefix = GetStringByStrRef(57437+0x01000000); //"Epic Wildshape Spell-Like Abilities:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + + int bPrinted = FALSE; + + object oTemplateHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate); + itemproperty iProp = GetFirstItemProperty(oTemplateHide); + while(GetIsItemPropertyValid(iProp)) + { + if(GetItemPropertyDurationType(iProp) == DURATION_TYPE_PERMANENT && GetItemPropertyType(iProp) == ITEM_PROPERTY_CAST_SPELL) + { + _prc_inc_PrintItemProperty("=== ", oPC, iProp); + bPrinted = TRUE; + } + iProp = GetNextItemProperty(oTemplateHide); + } + + // Loop over shifter_abilitie.2da + string sNumUses; + int nSpell, nNumUses, nProps; + int i = 0; + while(nSpell = StringToInt(Get2DACache("shifter_abilitie", "Spell", i))) + { + // See if the template has this spell + if(GetHasSpell(nSpell, oTemplate)) + { + // Determine the number of uses from the 2da + sNumUses = Get2DACache("shifter_abilitie", "IPCSpellNumUses", i); + if(sNumUses == "1_USE_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_1_USE_PER_DAY; + else if(sNumUses == "2_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_2_USES_PER_DAY; + else if(sNumUses == "3_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_3_USES_PER_DAY; + else if(sNumUses == "4_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_4_USES_PER_DAY; + else if(sNumUses == "5_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_5_USES_PER_DAY; + else if(sNumUses == "UNLIMITED_USE") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE; + else{ + if(DEBUG) DoDebug("prc_inc_shifting: _prc_inc_shifting_PrintShifterActiveAbilities(): Unknown IPCSpellNumUses in shifter_abilitie.2da line " + IntToString(i) + ": " + sNumUses); + nNumUses = -1; + } + + // Create the itemproperty and print it + iProp = ItemPropertyCastSpell(StringToInt(Get2DACache("shifter_abilitie", "IPSpell", i)), nNumUses); + _prc_inc_PrintItemProperty("=== ", oPC, iProp); + bPrinted = TRUE; + //TODO: DESTROY iProp? + + // Increment property counter + nProps += 1; + } + + // Increment loop counter + i += 1; + } + + if(!bPrinted) + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57481+0x01000000)); //"None" +} + +void _prc_inc_shifting_PrintFeats(object oPC, object oTemplate, int nStartIndex, int nLimitIndex) +{ + //Loop over shifter_feats.2da + string sFeat; + int i = nStartIndex; + while((i < nLimitIndex) && (sFeat = Get2DACache("shifter_feats", "Feat", i)) != "") + { + if (_prc_inc_GetHasFeat(oTemplate, StringToInt(sFeat))) + { + string sFeatName = GetStringByStrRef(StringToInt(Get2DACache("feat", "Feat", StringToInt(sFeat)))); + _prc_inc_PrintShapeInfo(oPC, "=== " + sFeatName); + } + i += 1; + } +} + +void _prc_inc_PrintNaturalAC(object oPC, object oTemplate) +{ + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57435+0x01000000) + " " + IntToString(_prc_inc_CreatureNaturalAC(oTemplate))); //Natural AC of form +} + +//TODO: TLK Entries +void _prc_inc_PrintDebugItem(object oPC, string oItemType, object oItem) +{ + if (GetIsObjectValid(oItem)) + { + _prc_inc_PrintShapeInfo(oPC, "===================================="); + _prc_inc_PrintShapeInfo(oPC, "====== " + oItemType); + _prc_inc_PrintShapeInfo(oPC, "====== NAME: " + GetName(oItem)); + _prc_inc_PrintShapeInfo(oPC, "====== RESREF: " + GetResRef(oItem)); + _prc_inc_PrintShapeInfo(oPC, "====== ITEM PROPERTIES ======"); + _prc_inc_PrintAllItemProperties("=== ", oPC, oItem, TRUE); + _prc_inc_PrintShapeInfo(oPC, "====== EFFECTS ======"); + _prc_inc_PrintAllEffects("=== ", oPC, oItem); + _prc_inc_PrintShapeInfo(oPC, "====== OTHER ======"); + if (GetObjectType(oItem) == OBJECT_TYPE_CREATURE) + { + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(1, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(2, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(3, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(4, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(5, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(6, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(7, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(8, oItem)); + + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== Main hand weapon: " + (GetIsWeaponEffective(oItem, FALSE) ? "Effective" : "Ineffective")); + _prc_inc_PrintShapeInfo(oPC, "====== Off hand weapon: " + (GetIsWeaponEffective(oItem, TRUE) ? "Effective" : "Ineffective")); + _prc_inc_PrintShapeInfo(oPC, "====== Immortal: " + (GetImmortal(oItem) ? "Yes" : "No")); + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== Level: " + IntToString(GetHitDice(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== CR: " + FloatToString(GetChallengeRating(oItem), 4, 1)); + _prc_inc_PrintShapeInfo(oPC, "====== Caster Level: " + IntToString(GetCasterLevel(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== XP: " + IntToString(GetXP(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== Alignment: " + IntToString(GetLawChaosValue(oItem)) + " / " + IntToString(GetGoodEvilValue(oItem))); + //TODO: + // int GetAlignmentLawChaos(object oCreature); + // int GetAlignmentGoodEvil(object oCreature); + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== BAB: " + IntToString(GetBaseAttackBonus(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== HP: " + IntToString(GetCurrentHitPoints(oItem)) + " / " + IntToString(GetMaxHitPoints(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== AC: " + IntToString(GetAC(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== SR: " + IntToString(GetSpellResistance(oItem))); + _prc_inc_PrintShapeInfo(oPC, "------"); + + //TODO: look up names in 2da/TLK? + _prc_inc_PrintShapeInfo(oPC, "====== STR: " + IntToString(GetAbilityScore(oItem, ABILITY_STRENGTH)) + " (" + IntToString(GetAbilityModifier(ABILITY_STRENGTH, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== DEX: " + IntToString(GetAbilityScore(oItem, ABILITY_DEXTERITY)) + " (" + IntToString(GetAbilityModifier(ABILITY_DEXTERITY, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== CON: " + IntToString(GetAbilityScore(oItem, ABILITY_CONSTITUTION)) + " (" + IntToString(GetAbilityModifier(ABILITY_CONSTITUTION, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== INT: " + IntToString(GetAbilityScore(oItem, ABILITY_INTELLIGENCE)) + " (" + IntToString(GetAbilityModifier(ABILITY_INTELLIGENCE, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== WIS: " + IntToString(GetAbilityScore(oItem, ABILITY_WISDOM)) + " (" + IntToString(GetAbilityModifier(ABILITY_WISDOM, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== CHA: " + IntToString(GetAbilityScore(oItem, ABILITY_CHARISMA)) + " (" + IntToString(GetAbilityModifier(ABILITY_CHARISMA, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "------"); + + //TODO: look up names in 2da/TLK? + _prc_inc_PrintShapeInfo(oPC, "====== Fortitude: " + IntToString(GetFortitudeSavingThrow(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== Will: " + IntToString(GetWillSavingThrow(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== Reflex: " + IntToString(GetReflexSavingThrow(oItem))); + _prc_inc_PrintShapeInfo(oPC, "------"); + + int i = 0; + string sSkillName; + while((sSkillName = Get2DACache("skills", "Name", i)) != "") + { + sSkillName = GetStringByStrRef(StringToInt(sSkillName)); + _prc_inc_PrintShapeInfo(oPC, "====== " + sSkillName + ": " + IntToString(GetSkillRank(i, oItem))); + i += 1; + } + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== Gender: " + IntToString(GetGender(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Size: " + IntToString(GetCreatureSize(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Race: " + IntToString(GetRacialType(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Speed: " + IntToString(GetMovementRate(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Dead: " + (GetIsDead(oItem) ? "Yes" : "No")); + _prc_inc_PrintShapeInfo(oPC, "====== Tag: " + GetTag(oItem)); + _prc_inc_PrintShapeInfo(oPC, "====== Object Type: " + IntToString(GetObjectType(oItem))); //TODO: look up values in 2da? + + //TODO?: + //int GetGold(object oTarget=OBJECT_SELF); + //location GetLocalLocation(object oObject, string sVarName); + // vector GetPositionFromLocation(location lLocation); + // object GetAreaFromLocation(location lLocation); + // float GetFacingFromLocation(location lLocation); + //int GetCommandable(object oTarget=OBJECT_SELF); + //int GetIsListening(object oObject); + //int GetReputation(object oSource, object oTarget); + //location GetLocation(object oObject); + //int GetIsPC(object oCreature); + // int GetIsEnemy(object oTarget, object oSource=OBJECT_SELF); + // int GetIsFriend(object oTarget, object oSource=OBJECT_SELF); + // int GetIsNeutral(object oTarget, object oSource=OBJECT_SELF); + // int GetStealthMode(object oCreature); + // int GetDetectMode(object oCreature); + // int GetDefensiveCastingMode(object oCreature); + // int GetAppearanceType(object oCreature); + // int GetWeight(object oTarget=OBJECT_SELF); //Gets the weight of an item, or the total carried weight of a creature in tenths of pounds (as per the baseitems.2da). + // int GetAILevel(object oTarget=OBJECT_SELF); + // int GetActionMode(object oCreature, int nMode); + // int GetArcaneSpellFailure(object oCreature); + // int GetLootable( object oCreature ); + // int GetIsCreatureDisarmable(object oCreature); + // string GetDeity(object oCreature); + // string GetSubRace(object oTarget); + // int GetAge(object oCreature); + //int GetPlotFlag(object oTarget=OBJECT_SELF); + } + else + _prc_inc_PrintShapeInfo(oPC, "====== AC: " + IntToString(GetItemACValue(oItem))); + _prc_inc_PrintShapeInfo(oPC, "===================================="); + } +} + +//TODO: TLK Entries +void _prc_inc_ShapePrintDebug(object oPC, object oTarget, int bForceLogPrint) +{ + int nSaveValue = GetLocalInt(oPC, "PRC_EnableLogPrint"); + if (bForceLogPrint) + SetLocalInt(oPC, "PRC_EnableLogPrint", TRUE); + + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "CREATURE", oTarget)); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "CREATURE SKIN", GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTarget))); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "RIGHT CREATURE WEAPON", GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget))); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "LEFT CREATURE WEAPON", GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget))); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "SPECIAL CREATURE WEAPON", GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget))); + + int nSlot; + for(nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++) + { + switch (nSlot) + { + case INVENTORY_SLOT_CARMOUR: + case INVENTORY_SLOT_CWEAPON_R: + case INVENTORY_SLOT_CWEAPON_L: + case INVENTORY_SLOT_CWEAPON_B: + break; + + default: + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "INVENTORY ITEM " + IntToString(nSlot) + ": ", GetItemInSlot(nSlot, oTarget))); + } + } + + if (bForceLogPrint) + DelayCommand(0.1, SetLocalInt(oPC, "PRC_EnableLogPrint", nSaveValue)); +} + +//TODO: Add nShifterType parameter so that only applicable information is printed? +void _prc_inc_PrintShape(object oPC, object oTemplate, int bForceLogPrint) +{ + int nSaveValue = GetLocalInt(oPC, "PRC_EnableLogPrint"); + if (bForceLogPrint) + SetLocalInt(oPC, "PRC_EnableLogPrint", TRUE); + + _prc_inc_PrintShapeInfo(oPC, "================================================="); + + //Basic information + + _prc_inc_PrintShapeInfo(oPC, "=== " + GetName(oTemplate)); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetResRef(oTemplate)); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef((StringToInt(Get2DACache("racialtypes", "Name", MyPRCGetRacialType(oTemplate)))))); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57420+0x01000000) + IntToString(_prc_inc_shifting_ShifterLevelRequirement(oTemplate))); //"Required Shifter Level: " + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57421+0x01000000) + IntToString(_prc_inc_shifting_CharacterLevelRequirement(oTemplate))); //"Required Character Level: " + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57436+0x01000000) + FloatToString(GetChallengeRating(oTemplate), 4, 1)); //"Challenge Rating: " + + _prc_inc_PrintShapeInfo(oPC, "=========="); + + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(1, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(2, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(3, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(4, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(5, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(6, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(7, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(8, oTemplate)); + + _prc_inc_PrintShapeInfo(oPC, "=========="); + + //Harmlessly invisible? + + if(_prc_inc_shifting_GetIsCreatureHarmless(oTemplate)) + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57424+0x01000000)); //"Harmlessly Invisible" + + //Able to cast spells without Natural Spell? + + if(!_prc_inc_shifting_GetCanFormCast(oTemplate) && !GetHasFeat(FEAT_PRESTIGE_SHIFTER_NATURALSPELL, oTemplate)) + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57477+0x01000000)); //"Cannot cast spells" + else + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57476+0x01000000)); //"Can cast spells" + + //Natural AC + + _prc_inc_PrintNaturalAC(oPC, oTemplate); + + //STR, DEX, CON + + struct _prc_inc_ability_info_struct rInfoStruct = _prc_inc_shifter_GetAbilityInfo(oTemplate, oPC); + + //Extra information related to STR, DEX, CON + + string sExtra, sBonusOrPenalty = GetStringByStrRef(57427+0x01000000); + sExtra = " (" + sBonusOrPenalty + (rInfoStruct.nDeltaSTR>=0?"+":"") + IntToString(rInfoStruct.nDeltaSTR) + ")"; + _prc_inc_PrintShapeInfo(oPC, "=== " + _prc_inc_AbilityTypeString(0) + " " + IntToString(rInfoStruct.nTemplateSTR) + sExtra); + sExtra = " (" + sBonusOrPenalty + (rInfoStruct.nDeltaDEX>=0?"+":"") + IntToString(rInfoStruct.nDeltaDEX) + ")"; + _prc_inc_PrintShapeInfo(oPC, "=== " + _prc_inc_AbilityTypeString(1) + " " + IntToString(rInfoStruct.nTemplateDEX) + sExtra); + sExtra = " (" + sBonusOrPenalty + (rInfoStruct.nDeltaCON>=0?"+":"") + IntToString(rInfoStruct.nDeltaCON) + ")"; + _prc_inc_PrintShapeInfo(oPC, "=== " + _prc_inc_AbilityTypeString(2) + " " + IntToString(rInfoStruct.nTemplateCON) + sExtra); + + _prc_inc_PrintShapeInfo(oPC, "------"); + + int i = 0; + string sSkillName; + string sSTRBasedSkills, sDEXBasedSkills, sCONBasedSkills; + while((sSkillName = Get2DACache("skills", "Name", i)) != "") + { + sSkillName = GetStringByStrRef(StringToInt(sSkillName)); + string sSkillKeyAbility = Get2DACache("skills", "KeyAbility", i); + if (sSkillKeyAbility == "STR") + sSTRBasedSkills += sSkillName + ", "; + else if (sSkillKeyAbility == "DEX") + sDEXBasedSkills += sSkillName + ", "; + else if (sSkillKeyAbility == "CON") + sCONBasedSkills += sSkillName + ", "; + i += 1; + } + if (GetStringLength(sSTRBasedSkills)) + sSTRBasedSkills = GetStringLeft(sSTRBasedSkills, GetStringLength(sSTRBasedSkills) - 2); //Remove the final ", " + if (GetStringLength(sDEXBasedSkills)) + sDEXBasedSkills = GetStringLeft(sDEXBasedSkills, GetStringLength(sDEXBasedSkills) - 2); //Remove the final ", " + if (GetStringLength(sCONBasedSkills)) + sCONBasedSkills = GetStringLeft(sCONBasedSkills, GetStringLength(sCONBasedSkills) - 2); //Remove the final ", " + + int nSTRBonus = rInfoStruct.nExtraSTR / 2; + if (nSTRBonus > 0) + { + //TODO: cap AB bonus + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60544+0x01000000) + " +" + IntToString(nSTRBonus)); //Attack increase from STR increase + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60546+0x01000000) + " +" + IntToString(nSTRBonus)); //Damage increase from STR increase + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60528+0x01000000), "%(SKILLS)", sSTRBasedSkills) + " +" + IntToString(nSTRBonus)); //Skill bonus from STR increase + } + else if (nSTRBonus < 0) + { + //TODO: cap AB penalty--at what? + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60545+0x01000000) + " " + IntToString(nSTRBonus)); //Attack decrease from STR decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60547+0x01000000) + " " + IntToString(nSTRBonus)); //Damage decrease from STR decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60529+0x01000000), "%(SKILLS)", sSTRBasedSkills) + " " + IntToString(nSTRBonus)); //Skill penalty from STR decrease + } + + int nDEXBonus = rInfoStruct.nExtraDEX / 2; + if (nDEXBonus > 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60548+0x01000000) + " +" + IntToString(nDEXBonus)); //AC increase from DEX increase + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60530+0x01000000), "%(SKILLS)", sDEXBasedSkills) + " +" + IntToString(nDEXBonus)); //Skill bonus from DEX increase + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60531+0x01000000) + " +" + IntToString(nDEXBonus)); //Saving throw bonus from DEX increase + } + else if (nDEXBonus < 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60549+0x01000000) + " " + IntToString(nDEXBonus)); //AC decrease from DEX increase + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60532+0x01000000), "%(SKILLS)", sDEXBasedSkills) + " " + IntToString(nDEXBonus)); //Skill penalty from DEX decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60533+0x01000000) + " " + IntToString(nDEXBonus)); //Saving throw penalty from DEX decrease + } + + int nCONBonus = rInfoStruct.nExtraCON / 2; + if (nCONBonus > 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60534+0x01000000), "%(SKILLS)", sCONBasedSkills) + " +" + IntToString(nCONBonus)); //Skill bonus from CON increase + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60535+0x01000000) + " +" + IntToString(nCONBonus)); //Saving throw bonus from CON increase + int tempHP = rInfoStruct.nExtraCON * GetHitDice(oPC); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57431+0x01000000) + " " + IntToString(tempHP)); //Temporary HP from CON increase + } + else if (nCONBonus < 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60536+0x01000000), "%(SKILLS)", sCONBasedSkills) + " " + IntToString(nCONBonus)); //Skill penalty from CON decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60537+0x01000000) + " " + IntToString(nCONBonus)); //Saving throw penalty from CON decrease + } + + _prc_inc_PrintShapeInfo(oPC, "=========="); + + //Hide and creature weapon properties + + object oTemplateCWpR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTemplate); + object oTemplateCWpL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTemplate); + object oTemplateCWpB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTemplate); + object oTemplateHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate); + + if(GetIsObjectValid(oTemplateCWpR)) + { + string sPrefix = GetStringByStrRef(57432+0x01000000); //"Right Creature Weapon:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateCWpR); + } + + if(GetIsObjectValid(oTemplateCWpL)) + { + string sPrefix = GetStringByStrRef(57433+0x01000000); //"Left Creature Weapon:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateCWpL); + } + + if(GetIsObjectValid(oTemplateCWpB)) + { + string sPrefix = GetStringByStrRef(57434+0x01000000); //"Special Attack Creature Weapon:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateCWpB); + } + + if(GetIsObjectValid(oTemplateHide)) + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateHide); + + //Spell-like abilities + + _prc_inc_shifting_PrintShifterActiveAbilities(oPC, oTemplate); + + //Feats + + i = 0; + string sFeat; + int CHUNK_SIZE = 25; //50 was too big, so use 25 + + while((sFeat = Get2DACache("shifter_feats", "Feat", i)) != "") + { + DelayCommand(0.0f, _prc_inc_shifting_PrintFeats(oPC, oTemplate, i, i+CHUNK_SIZE)); + i += CHUNK_SIZE; + } + DelayCommand(0.0f, _prc_inc_PrintShapeInfo(oPC, "=================================================")); + + if (bForceLogPrint) + DelayCommand(0.1, SetLocalInt(oPC, "PRC_EnableLogPrint", nSaveValue)); + + if (GetLocalInt(oPC, "prc_shifter_debug")) + DelayCommand(0.2f, _prc_inc_ShapePrintDebug(oPC, oTemplate, bForceLogPrint)); +} diff --git a/trunk/include/psi_inc_core.nss b/trunk/include/psi_inc_core.nss new file mode 100644 index 00000000..ff94e44f --- /dev/null +++ b/trunk/include/psi_inc_core.nss @@ -0,0 +1,1312 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Psionic Core Files +//:: psi_inc_core +//:://///////////////////////////////////////////// +/** @file + Core functions removed from + + psi_inc_psifunc + psi_inc_focus (depreciated) + + as they are required by many of the other psi groups + + @author Ornedan/ElgarL + @date Created - 2005.11.10/23.07.2010 + */ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Included here to provide the values for the constants below +#include "prc_class_const" + +const int POWER_LIST_PSION = CLASS_TYPE_PSION; +const int POWER_LIST_WILDER = CLASS_TYPE_WILDER; +const int POWER_LIST_PSYWAR = CLASS_TYPE_PSYWAR; +const int POWER_LIST_PSYROG = CLASS_TYPE_PSYCHIC_ROGUE; +const int POWER_LIST_FIST_OF_ZUOKEN = CLASS_TYPE_FIST_OF_ZUOKEN; +const int POWER_LIST_WARMIND = CLASS_TYPE_WARMIND; + +#include "psi_inc_const" + +//:: Test Main +//void main (){} + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Attempts to use psionic focus. If the creature was focused, it + * loses the focus. If it has Epic Psionic Focus feats, it will + * be able to use the focus for a number of times equal to the + * number of those feats it has during the next 0.5s + * + * @param oUser Creature expending it's psionic focus + * @return TRUE if the creature was psionically focus or had + * Epic Psionic Focus uses remaining. FALSE otherwise. + */ +int UsePsionicFocus(object oUser = OBJECT_SELF); + +/** + * Sets psionic focus active and triggers feats dependant + * on it. + * + * Feats currently keyed to activity of psionic focus: + * Psionic Dodge + * Speed of Thought + * + * @param oGainee Creature gaining psionic focus. + */ +void GainPsionicFocus(object oGainee = OBJECT_SELF); + +/** + * Gets the number of psionic focus uses the creature has available + * to it at this moment. If the creature is psionically focused, + * the number equal to GetPsionicFocusUsesPerExpenditure(), otherwise + * it is however many focus uses the creature still has remaining of + * that number. + * + * @param oCreature Creaute whose psionic focus use count to evaluate + * @return The total number of times UsePsionicFocus() will + * return TRUE if called at this moment. + */ +int GetPsionicFocusesAvailable(object oCreature = OBJECT_SELF); + +/** + * Calculates the number of times a creature may use it's psionic focus when expending it. + * Base is 1. + * In addition, 1 more use for each Epic Psionic Focus feat the creature has. + * + * @param oCreature Creaute whose psionic focus use count to evaluate + * @return The total number of times UsePsionicFocus() will return + * TRUE for a single expending of psionic focus. + */ +int GetPsionicFocusUsesPerExpenditure(object oCreature = OBJECT_SELF); + +/** + * Sets the given creature's psionic focus off and deactivates all feats keyed to it. + * + * @param oLoser Creature losing it's psionic focus + */ +void LosePsionicFocus(object oLoser = OBJECT_SELF); + +/** + * Checks whether the given creature is psionically focused. + * + * @param oCreature Creature whose psionic focus's state to examine + * @return TRUE if the creature is psionically focused, FALSE + * otherwise. + */ +int GetIsPsionicallyFocused(object oCreature = OBJECT_SELF); + +/** + * Determines the number of feats that would use psionic focus + * when triggered the given creature has active. + * + * Currently accounts for: + * Talented + * Power Specialization + * Power Penetration + * Psionic Endowment + * Chain Power + * Empower Power + * Extend Power + * Maximize Power + * Split Psionic Ray + * Twin Power + * Widen Power + * Quicken Power + * + * @param oCreature Creature whose feats to examine + * @return How many of the listed feats are active + */ +int GetPsionicFocusUsingFeatsActive(object oCreature = OBJECT_SELF); + +/** + * Calculates the DC of the power being currently manifested. + * Base value is 10 + power level + ability modifier in manifesting stat + * + * WARNING: Return value is not defined when a power is not being manifested. + * + */ +int GetManifesterDC(object oManifester = OBJECT_SELF); + +/** + * Determines the spell school matching a discipline according to the + * standard transparency rules. + * Disciplines which have no matching spell school are matched with + * SPELL_SCHOOL_GENERAL. + * + * @param nDiscipline Discipline to find matching spell school for + * @return SPELL_SCHOOL_* of the match + */ +int DisciplineToSpellSchool(int nDiscipline); + +/** + * Determines the discipline matching a spell school according to the + * standard transparency rules. + * Spell schools that have no matching disciplines are matched with + * DISCIPLINE_NONE. + * + * @param nSpellSchool Spell schools to find matching discipline for + * @return DISCIPLINE_* of the match + */ +int SpellSchoolToDiscipline(int nSpellSchool); + +/** + * Determines the discipline of a power, using the School column of spells.2da. + * + * @param nSpellID The spellID of the power to determine the discipline of + * @return DISCIPLINE_* constant. DISCIPLINE_NONE if the power's + * School designation does not match any of the discipline's. + */ +int GetPowerDiscipline(int nSpellID); + +/** + * Determines whether a given power is a power of the Telepahty discipline. + * + * @param nSpellID The spells.2da row of the power. If left to default, + * PRCGetSpellId() is used. + * @return TRUE if the power's discipline is Telepathy, FALSE otherwise + */ +int GetIsTelepathyPower(int nSpellID = -1); + +/** + * Checks whether the PC possesses the feats the given feat has as it's + * prerequisites. Possession of a feat is checked using GetHasFeat(). + * + * + * @param nFeat The feat for which determine the possession of prerequisites + * @param oPC The creature whose feats to check + * @return TRUE if the PC possesses the prerequisite feats AND does not + * already posses nFeat, FALSE otherwise. + */ +int CheckPowerPrereqs(int nFeat, object oPC); + +/** + * Determines the manifester's level in regards to manifester checks to overcome + * spell resistance. + * + * WARNING: Return value is not defined when a power is not being manifested. + * + * @param oManifester A creature manifesting a power at the moment + * @return The creature's manifester level, adjusted to account for + * modifiers that affect spell resistance checks. + */ +int GetPsiPenetration(object oManifester = OBJECT_SELF); + +/** + * Determines whether a given creature possesses the Psionic subtype. + * Ways of possessing the subtype: + * - Being of a naturally psionic race + * - Having class levels in a psionic class + * - Possessing the Wild Talent feat + * + * @param oCreature Creature to test + * @return TRUE if the creature is psionic, FALSE otherwise. + */ +int GetIsPsionicCharacter(object oCreature); + +/** + * Creates the creature weapon for powers like Bite of the Wolf and Claws of the + * Beast. If a creature weapon of the correct type is already present, it is + * used instead of a new one. + * + * Preserving existing weapons may cause problems, if so, make the function instead delete the old object - Ornedan + * + * @param oCreature Creatue whose creature weapons to mess with. + * @param sResRef Resref of the creature weapon. Assumed to be one of the + * PRC creature weapons, so this considered to is also be + * the tag. + * @param nIventorySlot Inventory slot where the creature weapon is to be equipped. + * @param fDuration If a new weapon is created, it will be destroyed after + * this duration. + * + * @return The newly created creature weapon. Or an existing weapon, + * if there was one. + */ +object GetPsionicCreatureWeapon(object oCreature, string sResRef, int nInventorySlot, float fDuration); + +/** + * Applies modifications to a power's damage that depend on some property + * of the target. + * Currently accounts for: + * - Mental Resistance + * - Greater Power Specialization + * - Intellect Fortress + * + * @param oTarget A creature being dealt damage by a power + * @param oManifester The creature manifesting the damaging power + * @param nDamage The amount of damage the creature would be dealt + * + * @param bIsHitPointDamage Is the damage HP damage or something else? + * @param bIsEnergyDamage Is the damage caused by energy or something else? Only relevant if the damage is HP damage. + * + * @return The amount of damage, modified by oTarget's abilities + */ +int GetTargetSpecificChangesToDamage(object oTarget, object oManifester, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE); + +/** + * Gets the manifester level adjustment from the Practiced Manifester feats. + * + * @param oManifester The creature to check + * @param iManifestingClass The CLASS_TYPE* that the power was cast by. + * @param iManifestingLevels The manifester level for the power calculated so far + * ie. BEFORE Practiced Manifester. + */ +int PracticedManifesting(object oManifester, int iManifestingClass, int iManifestingLevels); + +/** + * Determines the given creature's manifester level. If a class is specified, + * then returns the manifester level for that class. Otherwise, returns + * the manifester level for the currently active manifestation. + * + * @param oManifester The creature whose manifester level to determine + * @param nSpecificClass The class to determine the creature's manifester + * level in. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * manifester level in regards to an ongoing manifestation + * is determined instead. + * @param nMaxPowerLevel For learning powers. Practiced Manifester is breaking things otherwise. + * @return The manifester level + */ +int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE); + +/** + * Determines the given creature's highest undmodified manifester level among it's + * manifesting classes. + * + * @param oCreature Creature whose highest manifester level to determine + * @return The highest unmodified manifester level the creature can have + */ +int GetHighestManifesterLevel(object oCreature); + +/** + * Gets the level of the power being currently manifested. + * WARNING: Return value is not defined when a power is not being manifested. + * + * @param oManifester The creature currently manifesting a power + * @return The level of the power being manifested + */ +int GetPowerLevel(object oManifester); + +/** + * Determines a creature's ability score in the manifesting ability of a given + * class. + * + * @param oManifester Creature whose ability score to get + * @param nClass CLASS_TYPE_* constant of a manifesting class + */ +int GetAbilityScoreOfClass(object oManifester, int nClass); + +/** + * Determines from what class's power list the currently being manifested + * power is manifested from. + * + * @param oManifester A creature manifesting a power at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetManifestingClass(object oManifester = OBJECT_SELF); + +/** + * Determines the manifesting ability of a class. + * + * @param nClass CLASS_TYPE_* constant of the class to determine the manifesting stat of + * @return ABILITY_* of the manifesting stat. ABILITY_CHARISMA for non-manifester + * classes. + */ +int GetAbilityOfClass(int nClass); + +/** + * Determines which of the character's classes is their highest or first psionic + * manifesting class, if any. This is the one which gains manifester level raise + * benefits from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first psionic manifesting class, + * CLASS_TYPE_INVALID if the creature does not posses any. + */ +int GetPrimaryPsionicClass(object oCreature = OBJECT_SELF); + +/** + * Calculates how many manifester levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added manifester levels for + * @return The number of manifester levels gained + */ +int GetPsionicPRCLevels(object oCreature); + +/** + * Determines the position of a creature's first psionic manifesting class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first psionic class {1, 2, 3} or 0 if + * the creature possesses no levels in psionic classes. + */ +int GetFirstPsionicClassPosition(object oCreature = OBJECT_SELF); + +/** + * Determines whether a given class is a psionic class or not. A psionic + * class is defined as one that gives base manifesting. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a psionic class, FALSE otherwise + */ +int GetIsPsionicClass(int nClass); + +/** + * Gets the amount of manifester levels the given creature is Wild Surging by. + * + * @param oManifester The creature to test + * @return The number of manifester levels added by Wild Surge. 0 if + * Wild Surge is not active. + */ +int GetWildSurge(object oManifester); + +/** + * Gets the highest power level the creature should know + * + * @param oManifester The creature to test + * @return Power level. + */ +int GetMaxPowerLevel(object oManifester); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_unarmed" + +////////////////////////////////////////////////// +/* Global Structures */ +////////////////////////////////////////////////// + +// These are used in psi_inc_psifunc and psi_inc_augment + +/** + * A structure that contains common data used during power manifestation. + */ +struct manifestation{ + /* Generic stuff */ + /// The creature manifesting the power + object oManifester; + /// Whether the manifestation is successfull or not + int bCanManifest; + /// How much Power Points the manifestation costs + int nPPCost; + /// How many psionic focus uses the manifester would have remaining at a particular point in the manifestation + int nPsiFocUsesRemain; + /// The creature's manifester level in regards to this power + int nManifesterLevel; + /// The power's spell ID + int nSpellID; + + /* Augmentation */ + /// How many times the first augmentation option of the power is used + int nTimesAugOptUsed_1; + /// How many times the second augmentation option of the power is used + int nTimesAugOptUsed_2; + /// How many times the third augmentation option of the power is used + int nTimesAugOptUsed_3; + /// How many times the fourth augmentation option of the power is used + int nTimesAugOptUsed_4; + /// How many times the fifth augmentation option of the power is used + int nTimesAugOptUsed_5; + /// How many times the PP used for augmentation triggered the generic augmentation of the power + int nTimesGenericAugUsed; + + /* Metapsionics */ + /// Whether Chain Power was used with this manifestation + int bChain; + /// Whether Empower Power was used with this manifestation + int bEmpower; + /// Whether Extend Power was used with this manifestation + int bExtend; + /// Whether Maximize Power was used with this manifestation + int bMaximize; + /// Whether Split Psionic Ray was used with this manifestation + int bSplit; + /// Whether Twin Power was used with this manifestation + int bTwin; + /// Whether Widen Power was used with this manifestation + int bWiden; + /// Whether Quicken Power was used with this manifestation + int bQuicken; +}; + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + + + +/* Begin PSI FOCUS */ +////////////////////////////////////////////////////// + +int UsePsionicFocus(object oUser = OBJECT_SELF) +{ + int bToReturn = FALSE; + // This does not expend your psionic focus, but rather the item's + if (GetLocalInt(oUser, "SimpleBow_Focus")) + { + DeleteLocalInt(oUser, "SimpleBow_Focus"); + return TRUE; + } + // Next, check if we have focus on + else if(GetLocalInt(oUser, PSIONIC_FOCUS)) + { + SetLocalInt(oUser, "PsionicFocusUses", GetPsionicFocusUsesPerExpenditure(oUser) - 1); + DelayCommand(0.5f, DeleteLocalInt(oUser, "PsionicFocusUses")); + SendMessageToPCByStrRef(oUser, 16826414); // "You have used your Psionic Focus" + + bToReturn = TRUE; + } + // We don't. Check if there are uses remaining + else if(GetLocalInt(oUser, "PsionicFocusUses")) + { + SetLocalInt(oUser, "PsionicFocusUses", GetLocalInt(oUser, "PsionicFocusUses") - 1); + + bToReturn = TRUE; + } + + // Lose focus if it was used + if(bToReturn) LosePsionicFocus(oUser); + + return bToReturn; +} + +void GainPsionicFocus(object oGainee = OBJECT_SELF) +{ + SetLocalInt(oGainee, PSIONIC_FOCUS, TRUE); + + // Speed Of Thought + if(GetHasFeat(FEAT_SPEED_OF_THOUGHT, oGainee)) + { + // Check for heavy armor before adding the bonus now + if(GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oGainee)) < 6) + AssignCommand(oGainee, ActionCastSpellAtObject(SPELL_FEAT_SPEED_OF_THOUGHT_BONUS, oGainee, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + + // Schedule a script to remove the bonus should they equip heavy armor + AddEventScript(oGainee, EVENT_ONPLAYEREQUIPITEM, "psi_spdfthgt_oeq", TRUE, FALSE); + // Schedule another script to add the bonus back if the unequip the armor + AddEventScript(oGainee, EVENT_ONPLAYERUNEQUIPITEM, "psi_spdfthgt_ueq", TRUE, FALSE); + } + // Psionic Dodge + if(GetHasFeat(FEAT_PSIONIC_DODGE, oGainee)) + SetCompositeBonus(GetPCSkin(oGainee), "PsionicDodge", 1, ITEM_PROPERTY_AC_BONUS); + + //Strength of Two - Kalashtar racial feat + if(GetHasFeat(FEAT_STRENGTH_OF_TWO, oGainee)) + { + SetCompositeBonus(GetPCSkin(oGainee), "StrengthOfTwo", 1, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_WILL); + } + // Danger Sense abilities for Psychic Rogue + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 5) + ExecuteScript("psi_psyrog_dngr", oGainee); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 7) // Uncanny Dodge + IPSafeAddItemProperty(GetPCSkin(oGainee), PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE1), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 9) // Improved Uncanny Dodge + IPSafeAddItemProperty(GetPCSkin(oGainee), PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE2), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_1d6, oGainee)) + { + int nPsySneak = 1; + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_2d6, oGainee)) + nPsySneak += 2; + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_3d6, oGainee)) + nPsySneak += 3; + + SetLocalInt(oGainee, "PsyRogueSneak",nPsySneak); + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oGainee)); + } +} + +int GetPsionicFocusesAvailable(object oCreature = OBJECT_SELF) +{ + // If the creature has a psionic focus active, return the maximum + if(GetLocalInt(oCreature, PSIONIC_FOCUS)) + return GetPsionicFocusUsesPerExpenditure(oCreature); + // Otherwise, return the amount currently remaining + else + return GetLocalInt(oCreature, "PsionicFocusUses"); +} + +int GetPsionicFocusUsesPerExpenditure(object oCreature = OBJECT_SELF) +{ + int nFocusUses = 1; + int i; + for(i = FEAT_EPIC_PSIONIC_FOCUS_1; i <= FEAT_EPIC_PSIONIC_FOCUS_10; i++) + if(GetHasFeat(i, oCreature)) nFocusUses++; + + return nFocusUses; +} + +void LosePsionicFocus(object oLoser = OBJECT_SELF) +{ + // Only remove focus if it's present + if(GetLocalInt(oLoser, PSIONIC_FOCUS)) + { + SetLocalInt(oLoser, PSIONIC_FOCUS, FALSE); + + // Loss of Speed of Thought effects + PRCRemoveSpellEffects(SPELL_FEAT_SPEED_OF_THOUGHT_BONUS, oLoser, oLoser); + RemoveEventScript(oLoser, EVENT_ONPLAYEREQUIPITEM, "psi_spdfthgt_oeq", TRUE); + RemoveEventScript(oLoser, EVENT_ONPLAYERUNEQUIPITEM, "psi_spdfthgt_ueq", TRUE); + // Loss of Psionic Dodge effects + SetCompositeBonus(GetPCSkin(oLoser), "PsionicDodge", 0, ITEM_PROPERTY_AC_BONUS); + // Loss of Strength of Two effects + SetCompositeBonus(GetPCSkin(oLoser), "StrengthOfTwo", 0, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_WILL); + + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 5) + PRCRemoveSpellEffects(POWER_DANGERSENSE, oLoser, oLoser); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 7) + RemoveItemProperty(GetPCSkin(oLoser), ItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE1)); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 9) + RemoveItemProperty(GetPCSkin(oLoser), ItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE2)); + + // Inform oLoser about the event + FloatingTextStrRefOnCreature(16826415, oLoser, FALSE); // "You have lost your Psionic Focus" + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_1d6, oLoser)) + { + DeleteLocalInt(oLoser, "PsyRogueSneak"); + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oLoser)); + } + } +} + +int GetIsPsionicallyFocused(object oCreature = OBJECT_SELF) +{ + return GetLocalInt(oCreature, PSIONIC_FOCUS); +} + +int GetPsionicFocusUsingFeatsActive(object oCreature = OBJECT_SELF) +{ + int nFeats; + + if(GetLocalInt(oCreature, "TalentedActive")) nFeats++; + if(GetLocalInt(oCreature, "PowerSpecializationActive")) nFeats++; + if(GetLocalInt(oCreature, "PowerPenetrationActive")) nFeats++; + if(GetLocalInt(oCreature, "PsionicEndowmentActive")) nFeats++; + + if(GetLocalInt(oCreature, METAPSIONIC_CHAIN_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_EMPOWER_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_EXTEND_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_MAXIMIZE_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_SPLIT_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_TWIN_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_WIDEN_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_QUICKEN_VAR)) nFeats++; + + return nFeats; +} + +////////////////////////////////////////////////////// +/* END PSI FOCUS */ +////////////////////////////////////////////////////// + +int GetManifesterDC(object oManifester = OBJECT_SELF) +{ + int nClass = GetManifestingClass(oManifester); + int nDC = 10; + nDC += GetPowerLevel(oManifester); + nDC += GetAbilityModifier(GetAbilityOfClass(nClass), oManifester); + + // Stuff that applies only to powers, not psi-like abilities goes inside + if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) + { + if (GetLocalInt(oManifester, "PsionicEndowmentActive") == TRUE && UsePsionicFocus(oManifester)) + { + nDC += GetHasFeat(FEAT_GREATER_PSIONIC_ENDOWMENT, oManifester) ? 4 : 2; + } + } + + // Needed to do some adjustments here. + object oTarget = PRCGetSpellTargetObject(); + int nPower = PRCGetSpellId(); + + // Other DC adjustments + // Soul Manifester + nDC += Soulcaster(oManifester, PRCGetSpellId()); + // Charming Veil meld + if(GetHasSpellEffect(MELD_CHARMING_VEIL, oManifester) && (GetIsOfSubschool(nPower, SUBSCHOOL_CHARM) || GetIsOfSubschool(nPower, SUBSCHOOL_COMPULSION))) nDC += GetEssentiaInvested(oManifester, MELD_CHARMING_VEIL)+1; + // Soul Eater + nDC += (GetLocalInt(oManifester, "PRC_SoulEater_HasDrained") && GetLevelByClass(CLASS_TYPE_SOUL_EATER, oManifester) >= 10) ? 2 : 0; + // Closed Mind + if(GetHasFeat(FEAT_CLOSED_MIND, oTarget)) nDC -= 2; + // Strong Mind + if(GetHasFeat(FEAT_STRONG_MIND, oTarget)) nDC -= 3; + // Fist of Dal Quor + if(GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oTarget) >= 4) nDC -= 2; + + return nDC; +} + +int DisciplineToSpellSchool(int nDiscipline) +{ + int nSpellSchool = SPELL_SCHOOL_GENERAL; + + switch(nDiscipline) + { + case DISCIPLINE_CLAIRSENTIENCE: nSpellSchool = SPELL_SCHOOL_DIVINATION; break; + case DISCIPLINE_METACREATIVITY: nSpellSchool = SPELL_SCHOOL_CONJURATION; break; + case DISCIPLINE_PSYCHOKINESIS: nSpellSchool = SPELL_SCHOOL_EVOCATION; break; + case DISCIPLINE_PSYCHOMETABOLISM: nSpellSchool = SPELL_SCHOOL_TRANSMUTATION; break; + case DISCIPLINE_TELEPATHY: nSpellSchool = SPELL_SCHOOL_ENCHANTMENT; break; + + default: nSpellSchool = SPELL_SCHOOL_GENERAL; break; + } + + return nSpellSchool; +} + +int SpellSchoolToDiscipline(int nSpellSchool) +{ + int nDiscipline = DISCIPLINE_NONE; + + switch(nSpellSchool) + { + case SPELL_SCHOOL_GENERAL: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_ABJURATION: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_CONJURATION: nDiscipline = DISCIPLINE_METACREATIVITY; break; + case SPELL_SCHOOL_DIVINATION: nDiscipline = DISCIPLINE_CLAIRSENTIENCE; break; + case SPELL_SCHOOL_ENCHANTMENT: nDiscipline = DISCIPLINE_TELEPATHY; break; + case SPELL_SCHOOL_EVOCATION: nDiscipline = DISCIPLINE_PSYCHOKINESIS; break; + case SPELL_SCHOOL_ILLUSION: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_NECROMANCY: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_TRANSMUTATION: nDiscipline = DISCIPLINE_PSYCHOMETABOLISM; break; + + default: nDiscipline = DISCIPLINE_NONE; + } + + return nDiscipline; +} + +int GetPowerDiscipline(int nSpellID) +{ + string sSpellSchool = Get2DACache("spells", "School", nSpellID);//lookup_spell_school(nSpellID); + int nDiscipline; + + if (sSpellSchool == "A") nDiscipline = DISCIPLINE_NONE; + else if (sSpellSchool == "C") nDiscipline = DISCIPLINE_METACREATIVITY; + else if (sSpellSchool == "D") nDiscipline = DISCIPLINE_CLAIRSENTIENCE; + else if (sSpellSchool == "E") nDiscipline = DISCIPLINE_TELEPATHY; + else if (sSpellSchool == "V") nDiscipline = DISCIPLINE_PSYCHOKINESIS; + else if (sSpellSchool == "I") nDiscipline = DISCIPLINE_NONE; + else if (sSpellSchool == "N") nDiscipline = DISCIPLINE_NONE; + else if (sSpellSchool == "T") nDiscipline = DISCIPLINE_PSYCHOMETABOLISM; + else if (sSpellSchool == "G") nDiscipline = DISCIPLINE_PSYCHOPORTATION; + + return nDiscipline; +} + +int GetIsTelepathyPower(int nSpellID = -1) +{ + if(nSpellID == -1) nSpellID = PRCGetSpellId(); + + return GetPowerDiscipline(nSpellID) == DISCIPLINE_TELEPATHY; +} + +int CheckPowerPrereqs(int nFeat, object oPC) +{ + // Having the power already automatically disqualifies one from taking it again + if(GetHasFeat(nFeat, oPC)) + return FALSE; + // We assume that the 2da is correctly formatted, and as such, a prereq slot only contains + // data if the previous slots in order also contains data. + // ie, no PREREQFEAT2 if PREREQFEAT1 is empty + if(Get2DACache("feat", "PREREQFEAT1", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "PREREQFEAT1", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "PREREQFEAT2", nFeat) != "" + && !GetHasFeat(StringToInt(Get2DACache("feat", "PREREQFEAT2", nFeat)), oPC)) + return FALSE; + } + + if(Get2DACache("feat", "OrReqFeat0", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat0", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat1", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat1", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat2", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat2", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat3", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat3", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat4", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat4", nFeat)), oPC)) + return FALSE; + } } } } } + + //if youve reached this far then return TRUE + return TRUE; +} + +int GetPsiPenetration(object oManifester = OBJECT_SELF) +{ + int nPen = GetManifesterLevel(oManifester); + + // The stuff inside applies only to normal manifestation, not psi-like abilities + if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) + { + // Check for Power Pen feats being used + if(GetLocalInt(oManifester, "PowerPenetrationActive") == TRUE && UsePsionicFocus(oManifester)) + { + nPen += GetHasFeat(FEAT_GREATER_POWER_PENETRATION, oManifester) ? 8 : 4; + } + } + + return nPen; +} + +int GetIsPsionicCharacter(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_PSION, oCreature) || + GetLevelByClass(CLASS_TYPE_PSYWAR, oCreature) || + GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oCreature) || + GetLevelByClass(CLASS_TYPE_WILDER, oCreature) || + GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature) || + GetLevelByClass(CLASS_TYPE_WARMIND, oCreature) || + GetHasFeat(FEAT_WILD_TALENT, oCreature) || + GetHasFeat(FEAT_KALASHTAR_PP, oCreature) || + GetHasFeat(FEAT_NATPSIONIC_1, oCreature) || + GetHasFeat(FEAT_NATPSIONIC_2, oCreature) || + GetHasFeat(FEAT_NATPSIONIC_3, oCreature) + // Racial psionicity signifying feats go here + ); +} + +void LocalCleanExtraFists(object oCreature) +{ + int iIsCWeap, iIsEquip; + + object oClean = GetFirstItemInInventory(oCreature); + + while (GetIsObjectValid(oClean)) + { + iIsCWeap = GetIsPRCCreatureWeapon(oClean); + + iIsEquip = oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_L) || + oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_R) || + oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_B); + + if (iIsCWeap && !iIsEquip) + { + DestroyObject(oClean); + } + + oClean = GetNextItemInInventory(oCreature); + } +} +object GetPsionicCreatureWeapon(object oCreature, string sResRef, int nInventorySlot, float fDuration) +{ + int bCreatedWeapon = FALSE; + object oCWeapon = GetItemInSlot(nInventorySlot, oCreature); + + RemoveUnarmedAttackEffects(oCreature); + // Make sure they can actually equip them + UnarmedFeats(oCreature); + + // Determine if a creature weapon of the proper type already exists in the slot + if(!GetIsObjectValid(oCWeapon) || + GetStringUpperCase(GetTag(oCWeapon)) != GetStringUpperCase(sResRef) // Hack: The resref's and tags of the PRC creature weapons are the same + ) + { + if (GetHasItem(oCreature, sResRef)) + { + oCWeapon = GetItemPossessedBy(oCreature, sResRef); + SetIdentified(oCWeapon, TRUE); + //AssignCommand(oCreature, ActionEquipItem(oCWeapon, INVENTORY_SLOT_CWEAPON_L)); + ForceEquip(oCreature, oCWeapon, nInventorySlot); + } + else + { + oCWeapon = CreateItemOnObject(sResRef, oCreature); + SetIdentified(oCWeapon, TRUE); + //AssignCommand(oCreature, ActionEquipItem(oCWeapon, INVENTORY_SLOT_CWEAPON_L)); + ForceEquip(oCreature, oCWeapon, nInventorySlot); + bCreatedWeapon = TRUE; + } + } + + + // Clean up the mess of extra fists made on taking first level. + DelayCommand(6.0f, LocalCleanExtraFists(oCreature)); + + // Weapon finesse or intuitive attack? + SetLocalInt(oCreature, "UsingCreature", TRUE); + ExecuteScript("prc_intuiatk", oCreature); + DelayCommand(1.0f, DeleteLocalInt(oCreature, "UsingCreature")); + + // Add OnHitCast: Unique if necessary + if(GetHasFeat(FEAT_REND, oCreature)) + IPSafeAddItemProperty(oCWeapon, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + // This adds creature weapon finesse + ApplyUnarmedAttackEffects(oCreature); + + // Destroy the weapon if it was created by this function + if(bCreatedWeapon) + DestroyObject(oCWeapon, (fDuration + 6.0)); + + return oCWeapon; +} + +int GetTargetSpecificChangesToDamage(object oTarget, object oManifester, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE) +{ + // Greater Power Specialization - +2 damage on all HP-damaging powers when target is within 30ft + if(bIsHitPointDamage && + GetHasFeat(FEAT_GREATER_POWER_SPECIALIZATION, oManifester) && + GetDistanceBetween(oTarget, oManifester) <= FeetToMeters(30.0f) + ) + nDamage += 2; + // Intellect Fortress - Halve damage dealt by powers that allow PR. Goes before DR (-like) reductions + if(GetLocalInt(oTarget, "PRC_Power_IntellectFortress_Active") && + Get2DACache("spells", "ItemImmunity", PRCGetSpellId()) == "1" + ) + nDamage /= 2; + // Mental Resistance - 3 damage less for all non-energy damage and ability damage + if(GetHasFeat(FEAT_MENTAL_RESISTANCE, oTarget) && !bIsEnergyDamage) + nDamage -= 3; + + // Reasonable return values only + if(nDamage < 0) nDamage = 0; + + if (GetIsMeldBound(oManifester, MELD_PSYCHIC_FOCUS) == CHAKRA_THROAT && nDamage > 0 && !GetLocalInt(oManifester, "PsychicFocusMeld") && bIsHitPointDamage) + { + SetLocalInt(oManifester, "PsychicFocusMeld", TRUE); + DelayCommand(6.0, DeleteLocalInt(oManifester, "PsychicFocusMeld")); + int nClass = GetMeldShapedClass(oManifester, MELD_PSYCHIC_FOCUS); + int nDC = GetMeldshaperDC(oManifester, nClass, MELD_PSYCHIC_FOCUS); + if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oTarget, 6.0); + } + + return nDamage; +} + +int PracticedManifesting(object oManifester, int iManifestingClass, int iManifestingLevels) +{ + int nFeat; + int iAdjustment = GetHitDice(oManifester) - iManifestingLevels; + iAdjustment = iAdjustment > 4 ? 4 : iAdjustment < 0 ? 0 : iAdjustment; + + switch(iManifestingClass) + { + case CLASS_TYPE_PSION: nFeat = FEAT_PRACTICED_MANIFESTER_PSION; break; + case CLASS_TYPE_PSYWAR: nFeat = FEAT_PRACTICED_MANIFESTER_PSYWAR; break; + case CLASS_TYPE_PSYCHIC_ROGUE: nFeat = FEAT_PRACTICED_MANIFESTER_PSYROG; break; + case CLASS_TYPE_WILDER: nFeat = FEAT_PRACTICED_MANIFESTER_WILDER; break; + case CLASS_TYPE_WARMIND: nFeat = FEAT_PRACTICED_MANIFESTER_WARMIND; break; + case CLASS_TYPE_FIST_OF_ZUOKEN: nFeat = FEAT_PRACTICED_MANIFESTER_FIST_OF_ZUOKEN; break; + default: nFeat = -1; + } + + if(GetHasFeat(nFeat, oManifester)) + return iAdjustment; + + return 0; +} + +int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE) +{ + int nLevel; + int nAdjust = GetLocalInt(oManifester, PRC_CASTERLEVEL_ADJUSTMENT); + nAdjust -= GetLocalInt(oManifester, "WoLManifPenalty"); + + // The function user needs to know the character's manifester level in a specific class + // instead of whatever the character last manifested a power as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + if(GetIsPsionicClass(nSpecificClass)) + { + nLevel = GetLevelByClass(nSpecificClass, oManifester); + // Add levels from +ML PrCs only for the first manifesting class + if(nSpecificClass == GetPrimaryPsionicClass(oManifester)) + nLevel += GetPsionicPRCLevels(oManifester); + + // Psionic vestiges are tucked in here to override things. + // This assumes that there will never be a psion with this spell effect manifesting things + if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AretePsion"); + nMaxPowerLevel = TRUE; + } + if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "TheTriadPsion"); + nMaxPowerLevel = TRUE; + } + if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AbysmPsion"); + nMaxPowerLevel = TRUE; + } + + // This is for learning powers, we need to ignore some adjustments + if (nMaxPowerLevel) return nLevel; + + nLevel += PracticedManifesting(oManifester, nSpecificClass, nLevel); //gotta be the last one + + return nLevel + nAdjust; + } + // A character's manifester level gained from non-manifesting classes is always a nice, round zero + else + return 0; + } + + // Item Spells + if(GetItemPossessor(GetSpellCastItem()) == oManifester) + { + if(DEBUG) SendMessageToPC(oManifester, "Item casting at level " + IntToString(GetCasterLevel(oManifester))); + + return GetCasterLevel(oManifester) + nAdjust; + } + + // For when you want to assign the caster level. + else if(GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE) != 0) + { + if(DEBUG) SendMessageToPC(oManifester, "Forced-level manifesting at level " + IntToString(GetCasterLevel(oManifester))); + + DelayCommand(1.0, DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE)); + nLevel = GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE); + } + else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID) + { + //Gets the manifesting class + int nManifestingClass = GetManifestingClass(oManifester); +// if(DEBUG) DoDebug("Manifesting class: " + IntToString(nManifestingClass), oManifester); + nLevel = GetLevelByClass(nManifestingClass, oManifester); + // Add levels from +ML PrCs only for the first manifesting class + nLevel += nManifestingClass == GetPrimaryPsionicClass(oManifester) ? GetPsionicPRCLevels(oManifester) : 0; + + // Psionic vestiges are tucked in here to override things. + // This assumes that there will never be a psion with this spell effect manifesting things + if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AretePsion"); + nMaxPowerLevel = TRUE; + } + if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "TheTriadPsion"); + nMaxPowerLevel = TRUE; + } + if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AbysmPsion"); + nMaxPowerLevel = TRUE; + } + + // This is for learning powers, we need to ignore some adjustments + if (nMaxPowerLevel) return nLevel; + + nLevel += PracticedManifesting(oManifester, nManifestingClass, nLevel); //gotta be the last one +// if(DEBUG) DoDebug("Level gotten via GetLevelByClass: " + IntToString(nLevel), oManifester); + } + + // If you have a primary psionic class and no manifester level yet, get levels based on that + if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) + { + int nClass = GetPrimaryPsionicClass(oManifester); + nLevel = GetLevelByClass(nClass, oManifester); + nLevel += GetPsionicPRCLevels(oManifester); + nLevel += PracticedManifesting(oManifester, nClass, nLevel); //gotta be the last one + } + + // If everything else fails, you are not a manifester + if(nLevel == 0) + { + if(DEBUG) DoDebug("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + //else WriteTimestampedLogEntry("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + + return 0; + } + + + // The bonuses inside only apply to normal manifestation + if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) + { + //Adding wild surge + int nSurge = GetWildSurge(oManifester); + if (nSurge > 0) nLevel += nSurge; + + // Adding overchannel + int nOverchannel = GetLocalInt(oManifester, PRC_OVERCHANNEL); + if(nOverchannel > 0) nLevel += nOverchannel; + + // Adding Soul Manifester + nLevel += Soulcaster(oManifester, PRCGetSpellId()); + } + + nLevel += nAdjust; + + // This spam is technically no longer necessary once the manifester level getting mechanism has been confirmed to work +// if(DEBUG) FloatingTextStringOnCreature("Manifester Level: " + IntToString(nLevel), oManifester, FALSE); + + return nLevel; +} + +int GetHighestManifesterLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetManifesterLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestManifesterLevel(object oCreature) +{ + return max(max(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetPowerLevel(object oManifester) +{ + return GetLocalInt(oManifester, PRC_POWER_LEVEL); +} + +int GetAbilityScoreOfClass(object oManifester, int nClass) +{ + return GetAbilityScore(oManifester, GetAbilityOfClass(nClass)); +} + +int GetManifestingClass(object oManifester = OBJECT_SELF) +{ + return GetLocalInt(oManifester, PRC_MANIFESTING_CLASS) - 1; +} + +int GetAbilityOfClass(int nClass) +{ + switch(nClass) + { + case CLASS_TYPE_DIAMOND_DRAGON: + case CLASS_TYPE_PSION: + case CLASS_TYPE_PSYCHIC_ROGUE: + return ABILITY_INTELLIGENCE; + case CLASS_TYPE_PSYWAR: + case CLASS_TYPE_FIST_OF_ZUOKEN: + case CLASS_TYPE_WARMIND: + return ABILITY_WISDOM; + case CLASS_TYPE_WILDER: + return ABILITY_CHARISMA; + } + + // If there's no class, it's racial. Use Charisma + return ABILITY_CHARISMA; +} + +int GetPrimaryPsionicClass(object oCreature = OBJECT_SELF) +{ + int nClass; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int iPsionicPos = GetFirstPsionicClassPosition(oCreature); + if (!iPsionicPos) return CLASS_TYPE_INVALID; // no Psionic casting class + + nClass = GetClassByPosition(iPsionicPos, oCreature); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsPsionicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsPsionicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsPsionicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsPsionicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsPsionicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsPsionicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsPsionicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsPsionicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetPsionicPRCLevels(object oCreature) +{ + int nLevel = 0; + + // Cerebremancer and Psychic Theurge add manifester levels on each level + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + if (GetFirstPsionicClassPosition(oCreature)) nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + + // No manifester level boost at level 1 and 10 for Thrallherd + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) >= 10) nLevel -= 1; + } + // No manifester level boost at levels 2, 5 and 8 for Shadow Mind + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + // No manifester level boost at level 1 and 6 for Iron Mind + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + // No manifester level boost at level 1 and 6 for Diamond Dragon + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + // No manifester level boost at level 1 for Sanctified Mind + if(GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + return nLevel; +} + +int GetFirstPsionicClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsPsionicClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsPsionicClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsPsionicClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsPsionicClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsPsionicClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsPsionicClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsPsionicClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsPsionicClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int GetIsPsionicClass(int nClass) +{ + return (nClass==CLASS_TYPE_PSION + || nClass==CLASS_TYPE_PSYWAR + || nClass==CLASS_TYPE_PSYCHIC_ROGUE + || nClass==CLASS_TYPE_WILDER + || nClass==CLASS_TYPE_FIST_OF_ZUOKEN + || nClass==CLASS_TYPE_WARMIND + ); +} + +int GetWildSurge(object oManifester) +{ + int nWildSurge = GetLocalInt(oManifester, PRC_IS_PSILIKE) ? + 0 : // Wild Surge does not apply to psi-like abilities + GetLocalInt(oManifester, PRC_WILD_SURGE); + + if(DEBUG) DoDebug("GetWildSurge():\n" + + "oManifester = " + DebugObject2Str(oManifester) + "\n" + + "nWildSurge = " + IntToString(nWildSurge) + "\n" + ); + + return nWildSurge; +} + +int GetMaxPowerLevel(object oManifester) +{ + int nClass = GetPrimaryPsionicClass(oManifester); + string sFile = GetAMSKnownFileName(nClass); + int nLevel = GetHighestManifesterLevel(oManifester); + int nMax = StringToInt(Get2DACache(sFile, "MaxPowerLevel", nLevel)); + if (DEBUG) DoDebug("GetMaxPowerLevel is "+IntToString(nMax)); + return nMax; +} \ No newline at end of file diff --git a/trunk/include/shd_inc_shdfunc.nss b/trunk/include/shd_inc_shdfunc.nss new file mode 100644 index 00000000..ce4d0590 --- /dev/null +++ b/trunk/include/shd_inc_shdfunc.nss @@ -0,0 +1,669 @@ +//:://///////////////////////////////////////////// +//:: Shadowcasting main include: Miscellaneous +//:: shd_inc_shdfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Shadowcasting. + + Also acts as inclusion nexus for the general + shadowcasting includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2019.02.08 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + +//:: Test Void +// void main (){} + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's mystery list the currently being shadowcast + * mystery is shadowcast from. + * + * @param oShadow A creature shadowcasting a mystery at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetShadowcastingClass(object oShadow = OBJECT_SELF); + +/** + * Determines the given creature's Shadowcaster level. If a class is specified, + * then returns the Shadowcaster level for that class. Otherwise, returns + * the Shadowcaster level for the currently active mystery. + * + * @param oShadow The creature whose Shadowcaster level to determine + * @param nSpecificClass The class to determine the creature's Shadowcaster + * level in. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * Shadowcaster level in regards to an ongoing mystery + * is determined instead. + * @return The Shadowcaster level + */ +int GetShadowcasterLevel(object oShadow = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID); + +/** + * Determines whether a given creature uses ShadowMagic. + * Requires either levels in a ShadowMagic-related class or + * natural ShadowMagic ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use ShadowMagics, FALSE otherwise. + */ +int GetIsShadowMagicUser(object oCreature); + +/** + * Determines the given creature's highest unmodified Shadowcaster level among its + * shadowcasting classes. + * + * @param oCreature Creature whose highest Shadowcaster level to determine + * @return The highest unmodified Shadowcaster level the creature can have + */ +int GetHighestShadowcasterLevel(object oCreature); + +/** + * Determines whether a given class is a ShadowMagic-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a ShadowMagic-related class, FALSE otherwise + */ +int GetIsShadowMagicClass(int nClass); + +/** + * Gets the level of the mystery being currently shadowcast or the level + * of the mystery ID passed to it. + * + * @param oShadow The creature currently shadowcasting a mystery + * @return The level of the mystery being shadowcast + */ +int GetMysteryLevel(object oShadow, int nMystId = 0); + +/** + * Returns the name of the Path + * + * @param nPath PATH_* to name + */ +string GetPathName(int nPath); + +/** + * Returns the Path the mystery is in + * @param nMystId Mystery to check + * + * @return PATH_* + */ +int GetPathByMystery(int nMystId); + +/** + * Returns true or false if the character has Path + * focus in the chosen path + * @param oShadow Person to check + * @param nPath Path to check + * + * @return TRUE or FALSE + */ +int GetHasPathFocus(object oShadow, int nPath); + +/** + * Calculates how many shadowcaster levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added shadowcaster levels for + * @return The number of shadowcaster levels gained + */ +int GetShadowMagicPRCLevels(object oShadow); + +/** + * Determines which of the character's classes is their highest or first + * shadowcasting class, if any. This is the one which gains shadowcaster + * level raise benefits from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first shadowcasting class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryShadowMagicClass(object oCreature = OBJECT_SELF); + +/** + * Determines the position of a creature's first shadowcasting class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first shadowcasting class {1, 2, 3} or 0 if + * the creature possesses no levels in shadowcasting classes. + */ +int GetFirstShadowMagicClassPosition(object oCreature = OBJECT_SELF); + +/** + * Returns ability score needed to Shadowcast + * Type 1 is score to cast, Type 2 is score for DC + * + * @param nClass The class to check + * @return ABILITY_* + */ +int GetShadowAbilityOfClass(int nClass, int nType); + +/** + * Calculates the DC of the Mystery being currently shadowcast. + * + * WARNING: Return value is not defined when a mystery isn't being shadowcast. + * + */ +int GetShadowcasterDC(object oShadow = OBJECT_SELF); + +/** + * Calculates the SpellPen of the Mystery being currently shadowcast. + * Whether a Mystery is supernatural or not is checked in EvaluateMystery + * + * Currently just a placeholder returning GetShadowcasterLevel + */ +int ShadowSRPen(object oShadow, int nShadowcasterLevel); + +/** + * Stores a mystery structure as a set of local variables. If + * a structure was already stored with the same name on the same object, + * it is overwritten. + * + * @param oObject The object on which to store the structure + * @param sName The name under which to store the structure + * @param myst The mystery structure to store + */ +void SetLocalMystery(object oObject, string sName, struct mystery myst); + +/** + * Retrieves a previously stored mystery structure. If no structure is stored + * by the given name, the structure returned is empty. + * + * @param oObject The object from which to retrieve the structure + * @param sName The name under which the structure is stored + * @return The structure built from local variables stored on oObject under sName + */ +struct mystery GetLocalMystery(object oObject, string sName); + +/** + * Returns the boost to caster level from feats + * + * @param oShadow The caster + * @param nMyst The mystery being cast + * @return Total bonus to caster level + */ +int ShadowcastingFeats(object oShadow, int nMyst); + +/** + * Returns the boost to DC from nocturnal caster + * + * @param oShadow The caster + * @param nPath The path to check for + * @return Total bonus to caster level + */ +int GetHasNocturnal(object oShadow, int nPath); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_alterations" +#include "shd_inc_myst" +#include "shd_inc_mystknwn" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetShadowcastingClass(object oShadow = OBJECT_SELF) +{ + int nReturn = GetLocalInt(oShadow, PRC_SHADOWCASTING_CLASS) - 1; + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcastingClass: GetShadowcastingClass value is "+IntToString(nReturn), oShadow); + return nReturn; +} + +int GetShadowcasterLevel(object oShadow = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID) +{ + int nAdjust = GetLocalInt(oShadow, PRC_CASTERLEVEL_ADJUSTMENT); + int nLevel = GetLocalInt(oShadow, PRC_CASTERLEVEL_OVERRIDE); + int nMyst = PRCGetSpellId(); // The fact that this will return 0 sometimes is relied upon + if (GetIsFundamental(nMyst)) nSpecificClass = CLASS_TYPE_SHADOWCASTER; + + // For when you want to assign the caster level. + if(nLevel) + { + if(DEBUG) SendMessageToPC(oShadow, "GetShadowcasterLevel(): Forced-level shadowcasting at level " + IntToString(nLevel)); + //DelayCommand(1.0, DeleteLocalInt(oShadow, PRC_CASTERLEVEL_OVERRIDE)); + return nLevel + nAdjust; + } + + if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: "+GetName(oShadow)+" is a "+IntToString(nSpecificClass), oShadow); + // The function user needs to know the character's Shadowcaster level in a specific class + // instead of whatever the character last shadowcast a mystery as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: Class is Valid", oShadow); + if(GetIsShadowMagicClass(nSpecificClass)) + { + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: Class is Shadow Magic Class", oShadow); + // Shadowcaster level is class level + prestige + nLevel = GetLevelByClass(nSpecificClass, oShadow); + if(nLevel) + { + nLevel += GetShadowMagicPRCLevels(oShadow); + nLevel += ShadowcastingFeats(oShadow, nMyst); + if (GetLocalInt(oShadow, "CaptureMagic")) + { + nLevel += GetLocalInt(oShadow, "CaptureMagic"); + DeleteLocalInt(oShadow, "CaptureMagic"); + } + if (GetLocalInt(oShadow, "EldritchDisrupt")) + nLevel -= 4; + if (GetLocalInt(oShadow, "EldritchVortex")) + nLevel -= 4; + } + } + } + else if(GetShadowcastingClass(oShadow) != -1) + { + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass", oShadow); + nLevel = GetLevelByClass(GetShadowcastingClass(oShadow), oShadow); + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass level "+IntToString(nLevel), oShadow); + nLevel += GetShadowMagicPRCLevels(oShadow); + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass prestige level "+IntToString(nLevel), oShadow); + nLevel += ShadowcastingFeats(oShadow, nMyst); + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass feat level "+IntToString(nLevel), oShadow); + if (GetLocalInt(oShadow, "CaptureMagic")) + { + nLevel += GetLocalInt(oShadow, "CaptureMagic"); + DeleteLocalInt(oShadow, "CaptureMagic"); + } + if (GetLocalInt(oShadow, "EldritchDisrupt")) + nLevel -= 4; + if (GetLocalInt(oShadow, "EldritchVortex")) + nLevel -= 4; + } + + if(DEBUG) FloatingTextStringOnCreature("Shadowcaster Level: " + IntToString(nLevel), oShadow, FALSE); + + return nLevel + nAdjust; +} + +int GetIsShadowMagicUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_SHADOWCASTER, oCreature) + || GetLevelByClass(CLASS_TYPE_SHADOWSMITH, oCreature)); +} + +int GetHighestShadowcasterLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetShadowcasterLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestShadowcasterLevel(object oCreature) +{ + return max(max(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetShadowcasterLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetShadowcasterLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetShadowcasterLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsShadowMagicClass(int nClass) +{ + return nClass == CLASS_TYPE_SHADOWCASTER + || nClass == CLASS_TYPE_SHADOWSMITH; +} + +int GetMysteryLevel(object oShadow, int nMystId = 0) +{ + if (nMystId > 0) return StringToInt(lookup_spell_innate(nMystId)); + int nLevel = GetLocalInt(oShadow, PRC_MYSTERY_LEVEL); + if (nLevel > 0) return nLevel; + + return 0; +} + +string GetPathName(int nPath) +{ + int nStrRef; + switch(nPath) + { + /* case PATH_DESERT_WIND: nStrRef = 16829714; break; + case PATH_DEVOTED_SPIRIT: nStrRef = 16829715; break; + case PATH_DIAMOND_MIND: nStrRef = 16829716; break; + case PATH_IRON_HEART: nStrRef = 16829717; break; + case PATH_SETTING_SUN: nStrRef = 16829718; break; + case PATH_SHADOW_HAND: nStrRef = 16829719; break; + case PATH_STONE_DRAGON: nStrRef = 16829720; break; + case PATH_TIGER_CLAW: nStrRef = 16829721; break; + case PATH_WHITE_RAVEN: nStrRef = 16829722; break;*/ + } + return GetStringByStrRef(nStrRef); +} + +int GetPathByMystery(int nMystId) +{ + // Shadowcaster has every mystery ever, so this is just the easy way out. + int i = GetPowerfileIndexFromRealSpellID(nMystId); + string sClass = GetAMSDefinitionFileName(CLASS_TYPE_SHADOWCASTER); + int nReturn = StringToInt(Get2DACache(sClass, "Path", i)); + /*if (DEBUG) DoDebug("GetPathByMystery() i "+IntToString(i)); + if (DEBUG) DoDebug("GetPathByMystery() sClass "+sClass); + if (DEBUG) DoDebug("GetPathByMystery() nReturn "+IntToString(nReturn)); */ + + return nReturn; +} + +int GetShadowMagicPRCLevels(object oShadow) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oShadow); + + // These two don't add at 1st level + if (GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oShadow) - 1; + if (GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oShadow) - 1; + + return nLevel; +} + +int GetPrimaryShadowMagicClass(object oCreature = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nShadowMagicPos = GetFirstShadowMagicClassPosition(oCreature); + if (!nShadowMagicPos) return CLASS_TYPE_INVALID; // no Blade Magic shadowcasting class + + nClass = GetClassByPosition(nShadowMagicPos, oCreature); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsShadowMagicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsShadowMagicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsShadowMagicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsShadowMagicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsShadowMagicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsShadowMagicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsShadowMagicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsShadowMagicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstShadowMagicClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsShadowMagicClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsShadowMagicClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsShadowMagicClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsShadowMagicClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsShadowMagicClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsShadowMagicClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsShadowMagicClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsShadowMagicClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int GetHasPathFocus(object oShadow, int nPath) +{ + //if (DEBUG) DoDebug("GetHasPathFocus() nPath "+IntToString(nPath)); + int nFocus, nGRFocus, nReturn; + switch(nPath) + { + case PATH_CLOAK_SHADOWS: nFocus = FEAT_PATH_FOCUS_CLOAK_SHADOWS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_CLOAK_SHADOWS ; break; + case PATH_DARK_TERRAIN: nFocus = FEAT_PATH_FOCUS_DARK_TERRAIN ; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARK_TERRAIN ; break; + case PATH_EBON_WHISPERS: nFocus = FEAT_PATH_FOCUS_EBON_WHISPERS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EBON_WHISPERS ; break; + case PATH_EYES_DARKNESS: nFocus = FEAT_PATH_FOCUS_EYES_DARKNESS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EYES_DARKNESS ; break; + case PATH_SHUTTERS_CLOUDS: nFocus = FEAT_PATH_FOCUS_SHUTTERS_CLOUDS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_SHUTTERS_CLOUDS ; break; + case PATH_TOUCH_TWILIGHT: nFocus = FEAT_PATH_FOCUS_TOUCH_TWILIGHT ; nGRFocus = FEAT_GREATER_PATH_FOCUS_TOUCH_TWILIGHT ; break; + case PATH_UMBRAL_MIND: nFocus = FEAT_PATH_FOCUS_UMBRAL_MIND ; nGRFocus = FEAT_GREATER_PATH_FOCUS_UMBRAL_MIND ; break; + case PATH_BLACK_MAGIC: nFocus = FEAT_PATH_FOCUS_BLACK_MAGIC ; nGRFocus = FEAT_GREATER_PATH_FOCUS_BLACK_MAGIC ; break; + case PATH_BODY_SOUL: nFocus = FEAT_PATH_FOCUS_BODY_SOUL ; nGRFocus = FEAT_GREATER_PATH_FOCUS_BODY_SOUL ; break; + case PATH_DARK_REFLECTIONS: nFocus = FEAT_PATH_FOCUS_DARK_REFLECTIONS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARK_REFLECTIONS ; break; + case PATH_EBON_ROADS: nFocus = FEAT_PATH_FOCUS_EBON_ROADS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EBON_ROADS ; break; + case PATH_ELEMENTAL_SHADOWS: nFocus = FEAT_PATH_FOCUS_ELEMENTAL_SHADOWS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_ELEMENTAL_SHADOWS ; break; + case PATH_UNBINDING_SHADE: nFocus = FEAT_PATH_FOCUS_UNBINDING_SHADE ; nGRFocus = FEAT_GREATER_PATH_FOCUS_UNBINDING_SHADE ; break; + case PATH_VEIL_SHADOWS: nFocus = FEAT_PATH_FOCUS_VEIL_SHADOWS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_VEIL_SHADOWS ; break; + case PATH_BREATH_TWILIGHT: nFocus = FEAT_PATH_FOCUS_BREATH_TWILIGHT ; nGRFocus = FEAT_GREATER_PATH_FOCUS_BREATH_TWILIGHT ; break; + case PATH_DARK_METAMORPHOSIS: nFocus = FEAT_PATH_FOCUS_DARK_METAMORPHOSIS; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARK_METAMORPHOSIS; break; + case PATH_EBON_WALLS: nFocus = FEAT_PATH_FOCUS_EBON_WALLS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EBON_WALLS ; break; + case PATH_EYES_NIGHT_SKY: nFocus = FEAT_PATH_FOCUS_EYES_NIGHT_SKY ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EYES_NIGHT_SKY ; break; + case PATH_HEART_SOUL: nFocus = FEAT_PATH_FOCUS_HEART_SOUL ; nGRFocus = FEAT_GREATER_PATH_FOCUS_HEART_SOUL ; break; + case PATH_SHADOW_CALLING: nFocus = FEAT_PATH_FOCUS_SHADOW_CALLING ; nGRFocus = FEAT_GREATER_PATH_FOCUS_SHADOW_CALLING ; break; + case PATH_NIGHTS_LONG_FINGERS: nFocus = FEAT_PATH_FOCUS_NIGHTS_LONG_FINGERS; nGRFocus = FEAT_GREATER_PATH_FOCUS_NIGHTS_LONG_FINGERS; break; + case PATH_DARKENED_ALLEYS: nFocus = FEAT_PATH_FOCUS_DARKENED_ALLEYS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARKENED_ALLEYS ; break; + case PATH_SHADOWSCAPE: nFocus = FEAT_PATH_FOCUS_SHADOWSCAPE ; nGRFocus = FEAT_GREATER_PATH_FOCUS_SHADOWSCAPE ; break; + } + if(GetHasFeat(nFocus, oShadow)) + nReturn = 1; + if(GetHasFeat(nGRFocus, oShadow)) + nReturn = 2; + + //if (DEBUG) DoDebug("GetHasPathFocus() nReturn "+IntToString(nReturn)); + + // If none of those trigger. + return nReturn; +} + +int GetShadowAbilityOfClass(int nClass, int nType) +{ + if (nClass == CLASS_TYPE_SHADOWSMITH) return ABILITY_INTELLIGENCE; + // Intelligence for max mystery known + if (nClass == CLASS_TYPE_SHADOWCASTER && nType == 1) return ABILITY_INTELLIGENCE; + // Charisma for DC + if (nClass == CLASS_TYPE_SHADOWCASTER && nType == 2) return ABILITY_CHARISMA; + + // Technically, never gets here but the compiler does not realise that + return -1; +} + +int GetShadowcasterDC(object oShadow = OBJECT_SELF) +{ + // Things we need for DC Checks + int nMystId = PRCGetSpellId(); + int nShadEvo = GetLocalInt(oShadow, "ShadowEvoking"); + if (nShadEvo > 0) + nMystId = nShadEvo; // This is used to get the proper DC for Shadow Evocation mysteries + + int nLevel = GetMysteryLevel(oShadow, nMystId); + int nClass = GetShadowcastingClass(oShadow); + int nShadow = GetShadowcasterLevel(oShadow); + int nAbi = GetAbilityModifier(GetShadowAbilityOfClass(nClass, 2), oShadow); + int nPath = GetPathByMystery(nMystId); + int nPFocus = GetHasPathFocus(oShadow, nPath); + int nNoct = GetHasNocturnal(oShadow, nPath); + nShadow -= nPFocus; // These don't count here + + // DC is 10 + Mystery level + ability + int nDC = 10 + nLevel + nAbi; + + // If total Shadowcaster level is >= 13, change the DC for level 3 and under mysteries + // DC is 10 + 1/2 Shadowcaster level + ability + if (GetIsMysterySupernatural(oShadow, nMystId, nClass)) + nDC = 10 + nShadow/2 + nAbi; + + nDC += nPFocus; + nDC += nNoct;// It's a 0 if it doesn't exist + + return nDC; +} + +int ShadowSRPen(object oShadow, int nShadowcasterLevel) +{ + return nShadowcasterLevel; +} + +void SetLocalMystery(object oObject, string sName, struct mystery myst) +{ + //SetLocal (oObject, sName + "_", ); + SetLocalObject(oObject, sName + "_oShadow", myst.oShadow); + + SetLocalInt(oObject, sName + "_bCanMyst", myst.bCanMyst); + SetLocalInt(oObject, sName + "_nShadowcasterLevel", myst.nShadowcasterLevel); + SetLocalInt(oObject, sName + "_nMystId", myst.nMystId); + SetLocalInt(oObject, sName + "_nPen", myst.nPen); + SetLocalInt(oObject, sName + "_bIgnoreSR", myst.bIgnoreSR); + + SetLocalInt(oObject, sName + "_bEmpower", myst.bEmpower); + SetLocalInt(oObject, sName + "_bExtend", myst.bExtend); + SetLocalInt(oObject, sName + "_bMaximize", myst.bMaximize); + SetLocalInt(oObject, sName + "_bQuicken", myst.bQuicken); + + SetLocalInt(oObject, sName + "_nSaveDC", myst.nSaveDC); + SetLocalFloat(oObject, sName + "_fDur", myst.fDur); +} + +struct mystery GetLocalMystery(object oObject, string sName) +{ + struct mystery myst; + myst.oShadow = GetLocalObject(oObject, sName + "_oShadow"); + + myst.bCanMyst = GetLocalInt(oObject, sName + "_bCanMyst"); + myst.nShadowcasterLevel = GetLocalInt(oObject, sName + "_nShadowcasterLevel"); + myst.nMystId = GetLocalInt(oObject, sName + "_nMystId"); + myst.nPen = GetLocalInt(oObject, sName + "_nPen"); + myst.bIgnoreSR = GetLocalInt(oObject, sName + "_bIgnoreSR"); + + myst.bEmpower = GetLocalInt(oObject, sName + "_bEmpower"); + myst.bExtend = GetLocalInt(oObject, sName + "_bExtend"); + myst.bMaximize = GetLocalInt(oObject, sName + "_bMaximize"); + myst.bQuicken = GetLocalInt(oObject, sName + "_bQuicken"); + + myst.nSaveDC = GetLocalInt(oObject, sName + "_nSaveDC"); + myst.fDur = GetLocalFloat(oObject, sName + "_fDur"); + + return myst; +} + +int ShadowcastingFeats(object oShadow, int nMyst) +{ + int nReturn = 0; + int nPath = GetPathByMystery(nMyst); + nReturn += GetHasPathFocus(oShadow, nPath); + + return nReturn; +} + +int GetHasNocturnal(object oShadow, int nPath) +{ + int nNocturnal, nReturn; + switch(nPath) + { + case PATH_CLOAK_SHADOWS: nNocturnal = FEAT_NOCTURNAL_CASTER_CLOAK_SHADOWS ; break; + case PATH_DARK_TERRAIN: nNocturnal = FEAT_NOCTURNAL_CASTER_DARK_TERRAIN ; break; + case PATH_EBON_WHISPERS: nNocturnal = FEAT_NOCTURNAL_CASTER_EBON_WHISPERS ; break; + case PATH_EYES_DARKNESS: nNocturnal = FEAT_NOCTURNAL_CASTER_EYES_DARKNESS ; break; + case PATH_SHUTTERS_CLOUDS: nNocturnal = FEAT_NOCTURNAL_CASTER_SHUTTERS_CLOUDS ; break; + case PATH_TOUCH_TWILIGHT: nNocturnal = FEAT_NOCTURNAL_CASTER_TOUCH_TWILIGHT ; break; + case PATH_UMBRAL_MIND: nNocturnal = FEAT_NOCTURNAL_CASTER_UMBRAL_MIND ; break; + case PATH_BLACK_MAGIC: nNocturnal = FEAT_NOCTURNAL_CASTER_BLACK_MAGIC ; break; + case PATH_BODY_SOUL: nNocturnal = FEAT_NOCTURNAL_CASTER_BODY_SOUL ; break; + case PATH_DARK_REFLECTIONS: nNocturnal = FEAT_NOCTURNAL_CASTER_DARK_REFLECTIONS ; break; + case PATH_EBON_ROADS: nNocturnal = FEAT_NOCTURNAL_CASTER_EBON_ROADS ; break; + case PATH_ELEMENTAL_SHADOWS: nNocturnal = FEAT_NOCTURNAL_CASTER_ELEMENTAL_SHADOWS ; break; + case PATH_UNBINDING_SHADE: nNocturnal = FEAT_NOCTURNAL_CASTER_UNBINDING_SHADE ; break; + case PATH_VEIL_SHADOWS: nNocturnal = FEAT_NOCTURNAL_CASTER_VEIL_SHADOWS ; break; + case PATH_BREATH_TWILIGHT: nNocturnal = FEAT_NOCTURNAL_CASTER_BREATH_TWILIGHT ; break; + case PATH_DARK_METAMORPHOSIS: nNocturnal = FEAT_NOCTURNAL_CASTER_DARK_METAMORPHOSIS; break; + case PATH_EBON_WALLS: nNocturnal = FEAT_NOCTURNAL_CASTER_EBON_WALLS ; break; + case PATH_EYES_NIGHT_SKY: nNocturnal = FEAT_NOCTURNAL_CASTER_EYES_NIGHT_SKY ; break; + case PATH_HEART_SOUL: nNocturnal = FEAT_NOCTURNAL_CASTER_HEART_SOUL ; break; + case PATH_SHADOW_CALLING: nNocturnal = FEAT_NOCTURNAL_CASTER_SHADOW_CALLING ; break; + case PATH_NIGHTS_LONG_FINGERS:nNocturnal = FEAT_NOCTURNAL_CASTER_NIGHTS_LONG_FINGERS; break; + case PATH_DARKENED_ALLEYS: nNocturnal = FEAT_NOCTURNAL_CASTER_DARKENED_ALLEYS ; break; + case PATH_SHADOWSCAPE: nNocturnal = FEAT_NOCTURNAL_CASTER_SHADOWSCAPE ; break; + } + if(GetHasFeat(nNocturnal, oShadow) && GetIsNight()) + nReturn = 1; + + // If none of those trigger. + return nReturn; +} \ No newline at end of file diff --git a/trunk/include/tob_inc_recovery.nss b/trunk/include/tob_inc_recovery.nss new file mode 100644 index 00000000..9517730f --- /dev/null +++ b/trunk/include/tob_inc_recovery.nss @@ -0,0 +1,652 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Maneuver Recovery +//:: tob_inc_martlore +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to recovery and readying maneuvers + See page #28 of Tome of Battle + + Functions below are called by the initiator as + he makes a maneuver, or when recovering or readying + + @author Stratovarius + @date Created - 2007.3.25 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int MANEUVER_READIED = 1; +const int MANEUVER_RECOVERED = 2; +const int MANEUVER_GRANTED = 3; +const int MANEVUER_WITHHELD = 4; + +const string _MANEUVER_LIST_RDYMODIFIER = "_ReadyModifier"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gets the number of Maneuvers a character has readied + * + * @param oPC The creature whose Maneuvers to check + * @param nList The list to check. One of MANEUVER_LIST_* + * @return The number of Maneuvers readied + */ +int GetReadiedCount(object oPC, int nList); + +/** + * Gets the maximum number of Maneuvers a character may ready. + * + * @param oPC Character to determine maximum Maneuvers readied + * @param nList MANEUVER_LIST_* of the list to determine maximum Maneuvers for + * @return Maximum number of Maneuvers that oPC may ready + */ +int GetMaxReadiedCount(object oPC, int nList); + +/** + * Gets the value of the Maneuvers readied modifier, which is a value that is added + * to the 2da-specified maximum Maneuvers readied to determine the actual maximum. + * + * @param oCreature The creature whose modifier to get + * @param nList The list the maximum Maneuvers readied from which the modifier + * modifies. One of MANEUVER_LIST_* + */ +int GetReadiedManeuversModifier(object oCreature, int nList); + +/** + * Sets the value of the Maneuvers readied modifier, which is a value that is added + * to the 2da-specified maximum Maneuvers readied to determine the actual maximum. + * + * @param oCreature The creature whose modifier to set + * @param nList The list the maximum Maneuvers readied from which the modifier + * modifies. One of MANEUVER_LIST_* + */ +void SetReadiedManeuversModifier(object oCreature, int nList, int nNewValue); + +/** + * Readies the chosen Maneuver. Also checks to see if there are any slots left + * + * @param oPC Character readying maneuver + * @param nList MANEUVER_LIST_* of the list to ready + * @param nMoveId Maneuver to ready + */ +void ReadyManeuver(object oPC, int nList, int nMoveId); + +/** + * Returns whether maneuver is readied or not + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to check + * @return TRUE or FALSE + */ +int GetIsManeuverReadied(object oPC, int nList, int nMoveId); + +/** + * Returns whether maneuver is expended or not + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to check + * @return TRUE or FALSE + */ +int GetIsManeuverExpended(object oPC, int nList, int nMoveId); + +/** + * Expends the chosen Maneuver. + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to expend + */ +void ExpendManeuver(object oPC, int nList, int nMoveId); + +/** + * Clears all local ints marking maneuvers as expended + * + * @param oPC Character to clear + * @param nList MANEUVER_LIST_* + */ +void RecoverExpendedManeuvers(object oPC, int nList); + +/** + * Recovers the chosen Maneuver. + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to recover + */ +void RecoverManeuver(object oPC, int nList, int nMoveId); + +/** + * Checks to see if the PC is in a Warblade recovery round + * This prevents all use of maneuvers or stances during that round. + * + * @param oPC Character to clear + * @return TRUE or FALSE + */ +int GetIsWarbladeRecoveryRound(object oPC); + +/** + * Marks maneuvers as granted or withheld. + * + * @param oPC Character to grant maneuvers to + * @param nList MANEUVER_LIST_* + */ +void GrantManeuvers(object oPC, int nList); + +/** + * Clears all local ints marking maneuvers as readied + * + * @param oPC Character to clear + * @param nList MANEUVER_LIST_* + */ +void ClearReadiedManeuvers(object oPC, int nList); + +/** + * Grants a withheld maneuver + * Only works on Crusaders + * + * @param oPC Character to grant maneuvers to + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to grant + */ +void GrantWithheldManeuver(object oPC, int nList, int nMoveId = -1); + +/** + * Returns whether maneuver is granted or not + * Only works on Crusaders + * + * @param oPC Character to check + * @param nMoveId Maneuver to check + * @return TRUE or FALSE + */ +int GetIsManeuverGranted(object oPC, int nMoveId); + +/** + * Clears all local ints marking maneuvers as granted or withheld + * Only works on Crusaders + * + * @param oPC Character to clear + */ +void ClearGrantedWithheldManeuvers(object oPC); + +/** + * Starting function for Crusader recovery, calls DoCrusaderGranting + * Only works on Crusaders + * + * @param oPC Crusader + */ +void BeginCrusaderGranting(object oPC); + +/** + * Recursive function granting maneuvers each round in combat + * Will end when combat ends + * Only works on Crusaders + * + * @param oPC Crusader + * @param nTrip Round of combat. Takes values from 1 to 5, always starts with 1. + */ +void DoCrusaderGranting(object oPC, int nTrip); + + +/** + * Returns TRUE if a maneuver was expended, FALSE otherwise + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nDiscipline DISCIPLINE_* the maneuver has to be from + * + * @return TRUE or FALSE + */ +int ExpendRandomManeuver(object oPC, int nList, int nDiscipline = -1); + +/** + * Clears all local ints marking maneuvers as expended + * + * @param oPC Character to clear + * @param nPRC Specific PRC to recover, else all. + */ +void RecoverPrCAbilities(object oPC); + +/** + * Heals 3 + 1 point per character level ones per minute + * + * @param oPC Character to heal + */ +void VitalRecovery(object oPC); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_lookups" +#include "tob_inc_tobfunc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetReadiedCount(object oPC, int nList) +{ + return GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList)); +} + +int GetMaxReadiedCount(object oPC, int nList) +{ + int nLevel = GetLevelByClass(nList, oPC); + // 2das start at Row 0 + int nMaxReadied = StringToInt(Get2DACache(GetAMSKnownFileName(nList), "ManeuversReadied", nLevel-1)); + // Add in the custom modifier + nMaxReadied += GetReadiedManeuversModifier(oPC, nList); + if(nList == MANEUVER_LIST_SWORDSAGE) + nMaxReadied += GetHasFeat(FEAT_EXTRA_GRANTED_MANEUVER, oPC); + + if(DEBUG) DoDebug("tob_inc_recovery: MaxManeuvers Readied: " +IntToString(nMaxReadied)); + return nMaxReadied; +} + +int GetReadiedManeuversModifier(object oCreature, int nList) +{ + return GetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + _MANEUVER_LIST_RDYMODIFIER); +} + +void SetReadiedManeuversModifier(object oCreature, int nList, int nNewValue) +{ + SetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + _MANEUVER_LIST_RDYMODIFIER, nNewValue); +} + +void ReadyManeuver(object oPC, int nList, int nMoveId) +{ + int nCount = GetReadiedCount(oPC, nList); + int nMaxCount = GetMaxReadiedCount(oPC, nList); + + // If the PC can ready a maneuver and hasn't filled them all up + if(nMaxCount > nCount) + { + nCount++; + SetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(nCount), nMoveId); + SetLocalInt(oPC, "ManeuverReadied" + IntToString(nList), nCount); + if(DEBUG) DoDebug("tob_inc_recovery: ReadyManeuver: " +IntToString(nMoveId)); + } + else + FloatingTextStringOnCreature("All maneuvers are readied", oPC, FALSE); +} + +int GetIsManeuverReadied(object oPC, int nList, int nMoveId) +{ + // Counting through the local ints to determine if this one is readied + int i, nMax = GetReadiedCount(oPC, nList); + for(i = 1; i <= nMax; i++) + { + // If the value is valid, return true + if(GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)) == nMoveId) + { + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverReadied: " + IntToString(nMoveId)); + return TRUE; + } + } + return FALSE; +} + +int GetIsManeuverExpended(object oPC, int nList, int nMoveId) +{ + // Counting through the local ints to determine if this one is expended + int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // returns if the maneuver is expended + if(GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)) == nMoveId) + { + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverExpended: " +IntToString(nMoveId)); + return TRUE; + } + } + return FALSE; +} + +void ExpendManeuver(object oPC, int nList, int nMoveId) +{ + int nCount = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)) + 1; + + // This will mark the Maneuver Expended + SetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(nCount), nMoveId); + SetLocalInt(oPC, "ManeuverExpended" + IntToString(nList), nCount); + if(DEBUG) DoDebug("tob_inc_recovery: Expending Maneuver: " + IntToString(nMoveId)); +} + +void RecoverExpendedManeuvers(object oPC, int nList) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Clearing expended maneuvers"); + // Counting through the local ints to clear them all + int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // Clear them all + DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)); + } + // Do Grant/Withheld Maneuvers whenever this is called on a Crusader + if (nList == MANEUVER_LIST_CRUSADER) + { + // Make sure to clear them all first + ClearGrantedWithheldManeuvers(oPC); + // Then re-grant/withhold them + GrantManeuvers(oPC, nList); + } + if (GetHasFeat(FEAT_VITAL_RECOVERY, oPC)) VitalRecovery(oPC); +} + +void RecoverManeuver(object oPC, int nList, int nMoveId) +{ + // Counting through the local ints to determine if this one is expended + int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // If it has been expended, clear that + if(GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)) == nMoveId) + { + DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)); + if(DEBUG) DoDebug("tob_inc_recovery: Recovering Maneuver: " + IntToString(nMoveId)); + } + } + if (GetHasFeat(FEAT_VITAL_RECOVERY, oPC)) VitalRecovery(oPC); +} + +int GetIsWarbladeRecoveryRound(object oPC) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Warblade recovery check"); + return GetLocalInt(oPC, "WarbladeRecoveryRound"); +} + +void GrantRandomManeuver(object oPC, int nList = MANEUVER_LIST_CRUSADER) +{ + int nMax = GetLocalInt(oPC, "GrantRand#"); + if(!nMax) return;//nothing to grant + + SetLocalInt(oPC, "GrantRand#", nMax - 1); + int x = Random(nMax)+1; + int nMoveId = GetLocalInt(oPC, "GrantRand#" + IntToString(x)); + if(x != nMax) + SetLocalInt(oPC, "GrantRand#" + IntToString(x), GetLocalInt(oPC, "GrantRand#" + IntToString(nMax))); + DeleteLocalInt(oPC, "GrantRand#" + IntToString(nMax)); + + //GrantWithheldManeuver(oPC, MANEUVER_LIST_CRUSADER, MoveId); + // No point in granting an expended maneuver + if(GetIsManeuverExpended(oPC, nList, nMoveId)) + RecoverManeuver(oPC, nList, nMoveId); + + int i = 1; + while(i) + { + // If it hits a non-valid, break + if(!GetLocalInt(oPC, "ManeuverGranted" + IntToString(i))) break; + i++; + } + SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId); +} + +void ListGrantedManeuvers(object oPC) +{ + int i; + for(i = 1; i <= 4; i++) + { + int nMoveId = GetLocalInt(oPC, "ManeuverGranted" + IntToString(i)); + int nExpended = GetIsManeuverExpended(oPC, MANEUVER_LIST_CRUSADER, nMoveId); + if (nMoveId > 0 && !nExpended) FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE); + } +} + +void GrantManeuvers(object oPC, int nList = MANEUVER_LIST_CRUSADER) +{ + // Only crusader level matters for this + int nLevel = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC); + // 2das start at Row 0 + int nGranted = StringToInt(Get2DACache(GetAMSKnownFileName(nList), "ManeuversGranted", nLevel-1)); + nGranted += GetReadiedManeuversModifier(oPC, nList); + nGranted += GetHasFeat(FEAT_EXTRA_GRANTED_MANEUVER, oPC); + + // Counting through the local ints to determine how many are readied + int i, nMaxReadied = GetReadiedCount(oPC, nList); + SetLocalInt(oPC, "GrantRand#", nMaxReadied); + for(i = 1; i <= nMaxReadied; i++) + { + // build temporary array for GrantRandomManeuver() function + int nMoveId = GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)); + if(nMoveId) + SetLocalInt(oPC, "GrantRand#" + IntToString(i), nMoveId); + } + for(i = 1; i <= nGranted; i++) + { + GrantRandomManeuver(oPC); + } + ListGrantedManeuvers(oPC); +} + +void ClearReadiedManeuvers(object oPC, int nList) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Clearing readied maneuvers"); + // Counting through the local ints to clear them all + int i, nMax = GetReadiedCount(oPC, nList); + DeleteLocalInt(oPC, "ManeuverReadied" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // Clear them all + DeleteLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)); + } +} + +/*void GrantWithheldManeuver(object oPC, int nList, int nMoveId = -1) +{ + int i; + string sPsiFile = GetAMSKnownFileName(nList); + // 2das start at Row 0 + int nLevel = GetInitiatorLevel(oPC, nList); + int nGranted = StringToInt(Get2DACache(sPsiFile, "ManeuversGranted", nLevel-1)); + int nReadied = StringToInt(Get2DACache(sPsiFile, "ManeuversReadied", nLevel-1)); + if(DEBUG) DoDebug("tob_inc_recovery: Maneuvers Granted: " + IntToString(nGranted)); + if(DEBUG) DoDebug("tob_inc_recovery: Maneuvers Readied: " + IntToString(nReadied)); + + // If someone input a maneuver + if (nMoveId > 0) + { + // No point in granting an expended maneuver + if (GetIsManeuverExpended(oPC, nList, nMoveId)) + RecoverManeuver(oPC, nList, nMoveId); + + // 3 is always the number withheld + for(i = nGranted; i < nReadied; i++) + { + // Making sure it gets marked properly + int nGrantId = GetLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + // If it exists, mark it as ready and break out + if (nMoveId == nGrantId) + { + if(DEBUG) DoDebug("tob_inc_recovery: Withheld Maneuver Granted: " + IntToString(nMoveId)); + DeleteLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE); + SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId); + break; + } + } + } + else + { + // 3 is always the number withheld + for(i = nGranted; i < nReadied; i++) + { + nMoveId = GetLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + // If it exists, mark it as ready and break out + if (nMoveId > 0) + { + if(DEBUG) DoDebug("tob_inc_recovery: Withheld Maneuver Granted: " + IntToString(nMoveId)); + DeleteLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE); + SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId); + break; + } + } + } +}*/ + +int GetIsManeuverGranted(object oPC, int nMoveId) +{ + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverGranted Start"); + // Counting through the local ints to determine if this one is expended + int i, nMax = GetReadiedCount(oPC, MANEUVER_LIST_CRUSADER); + for(i = 1; i <= nMax; i++) + { + // returns if the maneuver is expended + if(GetLocalInt(oPC, "ManeuverGranted" + IntToString(i)) == nMoveId) + { + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverGranted: " + IntToString(nMoveId)); + return TRUE; + } + } + return FALSE; +} + +void ClearGrantedWithheldManeuvers(object oPC) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Clearing Granted and Withheld Maneuvers"); + // Counting through the local ints to clear them all + int i, nMax = GetReadiedCount(oPC, MANEUVER_LIST_CRUSADER); + for(i = 1; i <= nMax; i++) + { + // Clear them all + DeleteLocalInt(oPC, "ManeuverGranted" + IntToString(i)); + } +} + +void BeginCrusaderGranting(object oPC) +{ + if(DEBUG) DoDebug("BeginCrusaderGranting(): Entered Function"); + // Stops it from being called more than once. + if(GetLocalInt(oPC, "CrusaderGrantLoop")) return; + SetLocalInt(oPC, "CrusaderGrantLoop", TRUE); + + // Starts the granting process + if(DEBUG) DoDebug("BeginCrusaderGranting(): DoCrusaderGranting called"); + DoCrusaderGranting(oPC, 1); +} + +void DoCrusaderGranting(object oPC, int nTrip) +{ + if(DEBUG) DoDebug("DoCrusaderGranting(): Entered Function on Round #" + IntToString(nTrip)); + // First round of combat, no granting. + // Last round of the 5, clear and recover/grant maneuvers + if (nTrip >= 5) // Granted maneuvers empty, restart + { + if(DEBUG) DoDebug("DoCrusaderGranting(): RecoverExpendedManeuvers"); + RecoverExpendedManeuvers(oPC, MANEUVER_LIST_CRUSADER); + nTrip = 1; + } + else if (nTrip > 1) + { + // Rounds 2-4, grant a single maneuver + if(DEBUG) DoDebug("DoCrusaderGranting(): GrantWithheldManeuver"); + //GrantWithheldManeuver(oPC, MANEUVER_LIST_CRUSADER); + GrantRandomManeuver(oPC); + ListGrantedManeuvers(oPC); + } + + if(DEBUG) DoDebug("DoCrusaderGranting(): Above Recursive"); + // If in combat, keep the loop going + if (GetIsInCombat(oPC)) + { + if(DEBUG) DoDebug("DoCrusaderGranting(): In Combat"); + DelayCommand(6.0, DoCrusaderGranting(oPC, ++nTrip)); // Increment counter + } + else // Recover and stop loop otherwise. + { + if(DEBUG) DoDebug("DoCrusaderGranting(): Out of Combat Maneuver Recovery"); + RecoverExpendedManeuvers(oPC, MANEUVER_LIST_CRUSADER); + // Resent Int for next time out + DeleteLocalInt(oPC, "CrusaderGrantLoop"); + } + if(DEBUG) DoDebug("DoCrusaderGranting(): Ending"); +} + +int ExpendRandomManeuver(object oPC, int nList, int nDiscipline = -1) +{ + // Counting through the local ints to determine if maneuver can be expended + int i, nMax = GetReadiedCount(oPC, nList); + for(i = 1; i <= nMax; i++) + { + // If the value is valid, next step + int nMoveId = GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)); + if(nMoveId > 0) + { + // Make sure the disciplines match + if(nDiscipline == -1 || GetDisciplineByManeuver(nMoveId) == nDiscipline) + { + // If not expended + if(!GetIsManeuverExpended(oPC, nList, nMoveId)) + { + // Expend the damn thing and go home + ExpendManeuver(oPC, nList, nMoveId); + return TRUE; + } + } + } + } + + // If we're here, failed. + return FALSE; +} + +void RecoverPrCAbilities(object oPC) +{ + int i; + for(i = 2; i <= 8; i++) // PrC abilities: check last seven slots + { + int nClass = GetClassByPosition(i, oPC); + if(DEBUG) DoDebug("RecoverPrCAbilities" + IntToString(nClass)); + switch(nClass) + { + case CLASS_TYPE_INVALID: + if(DEBUG) DoDebug("RecoverPrCAbilities: no class to recover"); + break; + case CLASS_TYPE_JADE_PHOENIX_MAGE: + DeleteLocalInt(oPC, "JPM_Empowering_Strike_Expended"); + DeleteLocalInt(oPC, "JPM_Quickening_Strike_Expended"); + break; + case CLASS_TYPE_DEEPSTONE_SENTINEL: + DeleteLocalInt(oPC, "DPST_Awaken_Stone_Dragon_Expended"); + break; + case CLASS_TYPE_ETERNAL_BLADE: + DeleteLocalInt(oPC, "ETBL_Eternal_Training_Expended"); + DeleteLocalInt(oPC, "ETBL_Island_In_Time_Expended"); + // Remove bonus to racial type from eternal training + PRCRemoveEffectsFromSpell(oPC, ETBL_RACIAL_TYPE); + break; + } + } +} + +void VitalRecovery(object oPC) +{ + if (GetLocalInt(oPC, "VitalRecovery")) return; //Once a minute + int nHD = GetHitDice(oPC); + effect eHeal = EffectHeal(nHD+3); // That's it + effect eVis = EffectVisualEffect(VFX_IMP_HEALING_M); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oPC); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + + SetLocalInt(oPC, "VitalRecovery", TRUE); + DelayCommand(60.0, DeleteLocalInt(oPC, "VitalRecovery")); +} \ No newline at end of file diff --git a/trunk/include/tob_inc_tobfunc.nss b/trunk/include/tob_inc_tobfunc.nss new file mode 100644 index 00000000..04230160 --- /dev/null +++ b/trunk/include/tob_inc_tobfunc.nss @@ -0,0 +1,1261 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Miscellaneous +//:: tob_inc_tobfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the Tome of Battle implementation. + + Also acts as inclusion nexus for the general + tome of battle includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2007.3.19 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/11 + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int DISCIPLINE_DESERT_WIND = 1; +const int DISCIPLINE_DEVOTED_SPIRIT = 2; +const int DISCIPLINE_DIAMOND_MIND = 4; +const int DISCIPLINE_IRON_HEART = 8; +const int DISCIPLINE_SETTING_SUN = 16; +const int DISCIPLINE_SHADOW_HAND = 32; +const int DISCIPLINE_STONE_DRAGON = 64; +const int DISCIPLINE_TIGER_CLAW = 128; +const int DISCIPLINE_WHITE_RAVEN = 256; + +const string PRC_INITIATING_CLASS = "PRC_CurrentManeuver_InitiatingClass"; +const string PRC_MANEUVER_LEVEL = "PRC_CurrentManeuver_Level"; + +const int MANEUVER_TYPE_STANCE = 1; +const int MANEUVER_TYPE_STRIKE = 2; +const int MANEUVER_TYPE_COUNTER = 3; +const int MANEUVER_TYPE_BOOST = 4; +//global constant (strike & counter & boost) +const int MANEUVER_TYPE_MANEUVER = 5; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's maneuver list the currently being initiated + * maneuver is initiated from. + * + * @param oInitiator A creature initiating a maneuver at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetInitiatingClass(object oInitiator = OBJECT_SELF); + +/** + * Determines the given creature's Initiator level. If a class is specified, + * then returns the Initiator level for that class. Otherwise, returns + * the Initiator level for the currently active maneuver. + * + * @param oInitiator The creature whose Initiator level to determine + * @param nSpecificClass The class to determine the creature's Initiator + * level in. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * Initiator level in regards to an ongoing maneuver + * is determined instead. + * @return The Initiator level + */ +int GetInitiatorLevel(object oInitiator = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID); + +/** + * Determines whether a given creature uses BladeMagic. + * Requires either levels in a BladeMagic-related class or + * natural BladeMagic ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use BladeMagics, FALSE otherwise. + */ +int GetIsBladeMagicUser(object oCreature); + +/** + * Determines the given creature's highest undmodified Initiator level among it's + * initiating classes. + * + * @param oCreature Creature whose highest Initiator level to determine + * @return The highest unmodified Initiator level the creature can have + */ +int GetHighestInitiatorLevel(object oCreature); + +/** + * Determines whether a given class is a BladeMagic-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a BladeMagic-related class, FALSE otherwise + */ +int GetIsBladeMagicClass(int nClass); + +/** + * Gets the level of the maneuver being currently initiated or the level + * of the maneuver ID passed to it. + * + * @param oInitiator The creature currently initiating a maneuver + * @return The level of the maneuver being initiated + */ +int GetManeuverLevel(object oInitiator, int nMoveId = 0); + +/** + * Returns the type of the maneuver + * + * @param nSpellId SpellId of the maneuver + */ +int GetManeuverType(int nSpellId); + +/** + * Returns the name of the maneuver + * + * @param nSpellId SpellId of the maneuver + */ +string GetManeuverName(int nSpellId); + +/** + * Returns the name of the Discipline + * + * @param nDiscipline DISCIPLINE_* to name + */ +string GetDisciplineName(int nDiscipline); + +/** + * Returns the Discipline the maneuver is in + * @param nMoveId maneuver to check + * + * @return DISCIPLINE_* + */ +int GetDisciplineByManeuver(int nMoveId); + +/** + * Returns true or false if the initiator has the Discipline + * @param oInitiator Person to check + * @param nDiscipline Discipline to check + * + * @return TRUE or FALSE + */ +int TOBGetHasDiscipline(object oInitiator, int nDiscipline); + +/** + * Returns true or false if the swordsage has Discipline + * focus in the chosen discipline + * @param oInitiator Person to check + * @param nDiscipline Discipline to check + * + * @return TRUE or FALSE + */ +int TOBGetHasDisciplineFocus(object oInitiator, int nDiscipline); + +/** + * Calculates how many initiator levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added initiator levels for + * @return The number of initiator levels gained + */ +int GetBladeMagicPRCLevels(object oInitiator); + +/** + * Determines which of the character's classes is their highest or first blade magic + * initiating class, if any. This is the one which gains initiator level raise benefits + * from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first blade magic initiating class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryBladeMagicClass(object oCreature = OBJECT_SELF); + +/** + * Determines the position of a creature's first blade magic initiating class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first blade magic class {1, 2, 3} or 0 if + * the creature possesses no levels in blade magic classes. + */ +int GetFirstBladeMagicClassPosition(object oCreature = OBJECT_SELF); + +/** + * Checks whether the PC has the prereqs for the maneuver + * + * @param nClass The class that is trying to learn the feat + * @param nFeat The maneuver's FeatId + * @param oPC The creature whose feats to check + * @return TRUE if the PC possesses the prerequisite feats AND does not + * already posses nFeat, FALSE otherwise. + */ +int CheckManeuverPrereqs(int nClass, int nPrereqs, int nDiscipline, object oPC); + +/** + * Checks whether the maneuver is supernatural or not + * Mainly used to check for AMF areas. + * Mostly from Swordsage maneuvers + * + * @param nMoveId The Maneuver to Check + * @return TRUE if Maneuver is (Su), else FALSE + */ +int GetIsManeuverSupernatural(int nMoveId); + +/** + * Checks whether the initiator has an active stance + * + * @param oInitiator The Initiator + * @return The SpellId or FALSE + */ +int GetHasActiveStance(object oInitiator); + +/** + * Clears spell effects for Stances + * Will NOT clear nDontClearMove + * + * @param oInitiator The Initiator + * @param nDontClearMove A single Stance not to clear + */ +void ClearStances(object oInitiator, int nDontClearMove); + +/** + * Marks a stance active via local ints + * + * @param oInitiator The Initiator + * @param nStance The stance to mark active + */ +void MarkStanceActive(object oInitiator, int nStance); + +/** + * This will take an effect that is supposed to be based on size + * And use vs racial effects to approximate it + * + * @param oInitiator The Initiator + * @param eEffect The effect to scale + * @param nSize 0 affects creature one size or more smaller. + * 1 affects creatures one size or more larger + */ +effect VersusSizeEffect(object oInitiator, effect eEffect, int nSize); + +/** + * Checks every 6 seconds whether an adept has moved too far for a stance + * Or whether the adept has moved far enough to get a bonus from a stance + * + * @param oPC The Initiator + * @param nMoveId The stance + * @param fFeet The distance to check + */ +void InitiatorMovementCheck(object oPC, int nMoveId, float fFeet = 10.0); + +/** + * Checks whether the maneuver is a stance + * + * @param nMoveId The Maneuver + * @return TRUE or FALSE + */ +int GetIsStance(int nMoveId); + +/** + * Sets up everything for the Damage boosts (xd6 + IL fire damage) + * That the Desert Wind discipline has. + * + * @param oPC The PC + */ +void DoDesertWindBoost(object oPC); + +/** + * Determines which PC in the area is weakest, and + * returns that PC. + * + * @param oPC The PC + * @param fDistance The distance to check in feet + * @return The Target + */ +object GetCrusaderHealTarget(object oPC, float fDistance); + +/** + * Returns true or false if the swordsage has Insightful Strike in the chosen discipline + * @param oInitiator Person to check + * + * @return TRUE or FALSE + */ +int GetHasInsightfulStrike(object oInitiator); + +/** + * Returns true or false if the swordsage has Defensive Stance + * ONLY CALL THIS FROM WITHIN STANCES + * @param oInitiator Person to check + * @param nDiscipline DISCIPLINE_ constant of the school of the maneuver. + * + * @return TRUE or FALSE + */ +int GetHasDefensiveStance(object oInitiator, int nDiscipline); + +/** + * Returns true if it is a weapon of the appropriate discipline + * @param oWeapon Weapon to check + * @param nDiscipline DISCIPLINE_ constant of the school of the maneuver. + * + * @return TRUE or FALSE + */ +int GetIsDisciplineWeapon(object oWeapon, int nDiscipline); + +/** + * Returns a numerical bonus to attacks for use in strikes + * @param oInitiator Person to check + * @param nDiscipline DISCIPLINE_ constant of the school of the maneuver. + * @param nClass CLASS_TYPE_ constant + * + * @return Bonus total + */ +int TOBSituationalAttackBonuses(object oInitiator, int nDiscipline, int nClass = CLASS_TYPE_INVALID); + +/** + * Returns the skill for the named discipline + * @param nDiscipline DISCIPLINE_ constant + * + * @return Discipline skill + */ +int GetDisciplineSkill(int nDiscipline); + +/** + * Returns the discipline for the Blade Meditation feat + * @param oInitiator Person to check + * + * @return DISCIPLINE_ constant + */ +int BladeMeditationFeat(object oInitiator); + +/** + * Returns 1 if feat and maneuver match + * @param oInitiator Person to check + * @param nMoveId Maneuver to check + * + * @return 1 or -1 + */ +int BladeMeditationDamage(object oInitiator, int nMoveId); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "tob_move_const" +#include "prc_alterations" +#include "tob_inc_moveknwn" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + + +int _CheckPrereqsByDiscipline(object oPC, int nDiscipline, int nCount = 1) +{ + int nPrereqCount = GetManeuverCountByDiscipline(oPC, nDiscipline, MANEUVER_TYPE_MANEUVER) + + GetManeuverCountByDiscipline(oPC, nDiscipline, MANEUVER_TYPE_STANCE); + + if(nPrereqCount >= nCount) + return nPrereqCount; + + return 0; +} + +void _RecursiveStanceCheck(object oPC, object oTestWP, int nMoveId, float fFeet = 10.0) +{ + // Seeing if this works better + string sWPTag = "PRC_BMWP_" + GetName(oPC) + IntToString(nMoveId); + oTestWP = GetWaypointByTag(sWPTag); + // Distance moved in the last round + float fDist = FeetToMeters(GetDistanceBetween(oPC, oTestWP)); + // Giving them a little extra distance because of NWN's dance of death + float fCheck = FeetToMeters(fFeet); + if(DEBUG) DoDebug("_RecursiveStanceCheck: fDist: " + FloatToString(fDist)); + if(DEBUG) DoDebug("_RecursiveStanceCheck: fCheck: " + FloatToString(fCheck)); + if(DEBUG) DoDebug("_RecursiveStanceCheck: nMoveId: " + IntToString(nMoveId)); + + + // Moved the distance + if (fDist >= fCheck) + { + if(DEBUG) DoDebug("_RecursiveStanceCheck: fDist > fCheck"); + // Stances that clean up + if (nMoveId == MOVE_SD_STONEFOOT_STANCE) + { + PRCRemoveEffectsFromSpell(oPC, nMoveId); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Moved too far, cancelling stances."); + // Clean up the test WP as well + DestroyObject(oTestWP); + } + // Stances that clean up + else if (nMoveId == MOVE_MOUNTAIN_FORTRESS) + { + PRCRemoveEffectsFromSpell(oPC, nMoveId); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Moved too far, cancelling stances."); + // Clean up the test WP as well + DestroyObject(oTestWP); + } + // Stances that clean up + else if (nMoveId == MOVE_SD_ROOT_MOUNTAIN) + { + PRCRemoveEffectsFromSpell(oPC, nMoveId); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Moved too far, cancelling stances."); + // Clean up the test WP as well + DestroyObject(oTestWP); + } + else if (nMoveId == MOVE_SH_CHILD_SHADOW) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectConcealment(20)), oPC, 6.0); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Applying bonuses."); + // Clean up the test WP + DestroyObject(oTestWP); + // Create waypoint for the movement for next round + CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oPC), FALSE, sWPTag); + } + else if (nMoveId == MOVE_IH_ABSOLUTE_STEEL) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(2)), oPC, 6.0); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Applying bonuses."); + // Clean up the test WP + DestroyObject(oTestWP); + // Create waypoint for the movement for next round + CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oPC), FALSE, sWPTag); + } + + else if (nMoveId == MOVE_SD_GIANTS_STANCE) + { + DeleteLocalInt(oPC, "DWGiantsStance"); + DeleteLocalInt(oPC, "PRC_Power_Expansion_SizeIncrease"); + PRCRemoveEffectsFromSpell(oPC, nMoveId); + DestroyObject(oTestWP); + } + + else if (nMoveId == MOVE_IH_DANCING_BLADE_FORM) + { + DeleteLocalInt(oPC, "DWDancingBladeForm"); + DestroyObject(oTestWP); + } + + } + // If they still have the spell, keep going + if (GetHasSpellEffect(nMoveId, oPC)) + { + DelayCommand(6.0, _RecursiveStanceCheck(oPC, oTestWP, nMoveId)); + if(DEBUG) DoDebug("_RecursiveStanceCheck: DelayCommand(6.0, _RecursiveStanceCheck(oPC, oTestWP, nMoveId))."); + } + + if(DEBUG) DoDebug("_RecursiveStanceCheck: Exiting"); +} + +int _AllowedDiscipline(object oInitiator, int nClass, int nDiscipline) +{ + //maneuver choice for prestige classes is restricted only to those disciplines + int nOverride = GetPersistantLocalInt(oInitiator, "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 & nDiscipline; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetInitiatingClass(object oInitiator = OBJECT_SELF) +{ + return GetLocalInt(oInitiator, PRC_INITIATING_CLASS) - 1; +} + +int GetInitiatorLevel(object oInitiator = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID) +{ + int nAdjust = GetLocalInt(oInitiator, PRC_CASTERLEVEL_ADJUSTMENT); + int nLevel = GetLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE); + + // For when you want to assign the caster level. + if(nLevel) + { + if(DEBUG) SendMessageToPC(oInitiator, "GetInitiatorLevel(): Forced-level initiating at level " + IntToString(nLevel)); + //DelayCommand(1.0, DeleteLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE)); + return nLevel + nAdjust; + } + + int nTotalHD = GetHitDice(oInitiator); + + // The function user needs to know the character's Initiator level in a specific class + // instead of whatever the character last initiated a maneuver as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + if(GetIsBladeMagicClass(nSpecificClass)) + { + // Initiator level is class level + 1/2 levels in all other classes + // See ToB p39 + // Max level is therefor the level plus 1/2 of remaining levels + // Prestige classes are stuck in here + int nClassLevel = GetLevelByClass(nSpecificClass, oInitiator); + if(nClassLevel) + { + nClassLevel += GetBladeMagicPRCLevels(oInitiator); + nLevel = nClassLevel + ((nTotalHD - nClassLevel)/2); + } + } + } + else if(GetInitiatingClass(oInitiator) != -1) + { + int nClassLevel = GetLevelByClass(GetInitiatingClass(oInitiator), oInitiator); + nClassLevel += GetBladeMagicPRCLevels(oInitiator); + nLevel = nClassLevel + ((nTotalHD - nClassLevel)/2); + } + + // A character with no initiator levels has an init level of 1/2 HD (min 1) + if(!nLevel) + nLevel = max(1, nTotalHD/2); + + // This spam is technically no longer necessary once the Initiator level getting mechanism has been confirmed to work +// if(DEBUG) FloatingTextStringOnCreature("Initiator Level: " + IntToString(nLevel), oInitiator, FALSE); + + return nLevel + nAdjust; +} + +int GetIsBladeMagicUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_CRUSADER, oCreature) + || GetLevelByClass(CLASS_TYPE_SWORDSAGE, oCreature) + || GetLevelByClass(CLASS_TYPE_WARBLADE, oCreature)); +} + +int GetHighestInitiatorLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetInitiatorLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestInitiatorLevel(object oCreature) +{ + return max(max(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetInitiatorLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetInitiatorLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetInitiatorLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsBladeMagicClass(int nClass) +{ + return nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_WARBLADE; +} + +int GetManeuverLevel(object oInitiator, int nMoveId = 0) +{ + int nLevel = GetLocalInt(oInitiator, PRC_MANEUVER_LEVEL); + if (nLevel > 0) return nLevel; + else if (nMoveId > 0) return StringToInt(lookup_spell_innate(nMoveId)); + + return 0; +} + +string GetManeuverName(int nSpellId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))); +} + +int GetManeuverType(int nSpellId) +{ + return StringToInt(GetStringRight(Get2DACache("spells", "MetaMagic", nSpellId), 1)); +} + +int GetIsStance(int nMoveId) +{ + return GetManeuverType(nMoveId) == MANEUVER_TYPE_STANCE; +} + +string GetDisciplineName(int nDiscipline) +{ + int nStrRef; + switch(nDiscipline) + { + case DISCIPLINE_DESERT_WIND: nStrRef = 16829714; break; + case DISCIPLINE_DEVOTED_SPIRIT: nStrRef = 16829715; break; + case DISCIPLINE_DIAMOND_MIND: nStrRef = 16829716; break; + case DISCIPLINE_IRON_HEART: nStrRef = 16829717; break; + case DISCIPLINE_SETTING_SUN: nStrRef = 16829718; break; + case DISCIPLINE_SHADOW_HAND: nStrRef = 16829719; break; + case DISCIPLINE_STONE_DRAGON: nStrRef = 16829720; break; + case DISCIPLINE_TIGER_CLAW: nStrRef = 16829721; break; + case DISCIPLINE_WHITE_RAVEN: nStrRef = 16829722; break; + } + return GetStringByStrRef(nStrRef); +} + +int GetDisciplineByManeuver(int nMoveId) +{ + string sSpellSchool = Get2DACache("spells", "School", nMoveId); + int nDiscipline; + + if (sSpellSchool == "A") nDiscipline = DISCIPLINE_DEVOTED_SPIRIT; + else if (sSpellSchool == "C") nDiscipline = DISCIPLINE_SETTING_SUN; + else if (sSpellSchool == "D") nDiscipline = DISCIPLINE_IRON_HEART; + else if (sSpellSchool == "E") nDiscipline = DISCIPLINE_DIAMOND_MIND; + else if (sSpellSchool == "V") nDiscipline = DISCIPLINE_DESERT_WIND; + else if (sSpellSchool == "I") nDiscipline = DISCIPLINE_SHADOW_HAND; + else if (sSpellSchool == "N") nDiscipline = DISCIPLINE_WHITE_RAVEN; + else if (sSpellSchool == "T") nDiscipline = DISCIPLINE_TIGER_CLAW; + else if (sSpellSchool == "G") nDiscipline = DISCIPLINE_STONE_DRAGON; + + return nDiscipline; +} + +int GetBladeMagicPRCLevels(object oInitiator) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_DEEPSTONE_SENTINEL, oInitiator) + + GetLevelByClass(CLASS_TYPE_BLOODCLAW_MASTER, oInitiator) + + GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oInitiator) + + GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oInitiator) + + GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator) + + GetLevelByClass(CLASS_TYPE_ETERNAL_BLADE, oInitiator) + + GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oInitiator); + + return nLevel; +} + +int GetPrimaryBladeMagicClass(object oCreature = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nBladeMagicPos = GetFirstBladeMagicClassPosition(oCreature); + if (!nBladeMagicPos) return CLASS_TYPE_INVALID; // no Blade Magic initiating class + + nClass = GetClassByPosition(nBladeMagicPos, oCreature); + } + else + { + /*int i, nLevel, nTest, nTestLevel; + for(i = 1; i < 4; i++) + { + nTest = GetClassByPosition(i, oCreature); + if(GetIsBladeMagicClass(nTest)) + { + nTestLevel = GetLevelByClass(nTest, oCreature); + if(nTestLevel > nLevel) + { + nClass = nTest; + nLevel = nTestLevel; + } + } + }*/ + + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsBladeMagicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsBladeMagicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsBladeMagicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsBladeMagicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsBladeMagicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsBladeMagicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsBladeMagicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsBladeMagicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstBladeMagicClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsBladeMagicClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsBladeMagicClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsBladeMagicClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsBladeMagicClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsBladeMagicClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsBladeMagicClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsBladeMagicClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsBladeMagicClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int CheckManeuverPrereqs(int nClass, int nPrereqs, int nDiscipline, object oPC) +{ + // Checking to see what the name of the feat is, and the row number + /*if (DEBUG) + { + DoDebug("CheckManeuverPrereqs: nFeat: " + IntToString(nFeat)); + string sFeatName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))); + DoDebug("CheckManeuverPrereqs: sFeatName: " + sFeatName); + }*/ + + // Prestige classes can only access certain disciplines + if(!_AllowedDiscipline(oPC, nClass, nDiscipline)) + return FALSE; + + // If this maneuver has a prereq, check for it + if(nPrereqs) + // if it returns false, exit, otherwise they can take the maneuver + return _CheckPrereqsByDiscipline(oPC, nDiscipline, nPrereqs); + + // if you've reached this far then return TRUE + return TRUE; +} + +int GetIsManeuverSupernatural(int nMoveId) +{ + if(nMoveId == MOVE_DW_BLISTERING_FLOURISH + || nMoveId == MOVE_DW_BURNING_BLADE + || nMoveId == MOVE_DW_BURNING_BRAND + || nMoveId == MOVE_DW_DEATH_MARK + || nMoveId == MOVE_DW_DISTRACTING_EMBER + || nMoveId == MOVE_DW_DRAGONS_FLAME + || nMoveId == MOVE_DW_FAN_FLAMES + || nMoveId == MOVE_DW_FIERY_ASSAULT + || nMoveId == MOVE_DW_FIRE_RIPOSTE + || nMoveId == MOVE_DW_FIRESNAKE + || nMoveId == MOVE_DW_FLAMES_BLESSING + || nMoveId == MOVE_DW_HATCHLINGS_FLAME + || nMoveId == MOVE_DW_HOLOCAUST_CLOAK + || nMoveId == MOVE_DW_INFERNO_BLADE + || nMoveId == MOVE_DW_INFERNO_BLAST + || nMoveId == MOVE_DW_LEAPING_FLAME + || nMoveId == MOVE_DW_LINGERING_INFERNO + || nMoveId == MOVE_DW_RING_FIRE + || nMoveId == MOVE_DW_RISING_PHOENIX + || nMoveId == MOVE_DW_SALAMANDER_CHARGE + || nMoveId == MOVE_DW_SEARING_BLADE + || nMoveId == MOVE_DW_SEARING_CHARGE + || nMoveId == MOVE_DW_WYRMS_FLAME + || nMoveId == MOVE_SH_BALANCE_SKY + || nMoveId == MOVE_SH_CHILD_SHADOW + || nMoveId == MOVE_SH_CLINGING_SHADOW + || nMoveId == MOVE_SH_CLOAK_DECEPTION + || nMoveId == MOVE_SH_ENERVATING_SHADOW + || nMoveId == MOVE_SH_FIVE_SHADOW_CREEPING + || nMoveId == MOVE_SH_GHOST_BLADE + || nMoveId == MOVE_SH_OBSCURING_SHADOW_VEIL + || nMoveId == MOVE_SH_SHADOW_BLADE_TECH + || nMoveId == MOVE_SH_SHADOW_GARROTTE + || nMoveId == MOVE_SH_SHADOW_NOOSE + || nMoveId == MOVE_SH_STRENGTH_DRAINING) + return TRUE; + + // If nothing returns TRUE, fail + return FALSE; +} + +int GetHasActiveStance(object oInitiator) +{ + int nStance = GetLocalInt(oInitiator, "TOBStanceOne"); + if(GetHasSpellEffect(nStance, oInitiator)) + return nStance; + + nStance = GetLocalInt(oInitiator, "TOBStanceTwo"); + if(GetHasSpellEffect(nStance, oInitiator)) + return nStance; + + return FALSE; +} + +void RemoveStance(object oInitiator, int nStance) +{ + PRCRemoveEffectsFromSpell(oInitiator, nStance); + + //stances with special handling goes here + if(nStance == MOVE_DS_AURA_CHAOS) + DeleteLocalInt(oInitiator, "DSChaos"); + else if(nStance == MOVE_DS_PERFECT_ORDER) + DeleteLocalInt(oInitiator, "DSPerfectOrder"); + else if(nStance == MOVE_DW_RISING_PHOENIX) + RemoveItemProperty(GetPCSkin(oInitiator), ItemPropertyBonusFeat(IP_CONST_FEAT_RISING_PHOENIX)); + else if(nStance == MOVE_SH_ASSASSINS_STANCE) + { + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oInitiator)); + if (DEBUG) DoDebug("Cleaning assassin's stance"); + } + else if(nStance == MOVE_MYSTIC_PHOENIX || nStance == MOVE_MYSTIC_PHOENIX_AUG) + { + if(DEBUG) DoDebug("Removing Mystic Phoenix Stance"); + DeleteLocalInt(oInitiator, "ToB_JPM_MystP"); + } + else if(nStance == MOVE_FIREBIRD_STANCE || nStance == MOVE_FIREBIRD_STANCE_AUG) + { + if(DEBUG) DoDebug("Removing Firebird Stance"); + DeleteLocalInt(oInitiator, "ToB_JPM_FireB"); + } + else if(nStance == MOVE_CHILD_SL_STANCE) + { + DeleteLocalInt(oInitiator, "SSN_CHILDSL_SETP"); + RemoveEventScript(oInitiator, EVENT_ONHEARTBEAT, "tob_ssn_childsl", TRUE, FALSE); + } +} + +void ClearStances(object oInitiator, int nDontClearMove) +{ + // Clears spell effects, will not clear DontClearMove + // This is used to allow Warblades to have two stances. + int nStance = GetLocalInt(oInitiator, "TOBStanceOne"); + if(GetHasSpellEffect(nStance, oInitiator) && nStance != nDontClearMove) + { + RemoveStance(oInitiator, nStance); + DeleteLocalInt(oInitiator, "TOBStanceOne"); + } + + nStance = GetLocalInt(oInitiator, "TOBStanceTwo"); + if(GetHasSpellEffect(nStance, oInitiator) && nStance != nDontClearMove) + { + RemoveStance(oInitiator, nStance); + DeleteLocalInt(oInitiator, "TOBStanceTwo"); + } +} + +void MarkStanceActive(object oInitiator, int nStance) +{ + // If the first stance is active, use second + // This should only be called with the first active when it is legal to have two stances + if(GetLocalInt(oInitiator, "TOBStanceOne") > 0) SetLocalInt(oInitiator, "TOBStanceTwo", nStance); + else SetLocalInt(oInitiator, "TOBStanceOne", nStance); +} + +effect VersusSizeEffect(object oInitiator, effect eEffect, int nSize) +{ + // Right now this only deals with medium and small PCs + int nPCSize = PRCGetCreatureSize(oInitiator); + effect eLink; + // Creatures larger than PC + if (nSize == 1) + { + eLink = VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ABERRATION); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_CONSTRUCT)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_DRAGON)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ELEMENTAL)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_GIANT)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_OUTSIDER)); + if (nPCSize == CREATURE_SIZE_SMALL) + { + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ANIMAL)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_BEAST)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_DWARF)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ELF)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HALFELF)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HALFORC)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMAN)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_GOBLINOID)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_MONSTROUS)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_ORC)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_REPTILIAN)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_MAGICAL_BEAST)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_OOZE)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_SHAPECHANGER)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_UNDEAD)); + } + }// Smaller + if (nSize == 0) + { + eLink = VersusRacialTypeEffect(eEffect, RACIAL_TYPE_FEY); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_VERMIN)); + if (nPCSize == CREATURE_SIZE_MEDIUM) + { + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_GNOME)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HALFLING)); + } + } + + return eLink; +} + +void InitiatorMovementCheck(object oPC, int nMoveId, float fFeet = 10.0) +{ + // Check to see if the WP is valid + string sWPTag = "PRC_BMWP_" + GetName(oPC) + IntToString(nMoveId); + object oTestWP = GetWaypointByTag(sWPTag); + if (!GetIsObjectValid(oTestWP)) + { + // Create waypoint for the movement + CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oPC), FALSE, sWPTag); + if(DEBUG) DoDebug("InitiatorMovementCheck: WP for " + DebugObject2Str(oPC) + " didn't exist, creating. Tag: " + sWPTag); + } + // Start the recursive HB check for movement + // Seeing if this solves some of the issues with it + DelayCommand(2.0, _RecursiveStanceCheck(oPC, oTestWP, nMoveId, fFeet)); +} + +void DoDesertWindBoost(object oPC) +{ + if(DEBUG) DoDebug("DoDesertWindBoost running"); + + object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (IPGetIsMeleeWeapon(oItem)) + { + effect eVis = EffectLinkEffects(EffectVisualEffect(VFX_IMP_FLAME_M), EffectVisualEffect(VFX_IMP_PULSE_FIRE)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + // Add eventhook to the item + AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE); + DelayCommand(6.0, RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE)); + // Add the OnHit and vfx + IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_FIRE), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + SetLocalInt(oPC, "DesertWindBoost", PRCGetSpellId()); + DelayCommand(6.0, DeleteLocalInt(oPC, "DesertWindBoost")); + } + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + if (IPGetIsMeleeWeapon(oItem)) + { + // Add eventhook to the item + AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE); + DelayCommand(6.0, RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE)); + // Add the OnHit and vfx + IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_FIRE), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } +} + +object GetCrusaderHealTarget(object oPC, float fDistance) +{ + object oReturn; + int nTest, nCurrentMin = 100; + location lTarget = GetLocation(oPC); + object oTest = MyFirstObjectInShape(SHAPE_SPHERE, fDistance, lTarget); + while(GetIsObjectValid(oTest)) + { + if(GetIsFriend(oTest, oPC)) + { + nTest = (GetCurrentHitPoints(oTest) * 100) / GetMaxHitPoints(oTest); + // Check HP vs current biggest loss + if(nTest < nCurrentMin && GetCurrentHitPoints(oTest) > 0 && !GetIsDead(oTest)) + { + nCurrentMin = nTest; + oReturn = oTest; + } + } + //Get the next target in the specified area around the caster + oTest = MyNextObjectInShape(SHAPE_SPHERE, fDistance, lTarget); + } + if(DEBUG) DoDebug("GetCrusaderHealTarget: oReturn " + GetName(oReturn)); + return oReturn; +} + +int GetHasInsightfulStrike(object oInitiator) +{ + int nDiscToCheck = GetDisciplineByManeuver(PRCGetSpellId()); + int nFeat; + switch(nDiscToCheck) + { + case DISCIPLINE_DESERT_WIND: nFeat = FEAT_SS_DF_IS_DW; break; + case DISCIPLINE_DIAMOND_MIND: nFeat = FEAT_SS_DF_IS_DM; break; + case DISCIPLINE_SETTING_SUN: nFeat = FEAT_SS_DF_IS_SS; break; + case DISCIPLINE_SHADOW_HAND: nFeat = FEAT_SS_DF_IS_SH; break; + case DISCIPLINE_STONE_DRAGON: nFeat = FEAT_SS_DF_IS_SD; break; + case DISCIPLINE_TIGER_CLAW: nFeat = FEAT_SS_DF_IS_TC; break; + } + if(GetHasFeat(nFeat, oInitiator)) + return TRUE; + + return FALSE; +} + +int GetHasDefensiveStance(object oInitiator, int nDiscipline) +{ + // Because this is only called from inside the proper stances + // Its just a check to see if they should link in the save boost. + int nFeat; + switch(nDiscipline) + { + case DISCIPLINE_DESERT_WIND: nFeat = FEAT_SS_DF_DS_DW; break; + case DISCIPLINE_DIAMOND_MIND: nFeat = FEAT_SS_DF_DS_DM; break; + case DISCIPLINE_SETTING_SUN: nFeat = FEAT_SS_DF_DS_SS; break; + case DISCIPLINE_SHADOW_HAND: nFeat = FEAT_SS_DF_DS_SH; break; + case DISCIPLINE_STONE_DRAGON: nFeat = FEAT_SS_DF_DS_SD; break; + case DISCIPLINE_TIGER_CLAW: nFeat = FEAT_SS_DF_DS_TC; break; + } + if(GetHasFeat(nFeat, oInitiator)) + return TRUE; + + return FALSE; +} + +int TOBGetHasDiscipline(object oInitiator, int nDiscipline) +{ + switch(nDiscipline) + { + case DISCIPLINE_DEVOTED_SPIRIT: return GetLevelByClass(CLASS_TYPE_CRUSADER, oInitiator); + case DISCIPLINE_DESERT_WIND: + case DISCIPLINE_SETTING_SUN: + case DISCIPLINE_SHADOW_HAND: return GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator); + case DISCIPLINE_IRON_HEART: return GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + case DISCIPLINE_DIAMOND_MIND: + case DISCIPLINE_TIGER_CLAW: return GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator) || GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + case DISCIPLINE_WHITE_RAVEN: return GetLevelByClass(CLASS_TYPE_CRUSADER, oInitiator) || GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + case DISCIPLINE_STONE_DRAGON: return GetLevelByClass(CLASS_TYPE_CRUSADER, oInitiator) || GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator) || GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + } + return FALSE; +} + +int TOBGetHasDisciplineFocus(object oInitiator, int nDiscipline) +{ + int nFeat1, nFeat2, nFeat3; + switch(nDiscipline) + { + case DISCIPLINE_DESERT_WIND: nFeat1 = FEAT_SS_DF_DS_DW; nFeat2 = FEAT_SS_DF_IS_DW; nFeat3 = FEAT_SS_DF_WF_DW; break; + case DISCIPLINE_DIAMOND_MIND: nFeat1 = FEAT_SS_DF_DS_DM; nFeat2 = FEAT_SS_DF_IS_DM; nFeat3 = FEAT_SS_DF_WF_DM; break; + case DISCIPLINE_SETTING_SUN: nFeat1 = FEAT_SS_DF_DS_SS; nFeat2 = FEAT_SS_DF_IS_SS; nFeat3 = FEAT_SS_DF_WF_SS; break; + case DISCIPLINE_SHADOW_HAND: nFeat1 = FEAT_SS_DF_DS_SH; nFeat2 = FEAT_SS_DF_IS_SH; nFeat3 = FEAT_SS_DF_WF_SH; break; + case DISCIPLINE_STONE_DRAGON: nFeat1 = FEAT_SS_DF_DS_SD; nFeat2 = FEAT_SS_DF_IS_SD; nFeat3 = FEAT_SS_DF_WF_SD; break; + case DISCIPLINE_TIGER_CLAW: nFeat1 = FEAT_SS_DF_DS_TC; nFeat2 = FEAT_SS_DF_IS_TC; nFeat3 = FEAT_SS_DF_WF_TC; break; + } + if(GetHasFeat(nFeat1, oInitiator) || GetHasFeat(nFeat2, oInitiator) || GetHasFeat(nFeat3, oInitiator)) + return TRUE; + + // If none of those trigger. + return FALSE; +} + +int GetIsDisciplineWeapon(object oWeapon, int nDiscipline) +{ + int nType = GetBaseItemType(oWeapon); + if(nDiscipline == DISCIPLINE_DESERT_WIND) + { + if(nType == BASE_ITEM_SCIMITAR + || nType == BASE_ITEM_LIGHTMACE + || nType == BASE_ITEM_SHORTSPEAR + || nType == BASE_ITEM_LIGHT_PICK + || nType == BASE_ITEM_FALCHION) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_DEVOTED_SPIRIT) + { + if(nType == BASE_ITEM_LONGSWORD + || nType == BASE_ITEM_HEAVYFLAIL + || nType == BASE_ITEM_MAUL + || nType == BASE_ITEM_FALCHION) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_DIAMOND_MIND) + { + if(nType == BASE_ITEM_BASTARDSWORD + || nType == BASE_ITEM_KATANA + || nType == BASE_ITEM_SHORTSPEAR + || nType == BASE_ITEM_RAPIER) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_IRON_HEART) + { + if(nType == BASE_ITEM_BASTARDSWORD + || nType == BASE_ITEM_KATANA + || nType == BASE_ITEM_LONGSWORD + || nType == BASE_ITEM_TWOBLADEDSWORD + || nType == BASE_ITEM_DWARVENWARAXE) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_SETTING_SUN) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_INVALID + || nType == BASE_ITEM_QUARTERSTAFF + || nType == BASE_ITEM_SHORTSWORD + || nType == BASE_ITEM_NUNCHAKU) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_SHADOW_HAND) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_DAGGER + || nType == BASE_ITEM_INVALID + || nType == BASE_ITEM_SHORTSWORD + || nType == BASE_ITEM_SAI) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_STONE_DRAGON) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_GREATAXE + || nType == BASE_ITEM_INVALID + || nType == BASE_ITEM_GREATSWORD + || nType == BASE_ITEM_HEAVY_MACE) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_TIGER_CLAW) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_KUKRI + || nType == BASE_ITEM_KAMA + || nType == BASE_ITEM_HANDAXE + || nType == BASE_ITEM_GREATAXE + || nType == BASE_ITEM_INVALID) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_WHITE_RAVEN) + { + if(nType == BASE_ITEM_BATTLEAXE + || nType == BASE_ITEM_LONGSWORD + || nType == BASE_ITEM_HALBERD + || nType == BASE_ITEM_WARHAMMER + || nType == BASE_ITEM_GREATSWORD) + return TRUE; + } + + // If none of those trigger. + return FALSE; +} + +int TOBSituationalAttackBonuses(object oInitiator, int nDiscipline, int nClass = CLASS_TYPE_INVALID) +{ + int nBonus = 0; + if(GetLevelByClass(CLASS_TYPE_BLOODCLAW_MASTER, oInitiator) >= 4 + && nDiscipline == DISCIPLINE_TIGER_CLAW) + nBonus += 1; + + return nBonus; +} + +int GetDisciplineSkill(int nDiscipline) +{ + if(nDiscipline == DISCIPLINE_DESERT_WIND) + { + return SKILL_TUMBLE; + } + else if(nDiscipline == DISCIPLINE_DEVOTED_SPIRIT) + { + return SKILL_INTIMIDATE; + } + else if(nDiscipline == DISCIPLINE_DIAMOND_MIND) + { + return SKILL_CONCENTRATION; + } + else if(nDiscipline == DISCIPLINE_IRON_HEART) + { + return SKILL_BALANCE; + } + else if(nDiscipline == DISCIPLINE_SETTING_SUN) + { + return SKILL_SENSE_MOTIVE; + } + else if(nDiscipline == DISCIPLINE_SHADOW_HAND) + { + return SKILL_HIDE; + } + else if(nDiscipline == DISCIPLINE_STONE_DRAGON) + { + return SKILL_BALANCE; + } + else if(nDiscipline == DISCIPLINE_TIGER_CLAW) + { + return SKILL_JUMP; + } + else if(nDiscipline == DISCIPLINE_WHITE_RAVEN) + { + return SKILL_PERSUADE; + } + + // If none of those trigger. + return -1; +} + +int BladeMeditationFeat(object oInitiator) +{ + if(GetHasFeat(FEAT_BLADE_MEDITATION_DESERT_WIND , oInitiator)) return DISCIPLINE_DESERT_WIND ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_DEVOTED_SPIRIT, oInitiator)) return DISCIPLINE_DEVOTED_SPIRIT; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_DIAMOND_MIND , oInitiator)) return DISCIPLINE_DIAMOND_MIND ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_IRON_HEART , oInitiator)) return DISCIPLINE_IRON_HEART ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_SETTING_SUN , oInitiator)) return DISCIPLINE_SETTING_SUN ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_SHADOW_HAND , oInitiator)) return DISCIPLINE_SHADOW_HAND ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_STONE_DRAGON , oInitiator)) return DISCIPLINE_STONE_DRAGON ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_TIGER_CLAW , oInitiator)) return DISCIPLINE_TIGER_CLAW ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_WHITE_RAVEN , oInitiator)) return DISCIPLINE_WHITE_RAVEN ; + + return -1; +} + +int BladeMeditationDamage(object oInitiator, int nMoveId) +{ + int nDisc = BladeMeditationFeat(oInitiator); + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator); + if (nDisc == GetDisciplineByManeuver(nMoveId) && GetIsDisciplineWeapon(oWeapon, nDisc)) + return 1; + + return -1; +} \ No newline at end of file diff --git a/trunk/include/true_inc_trufunc.nss b/trunk/include/true_inc_trufunc.nss new file mode 100644 index 00000000..f60ee313 --- /dev/null +++ b/trunk/include/true_inc_trufunc.nss @@ -0,0 +1,839 @@ +//:://///////////////////////////////////////////// +//:: Truenaming include: Misceallenous +//:: true_inc_trufunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the truenaming implementation. + + Also acts as inclusion nexus for the general + truenaming includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2006.7.18 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/11 + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's Utterance list the currently being truespoken + * Utterance is truespoken from. + * + * @param oTrueSpeaker A creature uttering a Utterance at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetTruespeakingClass(object oTrueSpeaker = OBJECT_SELF); + +/** + * Determines the given creature's truespeaker level. If a class is specified, + * then returns the truespeaker level for that class. Otherwise, returns + * the truespeaker level for the currently active utterance. + * + * @param oTrueSpeaker The creature whose truespeaker level to determine + * @param nSpecificClass The class to determine the creature's truespeaker + * level in. + * @param nUseHD If this is set, it returns the Character Level of the calling creature. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * truespeaker level in regards to an ongoing utterance + * is determined instead. + * @return The truespeaker level + */ +int GetTruespeakerLevel(object oTrueSpeaker, int nSpecificClass = CLASS_TYPE_INVALID, int nUseHD = FALSE); + +/** + * Determines whether a given creature uses truenaming. + * Requires either levels in a truenaming-related class or + * natural truenaming ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use truenames, FALSE otherwise. + */ +int GetIsTruenamingUser(object oCreature); + +/** + * Determines the given creature's highest undmodified truespeaker level among it's + * uttering classes. + * + * @param oCreature Creature whose highest truespeaker level to determine + * @return The highest unmodified truespeaker level the creature can have + */ +int GetHighestTrueSpeakerLevel(object oCreature); + +/** + * Determines whether a given class is a truenaming-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a truenaming-related class, FALSE otherwise + */ +int GetIsTruenamingClass(int nClass); + +/** + * Gets the level of the Utterance being currently truespoken. + * WARNING: Return value is not defined when a Utterance is not being truespoken. + * + * @param oTrueSpeaker The creature currently uttering a utterance + * @return The level of the Utterance being truespoken + */ +int GetUtteranceLevel(object oTrueSpeaker); + +/** + * Determines a creature's ability score in the uttering ability of a given + * class. + * + * @param oTrueSpeaker Creature whose ability score to get + * @param nClass CLASS_TYPE_* constant of a uttering class + */ +int GetTruenameAbilityScoreOfClass(object oTrueSpeaker, int nClass); + +/** + * Determines the uttering ability of a class. + * + * @param nClass CLASS_TYPE_* constant of the class to determine the uttering stat of + * @return ABILITY_* of the uttering stat. ABILITY_CHARISMA for non-TrueSpeaker + * classes. + */ +int GetTruenameAbilityOfClass(int nClass); + +/** + * Calculates the DC of the Utterance being currently truespoken. + * Base value is 10 + Utterance level + ability modifier in uttering stat + * + * WARNING: Return value is not defined when a Utterance is not being truespoken. + * + */ +int GetTrueSpeakerDC(object oTrueSpeaker = OBJECT_SELF); + +/** + * Determines the truespeaker's level in regards to truespeaker checks to overcome + * spell resistance. + * + * WARNING: Return value is not defined when a Utterance is not being truespoken. + * + * @param oTrueSpeaker A creature uttering a Utterance at the moment + * @return The creature's truespeaker level, adjusted to account for + * modifiers that affect spell resistance checks. + */ +int GetTrueSpeakPenetration(object oTrueSpeaker = OBJECT_SELF); + +/** + * Marks an utterance as active for the Law of Sequence. + * Called from the Utterance + * + * @param oTrueSpeaker Caster of the Utterance + * @param nSpellId SpellId of the Utterance + * @param fDur Duration of the Utterance + */ +void DoLawOfSequence(object oTrueSpeaker, int nSpellId, float fDur); + +/** + * Checks to see whether the law of sequence is active + * Utterance fails if it is. + * + * @param oTrueSpeaker Caster of the Utterance + * @param nSpellId SpellId of the Utterance + * + * @return True if the Utterance is active, False if it is not. + */ +int CheckLawOfSequence(object oTrueSpeaker, int nSpellId); + +/** + * Returns the name of the Utterance + * + * @param nSpellId SpellId of the Utterance + */ +string GetUtteranceName(int nSpellId); + +/** + * Returns the name of the Lexicon + * + * @param nLexicon LEXICON_* to name + */ +string GetLexiconName(int nLexicon); + +/** + * Returns the Lexicon the Utterance is in + * @param nSpellId Utterance to check + * + * @return LEXICON_* + */ +int GetLexiconByUtterance(int nSpellId); + +/** + * Affects all of the creatures with Speak Unto the Masses + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Original Target of Utterance + * @param utter The utterance structure returned by EvaluateUtterance + */ +void DoSpeakUntoTheMasses(object oTrueSpeaker, object oTarget, struct utterance utter); + +/** + * Affects all of the creatures with Speak Unto the Masses + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Original Target of Utterance + * @param utter The utterance structure returned by EvaluateUtterance + */ +void DoWordOfNurturingReverse(object oTrueSpeaker, object oTarget, struct utterance utter); + +/** + * Affects all of the creatures with Speak Unto the Masses + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Original Target of Utterance + * @param utter The utterance structure returned by EvaluateUtterance + * @param nBeats Number of rounds to fire this utterance + * @param nDamageType DAMAGE_TYPE_* + */ +void DoEnergyNegation(object oTrueSpeaker, object oTarget, struct utterance utter, int nBeats, int nDamageType); + +/** + * Checks to see if the chosen target of the Crafted Tool utterance is valid. + * If it is not valid, it will search through all slots, starting with right hand weapon + * to try and find a valid target. + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Target of the utterance + * + * @return Item in slot, or, if there are no valid objects on the creature, OBJECT_INVALID. + * If the target is an item, it returns the item. + */ +object CraftedToolTarget(object oTrueSpeaker, object oTarget); + +/** + * Enforces the cross class cap on the Truespeech skill + * + * @param oTrueSpeaker The PC whose feats to check. + * @return TRUE if needed to relevel, FALSE otherwise. + */ +int CheckTrueSpeechSkill(object oTrueSpeaker); + +/** + * Applies modifications to a utterance's damage that depend on some property + * of the target. + * Currently accounts for: + * - Mental Resistance + * - Greater Utterance Specialization + * - Intellect Fortress + * + * @param oTarget A creature being dealt damage by a utterance + * @param oTrueSpeaker The creature uttering the damaging utterance + * @param nDamage The amount of damage the creature would be dealt + * + * @param bIsHitPointDamage Is the damage HP damage or something else? + * @param bIsEnergyDamage Is the damage caused by energy or something else? Only relevant if the damage is HP damage. + * + * @return The amount of damage, modified by oTarget's abilities + */ +/*int GetTargetSpecificChangesToDamage(object oTarget, object oTrueSpeaker, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE); + +*/ + +/** + * Returns how many Cadence feats an Acolyte of the Ego has + * + * @param oTrueSpeaker The PC whose feats to check. + * @return The count of feats + */ +int GetCadenceCount(object oTrueSpeaker); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_alterations" +#include "true_inc_utter" +#include "true_inc_truknwn" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetTruespeakingClass(object oTrueSpeaker = OBJECT_SELF) +{ + return GetLocalInt(oTrueSpeaker, PRC_TRUESPEAKING_CLASS) - 1; +} + +int GetTruespeakerLevel(object oTrueSpeaker, int nSpecificClass = CLASS_TYPE_INVALID, int nUseHD = FALSE) +{ + int nLevel; + int nAdjust = GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_ADJUSTMENT); + // Bereft's speak syllables and use their character level. + if (GetIsSyllable(PRCGetSpellId())) nUseHD = TRUE; + + // If this is set, return the user's HD + if (nUseHD) return GetHitDice(oTrueSpeaker); + + // The function user needs to know the character's truespeaker level in a specific class + // instead of whatever the character last truespoken a Utterance as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + if(GetIsTruenamingClass(nSpecificClass)) + { + int nClassLevel = GetLevelByClass(nSpecificClass, oTrueSpeaker); + if (nClassLevel > 0) + { + nLevel = nClassLevel; + } + } + // A character's truespeaker level gained from non-uttering classes is always a nice, round zero + else + return 0; + } + + // Item Spells + if(GetItemPossessor(GetSpellCastItem()) == oTrueSpeaker) + { + if(DEBUG) SendMessageToPC(oTrueSpeaker, "Item casting at level " + IntToString(GetCasterLevel(oTrueSpeaker))); + + return GetCasterLevel(oTrueSpeaker) + nAdjust; + } + + // For when you want to assign the caster level. + else if(GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE) != 0) + { + if(DEBUG) SendMessageToPC(oTrueSpeaker, "Forced-level uttering at level " + IntToString(GetCasterLevel(oTrueSpeaker))); + + DelayCommand(1.0, DeleteLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE)); + nLevel = GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE); + } + else if(GetTruespeakingClass(oTrueSpeaker) != CLASS_TYPE_INVALID) + { + //Gets the level of the uttering class + int nUtteringClass = GetTruespeakingClass(oTrueSpeaker); + nLevel = GetLevelByClass(nUtteringClass, oTrueSpeaker); + } + + // If everything else fails, use the character's first class position + if(nLevel == 0) + { + if(DEBUG) DoDebug("Failed to get truespeaker level for creature " + DebugObject2Str(oTrueSpeaker) + ", using first class slot"); + else WriteTimestampedLogEntry("Failed to get truespeaker level for creature " + DebugObject2Str(oTrueSpeaker) + ", using first class slot"); + + nLevel = GetLevelByPosition(1, oTrueSpeaker); + } + + nLevel += nAdjust; + + // This spam is technically no longer necessary once the truespeaker level getting mechanism has been confirmed to work +// if(DEBUG) FloatingTextStringOnCreature("TrueSpeaker Level: " + IntToString(nLevel), oTrueSpeaker, FALSE); + + return nLevel; +} + +int GetIsTruenamingUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_TRUENAMER, oCreature) || + GetLevelByClass(CLASS_TYPE_BEREFT, oCreature) + ); +} + +int GetHighestTrueSpeakerLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetTruespeakerLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestTrueSpeakerLevel(object oCreature) +{ + return max(max(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsTruenamingClass(int nClass) +{ + return (nClass == CLASS_TYPE_TRUENAMER || + nClass == CLASS_TYPE_BEREFT + ); +} + +int GetUtteranceLevel(object oTrueSpeaker) +{ + return GetLocalInt(oTrueSpeaker, PRC_UTTERANCE_LEVEL); +} + +int GetTruenameAbilityScoreOfClass(object oTrueSpeaker, int nClass) +{ + return GetAbilityScore(oTrueSpeaker, GetTruenameAbilityOfClass(nClass)); +} + +int GetTruenameAbilityOfClass(int nClass){ + switch(nClass) + { + case CLASS_TYPE_TRUENAMER: + return ABILITY_CHARISMA; + default: + return ABILITY_CHARISMA; + } + + // Technically, never gets here but the compiler does not realise that + return -1; +} + +int GetTrueSpeakerDC(object oTrueSpeaker = OBJECT_SELF) +{ + // Things we need for DC Checks + int nSpellId = PRCGetSpellId(); + object oTarget = PRCGetSpellTargetObject(); + int nRace = MyPRCGetRacialType(oTarget); + // DC is 10 + 1/2 Truenamer level + Ability (Charisma) + int nClass = GetTruespeakingClass(oTrueSpeaker); + int nDC = 10; + nDC += GetLevelByClass(nClass, oTrueSpeaker) / 2; + nDC += GetAbilityModifier(GetTruenameAbilityOfClass(nClass), oTrueSpeaker); + int nFeat = -1; + + // Focused Lexicon. Bonus vs chosen racial type //:: [PRC .35] Needs update for new racialtypes + switch(nRace) + { + case RACIAL_TYPE_ABERRATION: nFeat = FEAT_FOCUSED_LEXICON_ABERRATION; break; + case RACIAL_TYPE_ANIMAL: nFeat = FEAT_FOCUSED_LEXICON_ANIMAL; break; + case RACIAL_TYPE_BEAST: nFeat = FEAT_FOCUSED_LEXICON_BEAST; break; + case RACIAL_TYPE_CONSTRUCT: nFeat = FEAT_FOCUSED_LEXICON_CONSTRUCT; break; + case RACIAL_TYPE_DRAGON: nFeat = FEAT_FOCUSED_LEXICON_DRAGON; break; + case RACIAL_TYPE_DWARF: nFeat = FEAT_FOCUSED_LEXICON_DWARF; break; + case RACIAL_TYPE_ELEMENTAL: nFeat = FEAT_FOCUSED_LEXICON_ELEMENTAL; break; + case RACIAL_TYPE_ELF: nFeat = FEAT_FOCUSED_LEXICON_ELF; break; + case RACIAL_TYPE_FEY: nFeat = FEAT_FOCUSED_LEXICON_FEY; break; + case RACIAL_TYPE_GIANT: nFeat = FEAT_FOCUSED_LEXICON_GIANT; break; + case RACIAL_TYPE_GNOME: nFeat = FEAT_FOCUSED_LEXICON_GNOME; break; + case RACIAL_TYPE_HALFELF: nFeat = FEAT_FOCUSED_LEXICON_HALFELF; break; + case RACIAL_TYPE_HALFLING: nFeat = FEAT_FOCUSED_LEXICON_HALFLING; break; + case RACIAL_TYPE_HALFORC: nFeat = FEAT_FOCUSED_LEXICON_HALFORC; break; + case RACIAL_TYPE_HUMAN: nFeat = FEAT_FOCUSED_LEXICON_HUMAN; break; + case RACIAL_TYPE_HUMANOID_GOBLINOID: nFeat = FEAT_FOCUSED_LEXICON_GOBLINOID; break; + case RACIAL_TYPE_HUMANOID_MONSTROUS: nFeat = FEAT_FOCUSED_LEXICON_MONSTROUS; break; + case RACIAL_TYPE_HUMANOID_ORC: nFeat = FEAT_FOCUSED_LEXICON_ORC; break; + case RACIAL_TYPE_HUMANOID_REPTILIAN: nFeat = FEAT_FOCUSED_LEXICON_REPTILIAN; break; + case RACIAL_TYPE_MAGICAL_BEAST: nFeat = FEAT_FOCUSED_LEXICON_MAGICALBEAST; break; + case RACIAL_TYPE_OOZE: nFeat = FEAT_FOCUSED_LEXICON_OOZE; break; + case RACIAL_TYPE_OUTSIDER: nFeat = FEAT_FOCUSED_LEXICON_OUTSIDER; break; + case RACIAL_TYPE_SHAPECHANGER: nFeat = FEAT_FOCUSED_LEXICON_SHAPECHANGER; break; + case RACIAL_TYPE_UNDEAD: nFeat = FEAT_FOCUSED_LEXICON_UNDEAD; break; + case RACIAL_TYPE_VERMIN: nFeat = FEAT_FOCUSED_LEXICON_VERMIN; break; + } + if(nFeat > -1 && GetHasFeat(nFeat, oTrueSpeaker)) + { + nDC += 1; + nFeat = -1; + } + + // Utterance Focus. DC Bonus for a chosen utterance + switch(nSpellId) + { + case UTTER_BREATH_CLEANSING_R: nFeat = FEAT_UTTERANCE_FOCUS_BREATH_CLEANSING; break; + case UTTER_BREATH_RECOVERY_R: nFeat = FEAT_UTTERANCE_FOCUS_BREATH_RECOVERY; break; + case UTTER_ELDRITCH_ATTRACTION: nFeat = FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION; break; + case UTTER_ELDRITCH_ATTRACTION_R: nFeat = FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION; break; + case UTTER_MORALE_BOOST_R: nFeat = FEAT_UTTERANCE_FOCUS_MORALE_BOOST; break; + case UTTER_PRETERNATURAL_CLARITY_R: nFeat = FEAT_UTTERANCE_FOCUS_PRETERNATURAL_CLARITY; break; + case UTTER_SENSORY_FOCUS_R: nFeat = FEAT_UTTERANCE_FOCUS_SENSORY_FOCUS; break; + case UTTER_SILENT_CASTER_R: nFeat = FEAT_UTTERANCE_FOCUS_SILENT_CASTER; break; + case UTTER_SINGULAR_MIND_R: nFeat = FEAT_UTTERANCE_FOCUS_SINGULAR_MIND; break; + case UTTER_TEMPORAL_SPIRAL_R: nFeat = FEAT_UTTERANCE_FOCUS_TEMPORAL_SPIRAL; break; + case UTTER_TEMPORAL_TWIST_R: nFeat = FEAT_UTTERANCE_FOCUS_TEMPORAL_TWIST; break; + case UTTER_WARD_PEACE_R: nFeat = FEAT_UTTERANCE_FOCUS_WARD_PEACE; break; + case UTTER_SHOCKWAVE: nFeat = FEAT_UTTERANCE_FOCUS_SHOCKWAVE; break; + } + if(nFeat > -1 && GetHasFeat(nFeat, oTrueSpeaker)) + nDC += 1; + + return nDC; +} + +int GetTrueSpeakPenetration(object oTrueSpeaker = OBJECT_SELF) +{ + int nPen = GetTruespeakerLevel(oTrueSpeaker); + + // According to Page 232 of Tome of Magic, Spell Pen as a feat counts, so here it is. + if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oTrueSpeaker)) nPen += 6; + else if(GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oTrueSpeaker)) nPen += 4; + else if(GetHasFeat(FEAT_SPELL_PENETRATION, oTrueSpeaker)) nPen += 2; + + // Blow away SR totally, just add 9000 + // Does not work on Syllables, only utterances + if (GetLocalInt(oTrueSpeaker, TRUE_IGNORE_SR) && !GetIsSyllable(PRCGetSpellId())) nPen += 9000; + + if(DEBUG) DoDebug("GetTrueSpeakPenetration(" + GetName(oTrueSpeaker) + "): " + IntToString(nPen)); + + return nPen; +} + +void DoLawOfSequence(object oTrueSpeaker, int nSpellId, float fDur) +{ + // This makes sure everything is stored using the Normal, and not the reverse + string sSpellId = GetNormalUtterSpellId(nSpellId); + SetLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + sSpellId, TRUE); + DelayCommand(fDur, DeleteLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + sSpellId)); +} + +int CheckLawOfSequence(object oTrueSpeaker, int nSpellId) +{ + // Turns this off + if (GetPRCSwitch(PRC_LAW_OF_SEQUENCE)) return FALSE; + // This makes sure everything is stored using the Normal, and not the reverse + return GetLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + GetNormalUtterSpellId(nSpellId)); +} + +string GetUtteranceName(int nSpellId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))); +} + +string GetLexiconName(int nLexicon) +{ + int nStrRef; + switch(nLexicon) + { + case LEXICON_EVOLVING_MIND: nStrRef = 16828478; break; + case LEXICON_CRAFTED_TOOL: nStrRef = 16828479; break; + case LEXICON_PERFECTED_MAP: nStrRef = 16828480; break; + } + + return GetStringByStrRef(nStrRef); +} + +int GetLexiconByUtterance(int nSpellId) +{ + int i, nUtter; + for(i = 0; i < GetPRCSwitch(FILE_END_CLASS_POWER) ; i++) + { + nUtter = StringToInt(Get2DACache("cls_true_utter", "SpellID", i)); + if(nUtter == nSpellId) + { + return StringToInt(Get2DACache("cls_true_utter", "Lexicon", i)); + } + } + // This should never happen + return -1; +} + +void DoSpeakUntoTheMasses(object oTrueSpeaker, object oTarget, struct utterance utter) +{ + // Check for Speak Unto the Masses, exit function if not set + if (!GetLocalInt(oTrueSpeaker, TRUE_SPEAK_UNTO_MASSES)) return; + + // Speak to the Masses affects all creatures of the same race in the AoE + int nRacial = MyPRCGetRacialType(oTarget); + object oSkin; + + // Loop over targets + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: While entered"); + // Skip the original target/truespeaker, its already been hit + if (oAreaTarget != oTarget && oAreaTarget != oTrueSpeaker) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Target check"); + // Targeting limitations + if(MyPRCGetRacialType(oAreaTarget) == nRacial) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Racial Check"); + // Only affect friends or ignore it + if (GetIsFriend(oAreaTarget, oTrueSpeaker) || !utter.bFriend) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Friend Check"); + // Do SR, or ignore if its a friendly utterance. + if (!PRCDoResistSpell(utter.oTrueSpeaker, oAreaTarget, utter.nPen) || utter.bIgnoreSR) + { + if(DEBUG) DoDebug("Speak Unto the Masses: SR Check"); + // Saving throw, ignore it if there is no DC to check + if(!PRCMySavingThrow(utter.nSaveThrow, oAreaTarget, utter.nSaveDC, utter.nSaveType, OBJECT_SELF) || + utter.nSaveDC == 0) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Saving Throw"); + // Itemproperty, if there is one + oSkin = GetPCSkin(oAreaTarget); + if (GetIsItemPropertyValid(utter.ipIProp1)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: IProp1"); + IPSafeAddItemProperty(oSkin, utter.ipIProp1, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + // Itemproperty, if there is one + if (GetIsItemPropertyValid(utter.ipIProp2)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: IProp2"); + IPSafeAddItemProperty(oSkin, utter.ipIProp2, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + // Itemproperty, if there is one + if (GetIsItemPropertyValid(utter.ipIProp3)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: IProp3"); + IPSafeAddItemProperty(oSkin, utter.ipIProp3, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + // Duration Effects + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, utter.eLink, oAreaTarget, utter.fDur, TRUE, utter.nSpellId, utter.nTruespeakerLevel); + if(DEBUG) DoDebug("Speak Unto the Masses: Duration"); + // Impact Effects + SPApplyEffectToObject(DURATION_TYPE_INSTANT, utter.eLink2, oAreaTarget); + if(DEBUG) DoDebug("Speak Unto the Masses: Instant"); + // Utterance Specific code down here + DoWordOfNurturingReverse(oTrueSpeaker, oAreaTarget, utter); + if(DEBUG) DoDebug("Speak Unto the Masses: Word of Nurturing Reverse"); + if (utter.nSpellId == UTTER_ENERGY_NEGATION_R) + DoEnergyNegation(oTrueSpeaker, oTarget, utter, FloatToInt(utter.fDur / 6.0), GetLocalInt(oTrueSpeaker, "TrueEnergyNegation")); + } // end if - Saving Throw + } // end if - Spell Resistance + } // end if - Friend Check + }// end if - Targeting check + } + + // Get next target + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE); + }// end while - Target loop +} + +void DoWordOfNurturingReverse(object oTrueSpeaker, object oTarget, struct utterance utter) +{ + // Returns TRUE upon concentration failure + if (GetBreakConcentrationCheck(oTrueSpeaker)) return; + + int nDamage; + // First, find out what utterance we're using + if (utter.nSpellId == UTTER_WORD_NURTURING_MINOR_R) nDamage = d6(); + else if (utter.nSpellId == UTTER_WORD_NURTURING_LESSER_R) nDamage = d6(2); + else if (utter.nSpellId == UTTER_WORD_NURTURING_MODERATE_R) nDamage = d6(4); + else if (utter.nSpellId == UTTER_WORD_NURTURING_POTENT_R) nDamage = d6(6); + else if (utter.nSpellId == UTTER_WORD_NURTURING_CRITICAL_R) nDamage = d6(8); + else if (utter.nSpellId == UTTER_WORD_NURTURING_GREATER_R) nDamage = d6(10); + // Empower it + if(utter.bEmpower) nDamage += (nDamage/2); + // If we're using this, target has already failed SR and Saves + effect eImp = EffectLinkEffects(EffectVisualEffect(VFX_IMP_MAGLAW), EffectDamage(nDamage)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eImp, oTarget); +} + +void DoEnergyNegation(object oTrueSpeaker, object oTarget, struct utterance utter, int nBeats, int nDamageType) +{ + int nDamage = d6(2); + // Empower it + if(utter.bEmpower) nDamage += (nDamage/2); + // Impact VFX + utter.eLink2 = EffectLinkEffects(EffectVisualEffect(VFX_IMP_MAGVIO), EffectDamage(nDamage, nDamageType)); + // Impact Effects + SPApplyEffectToObject(DURATION_TYPE_INSTANT, utter.eLink2, oTarget); + + nBeats -= 1; + if (nBeats > 0) + DelayCommand(6.0, DoEnergyNegation(oTrueSpeaker, oTarget, utter, nBeats, nDamageType)); +} + +object CraftedToolTarget(object oTrueSpeaker, object oTarget) +{ + // Check to see if its a weapon or item of some sort + // Return it if its a valid base item type + if (GetBaseItemType(oTarget) != BASE_ITEM_INVALID) return oTarget; + + object oItem = OBJECT_INVALID; + + // These are utterances that only target weapons + if (PRCGetSpellId() == UTTER_KEEN_WEAPON || PRCGetSpellId() == UTTER_SUPPRESS_WEAPON || PRCGetSpellId() == UTTER_TRANSMUTE_WEAPON) + { + // By the time we're here, it should only be creatures, not items as targets + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + // Only do this for Keen + if (PRCGetSpellId() == UTTER_KEEN_WEAPON) + { + // Put the bonus on the ammo rather than the bow if its ranged + if( GetBaseItemType(oItem) == BASE_ITEM_LONGBOW || GetBaseItemType(oItem) == BASE_ITEM_SHORTBOW ) + { + oItem = GetItemInSlot(INVENTORY_SLOT_ARROWS, oTarget); + } + else if(GetBaseItemType(oItem) == BASE_ITEM_LIGHTCROSSBOW || GetBaseItemType(oItem) == BASE_ITEM_HEAVYCROSSBOW) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BOLTS, oTarget); + } + else if(GetBaseItemType(oItem) == BASE_ITEM_SLING) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BULLETS, oTarget); + } + } + // If its a valid weapon, return it + if (GetBaseItemType(oItem) != BASE_ITEM_INVALID) return oItem; + // Check the spare hand, and make sure its not a shield + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + // If its a valid weapon and not a shield, return it + if (GetBaseItemType(oItem) != BASE_ITEM_INVALID && + GetBaseItemType(oItem) != BASE_ITEM_LARGESHIELD && + GetBaseItemType(oItem) != BASE_ITEM_SMALLSHIELD && + GetBaseItemType(oItem) != BASE_ITEM_TOWERSHIELD) return oItem; + }// These ones target only armour + else if (PRCGetSpellId() == UTTER_FORTIFY_ARMOUR_SNEAK || PRCGetSpellId() == UTTER_FORTIFY_ARMOUR_CRIT) + { + return GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + }// This one targets scrolls and potions + else if (PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_EMP || PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_EXT || + PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_MAX) + { + oItem = GetFirstItemInInventory(oTarget); + while(GetIsObjectValid(oItem)) + { + if (GetBaseItemType(oItem) == BASE_ITEM_SCROLL || GetBaseItemType(oItem) == BASE_ITEM_POTIONS) + { + return oItem; + } + oItem = GetNextItemInInventory(oTarget); + } + } + else // For the rest of the utterances, any item is a valid target. + { + // Get the PC's chosen inventory slot + int nSlot = GetLocalInt(oTrueSpeaker, "TrueCraftedToolTargetSlot"); + oItem = GetItemInSlot(nSlot, oTarget); + // If the chosen item isn't valid, we go into the choice progession + // Yes, its a long chain + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_HEAD, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTRING, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_NECK, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_CLOAK, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_ARMS, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BOOTS, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BELT, oTarget); + } + } + } + } + } + } + } + } + } + } + } + } + return oItem; +} + +int CheckTrueSpeechSkill(object oTrueSpeaker) +{ + // The max for a class skill is 3 + 1 per level. We just divide this in half for Cross Class + int nMax = GetHitDice(oTrueSpeaker) + 3; + nMax /= 2; + // We want base ranks only + int nRanks = GetSkillRank(SKILL_TRUESPEAK, oTrueSpeaker, TRUE); + + // The Truenamer class has Truespeech as a class skill, so no relevel + if (GetLevelByClass(CLASS_TYPE_TRUENAMER, oTrueSpeaker) > 0) return FALSE; + // Same for this class + else if (GetLevelByClass(CLASS_TYPE_BEREFT, oTrueSpeaker) > 0) return FALSE; + // And this one + else if (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oTrueSpeaker) > 0) return FALSE; + // If they have the feat, no relevel + else if(GetHasFeat(FEAT_TRUENAME_TRAINING, oTrueSpeaker)) return FALSE; + // Now we check the values. If they have too many ranks, relevel. + else if (nRanks > nMax) + { + // Relevel + FloatingTextStringOnCreature("You cannot have more than " + IntToString(nMax) + " in TrueSpeech.", oTrueSpeaker, FALSE); + return TRUE; + } + + // No relevel normally + return FALSE; +} + +/* +int GetTargetSpecificChangesToDamage(object oTarget, object oTrueSpeaker, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE) +{ + // Greater Utterance Specialization - +2 damage on all HP-damaging utterances when target is within 30ft + if(bIsHitPointDamage && + GetHasFeat(FEAT_GREATER_Utterance_SPECIALIZATION, oTrueSpeaker) && + GetDistanceBetween(oTarget, oTrueSpeaker) <= FeetToMeters(30.0f) + ) + nDamage += 2; + // Intellect Fortress - Halve damage dealt by utterances that allow PR. Goes before DR (-like) reductions + if(GetLocalInt(oTarget, "PRC_Utterance_IntellectFortress_Active") && + Get2DACache("spells", "ItemImmunity", PRCGetSpellId()) == "1" + ) + nDamage /= 2; + // Mental Resistance - 3 damage less for all non-energy damage and ability damage + if(GetHasFeat(FEAT_MENTAL_RESISTANCE, oTarget) && !bIsEnergyDamage) + nDamage -= 3; + + // Reasonable return values only + if(nDamage < 0) nDamage = 0; + + return nDamage; +} +*/ +// Test main +//void main(){} + +int GetCadenceCount(object oTrueSpeaker) +{ + int nClass = GetLevelByClass(CLASS_TYPE_ACOLYTE_EGO, oTrueSpeaker); + if (GetLocalInt(oTrueSpeaker, "ResonantVoice") == TRUE) + { + nClass += 3; //Adds 3 to class level + } + + int nCount = nClass/2; //Get a cadence feat at 2, 4, 6, 8, 10 levels. + + if (nCount > 6) nCount = 6; //Can't go above 6 with Resonant Voice active + + // Return total + return nCount; +} \ No newline at end of file