// 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;
}

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

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);
    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 += SaintHolySpellPower(oCaster);
    nDC += GetLocalInt(oCaster, PRC_DC_ADJUSTMENT);//this is for builder use
    return nDC;
}

// Test main
//void main(){}