Disabled Hidden Talent until it's actually finished. Fixed a few typos & some formatting.
789 lines
27 KiB
Plaintext
789 lines
27 KiB
Plaintext
// Get the DC to save against for a spell (10 + spell level + relevant ability
|
|
// bonus). This can be called by a creature or by an Area of Effect object.
|
|
// Takes into account PRC classes
|
|
int PRCGetSpellSaveDC(int nSpellID = -1, int nSchool = -1, object oCaster = OBJECT_SELF);
|
|
|
|
// Use this function to get the adjustments to a spell or SLAs saving throw
|
|
// from the various class effects
|
|
// Update this function if any new classes change saving throws
|
|
int PRCGetSaveDC(object oTarget, object oCaster, int nSpellID = -1);
|
|
|
|
//called just from above and from inc_epicspells
|
|
int GetChangesToSaveDC(object oTarget, object oCaster, int nSpellID, int nSchool);
|
|
|
|
#include "prc_add_spl_pen"
|
|
//#include "prc_inc_spells"
|
|
//#include "prc_class_const"
|
|
//#include "prc_feat_const"
|
|
//#include "lookup_2da_spell"
|
|
//#include "prcsp_archmaginc"
|
|
//#include "prc_alterations"
|
|
//#include "prc_inc_racial"
|
|
#include "inc_newspellbook"
|
|
|
|
int GetCorruptSpellFocus(int nSpellID, object oCaster)
|
|
{
|
|
int nCorrupt = FALSE;
|
|
if(nSpellID == SPELL_ABSORB_STRENGTH
|
|
|| nSpellID == SPELL_APOCALYPSE_FROM_THE_SKY
|
|
|| nSpellID == SPELL_CLAWS_OF_THE_BEBILITH
|
|
|| nSpellID == SPELL_DEATH_BY_THORNS
|
|
|| nSpellID == SPELL_EVIL_WEATHER
|
|
|| nSpellID == SPELL_FANGS_OF_THE_VAMPIRE_KING
|
|
|| nSpellID == SPELL_LAHMS_FINGER_DARTS
|
|
|| nSpellID == SPELL_POWER_LEECH
|
|
|| nSpellID == SPELL_RAPTURE_OF_RUPTURE
|
|
|| nSpellID == SPELL_RED_FESTER
|
|
|| nSpellID == SPELL_ROTTING_CURSE_OF_URFESTRA
|
|
|| nSpellID == SPELL_SEETHING_EYEBANE
|
|
|| nSpellID == SPELL_TOUCH_OF_JUIBLEX)
|
|
nCorrupt = TRUE;
|
|
|
|
if (GetHasFeat(FEAT_GREATER_CORRUPT_SPELL_FOCUS, oCaster) && nCorrupt) return 2;
|
|
else if (GetHasFeat(FEAT_CORRUPT_SPELL_FOCUS, oCaster) && nCorrupt) return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int GetHeartWarderDC(int spell_id, int nSchool, object oCaster)
|
|
{
|
|
// Check the curent school
|
|
if(nSchool != SPELL_SCHOOL_ENCHANTMENT)
|
|
return 0;
|
|
|
|
if(!GetHasFeat(FEAT_VOICE_SIREN, oCaster))
|
|
return 0;
|
|
|
|
// Bonus Requires Verbal Spells
|
|
string VS = GetStringLowerCase(Get2DACache("spells", "VS",spell_id));
|
|
if(FindSubString(VS, "v") == -1)
|
|
return 0;
|
|
|
|
// These feats provide greater bonuses or remove the Verbal requirement
|
|
if(PRCGetMetaMagicFeat(oCaster, FALSE) & METAMAGIC_SILENT
|
|
|| GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oCaster)
|
|
|| GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ENCHANTMENT, oCaster))
|
|
return 0;
|
|
|
|
return 2;
|
|
}
|
|
|
|
//Elemental Savant DC boost based on elemental spell type.
|
|
int ElementalSavantDC(int spell_id, int nElement, object oCaster)
|
|
{
|
|
int nDC = 0;
|
|
|
|
// All Elemental Savants will have this feat
|
|
// when they first gain a DC bonus.
|
|
if(GetHasFeat(FEAT_ES_FOCUS_1, oCaster))
|
|
{
|
|
// Any value that does not match one of the enumerated feats
|
|
int feat, nES;
|
|
nES = GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster);
|
|
|
|
// Specify the elemental type rather than lookup by class?
|
|
if(nElement & DESCRIPTOR_FIRE)
|
|
{
|
|
feat = FEAT_ES_FIRE;
|
|
}
|
|
else if(nElement & DESCRIPTOR_COLD)
|
|
{
|
|
feat = FEAT_ES_COLD;
|
|
}
|
|
else if(nElement & DESCRIPTOR_ELECTRICITY)
|
|
{
|
|
feat = FEAT_ES_ELEC;
|
|
}
|
|
else if(nElement & DESCRIPTOR_ACID)
|
|
{
|
|
feat = FEAT_ES_ACID;
|
|
}
|
|
|
|
// Now determine the bonus
|
|
if(feat && GetHasFeat(feat, oCaster))
|
|
nDC = (nES + 1) / 3;
|
|
}
|
|
// SendMessageToPC(GetFirstPC(), "Your Elemental Focus modifier is " + IntToString(nDC));
|
|
return nDC;
|
|
}
|
|
|
|
// This does other spell focus feats, starting with Spell Focus: Cold
|
|
int SpellFocus(int nSpellId, int nElement, object oCaster)
|
|
{
|
|
int nDC = 0;
|
|
|
|
// Specify the elemental type
|
|
if(nElement & DESCRIPTOR_COLD)
|
|
{
|
|
if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_COLD, oCaster))
|
|
nDC += 2;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_COLD, oCaster))
|
|
nDC += 1;
|
|
}
|
|
if (GetHasDescriptor(nSpellId, DESCRIPTOR_CHAOTIC) && GetHasFeat(FEAT_SPELL_FOCUS_CHAOS, oCaster)) nDC += 1;
|
|
if (GetHasDescriptor(nSpellId, DESCRIPTOR_EVIL) && GetHasFeat(FEAT_SPELL_FOCUS_EVIL, oCaster)) nDC += 1;
|
|
if (GetHasDescriptor(nSpellId, DESCRIPTOR_GOOD) && GetHasFeat(FEAT_SPELL_FOCUS_GOOD, oCaster)) nDC += 1;
|
|
if (GetHasDescriptor(nSpellId, DESCRIPTOR_LAWFUL) && GetHasFeat(FEAT_SPELL_FOCUS_LAWFUL, oCaster)) nDC += 1;
|
|
|
|
return nDC;
|
|
}
|
|
|
|
//Red Wizard DC boost based on spell school specialization
|
|
int RedWizardDC(int spell_id, int nSchool, object oCaster)
|
|
{
|
|
int iRedWizard = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster);
|
|
int nDC;
|
|
|
|
if(iRedWizard)
|
|
{
|
|
int iRWSpec;
|
|
switch(nSchool)
|
|
{
|
|
case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break;
|
|
case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break;
|
|
case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break;
|
|
case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break;
|
|
case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break;
|
|
case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break;
|
|
case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break;
|
|
case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break;
|
|
}
|
|
|
|
if(iRWSpec && GetHasFeat(iRWSpec, oCaster))
|
|
nDC = iRedWizard / 2;
|
|
}
|
|
// SendMessageToPC(GetFirstPC(), "Your Spell Power modifier is " + IntToString(nDC));
|
|
return nDC;
|
|
}
|
|
|
|
//Red Wizards recieve a bonus against their specialist schools
|
|
// this is done by lowering the DC of spells cast against them
|
|
int RedWizardDCPenalty(int spell_id, int nSchool, object oTarget)
|
|
{
|
|
int nDC;
|
|
int iRW = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oTarget);
|
|
if(iRW)
|
|
{
|
|
int iRWSpec;
|
|
switch(nSchool)
|
|
{
|
|
case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break;
|
|
case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break;
|
|
case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break;
|
|
case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break;
|
|
case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break;
|
|
case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break;
|
|
case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break;
|
|
case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break;
|
|
}
|
|
|
|
if(iRWSpec && GetHasFeat(iRWSpec, oTarget))
|
|
nDC -= iRW > 4 ? (iRW - 1) / 2 : (iRW + 1) / 2;
|
|
}
|
|
return nDC;
|
|
}
|
|
|
|
int ShadowAdeptDCPenalty(int spell_id, int nSchool, object oTarget)
|
|
{
|
|
int nDC;
|
|
int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oTarget);
|
|
if(iShadow)
|
|
{
|
|
if(nSchool == SPELL_SCHOOL_ENCHANTMENT
|
|
|| nSchool == SPELL_SCHOOL_NECROMANCY
|
|
|| nSchool == SPELL_SCHOOL_ILLUSION)
|
|
{
|
|
nDC -= (iShadow + 1) / 3;
|
|
}
|
|
//SendMessageToPC(GetFirstPC(), "Your Spell Save modifier is " + IntToString(nDC));
|
|
}
|
|
return nDC;
|
|
}
|
|
|
|
//Tattoo Focus DC boost based on spell school specialization
|
|
int TattooFocus(int spell_id, int nSchool, object oCaster)
|
|
{
|
|
int nDC;
|
|
int iRWSpec;
|
|
switch(nSchool)
|
|
{
|
|
case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break;
|
|
case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break;
|
|
case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break;
|
|
case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break;
|
|
case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break;
|
|
case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break;
|
|
case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break;
|
|
case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break;
|
|
}
|
|
|
|
if(iRWSpec && GetHasFeat(iRWSpec, oCaster))
|
|
nDC = 1;
|
|
|
|
return nDC;
|
|
}
|
|
|
|
//:: Jaebrins get a +1 to Enchantment spells.
|
|
int JaebrinEnchant(int nSchool, object oCaster)
|
|
{
|
|
int nDC;
|
|
|
|
if(nSchool == SPELL_SCHOOL_ENCHANTMENT && GetRacialType(oCaster) == RACIAL_TYPE_JAEBRIN)
|
|
nDC = 1;
|
|
|
|
return nDC;
|
|
}
|
|
|
|
int ShadowWeaveDC(int spell_id, int nSchool, object oCaster)
|
|
{
|
|
// Account for the Shadow Weave feat
|
|
int nDC = ShadowWeave(oCaster, spell_id, nSchool) == 1;
|
|
|
|
// Account for Shadow Adept levels
|
|
int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster);
|
|
if(iShadow && nDC)
|
|
// Shadow Spell Power
|
|
nDC += iShadow / 3;
|
|
|
|
return nDC;
|
|
}
|
|
|
|
int KOTCSpellFocusVsDemons(object oTarget, object oCaster)
|
|
{
|
|
if(GetLevelByClass(CLASS_TYPE_KNIGHT_CHALICE, oCaster) >= 1)
|
|
{
|
|
if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_OUTSIDER)
|
|
{
|
|
if(GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL)
|
|
{
|
|
return 2;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int BloodMagusBloodComponent(object oCaster)
|
|
{
|
|
int nDC = 0;
|
|
if (GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster) > 0 && GetLocalInt(oCaster, "BloodComponent") == TRUE)
|
|
{
|
|
nDC = 1;
|
|
effect eSelfDamage = EffectDamage(1, DAMAGE_TYPE_MAGICAL);
|
|
// To make sure it doesn't cause a conc check
|
|
DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eSelfDamage, oCaster));
|
|
}
|
|
return nDC;
|
|
}
|
|
|
|
int RunecasterRunePowerDC(object oCaster)
|
|
{
|
|
int nDC;
|
|
|
|
if(GetHasSpellEffect(SPELL_RUNE_CHANT))
|
|
{
|
|
int nClass = GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster);
|
|
|
|
if (nClass >= 30) nDC = 10;
|
|
else if (nClass >= 27) nDC = 9;
|
|
else if (nClass >= 24) nDC = 8;
|
|
else if (nClass >= 21) nDC = 7;
|
|
else if (nClass >= 18) nDC = 6;
|
|
else if (nClass >= 15) nDC = 5;
|
|
else if (nClass >= 12) nDC = 4;
|
|
else if (nClass >= 9) nDC = 3;
|
|
else if (nClass >= 5) nDC = 2;
|
|
else if (nClass >= 2) nDC = 1;
|
|
}
|
|
return nDC;
|
|
}
|
|
|
|
//Unheavened spell
|
|
int UnheavenedAdjustment(object oTarget, object oCaster)
|
|
{
|
|
if(GetHasSpellEffect(SPELL_UNHEAVENED, oTarget))
|
|
{
|
|
if((MyPRCGetRacialType(oCaster) == RACIAL_TYPE_OUTSIDER) && (GetAlignmentGoodEvil(oCaster) == ALIGNMENT_GOOD))
|
|
{
|
|
return -4;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Soul Eater's 10th level Soul Power ability. If they've drained in the last 24h, they get +2 to DCs
|
|
int SoulEaterSoulPower(object oCaster)
|
|
{
|
|
return (GetLocalInt(oCaster, "PRC_SoulEater_HasDrained") && GetLevelByClass(CLASS_TYPE_SOUL_EATER, oCaster) >= 10) ? 2 : 0;
|
|
}
|
|
|
|
//:: Saint Template gets a +2 DC on all spells, powers & abilites.
|
|
int SaintHolySpellPower(object oCaster)
|
|
{
|
|
if(GetHasFeat(FEAT_TEMPLATE_SAINT_HOLY_POWER, oCaster))
|
|
{
|
|
if (GetAlignmentGoodEvil(oCaster) == ALIGNMENT_GOOD)
|
|
{
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
//:: If it gets here, the caster does not have the feat
|
|
return 0;
|
|
}
|
|
|
|
//Draconic Power's elemental boost to spell DCs
|
|
int DraconicPowerDC(int spell_id, int nElement, object oCaster)
|
|
{
|
|
if(GetHasFeat(FEAT_DRACONIC_POWER, oCaster))
|
|
{
|
|
// Compare heritage type and elemental type
|
|
if(nElement & DESCRIPTOR_FIRE)
|
|
{
|
|
if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BS, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_GD, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_RD, oCaster))
|
|
return 1;
|
|
}
|
|
else if(nElement & DESCRIPTOR_COLD)
|
|
{
|
|
if(GetHasFeat(FEAT_DRACONIC_HERITAGE_CR, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_SR, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_TP, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_WH, oCaster))
|
|
return 1;
|
|
}
|
|
else if(nElement & DESCRIPTOR_ELECTRICITY)
|
|
{
|
|
if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BL, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_BZ, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_SA, oCaster))
|
|
return 1;
|
|
}
|
|
else if(nElement & DESCRIPTOR_ACID)
|
|
{
|
|
if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BK, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_CP, oCaster)
|
|
|| GetHasFeat(FEAT_DRACONIC_HERITAGE_GR, oCaster))
|
|
return 1;
|
|
}
|
|
else if(nElement & DESCRIPTOR_SONIC)
|
|
{
|
|
if(GetHasFeat(FEAT_DRACONIC_HERITAGE_EM, oCaster))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
//if it gets here, the caster does not have the feat, or is a heritage type without a NWN element (e.g. Amethyst)
|
|
return 0;
|
|
}
|
|
|
|
//Energy Draconc Aura's elemental boost to spell DCs
|
|
int EnergyAuraDC(int spell_id, int nElement, object oCaster)
|
|
{
|
|
// Compare aura type and elemental type
|
|
if(nElement & DESCRIPTOR_FIRE)
|
|
return GetLocalInt(oCaster, "FireEnergyAura");
|
|
|
|
else if(nElement & DESCRIPTOR_COLD)
|
|
return GetLocalInt(oCaster, "ColdEnergyAura");
|
|
|
|
else if(nElement & DESCRIPTOR_ELECTRICITY)
|
|
return GetLocalInt(oCaster, "ElecEnergyAura");
|
|
|
|
else if(nElement & DESCRIPTOR_ACID)
|
|
return GetLocalInt(oCaster, "AcidEnergyAura");
|
|
|
|
//if it gets here, the caster is not in this type of Draconic Aura
|
|
return 0;
|
|
}
|
|
|
|
//Spirit Folk get a better save vs elemental stuff
|
|
int SpiritFolkAdjustment(int spell_id, int nElement, object oTarget)
|
|
{
|
|
if(nElement & DESCRIPTOR_FIRE && GetHasFeat(FEAT_BONUS_SEA, oTarget))
|
|
{
|
|
return -2;
|
|
}
|
|
else if(nElement & DESCRIPTOR_COLD && GetHasFeat(FEAT_BONUS_RIVER, oTarget))
|
|
{
|
|
return -2;
|
|
}
|
|
else if(nElement & DESCRIPTOR_ACID && GetHasFeat(FEAT_BONUS_BAMBOO, oTarget))
|
|
{
|
|
return -2;
|
|
}
|
|
|
|
//if it gets here, the target is not a Spirit Folk
|
|
return 0;
|
|
}
|
|
|
|
//Angry Spell for Rage Mage class
|
|
int AngrySpell(int spell_id, int nSchool, object oCaster)
|
|
{
|
|
int nDC;
|
|
|
|
if(GetHasSpellEffect(SPELL_SPELL_RAGE, oCaster))
|
|
{
|
|
if(nSchool == SPELL_SCHOOL_ABJURATION
|
|
|| nSchool == SPELL_SCHOOL_CONJURATION
|
|
|| nSchool == SPELL_SCHOOL_EVOCATION
|
|
|| nSchool == SPELL_SCHOOL_NECROMANCY
|
|
|| nSchool == SPELL_SCHOOL_TRANSMUTATION)
|
|
{
|
|
if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) >= 10)
|
|
nDC = 4;
|
|
else if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) >= 5)
|
|
nDC = 2;
|
|
}
|
|
}
|
|
|
|
return nDC;
|
|
}
|
|
|
|
int CloakedCastingDC(int spell_id, object oTarget, object oCaster)
|
|
{
|
|
int nDC;
|
|
int iBeguiler = GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster);
|
|
|
|
if(iBeguiler)
|
|
{
|
|
if(GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE))
|
|
{
|
|
if(iBeguiler >= 14)
|
|
nDC = 2;
|
|
else if(iBeguiler >= 2)
|
|
nDC = 1;
|
|
}
|
|
}
|
|
|
|
return nDC;
|
|
}
|
|
|
|
// Wyrmbane Helm
|
|
int WyrmbaneHelmDC(object oTarget, object oCaster)
|
|
{
|
|
// You get nothing if you aren't wielding the legacy item
|
|
object oWOL = GetItemPossessedBy(oCaster, "WOL_Wyrmbane");
|
|
if(oWOL != GetItemInSlot(INVENTORY_SLOT_HEAD, oCaster)) return 0;
|
|
|
|
if((MyPRCGetRacialType(oTarget) == RACIAL_TYPE_DRAGON))
|
|
{
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Arkamoi Strength From Magic
|
|
int StrengthFromMagic(object oCaster)
|
|
{
|
|
if (GetRacialType(oCaster) != RACIAL_TYPE_ARKAMOI)
|
|
return 0;
|
|
|
|
if (GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster)))
|
|
return GetLocalInt(oCaster, "StrengthFromMagic");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Hobgoblin Warsoul Soul Tyrant
|
|
int SoulTyrant(object oCaster)
|
|
{
|
|
if (GetRacialType(oCaster) != RACIAL_TYPE_HOBGOBLIN_WARSOUL)
|
|
return 0;
|
|
|
|
if (GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster)))
|
|
return GetLocalInt(oCaster, "WarsoulTyrant");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int PRCGetSpellSaveDC(int nSpellID = -1, int nSchool = -1, object oCaster = OBJECT_SELF)
|
|
{
|
|
if(nSpellID == -1)
|
|
nSpellID = PRCGetSpellId();
|
|
if(nSchool == -1)
|
|
nSchool = GetSpellSchool(nSpellID);
|
|
|
|
int nClass = PRCGetLastSpellCastClass(oCaster);
|
|
int nDC = 10;
|
|
|
|
if(nClass == CLASS_TYPE_BARD)
|
|
nDC += StringToInt(Get2DACache("Spells", "Bard", nSpellID));
|
|
else if(nClass == CLASS_TYPE_CLERIC || nClass == CLASS_TYPE_UR_PRIEST || nClass == CLASS_TYPE_OCULAR)
|
|
nDC += StringToInt(Get2DACache("Spells", "Cleric", nSpellID));
|
|
else if(nClass == CLASS_TYPE_DRUID)
|
|
nDC += StringToInt(Get2DACache("Spells", "Druid", nSpellID));
|
|
else if(nClass == CLASS_TYPE_RANGER)
|
|
nDC += StringToInt(Get2DACache("Spells", "Ranger", nSpellID));
|
|
else if(nClass == CLASS_TYPE_PALADIN)
|
|
nDC += StringToInt(Get2DACache("Spells", "Paladin", nSpellID));
|
|
else if (nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK)
|
|
nDC += StringToInt(Get2DACache("spells", "Cultist", nSpellID));
|
|
else if (nClass == CLASS_TYPE_NENTYAR_HUNTER)
|
|
nDC += StringToInt(Get2DACache("spells", "Nentyar", nSpellID));
|
|
else if (nClass == CLASS_TYPE_SHADOWLORD)
|
|
nDC += StringToInt(Get2DACache("spells", "Telflammar", nSpellID));
|
|
else if (nClass == CLASS_TYPE_SLAYER_OF_DOMIEL)
|
|
nDC += StringToInt(Get2DACache("spells", "Domiel", nSpellID));
|
|
else if (nClass == CLASS_TYPE_SOHEI)
|
|
nDC += StringToInt(Get2DACache("spells", "Sohei", nSpellID));
|
|
else if (nClass == CLASS_TYPE_VASSAL)
|
|
nDC += StringToInt(Get2DACache("spells", "Bahamut", nSpellID));
|
|
else if (nClass == CLASS_TYPE_BLACKGUARD)
|
|
nDC += StringToInt(Get2DACache("spells", "Blackguard", nSpellID));
|
|
else if (nClass == CLASS_TYPE_KNIGHT_CHALICE)
|
|
nDC += StringToInt(Get2DACache("spells", "Chalice", nSpellID));
|
|
else if (nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE)
|
|
nDC += StringToInt(Get2DACache("spells", "MiddleCircle", nSpellID));
|
|
else if (nClass == CLASS_TYPE_SOLDIER_OF_LIGHT)
|
|
nDC += StringToInt(Get2DACache("spells", "SoLight", nSpellID));
|
|
else if (nClass == CLASS_TYPE_BLIGHTER)
|
|
nDC += StringToInt(Get2DACache("spells", "Blighter", nSpellID));
|
|
else if (nClass == CLASS_TYPE_HEALER)
|
|
nDC += StringToInt(Get2DACache("spells", "Healer", nSpellID));
|
|
else if (nClass == CLASS_TYPE_SHAMAN)
|
|
nDC += StringToInt(Get2DACache("spells", "Shaman", nSpellID));
|
|
else if(nClass == CLASS_TYPE_WIZARD
|
|
|| nClass == CLASS_TYPE_SORCERER)
|
|
nDC += StringToInt(Get2DACache("Spells", "Wiz_Sorc", nSpellID));
|
|
else if(nClass != CLASS_TYPE_INVALID)
|
|
{
|
|
int nSpellbookID = RealSpellToSpellbookID(nClass, nSpellID);
|
|
string sFile = GetFileForClass(nClass);
|
|
nDC += StringToInt(Get2DACache(sFile, "Level", nSpellbookID));
|
|
}
|
|
else
|
|
nDC += StringToInt(Get2DACache("Spells", "Innate", nSpellID));
|
|
|
|
// This is here because a Cleric casting a domain spell like Chain Lightning has a 0 in the cleric column, resulting in a DC of 10
|
|
if (nDC == 10 && nClass == CLASS_TYPE_CLERIC)
|
|
nDC += StringToInt(Get2DACache("Spells", "Innate", nSpellID));
|
|
|
|
nDC += GetDCAbilityModForClass(nClass, oCaster);
|
|
|
|
object oItem = GetSpellCastItem();
|
|
|
|
int nEpic = 6;
|
|
int nGreat = 4;
|
|
int nSF = 2;
|
|
|
|
if (GetPRCSwitch(PRC_35_SPELL_FOCUS))
|
|
{
|
|
nEpic = 3;
|
|
nGreat = 2;
|
|
nSF = 1;
|
|
}
|
|
|
|
if(DEBUG && !GetIsObjectValid(oItem)) DoDebug("PRCGetSpellSaveDC oItem is OBJECT_INVALID");
|
|
if(DEBUG) DoDebug("PRCGetSpellSaveDC oCaster "+GetName(oCaster)+", nSpell "+IntToString(nSpellID)+", nSchool "+IntToString(nSchool)+", nClass "+IntToString(nClass)+", oItem "+GetName(oItem));
|
|
|
|
if(!GetIsObjectValid(oItem) || (GetBaseItemType(oItem) == BASE_ITEM_MAGICSTAFF && GetPRCSwitch(PRC_STAFF_CASTER_LEVEL)))
|
|
{
|
|
if(nSchool == SPELL_SCHOOL_EVOCATION)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_EVOCATION, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_EVOCATION, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_EVOCATION, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_TRANSMUTATION)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_TRANSMUTATION, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_TRANSMUTATION, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_TRANSMUTATION, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_NECROMANCY)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_NECROMANCY, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_NECROMANCY, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_NECROMANCY, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_ILLUSION)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ILLUSION, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ILLUSION, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_ILLUSION, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_ABJURATION)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ABJURATION, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ABJURATION, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_ABJURATION, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_CONJURATION)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_CONJURATION, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_CONJURATION, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_CONJURATION, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_DIVINATION)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_DIVINATION, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_DIVINATION, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_DIVINATION, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_ENCHANTMENT)
|
|
{
|
|
if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ENCHANTMENT, oCaster))
|
|
nDC+=nEpic;
|
|
else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oCaster))
|
|
nDC+=nGreat;
|
|
else if(GetHasFeat(FEAT_SPELL_FOCUS_ENCHANTMENT, oCaster))
|
|
nDC+=nSF;
|
|
}
|
|
}
|
|
|
|
return nDC;
|
|
}
|
|
|
|
int PRCGetSaveDC(object oTarget, object oCaster, int nSpellID = -1)
|
|
{
|
|
object oItem = GetSpellCastItem();
|
|
if(nSpellID == -1)
|
|
nSpellID = PRCGetSpellId();
|
|
int nSchool = GetSpellSchool(nSpellID);
|
|
int nDC;
|
|
// at this point, if it's still -1 then this is running on an AoE
|
|
if (nSpellID == -1)
|
|
{
|
|
// get the needed values off the AoE
|
|
nSpellID = GetLocalInt(OBJECT_SELF, "X2_AoE_SpellID");
|
|
nDC = GetLocalInt(OBJECT_SELF, "X2_AoE_BaseSaveDC");
|
|
nSchool = GetSpellSchool(nSpellID);
|
|
}
|
|
else // not persistent AoE script
|
|
{
|
|
//10+spelllevel+stat(cha default)
|
|
nDC = PRCGetSpellSaveDC(nSpellID, nSchool, oCaster);
|
|
}
|
|
|
|
// 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);
|
|
if(DEBUG) DoDebug("Forced-DC PRC_DC_TOTAL_OVERRIDE casting at DC " + IntToString(nDC));
|
|
}
|
|
// For when you want to assign the caster DC
|
|
//this does take feat/race/class into account, it only overrides the baseDC
|
|
else if (GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE) != 0)
|
|
{
|
|
nDC = GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE);
|
|
if(nDC == -1)
|
|
nDC = PRCGetSpellSaveDC(nSpellID, nSchool, oCaster);
|
|
|
|
if(DEBUG) DoDebug("Forced Base-DC casting at DC " + IntToString(nDC));
|
|
nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, nSchool);
|
|
}
|
|
else if(GetIsObjectValid(oItem) && !(GetBaseItemType(oItem) == BASE_ITEM_MAGICSTAFF && GetPRCSwitch(PRC_STAFF_CASTER_LEVEL)))
|
|
{
|
|
//code for getting new ip type
|
|
itemproperty ipTest = GetFirstItemProperty(oItem);
|
|
while(GetIsItemPropertyValid(ipTest))
|
|
{
|
|
if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_DC)
|
|
{
|
|
int nSubType = GetItemPropertySubType(ipTest);
|
|
nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType));
|
|
if(nSubType == nSpellID)
|
|
{
|
|
nDC = GetItemPropertyCostTableValue (ipTest);
|
|
break;//end while
|
|
}
|
|
}
|
|
ipTest = GetNextItemProperty(oItem);
|
|
}
|
|
int nType = GetBaseItemType(oItem);
|
|
if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND)
|
|
{
|
|
if (GetHasFeat(FEAT_WAND_MASTERY, oCaster))
|
|
nDC += 2;
|
|
}
|
|
}
|
|
else
|
|
nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, nSchool);
|
|
|
|
// Karsus's Heavy Magic ability
|
|
if(GetIsObjectValid(oItem) && GetHasSpellEffect(VESTIGE_KARSUS, oCaster) && GetLocalInt(oCaster, "ExploitVestige") != VESTIGE_KARSUS_HEAVY_MAGIC && (GetLevelByClass(CLASS_TYPE_BINDER, oCaster) || GetHasFeat(FEAT_PRACTICED_BINDER, oCaster)))
|
|
nDC += 2;
|
|
|
|
//target-based adjustments go here
|
|
nDC += RedWizardDCPenalty(nSpellID, nSchool, oTarget);
|
|
nDC += ShadowAdeptDCPenalty(nSpellID, nSchool, oTarget);
|
|
|
|
if (GetPRCSwitch(PRC_ACTIVATE_MAX_SPELL_DC_CAP))
|
|
{
|
|
if (nDC > GetPRCSwitch(PRC_SET_MAX_SPELL_DC_CAP))
|
|
{
|
|
nDC = GetPRCSwitch(PRC_SET_MAX_SPELL_DC_CAP);
|
|
}
|
|
}
|
|
|
|
return nDC;
|
|
|
|
}
|
|
|
|
//called just from above and from inc_epicspells
|
|
int GetChangesToSaveDC(object oTarget, object oCaster, int nSpellID, int nSchool)
|
|
{
|
|
int nDC;
|
|
int nElement = GetIsElementalSpell(nSpellID);
|
|
|
|
if(nElement)
|
|
{
|
|
nDC += ElementalSavantDC(nSpellID, nElement, oCaster);
|
|
nDC += SpiritFolkAdjustment(nSpellID, nElement, oTarget);
|
|
nDC += SpellFocus(nSpellID, nElement, oCaster);
|
|
nDC += DraconicPowerDC(nSpellID, nElement, oCaster);
|
|
nDC += EnergyAuraDC(nSpellID, nElement, oCaster);
|
|
}
|
|
nDC += GetHeartWarderDC(nSpellID, nSchool, oCaster);
|
|
nDC += GetSpellPowerBonus(oCaster);
|
|
nDC += ShadowWeaveDC(nSpellID, nSchool, oCaster);
|
|
nDC += RedWizardDC(nSpellID, nSchool, oCaster);
|
|
nDC += TattooFocus(nSpellID, nSchool, oCaster);
|
|
nDC += KOTCSpellFocusVsDemons(oTarget, oCaster);
|
|
//nDC += BloodMagusBloodComponent(oCaster);
|
|
nDC += RunecasterRunePowerDC(oCaster);
|
|
nDC += UnheavenedAdjustment(oTarget, oCaster);
|
|
nDC += SoulEaterSoulPower(oCaster);
|
|
nDC += AngrySpell(nSpellID, nSchool, oCaster);
|
|
nDC += CloakedCastingDC(nSpellID, oTarget, oCaster);
|
|
nDC += GetCorruptSpellFocus(nSpellID, oCaster);
|
|
nDC += Soulcaster(oCaster, nSpellID);
|
|
nDC += WyrmbaneHelmDC(oTarget, oCaster);
|
|
nDC += StrengthFromMagic(oCaster);
|
|
nDC += SoulTyrant(oCaster);
|
|
nDC += SaintHolySpellPower(oCaster);
|
|
nDC += GetLocalInt(oCaster, PRC_DC_ADJUSTMENT);//this is for builder use
|
|
nDC += JaebrinEnchant(nSchool, oCaster);
|
|
return nDC;
|
|
}
|
|
|
|
// Test main
|
|
//:: void main(){}
|