/*

    prc_spellf_inc.nss - Spellfire functions



    By: Flaming_Sword
    Created: February 15, 2006
    Modified: February 15, 2006

    Naming conventions:
        nExpend <-> GetPersistantLocalInt(oPC, "SpellfireLevelExpend");
        nStored <-> GetPersistantLocalInt(oPC, "SpellfireLevelStored");

*/

//#include "inc_utility"
//#include "prc_alterations"
#include "prc_inc_sp_tch"
#include "prcsp_engine"
//int CheckSpellfire(object oCaster, object oTarget, int bFriendly = FALSE);

//Spellfire Functions

//Verifies levels to expend, returns new levels to expend
int SpellfireVerifyExpend(object oPC, int nExpend, int nStored)
{
    int nCON = GetAbilityScore(oPC, ABILITY_CONSTITUTION);

    //sanity check, at least 1, capped by CON and stored
    if(nExpend < 1)
    {
        nExpend = 1;
        SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend);
    }
    if(nExpend > nCON) nExpend = nCON;  //in case CON has changed
    if(nExpend > nStored) nExpend = nStored;    //can't spend more than you've got

    return nExpend;
}

//Adjusts the number of spellfire levels expended
//in the next use of spellfire
void AdjustSpellfire(object oPC, int nAdjust)
{
    int nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend");
    nExpend += nAdjust;
    if(nExpend < 1) nExpend = 1;    //at least 1
    SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend);
    SendMessageToPC(oPC, "Spellfire levels to expend: " + IntToString(nExpend));
}

//Sets flag to change quickselects a la psionics
void SpellfireQuickselectChange(object oPC)
{
    SetLocalInt(oPC, "Spellfire_Quickselect_Change", 1);    //set flag
    SendMessageToPC(oPC, "Select a quickselect to store the current setting");
    DelayCommand(10.0, DeleteLocalInt(oPC, "Spellfire_Quickselect_Change"));    //auto-delete
}

//Sets or changes quickselects
void SpellfireQuickselect(object oPC, int nSpellID)
{
    int nExpend;
    if(GetLocalInt(oPC, "Spellfire_Quickselect_Change"))
    {
        nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend");
        SetPersistantLocalInt(oPC, "Spellfire_Quickselect_" + IntToString(nSpellID), nExpend);
        SendMessageToPC(oPC, "Quickselect " + IntToString(nSpellID - SPELL_SPELLFIRE_QUICKSELECT_1 + 1) + " set to " + IntToString(nExpend));
        DeleteLocalInt(oPC, "Spellfire_Quickselect_Change");
    }
    else
    {
        nExpend = GetPersistantLocalInt(oPC, "Spellfire_Quickselect_" + IntToString(nSpellID));
        SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend);
        SendMessageToPC(oPC, "Spellfire levels to expend: " + IntToString(nExpend));
    }
}

//Expends spellfire and returns the new value
int ExpendSpellfire(object oPC)
{
    int nStored = GetPersistantLocalInt(oPC, "SpellfireLevelStored");
    if(!nStored) return 0;
    int nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend");

    nExpend = SpellfireVerifyExpend(oPC, nExpend, nStored);

    nStored -= nExpend;
    SetPersistantLocalInt(oPC, "SpellfireLevelStored", nStored);
    return nExpend;
}

//Applies spellfire damage to target
void SpellfireDamage(object oCaster, object oTarget, int nRoll, int nDamage, int bMaelstrom = FALSE)
{
    if(bMaelstrom)
    {
        //nDamage += ApplySpellBetrayalStrikeDamage(oTarget, oCaster);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, PRCEffectDamage(oTarget, nDamage / 2, DAMAGE_TYPE_MAGICAL), oTarget);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage - (nDamage / 2), DAMAGE_TYPE_FIRE), oTarget);
    }
    else
        ApplyTouchAttackDamage(oCaster, oTarget, nRoll, nDamage, DAMAGE_TYPE_MAGICAL, DAMAGE_TYPE_FIRE);
    SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SPELLF_FLAME), oTarget);
}

//Spellfire attack roll -> damage
void SpellfireAttackRoll(object oCaster, object oTarget, int nExpend, int iMod = 0, int nDC = 20, int bBeam = FALSE, int bMaelstrom = FALSE)
{
    int nRoll, nDamage;

    //Account for Energy Draconic Aura
    if (GetLocalInt(oCaster, "FireEnergyAura") > 0)
    {
        nDC += GetLocalInt(oCaster, "FireEnergyAura");
    }

    //Weapon Focus (spellfire) applies to spellfire only
    if(GetHasFeat(FEAT_WEAPON_FOCUS_SPELLFIRE, oCaster)) iMod++;
    if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SPELLFIRE, oCaster)) iMod += 2;

    if(oCaster == oTarget)  //yeah, like you could miss with a touch attack on yourself
    {
        nRoll = 1;
        nDamage = d6(nExpend);
    }
    else
    {
        nRoll = bMaelstrom ? 1 : GetAttackRoll(oTarget, oCaster, GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster), 0, 0, iMod, TRUE, 0.0, TRUE);
        nDamage = PRCGetReflexAdjustedDamage(d6(nExpend), oTarget, nDC, SAVING_THROW_TYPE_NONE, oCaster);
    }
    if(bBeam)
        SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(VFX_BEAM_SPELLFIRE, oCaster, BODY_NODE_HAND, !nRoll), oTarget, 1.2 /*+ (0.5 * IntToFloat(nAttacks))*/);
    if(nRoll && nDamage)
        SpellfireDamage(oCaster, oTarget, nRoll, nDamage, bMaelstrom);
}

//Hits target with spellfire, implements rapid blast
void SpellfireAttack(object oCaster, object oTarget, int bBeam, int nAttacks = 1)
{
    if(!GetPersistantLocalInt(oCaster, "SpellfireLevelStored"))
    {
        SendMessageToPC(oCaster, "You have no more stored spellfire levels");
        return;
    }
    int nLevel = GetLevelByClass(CLASS_TYPE_SPELLFIRE, oCaster);
    if(nAttacks > ((nLevel / 4) + 1))
    {
        SendMessageToPC(oCaster, "You do not have enough Spellfire Channeler levels use this feat");
        return;
    }
    int iMod = 0;
    int i;
    //looping attacks
    //SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(VFX_BEAM_SPELLFIRE, oCaster, BODY_NODE_HAND, !nRoll), oTarget, 1.2 + (0.5 * IntToFloat(nAttacks)));
    SignalEvent(oTarget, EventSpellCastAt(oCaster, SPELL_SPELLFIRE_ATTACK));
    for(i = 0; i < nAttacks; i++)
        DelayCommand(0.5 * IntToFloat(i), SpellfireAttackRoll(oCaster, oTarget, ExpendSpellfire(oCaster), iMod - (2 * i), 20, bBeam));
    //longer effect for more blasts
    DelayCommand(0.1, SendMessageToPC(oCaster, "Spellfire levels stored: " + IntToString(GetPersistantLocalInt(oCaster, "SpellfireLevelStored"))));
}

//Heals target
void SpellfireHeal(object oCaster, object oTarget)
{
    if(!GetPersistantLocalInt(oCaster, "SpellfireLevelStored"))
    {
        SendMessageToPC(oCaster, "You have no more stored spellfire levels");
        return;
    }
    int nExpend = ExpendSpellfire(oCaster);
    int nHeal = 2 * nExpend;
    if(GetHasFeat(FEAT_SPELLFIRE_IMPROVED_HEALING, oCaster))
        nHeal = d4(nExpend) + nExpend;

    SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHeal), oTarget);
    SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SPELLF_HEAL), oTarget);
    DelayCommand(0.1, SendMessageToPC(oCaster, "Spellfire levels stored: " + IntToString(GetPersistantLocalInt(oCaster, "SpellfireLevelStored"))));
}

//Maelstrom of fire
void SpellfireMaelstrom(object oCaster)
{
    if(!GetPersistantLocalInt(oCaster, "SpellfireLevelStored"))
    {
        SendMessageToPC(oCaster, "You have no more stored spellfire levels");
        return;
    }
    int nLevel = GetLevelByClass(CLASS_TYPE_SPELLFIRE, oCaster);
    int nCHA = GetAbilityModifier(ABILITY_CHARISMA, oCaster);
    int nDC = 10 + nLevel + nCHA;

    //Account for Energy Draconic Aura
    if (GetLocalInt(oCaster, "FireEnergyAura") > 0)
    {
        nDC += GetLocalInt(oCaster, "FireEnergyAura");
    }

    //expend once, hit multiple targets
    int nExpend = ExpendSpellfire(oCaster);
    float fDelay;
    location lTarget = GetLocation(oCaster);
    effect eExplode = EffectVisualEffect(VFX_FNF_SPELLF_EXP);
    //KABOOM!
    ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eExplode, lTarget);
    object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
    while(GetIsObjectValid(oTarget))
    {
        if(spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster))
        {   //ripped off fireball
            SignalEvent(oTarget, EventSpellCastAt(oCaster, SPELL_SPELLFIRE_MAELSTROM));
            //Get the distance between the explosion and the target to calculate delay
            fDelay = GetDistanceBetweenLocations(lTarget, GetLocation(oTarget)) / 20;
            DelayCommand(fDelay, SpellfireAttackRoll(oCaster, oTarget, nExpend, 0, nDC, FALSE, TRUE));
        }
        oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
    }
}

//Returns the caster level of an item with the cast spell property
int GetItemCasterLevelFromCastSpell(object oItem, itemproperty ip)
{
    int nCostTableValue = GetItemPropertyCostTableValue(ip);
    int nSubtype = GetItemPropertySubType(ip);
    int nCasterLevel = StringToInt(Get2DACache("iprp_spells", "CasterLvl", nSubtype));

    itemproperty ip2 = GetFirstItemProperty(oItem);
    while(GetIsItemPropertyValid(ip2))
    {
        if(GetItemPropertyType(ip2) == ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL &&
            //temporary caster level? i think not.
            GetItemPropertyDurationType(ip2) == DURATION_TYPE_PERMANENT &&
            GetItemPropertySubType(ip2) == nSubtype)
        {
            int nNewCasterLevel = GetItemPropertyCostTableValue(ip2);   //caster level property
            if(nNewCasterLevel > nCasterLevel) nCasterLevel = nNewCasterLevel;
            break;
        }
        ip2 = GetNextItemProperty(oItem);
    }
    return nCasterLevel;
}

//Returns TRUE if an item is drained
int SpellfireDrainItem(object oPC, object oItem, int bCharged = TRUE, int bSingleUse = TRUE)
{
    if(GetIsObjectValid(oItem) && GetIsMagicItem(oItem))
    {   //drain charged item
        if(bCharged)    //because big compound if statements are messy
        {
            int nBase = GetBaseItemType(oItem);
            int nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend");
            if(nExpend < 1)
            {
                nExpend = 1;
                SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend);
            }
            int nStored = GetPersistantLocalInt(oPC, "SpellfireLevelStored");
            int nCap = SpellfireMax(oPC) - nStored;
            itemproperty ip;
            int nSubType;
            int nStack = GetItemStackSize(oItem);
            int nCharges = GetItemCharges(oItem);
            if(nCharges)   //charged item
            {
                nExpend = min(min(nCharges, nCap), nExpend); //capped by charges and capacity
                SetItemCharges(oItem, nCharges - nExpend);  //will destroy item if all charges drained
                AddSpellfireLevels(oPC, nExpend);   //adds 1 level/charge
                return TRUE;
            }
            ip = GetFirstItemProperty(oItem);
            while(GetIsItemPropertyValid(ip))  //single use item
            {
                if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL &&
                    GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT &&
                    GetItemPropertyCostTableValue(ip) == IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE)
                    {
                        if(DEBUG)
                        {
                            DoDebug("SpellfireDrainItem(): nBase = " + IntToString(nBase), oPC);
                            DoDebug("SpellfireDrainItem(): nStack = " + IntToString(nStack), oPC);
                            DoDebug("SpellfireDrainItem(): nCap = " + IntToString(nCap), oPC);
                            DoDebug("SpellfireDrainItem(): nExpend = " + IntToString(nExpend), oPC);
                        }
                        nSubType = GetItemPropertySubType(ip);
                        //hardcoded filter
                        if(!(
                            (nSubType >= 329 && nSubType <= 344) ||
                            (nSubType == 359) ||
                            (nSubType >= 400 && nSubType <= 409) ||
                            (nSubType >= 411 && nSubType <= 446) ||
                            (nSubType >= 490 && nSubType <= 500) ||
                            (nSubType == 513) ||
                            (nSubType >= 521 && nSubType <= 537) ||
                            (nSubType >= 750 && nSubType <= 851) ||
                            (nSubType >= 1201)
                        ))
                        {

                            if((nBase == BASE_ITEM_POTIONS) ||
                                (nBase == BASE_ITEM_SCROLL) ||
                                (nBase == BASE_ITEM_SPELLSCROLL) ||
                                (nBase == BASE_ITEM_BLANK_POTION) ||
                                (nBase == BASE_ITEM_BLANK_SCROLL) ||
                                (nBase == BASE_ITEM_ENCHANTED_POTION) ||
                                (nBase == BASE_ITEM_ENCHANTED_SCROLL)
                                )
                            {
                                nExpend = min(min(nStack, nCap), nExpend); //capped by charges and capacity
                                if(nExpend == nStack)
                                    DestroyObject(oItem);
                                else
                                    SetItemStackSize(oItem, nStack - nExpend);  //modifies stack size as scrolls/potions are used up
                                AddSpellfireLevels(oPC, nExpend);   //adds 1 level/charge
                                return TRUE;
                            }
                            else
                            {
                                RemoveItemProperty(oItem, ip);  //just removes the cast spell property
                                AddSpellfireLevels(oPC, 1);     //adds 1 level
                                return TRUE;
                            }
                        }
                    }
                ip = GetNextItemProperty(oItem);
            }
        }
        else    //drain permanent item
        {
            itemproperty ip = GetFirstItemProperty(oItem);
            int nCostTableValue = GetItemPropertyCostTableValue(ip);
            while(GetIsItemPropertyValid(ip))
            {
                if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL &&
                    GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT &&
                    nCostTableValue >= IP_CONST_CASTSPELL_NUMUSES_0_CHARGES_PER_USE)
                    {
                        nCostTableValue = GetItemPropertyCostTableValue(ip);
                        int nSubtype = GetItemPropertySubType(ip);
                        int nLevel = GetItemCasterLevelFromCastSpell(oItem, ip) / 2;
                        RemoveItemProperty(oItem, ip);  //just removes the cast spell property
                        AddSpellfireLevels(oPC, nLevel);    //adds half the caster level

                        DelayCommand(HoursToSeconds(24),    //reapplying property
                                        IPSafeAddItemProperty(oItem,
                                            ItemPropertyCastSpell(nSubtype, nCostTableValue),
                                            0.0,
                                            X2_IP_ADDPROP_POLICY_KEEP_EXISTING));
                        return 1;
                    }
                ip = GetNextItemProperty(oItem);
            }
        }
    }
    return 0;
}

//Returns "'s" if sName has no s at the end, otherwise "'"
string Name_s(string sName = "")
{
    return (GetStringRight(sName, 1) == "s") ? "'" : "'s";
}

//Finds an item to drain, favours equipped items
//WARNING: This will loop through every item property on every item
//  until the right conditions are met
void SpellfireDrain(object oPC, object oTarget, int bCharged = TRUE, int bExemptCreatureItems = TRUE, int bSingleUse = TRUE)
{
    object oItem;
    int nObjectType = GetObjectType(oTarget);
    string sMessage_oPC = "";
    string sMessage_oTarget = "";
    string sName_oPC = GetName(oPC);
    string sName_oTarget = GetName(oTarget);
    int bFound = 0;

    if(nObjectType == OBJECT_TYPE_ITEM)
    {
        oItem = oTarget;
        int nBase = GetBaseItemType(oItem);
        if(GetPRCSwitch(PRC_SPELLFIRE_DISALLOW_DRAIN_SCROLL_POTION) &&
            ((nBase == BASE_ITEM_POTIONS) ||
            (nBase == BASE_ITEM_SCROLL) ||
            (nBase == BASE_ITEM_BLANK_POTION) ||
            (nBase == BASE_ITEM_BLANK_SCROLL)
            )
            )
        {
            sMessage_oPC = "Draining charges from scrolls and potions is not allowed";
        }
        else if(SpellfireDrainItem(oPC, oItem, bCharged))
        {
            bFound = 1;
            sMessage_oPC = "You drained " + sName_oTarget;
        }
    }
    else if(nObjectType == OBJECT_TYPE_CREATURE)
    {
        if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, 10, SAVING_THROW_TYPE_NONE, oPC))
        {
                    //creature items exempt
            int nSlotMax = bExemptCreatureItems ? INVENTORY_SLOT_CWEAPON_L : NUM_INVENTORY_SLOTS;
            //int nCharges = 0;
            int i;
            for(i = 0; i < nSlotMax; i++)
            {
                oItem = GetItemInSlot(i, oTarget);
                if(SpellfireDrainItem(oPC, oItem, bCharged))
                {
                    bFound = 1;
                    break;
                }
            }
            oItem = GetFirstItemInInventory(oTarget);
            if(bFound != 1)
            {
                while(GetIsObjectValid(oItem))
                {
                    if(SpellfireDrainItem(oPC, oItem, bCharged))
                    {
                        bFound = 1;
                        break;
                    }
                }
            }
            //search function
            if(bFound)
            {
                sMessage_oPC = "You drained " + GetName(oItem) + " from " + sName_oTarget;
                //sMessage_oTarget = sName_oPC + " drained " + GetName(oItem) " from you";
            }
            else
            {
                sMessage_oPC = "You tried to drain one of " + sName_oTarget + Name_s(sName_oTarget) + " items, but failed";
                sMessage_oTarget = sName_oPC + " tried to drain one of your items, but failed";
            }
        }
        else
        {
                sMessage_oPC = sName_oTarget + " resisted your attempt to drain one of <his/her> items";
                sMessage_oTarget = "You resisted " + sName_oPC + Name_s(sName_oPC) + " attempt to drain one of your items";
        }
    }
    else if(DEBUG)
        DoDebug("SpellfireDrain(): Invalid target object", oPC);

    if(bFound && !bCharged)
    {
        string s24 = " for 24 hours";
        sMessage_oPC += s24;
        sMessage_oTarget += s24;
    }

    if(GetIsPC(oPC) && sMessage_oPC != "") SendMessageToPC(oPC, sMessage_oPC);
    if(GetIsPC(oTarget) && sMessage_oTarget != "") SendMessageToPC(oTarget, sMessage_oTarget);
}

//Toggles a flag
void SpellfireToggleAbsorbFriendly(object oPC)
{
    if(GetLocalInt(oPC, "SpellfireAbsorbFriendly"))
    {
        DeleteLocalInt(oPC, "SpellfireAbsorbFriendly");
        FloatingTextStringOnCreature("*Absorb Friendly Spells Off*", oPC);
    }
    else
    {
        SetLocalInt(oPC, "SpellfireAbsorbFriendly", 1);
        FloatingTextStringOnCreature("*Absorb Friendly Spells On*", oPC);
    }
}

//Charges items
void SpellfireChargeItem(object oPC, object oItem)
{
    int nObjectType = GetObjectType(oItem);
    if(nObjectType == OBJECT_TYPE_ITEM)
    {
        int nExpend = ExpendSpellfire(oPC);
        int nCharges = GetItemCharges(oItem);
        int nNewCharges = nExpend + nCharges;
        if(nNewCharges > 50)
        {
            AddSpellfireLevels(oPC, nNewCharges - 50);
            nNewCharges = 50;
        }
        SetItemCharges(oItem, nCharges + nExpend);
        //Assuming 50 is the maximum
           //refunding excess charges
    }
    else if(DEBUG)
        DoDebug("SpellfireChargeItem(): Invalid target object", oPC);
}

//Applies Crown of Fire effects. Other effects are implemented
//through heartbeat and onhit scripts
void SpellfireCrown(object oPC)
{
    if(GetLocalInt(oPC, "SpellfireCrown"))
    {
        DeleteLocalInt(oPC, "SpellfireCrown");
        PRCRemoveEffectsFromSpell(oPC, SPELL_SPELLFIRE_CROWN);
        FloatingTextStringOnCreature("*Crown of Fire Deactivated*", oPC);
    }
    else
    {
        if(GetPersistantLocalInt(oPC, "SpellfireLevelStored") < 10)
        {
            SendMessageToPC(oPC, "You do not have enough stored spellfire levels");
            return;
        }
        SetLocalInt(oPC, "SpellfireCrown", 1);
        effect eDmgred = EffectDamageReduction(10, DAMAGE_POWER_PLUS_ONE);
        effect eResist = EffectSpellResistanceIncrease(32);
        effect eCrown = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_POSITIVE);
        effect eFlame = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD);
        effect eLight = EffectVisualEffect(VFX_DUR_LIGHT_WHITE_20);
        effect eLink = EffectLinkEffects(eDmgred, eResist);
        eLink = EffectLinkEffects(eLink, eCrown);
        eLink = EffectLinkEffects(eLink, eFlame);
        eLink = EffectLinkEffects(eLink, eLight);
        SPApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oPC);
        FloatingTextStringOnCreature("*Crown of Fire Activated*", oPC);
    }
}