//::///////////////////////////////////////////////
//:: Spells include: Spell Penetration
//:: prc_add_spl_pen
//::///////////////////////////////////////////////
/** @file
    Defines functions that may have something to do
    with modifying a spell's caster level in regards
    to Spell Resistance penetration.
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////


//////////////////////////////////////////////////
/*             Function prototypes              */
//////////////////////////////////////////////////

int GetHeartWarderPene(int spell_id, object oCaster = OBJECT_SELF);

int ElementalSavantSP(int spell_id, object oCaster = OBJECT_SELF);

int RedWizardSP(int spell_id, int nSchool, object oCaster = OBJECT_SELF);

int GetSpellPenetreFocusSchool(int nSchool, object oCaster = OBJECT_SELF);

int GetSpellPowerBonus(object oCaster = OBJECT_SELF);

int ShadowWeavePen(int spell_id, int nSchool, object oCaster = OBJECT_SELF);

int KOTCSpellPenVsDemons(object oCaster, object oTarget);

int RunecasterRunePowerSP(object oCaster);

int MarshalDeterminedCaster(object oCaster);

int DuskbladeSpellPower(object oCaster, object oTarget);

int DraconicMagicPower(object oCaster);

int TrueCastingSpell(object oCaster);

string ChangedElementalType(int spell_id, object oCaster = OBJECT_SELF);

// Use this function to get the adjustments to a spell or SLAs spell penetration
// from the various class effects
// Update this function if any new classes change spell pentration
int add_spl_pen(object oCaster = OBJECT_SELF);

int SPGetPenetr(object oCaster = OBJECT_SELF);

int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0);

//////////////////////////////////////////////////
/*                  Includes                    */
//////////////////////////////////////////////////

//#include "prc_inc_spells"
//#include "prc_alterations"
//#include "prcsp_archmaginc"
//#include "prc_inc_racial"


//////////////////////////////////////////////////
/*             Function definitions             */
//////////////////////////////////////////////////

//
//  Determine if a spell type is elemental
//
int IsSpellTypeElemental(string type)
{
    return type == "Acid"
        || type == "Cold"
        || type == "Electricity"
        || type == "Fire"
        || type == "Sonic";
}

int GetHeartWarderPene(int spell_id, object oCaster = OBJECT_SELF)
{
    // Guard Expensive Calculations
    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_SPELL_PENETRATION, oCaster)
    || GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oCaster)
    || GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oCaster))
        return 0;

    return 2;
}

//
//  Calculate Elemental Savant Contributions
//
int ElementalSavantSP(int spell_id, object oCaster = OBJECT_SELF)
{
    // get spell elemental type
    int element = GetIsElementalSpell(spell_id);

    //not an elemental spell
    if(!element)
        return 0;

    int nSP = 0;

    // All Elemental Savants will have this feat
    // when they first gain a penetration bonus.
    // Otherwise this would require checking ~4 items (class or specific feats)
    if(GetHasFeat(FEAT_ES_PEN_1, oCaster))
    {
        int feat, nES;
        nES = GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster);

        // Specify the elemental type rather than lookup by class?
        if(element & DESCRIPTOR_FIRE)
        {
            feat = FEAT_ES_FIRE;           
        }
        else if(element & DESCRIPTOR_COLD)
        {
            feat = FEAT_ES_COLD;
        }
        else if(element & DESCRIPTOR_ELECTRICITY)
        {
            feat = FEAT_ES_ELEC;
        }
        else if(element & DESCRIPTOR_ACID)
        {
            feat = FEAT_ES_ACID;
        }

        // Now determine the bonus
        if(feat && GetHasFeat(feat, oCaster))
            nSP = nES / 3;
    }
//  SendMessageToPC(GetFirstPC(), "Your Elemental Penetration modifier is " + IntToString(nSP));
    return nSP;
}

//Red Wizard SP boost based on spell school specialization
int RedWizardSP(int spell_id, int nSchool, object oCaster = OBJECT_SELF)
{
    int iRedWizard = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster);
    int nSP;

    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))
            nSP = (iRedWizard / 2) + 1;
    }
//  SendMessageToPC(GetFirstPC(), "Your Spell Power modifier is " + IntToString(nSP));
    return nSP;
}

int GetSpellPenetreFocusSchool(int nSchool, object oCaster = OBJECT_SELF)
{
    if(nSchool)
    {
        if(GetHasFeat(FEAT_FOCUSED_SPELL_PENETRATION_ABJURATION+nSchool-1, oCaster))
            return 4;
    }

    return 0;
}

int GetSpellPowerBonus(object oCaster = OBJECT_SELF)
{
    if(GetHasFeat(FEAT_SPELLPOWER_10, oCaster))
        return 10;
    else if(GetHasFeat(FEAT_SPELLPOWER_8, oCaster))
        return 8;
    else if(GetHasFeat(FEAT_SPELLPOWER_6, oCaster))
        return 6;
    else if(GetHasFeat(FEAT_SPELLPOWER_4, oCaster))
        return 4;
    else if(GetHasFeat(FEAT_SPELLPOWER_2, oCaster))
        return 2;

    return 0;
}

// Shadow Weave Feat
// +1 caster level vs SR (school Ench,Illu,Necro)
int ShadowWeavePen(int spell_id, int nSchool, object oCaster = OBJECT_SELF)
{
    int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster);
    int nSP;

    // Apply changes if the caster has level in Shadow Adept class
    // and this spell is eligible for the spell penetration check increase
    if (iShadow > 0 && ShadowWeave(oCaster, spell_id, nSchool) == 1)
        // Shadow Spell Power
        nSP = iShadow / 3;

    return nSP;
}

int KOTCSpellPenVsDemons(object oCaster, object oTarget)
{
    if(GetLevelByClass(CLASS_TYPE_KNIGHT_CHALICE, oCaster) >= 1)
    {
        if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_OUTSIDER)
        {
            if(GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL)
            {
                return 2;
            }
        }
    }
    return 0;
}

int RunecasterRunePowerSP(object oCaster)
{
    int nSP = 0;

    // casting from a rune
    if(GetResRef(GetSpellCastItem()) == "prc_rune_1")
    {
        nSP = StringToInt(GetTag(GetSpellCastItem()));
    }
    // caster is runechanting
    else if(GetHasSpellEffect(SPELL_RUNE_CHANT))
    {
        int nClass = GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster);

        if (nClass >= 30)        nSP = 10;
        else if (nClass >= 27)   nSP = 9;
        else if (nClass >= 24)   nSP = 8;
        else if (nClass >= 21)   nSP = 7;
        else if (nClass >= 18)   nSP = 6;
        else if (nClass >= 15)   nSP = 5;
        else if (nClass >= 12)   nSP = 4;
        else if (nClass >= 9)    nSP = 3;
        else if (nClass >= 5)    nSP = 2;
        else if (nClass >= 2)    nSP = 1;
    }

    return nSP;
}

int MarshalDeterminedCaster(object oCaster)
{
    return GetLocalInt(oCaster,"Marshal_DetCast");
}

int DuskbladeSpellPower(object oCaster, object oTarget)
{
    int nSP = 0;
    if(GetLocalInt(oTarget, "DuskbladeSpellPower"))
    {
        int nClass = GetLevelByClass(CLASS_TYPE_DUSKBLADE, oCaster);

        if(nClass >= 38)      nSP = 10;
        else if(nClass >= 36) nSP = 9;
        else if(nClass >= 31) nSP = 8;
        else if(nClass >= 26) nSP = 7;
        else if(nClass >= 21) nSP = 6;
        else if(nClass >= 18) nSP = 5;
        else if(nClass >= 16) nSP = 4;
        else if(nClass >= 11) nSP = 3;
        else if(nClass >= 6)  nSP = 2;
    }

    return nSP;
}

int DraconicMagicPower(object oCaster)
{
    return GetLocalInt(oCaster,"MagicPowerAura");
}

int TrueCastingSpell(object oCaster)
{
    if(GetHasSpellEffect(SPELL_TRUE_CASTING, oCaster))
        return 10;

    return 0;
}

// Beguilers of level 8+ gain +2 bonus to SR agianst enemis that are denided DEX bonus to AC
int CloakedCastingSR(object oCaster, object oTarget)
{
    if(GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster) >= 8)
    {
        if(GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE))
            return 2;
    }

    return 0;
}

int PenetratingBlast(object oCaster, object oTarget)
{
    if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))
    {
        if(GetLocalInt(oCaster, "BlastEssence") == INVOKE_PENETRATING_BLAST)
            return 4;
    }
    return 0;
}

int add_spl_pen(object oCaster = OBJECT_SELF)
{
    object oTarget = PRCGetSpellTargetObject();
    int spell_id = PRCGetSpellId();
    int nSchool = GetSpellSchool(spell_id);

    int nSP = ElementalSavantSP(spell_id, oCaster);
    nSP += GetHeartWarderPene(spell_id, oCaster);
    nSP += RedWizardSP(spell_id, nSchool, oCaster);
    nSP += GetSpellPowerBonus(oCaster);
    nSP += GetSpellPenetreFocusSchool(nSchool, oCaster);
    nSP += ShadowWeavePen(spell_id, nSchool, oCaster);
    nSP += RunecasterRunePowerSP(oCaster);
    nSP += MarshalDeterminedCaster(oCaster);
    nSP += DraconicMagicPower(oCaster);
    nSP += TrueCastingSpell(oCaster);
    nSP += GetEssentiaInvestedFeat(oCaster, FEAT_SOULTOUCHED_SPELLCASTING);
    if(GetIsObjectValid(oTarget))
    {
        nSP += CloakedCastingSR(oCaster, oTarget);
        nSP += PenetratingBlast(oCaster, oTarget);
        nSP += KOTCSpellPenVsDemons(oCaster, oTarget);
        nSP += DuskbladeSpellPower(oCaster, oTarget);
    }

    return nSP;
}

//
//  This function converts elemental types as needed
//
string ChangedElementalType(int spell_id, object oCaster = OBJECT_SELF)
{
    // Lookup the spell type
    string spellType = Get2DACache("spells", "ImmunityType", spell_id);//lookup_spell_type(spell_id);

    // Check if an override is set
    string sType = GetLocalString(oCaster, "archmage_mastery_elements_name");

    // If so, check if the spell qualifies for a change
    if (sType == "" || !IsSpellTypeElemental(spellType))
        sType = spellType;

    return sType;
}

//////////////////////////////////////////////////
/*             Function definitions             */
//////////////////////////////////////////////////

//
//  Get the Spell Penetration Bonuses
//
int SPGetPenetr(object oCaster = OBJECT_SELF)
{
    int nPenetr = 0;

    // This is a deliberate optimization attempt.
    // The first feat determines if the others even need
    // to be referenced.
    if(GetHasFeat(FEAT_SPELL_PENETRATION, oCaster))
    {
        nPenetr += 2;
        if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oCaster))
            nPenetr += 4;
        else if (GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oCaster))
            nPenetr += 2;
    }

    // Check for additional improvements
    nPenetr += add_spl_pen(oCaster);

    return nPenetr;
}

//
//  Interface for specific AOE requirements
//  TODO: Determine who or what removes the cached local var (bug?)
//  TODO: Try and remove this function completely? It does 2 things the
//  above function doesnt: Effective Caster Level and Cache
//
int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0)
{
    // Check the cache
    int nPenetr = GetLocalInt(OBJECT_SELF, "nPenetre");

    // Compute the result
    if (!nPenetr) {
        nPenetr = (nCasterLvl) ? nCasterLvl : PRCGetCasterLevel(oCaster);

        // Factor in Penetration Bonuses
        nPenetr += SPGetPenetr(oCaster);

        // Who removed this?
        SetLocalInt(OBJECT_SELF,"nPenetre",nPenetr);
    }

    return nPenetr;
}

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