//Reserve Feat Script
//prc_reservefeat
//by ebonfowl 5/5/2022
//Dedicated to Edgar, the real ebonfowl

//Lookup constants
//Domains
const string RESERVESPELL_DOMAIN_DESTRUCTION             = "destruc";
const string RESERVESPELL_DOMAIN_DEATH                   = "dethdom";
const string RESERVESPELL_DOMAIN_WAR                     = "wardom";

//Descriptors
const string RESERVESPELL_DESCRIPTOR_ACID                = "acid";
const string RESERVESPELL_DESCRIPTOR_AIR                 = "air";
const string RESERVESPELL_DESCRIPTOR_COLD                = "cold";
const string RESERVESPELL_DESCRIPTOR_DARKNESS            = "darknes";
const string RESERVESPELL_DESCRIPTOR_EARTH               = "earth";
const string RESERVESPELL_DESCRIPTOR_ELECTRICITY         = "electri";
const string RESERVESPELL_DESCRIPTOR_FIRE                = "fire";
const string RESERVESPELL_DESCRIPTOR_FORCE               = "force";
const string RESERVESPELL_DESCRIPTOR_GLAMER              = "glamer";
const string RESERVESPELL_DESCRIPTOR_HEALING             = "healing";
const string RESERVESPELL_DESCRIPTOR_LIGHT               = "light";
const string RESERVESPELL_DESCRIPTOR_POLYMORPH           = "polymor";
const string RESERVESPELL_DESCRIPTOR_SONIC               = "sonic";
const string RESERVESPELL_DESCRIPTOR_SUMMONING           = "summon";
const string RESERVESPELL_DESCRIPTOR_TELEPORTATION       = "teleprt";
const string RESERVESPELL_DESCRIPTOR_WATER               = "water";

//Schools
const string RESERVESPELL_SCHOOL_ABJURATION              = "abjurat";
const string RESERVESPELL_SCHOOL_ENCHANTMENT             = "enchant";
const string RESERVESPELL_SCHOOL_NECROMANCY              = "necro";


//Includes
#include "prc_feat_const"
#include "prc_x2_itemprop"
#include "psi_inc_psifunc"

//ebonfowl: this function is not currently used, but I am leaving it in just in case
/*
int GetHighestSpellAvailable(object oPC)
{
    //This will loop all the spells and return the highest level available to cast
    string sFile = "prc_all"; //May need to rename

    int i;
    int nSpellID;
    int nNewSpellLevel;
    int nSpellLevel = 0;

    string sSpellLabel;

    for (i = 0; i < 500; i++) // Adjust max i to reflect something close to the highest row number in the 2das
    {
        nSpellID = StringToInt(Get2DACache(sFile, "SpellID", i));
        sSpellLabel = Get2DACache(sFile, "Label", i);
        if(sSpellLabel != "") // Non-blank row
        {
            if(PRCGetHasSpell(nSpellID, oPC))
            {
                nNewSpellLevel = StringToInt(Get2DACache(sFile, "Innate", i));
                if (nNewSpellLevel > nSpellLevel) nSpellLevel = nNewSpellLevel;
            }    
        }
    }
    return nSpellLevel;
}
*/

int GetHighestSpellAvailableBySchool(object oPC, string sSchool)
{
    //This will loop all spells fpr a given school and return the highest level available to cast
    string sFile = "prc_desc_" + sSchool;

    int i;
    int nSpellID;
    int nNewSpellLevel;
    int nSpellLevel = 0;

    string sSpellLabel;

    for (i = 0; i < 126; i++) // Adjust max i to reflect something close to the highest row number in the 2das
    {
        nSpellID = StringToInt(Get2DACache(sFile, "SpellID", i));
        sSpellLabel = Get2DACache(sFile, "Label", i);
        if(sSpellLabel != "") // Non-blank row
        {
            if(PRCGetHasSpell(nSpellID, oPC))
            {
                nNewSpellLevel = StringToInt(Get2DACache(sFile, "Innate", i));
                if (nNewSpellLevel > nSpellLevel) nSpellLevel = nNewSpellLevel;
            }    
        }
    }
    return nSpellLevel;
}

int GetHighestDomainSpellAvailable(object oPC, string sDomain)  //This will loop all domain spells for a given doamain and return the highest level available to cast
{
    string sFile = "prc_desc_" + sDomain;

    int i;
    int nSpellID;
    int nNewSpellLevel;
    int nSpellLevel = 0;

    string sSpellLabel;

    for (i = 0; i < 12; i++) // Adjust max i to reflect something close to the highest row number in the 2das
    {
        nSpellID = StringToInt(Get2DACache(sFile, "SpellID", i));
        sSpellLabel = Get2DACache(sFile, "Label", i);
        if(sSpellLabel != "") // Non-blank row
        {
            if(PRCGetHasSpell(nSpellID, oPC))
            {
                nNewSpellLevel = StringToInt(Get2DACache(sFile, "Innate", i));
                if (nNewSpellLevel > nSpellLevel) nSpellLevel = nNewSpellLevel;
            }    
        }
    }
    return nSpellLevel;
}

int GetHighestSpellAvailableByDescriptor(object oPC, string sDescriptor)
{
    //This will loop all spells fpr a given descriptor and return the highest level available to cast
    string sFile = "prc_desc_" + sDescriptor;

    int i;
    int nSpellID;
    int nNewSpellLevel;
    int nSpellLevel = 0;

    string sSpellLabel;

    for (i = 0; i < 125; i++) // Adjust max i to reflect something close to the highest row number in the 2das
    {
        nSpellID = StringToInt(Get2DACache(sFile, "SpellID", i));
        sSpellLabel = Get2DACache(sFile, "Label", i);
        if(sSpellLabel != "") // Non-blank row
        {
            if(PRCGetHasSpell(nSpellID, oPC))
            {
                nNewSpellLevel = StringToInt(Get2DACache(sFile, "Innate", i));
                if (nNewSpellLevel > nSpellLevel) nSpellLevel = nNewSpellLevel;
            }    
        }
    }
    return nSpellLevel;
}

void UpdateReserveFeats(object oPC)  //This will check for each reserve feat, call the function to set the bonus, then set the bonus
{
    object oWeapon, oOffHand;
    
    int nBonus;
    int nDamage;

    effect eEffect;
    effect eCheckEffect;

    string sTag;
    
    //Holy Warrior
    if (GetHasFeat(FEAT_HOLY_WARRIOR, oPC)) //Update constant when feat constant is made
    {
        nBonus = GetHighestDomainSpellAvailable(oPC, RESERVESPELL_DOMAIN_WAR);
        nDamage = IPGetDamageBonusConstantFromNumber(nBonus);
        oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
        oOffHand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
        eEffect = EffectDamageIncrease(nDamage, DAMAGE_TYPE_SLASHING);
        eEffect = TagEffect(eEffect, "HolyWarriorBonus");
        eEffect = SupernaturalEffect(eEffect);
        eCheckEffect = GetFirstEffect(oPC);
            
        while (GetIsEffectValid(eCheckEffect))
        {
            if (GetEffectTag(eCheckEffect) == "HolyWarriorBonus")
            {
                RemoveEffect(oPC, eCheckEffect);
            }
            eCheckEffect = GetNextEffect(oPC);
        }

        if (IPGetIsMeleeWeapon(oWeapon) || 
            IPGetIsMeleeWeapon(oOffHand) || 
            GetWeaponRanged(oWeapon) || 
            GetWeaponRanged(oOffHand))
        {
            if (nBonus > 3) ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oPC);
        }
    }

    //Mystic Backlash
    if (GetHasFeat(FEAT_MYSTIC_BACKLASH, oPC)) //Update constant when feat constant is made
    {
        nBonus = GetHighestSpellAvailableBySchool(oPC, RESERVESPELL_SCHOOL_ABJURATION);
        DeleteLocalInt(oPC, "MysticBacklashBonus");
        
        if (nBonus > 4) SetLocalInt(oPC, "MysticBacklashBonus", nBonus);
    }

    //Acidic Splatter
    if(GetHasFeat(FEAT_ACIDIC_SPLATTER, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_ACID);
        DeleteLocalInt(oPC, "AcidicSplatterBonus");

        if (nBonus > 1) SetLocalInt(oPC, "AcidicSplatterBonus", nBonus);
    }

    //Fiery Burst
    if(GetHasFeat(FEAT_FIERY_BURST, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_FIRE);
        DeleteLocalInt(oPC, "FieryBurstBonus");

        if (nBonus > 1) SetLocalInt(oPC, "FieryBurstBonus", nBonus);
    }

    //Storm Bolt
    if(GetHasFeat(FEAT_STORM_BOLT, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_ELECTRICITY);
        DeleteLocalInt(oPC, "StormBoltBonus");

        if (nBonus > 2) SetLocalInt(oPC, "StormBoltBonus", nBonus);
    }

    //Winter's Blast
    if(GetHasFeat(FEAT_WINTERS_BLAST, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_COLD);
        DeleteLocalInt(oPC, "WintersBlastBonus");

        if (nBonus > 1) SetLocalInt(oPC, "WintersBlastBonus", nBonus);
    }

    //Clap of Thunder
    if(GetHasFeat(FEAT_CLAP_OF_THUNDER, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_SONIC);
        DeleteLocalInt(oPC, "ClapOfThunderBonus");

        if (nBonus > 2) SetLocalInt(oPC, "ClapOfThunderBonus", nBonus);
    }

    //Sickening Grasp
    if (GetHasFeat(FEAT_SICKENING_GRASP, oPC)) //Update constant when feat constant is made
    {
        nBonus = GetHighestSpellAvailableBySchool(oPC, RESERVESPELL_SCHOOL_NECROMANCY);
        DeleteLocalInt(oPC, "SickeningGraspBonus");
        
        if (nBonus > 2) SetLocalInt(oPC, "SickeningGraspBonus", nBonus);
    }

    //Touch of Healing
    if(GetHasFeat(FEAT_TOUCH_OF_HEALING, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_HEALING);
        DeleteLocalInt(oPC, "TouchOfHealingBonus");

        if (nBonus > 1) SetLocalInt(oPC, "TouchOfHealingBonus", nBonus);
    }

    //Dimensional Jaunt
    if(GetHasFeat(FEAT_DIMENSIONAL_JAUNT, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_TELEPORTATION);
        DeleteLocalInt(oPC, "DimensionalJauntBonus");

        if (nBonus > 3) SetLocalInt(oPC, "DimensionalJauntBonus", nBonus);
    }

    //Clutch of Earth
    if(GetHasFeat(FEAT_CLUTCH_OF_EARTH, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_EARTH);
        DeleteLocalInt(oPC, "ClutchOfEarthBonus");

        if (nBonus > 1) SetLocalInt(oPC, "ClutchOfEarthBonus", nBonus);
    }

    //Borne Aloft
    if(GetHasFeat(FEAT_BORNE_ALOFT, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_AIR);
        DeleteLocalInt(oPC, "BorneAloftBonus");

        if (nBonus > 4) SetLocalInt(oPC, "BorneAloftBonus", nBonus);
    }

    //Protective Ward
    if (GetHasFeat(FEAT_PROTECTIVE_WARD, oPC))
    {
        nBonus = GetHighestSpellAvailableBySchool(oPC, RESERVESPELL_SCHOOL_ABJURATION);
        DeleteLocalInt(oPC, "ProtectiveWardBonus");
        
        if (nBonus > 0) SetLocalInt(oPC, "ProtectiveWardBonus", nBonus);
    }

    //Shadow Veil
    if(GetHasFeat(FEAT_SHADOW_VEIL, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_DARKNESS);
        DeleteLocalInt(oPC, "ShadowVeilBonus");

        if (nBonus > 1) SetLocalInt(oPC, "ShadowVeilBonus", nBonus);
    }

    //Sunlight Eyes
    if(GetHasFeat(FEAT_SUNLIGHT_EYES, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_LIGHT);
        DeleteLocalInt(oPC, "SunlightEyesBonus");

        if (nBonus > 1) SetLocalInt(oPC, "SunlightEyesBonus", nBonus);
    }

    //Touch of Distraction
    if (GetHasFeat(FEAT_TOUCH_OF_DISTRACTION, oPC))
    {
        nBonus = GetHighestSpellAvailableBySchool(oPC, RESERVESPELL_SCHOOL_ENCHANTMENT);
        DeleteLocalInt(oPC, "TouchOfDistractionBonus");
        
        if (nBonus > 2) SetLocalInt(oPC, "TouchOfDistractionBonus", nBonus);
    }

    //Umbral Shroud
    if(GetHasFeat(FEAT_UMBRAL_SHROUD, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_DARKNESS);
        DeleteLocalInt(oPC, "UmbralShroudBonus");

        if (nBonus > 2) SetLocalInt(oPC, "UmbralShroudBonus", nBonus);
    }

    //Charnel Miasma
    if(GetHasFeat(FEAT_CHARNEL_MIASMA, oPC))
    {
        nBonus = GetHighestDomainSpellAvailable(oPC, RESERVESPELL_DOMAIN_DEATH);
        DeleteLocalInt(oPC, "CharnelMiasmaBonus");

        if (nBonus > 1) SetLocalInt(oPC, "CharnelMiasmaBonus", nBonus);
    }

    //Drowning Glance
    if(GetHasFeat(FEAT_DROWNING_GLANCE, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_WATER);
        DeleteLocalInt(oPC, "DrowningGlanceBonus");

        if (nBonus > 3) SetLocalInt(oPC, "DrowningGlanceBonus", nBonus);
    }

    //Invisible Needle
    if(GetHasFeat(FEAT_INVISIBLE_NEEDLE, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_FORCE);
        DeleteLocalInt(oPC, "InvisibleNeedleBonus");

        if (nBonus > 2) SetLocalInt(oPC, "InvisibleNeedleBonus", nBonus);
    }

    //Summon Elemental
    if(GetHasFeat(FEAT_SUMMON_ELEMENTAL, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_SUMMONING);
        DeleteLocalInt(oPC, "SummonElementalBonus");

        if (nBonus > 3) SetLocalInt(oPC, "SummonElementalBonus", nBonus);
    }

    //Dimensional Reach
    if(GetHasFeat(FEAT_DIMENSIONAL_REACH, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_SUMMONING);
        DeleteLocalInt(oPC, "DimensionalReachBonus");

        if (nBonus > 2) SetLocalInt(oPC, "DimensionalReachBonus", nBonus);
    }

    //Hurricane Breath
    if(GetHasFeat(FEAT_HURRICANE_BREATH, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_AIR);
        DeleteLocalInt(oPC, "HurricaneBreathBonus");

        if (nBonus > 1) SetLocalInt(oPC, "HurricaneBreathBonus", nBonus);
    }

    //Minor Shapeshift
    if(GetHasFeat(FEAT_MINOR_SHAPESHIFT, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_POLYMORPH);
        DeleteLocalInt(oPC, "MinorShapeshiftBonus");

        if (nBonus > 3) SetLocalInt(oPC, "MinorShapeshiftBonus", nBonus);
    }

    //Face-Changer
    if(GetHasFeat(FEAT_FACECHANGER, oPC))
    {
        nBonus = GetHighestSpellAvailableByDescriptor(oPC, RESERVESPELL_DESCRIPTOR_GLAMER);
        DeleteLocalInt(oPC, "FaceChangerBonus");

        if (nBonus > 2) SetLocalInt(oPC, "FaceChangerBonus", nBonus);
    }
}

void main()
{
    object oPC = OBJECT_SELF;
    
    if(!GetLocalInt(oPC, "ReserveFeatsRunning"))
    {
        SetLocalInt(oPC, "ReserveFeatsRunning", TRUE);

        //Hook this script into event system so it runs on hooked events
        AddEventScript(oPC, EVENT_ONACTIVATEITEM, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONACQUIREITEM, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONCLIENTENTER, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONCLIENTLEAVE, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONPLAYERLEVELDOWN, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONPLAYERLEVELUP, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONPLAYERRESPAWN, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONPLAYERUNEQUIPITEM, "prc_reservefeat", TRUE, FALSE);
        AddEventScript(oPC, EVENT_ONUNAQUIREITEM, "prc_reservefeat", TRUE, FALSE);

        //Update the feat bonuses
        UpdateReserveFeats(oPC);
    }
    else
    {
        UpdateReserveFeats(oPC);
    }
}