//::///////////////////////////////////////////////
//:: Tome of Battle include: Maneuver Recovery
//:: tob_inc_martlore
//::///////////////////////////////////////////////
/** @file
    Defines various functions and other stuff that
    do something related to recovery and readying maneuvers
    See page #28 of Tome of Battle

    Functions below are called by the initiator as
    he makes a maneuver, or when recovering or readying

    @author Stratovarius
    @date   Created - 2007.3.25
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////

//:: Updated for .35 by Jaysyn 2023/03/10

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

//////////////////////////////////////////////////
/*                 Constants                    */
//////////////////////////////////////////////////

const int MANEUVER_READIED   = 1;
const int MANEUVER_RECOVERED = 2;
const int MANEUVER_GRANTED   = 3;
const int MANEVUER_WITHHELD  = 4;

const string _MANEUVER_LIST_RDYMODIFIER      = "_ReadyModifier";

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

/**
 * Gets the number of Maneuvers a character has readied
 *
 * @param oPC       The creature whose Maneuvers to check
 * @param nList     The list to check. One of MANEUVER_LIST_*
 * @return          The number of Maneuvers readied
 */
int GetReadiedCount(object oPC, int nList);

/**
 * Gets the maximum number of Maneuvers a character may ready.
 *
 * @param oPC       Character to determine maximum Maneuvers readied
 * @param nList     MANEUVER_LIST_* of the list to determine maximum Maneuvers for
 * @return          Maximum number of Maneuvers that oPC may ready
 */
int GetMaxReadiedCount(object oPC, int nList);

/**
 * Gets the value of the Maneuvers readied modifier, which is a value that is added
 * to the 2da-specified maximum Maneuvers readied to determine the actual maximum.
 *
 * @param oCreature The creature whose modifier to get
 * @param nList     The list the maximum Maneuvers readied from which the modifier
 *                  modifies. One of MANEUVER_LIST_*
 */
int GetReadiedManeuversModifier(object oCreature, int nList);

/**
 * Sets the value of the Maneuvers readied modifier, which is a value that is added
 * to the 2da-specified maximum Maneuvers readied to determine the actual maximum.
 *
 * @param oCreature The creature whose modifier to set
 * @param nList     The list the maximum Maneuvers readied from which the modifier
 *                  modifies. One of MANEUVER_LIST_*
 */
void SetReadiedManeuversModifier(object oCreature, int nList, int nNewValue);

/**
 * Readies the chosen Maneuver. Also checks to see if there are any slots left
 *
 * @param oPC       Character readying maneuver
 * @param nList     MANEUVER_LIST_* of the list to ready
 * @param nMoveId   Maneuver to ready
 */
void ReadyManeuver(object oPC, int nList, int nMoveId);

/**
 * Returns whether maneuver is readied or not
 *
 * @param oPC       Character to check
 * @param nList     MANEUVER_LIST_*
 * @param nMoveId   Maneuver to check
 * @return          TRUE or FALSE
 */
int GetIsManeuverReadied(object oPC, int nList, int nMoveId);

/**
 * Returns whether maneuver is expended or not
 *
 * @param oPC       Character to check
 * @param nList     MANEUVER_LIST_*
 * @param nMoveId   Maneuver to check
 * @return          TRUE or FALSE
 */
int GetIsManeuverExpended(object oPC, int nList, int nMoveId);

/**
 * Expends the chosen Maneuver.
 *
 * @param oPC       Character to check
 * @param nList     MANEUVER_LIST_*
 * @param nMoveId   Maneuver to expend
 */
void ExpendManeuver(object oPC, int nList, int nMoveId);

/**
 * Clears all local ints marking maneuvers as expended
 *
 * @param oPC       Character to clear
 * @param nList     MANEUVER_LIST_*
 */
void RecoverExpendedManeuvers(object oPC, int nList);

/**
 * Recovers the chosen Maneuver.
 *
 * @param oPC       Character to check
 * @param nList     MANEUVER_LIST_*
 * @param nMoveId   Maneuver to recover
 */
void RecoverManeuver(object oPC, int nList, int nMoveId);

/**
 * Checks to see if the PC is in a Warblade recovery round
 * This prevents all use of maneuvers or stances during that round.
 *
 * @param oPC       Character to clear
 * @return          TRUE or FALSE
 */
int GetIsWarbladeRecoveryRound(object oPC);

/**
 * Marks maneuvers as granted or withheld.
 *
 * @param oPC       Character to grant maneuvers to
 * @param nList     MANEUVER_LIST_*
 */
void GrantManeuvers(object oPC, int nList);

/**
 * Clears all local ints marking maneuvers as readied
 *
 * @param oPC       Character to clear
 * @param nList     MANEUVER_LIST_*
 */
void ClearReadiedManeuvers(object oPC, int nList);

/**
 * Grants a withheld maneuver
 * Only works on Crusaders
 *
 * @param oPC       Character to grant maneuvers to
 * @param nList     MANEUVER_LIST_*
 * @param nMoveId   Maneuver to grant
 */
void GrantWithheldManeuver(object oPC, int nList, int nMoveId = -1);

/**
 * Returns whether maneuver is granted or not
 * Only works on Crusaders
 *
 * @param oPC       Character to check
 * @param nMoveId   Maneuver to check
 * @return          TRUE or FALSE
 */
int GetIsManeuverGranted(object oPC, int nMoveId);

/**
 * Clears all local ints marking maneuvers as granted or withheld
 * Only works on Crusaders
 *
 * @param oPC       Character to clear
 */
void ClearGrantedWithheldManeuvers(object oPC);

/**
 * Starting function for Crusader recovery, calls DoCrusaderGranting
 * Only works on Crusaders
 *
 * @param oPC       Crusader
 */
void BeginCrusaderGranting(object oPC);

/**
 * Recursive function granting maneuvers each round in combat
 * Will end when combat ends
 * Only works on Crusaders
 *
 * @param oPC       Crusader
 * @param nTrip     Round of combat. Takes values from 1 to 5, always starts with 1.
 */
void DoCrusaderGranting(object oPC, int nTrip);


/**
 * Returns TRUE if a maneuver was expended, FALSE otherwise
 * @param oPC           Character to check
 * @param nList         MANEUVER_LIST_*
 * @param nDiscipline   DISCIPLINE_* the maneuver has to be from
 *
 * @return              TRUE or FALSE
 */
int ExpendRandomManeuver(object oPC, int nList, int nDiscipline = -1);

/**
 * Clears all local ints marking maneuvers as expended
 *
 * @param oPC       Character to clear
 * @param nPRC      Specific PRC to recover, else all.
 */
void RecoverPrCAbilities(object oPC);

/**
 * Heals 3 + 1 point per character level ones per minute
 *
 * @param oPC       Character to heal
 */
void VitalRecovery(object oPC);

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

#include "inc_lookups"
#include "tob_inc_tobfunc"

//////////////////////////////////////////////////
/*             Internal functions               */
//////////////////////////////////////////////////

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

int GetReadiedCount(object oPC, int nList)
{
    return GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList));
}

int GetMaxReadiedCount(object oPC, int nList)
{
    int nLevel = GetLevelByClass(nList, oPC);
    // 2das start at Row 0
    int nMaxReadied = StringToInt(Get2DACache(GetAMSKnownFileName(nList), "ManeuversReadied", nLevel-1));
    // Add in the custom modifier
    nMaxReadied += GetReadiedManeuversModifier(oPC, nList);
    if(nList == MANEUVER_LIST_SWORDSAGE)
        nMaxReadied += GetHasFeat(FEAT_EXTRA_GRANTED_MANEUVER, oPC);

    if(DEBUG) DoDebug("tob_inc_recovery: MaxManeuvers Readied: " +IntToString(nMaxReadied));
    return nMaxReadied;
}

int GetReadiedManeuversModifier(object oCreature, int nList)
{
    return GetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + _MANEUVER_LIST_RDYMODIFIER);
}

void SetReadiedManeuversModifier(object oCreature, int nList, int nNewValue)
{
    SetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + _MANEUVER_LIST_RDYMODIFIER, nNewValue);
}

void ReadyManeuver(object oPC, int nList, int nMoveId)
{
    int nCount = GetReadiedCount(oPC, nList);
    int nMaxCount = GetMaxReadiedCount(oPC, nList);

    // If the PC can ready a maneuver and hasn't filled them all up
    if(nMaxCount > nCount)
    {
        nCount++;
        SetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(nCount), nMoveId);
        SetLocalInt(oPC, "ManeuverReadied" + IntToString(nList), nCount);
        if(DEBUG) DoDebug("tob_inc_recovery: ReadyManeuver: " +IntToString(nMoveId));
    }
    else
        FloatingTextStringOnCreature("All maneuvers are readied", oPC, FALSE);
}

int GetIsManeuverReadied(object oPC, int nList, int nMoveId)
{
    // Counting through the local ints to determine if this one is readied
    int i, nMax = GetReadiedCount(oPC, nList);
    for(i = 1; i <= nMax; i++)
    {
        // If the value is valid, return true
        if(GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)) == nMoveId)
        {
            if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverReadied: " + IntToString(nMoveId));
            return TRUE;
        }
    }
    return FALSE;
}

int GetIsManeuverExpended(object oPC, int nList, int nMoveId)
{
    // Counting through the local ints to determine if this one is expended
    int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList));
    for(i = 1; i <= nMax; i++)
    {
        // returns if the maneuver is expended
        if(GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)) == nMoveId)
        {
            if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverExpended: " +IntToString(nMoveId));
            return TRUE;
        }
    }
    return FALSE;
}

void ExpendManeuver(object oPC, int nList, int nMoveId)
{
    int nCount = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)) + 1;

    // This will mark the Maneuver Expended
    SetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(nCount), nMoveId);
    SetLocalInt(oPC, "ManeuverExpended" + IntToString(nList), nCount);
    if(DEBUG) DoDebug("tob_inc_recovery: Expending Maneuver: " + IntToString(nMoveId));
}

void RecoverExpendedManeuvers(object oPC, int nList)
{
    if(DEBUG) DoDebug("tob_inc_recovery: Clearing expended maneuvers");
    // Counting through the local ints to clear them all
    int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList));
    DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList));
    for(i = 1; i <= nMax; i++)
    {
        // Clear them all
        DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i));
    }
    // Do Grant/Withheld Maneuvers whenever this is called on a Crusader
    if (nList == MANEUVER_LIST_CRUSADER)
    {
        // Make sure to clear them all first
        ClearGrantedWithheldManeuvers(oPC);
        // Then re-grant/withhold them
        GrantManeuvers(oPC, nList);
    }
    if (GetHasFeat(FEAT_VITAL_RECOVERY, oPC)) VitalRecovery(oPC);
}

void RecoverManeuver(object oPC, int nList, int nMoveId)
{
    // Counting through the local ints to determine if this one is expended
    int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList));
    for(i = 1; i <= nMax; i++)
    {
        // If it has been expended, clear that
        if(GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)) == nMoveId)
        {
            DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i));
            if(DEBUG) DoDebug("tob_inc_recovery: Recovering Maneuver: " + IntToString(nMoveId));
        }
    }
    if (GetHasFeat(FEAT_VITAL_RECOVERY, oPC)) VitalRecovery(oPC);
}

int GetIsWarbladeRecoveryRound(object oPC)
{
    if(DEBUG) DoDebug("tob_inc_recovery: Warblade recovery check");
    return GetLocalInt(oPC, "WarbladeRecoveryRound");
}

void GrantRandomManeuver(object oPC, int nList = MANEUVER_LIST_CRUSADER)
{
    int nMax = GetLocalInt(oPC, "GrantRand#");
    if(!nMax) return;//nothing to grant

    SetLocalInt(oPC, "GrantRand#", nMax - 1);
    int x = Random(nMax)+1;
    int nMoveId = GetLocalInt(oPC, "GrantRand#" + IntToString(x));
    if(x != nMax)
        SetLocalInt(oPC, "GrantRand#" + IntToString(x), GetLocalInt(oPC, "GrantRand#" + IntToString(nMax)));
    DeleteLocalInt(oPC, "GrantRand#" + IntToString(nMax));

    //GrantWithheldManeuver(oPC, MANEUVER_LIST_CRUSADER, MoveId);
    // No point in granting an expended maneuver
    if(GetIsManeuverExpended(oPC, nList, nMoveId))
        RecoverManeuver(oPC, nList, nMoveId);

    int i = 1;
    while(i)
    {
        // If it hits a non-valid, break
        if(!GetLocalInt(oPC, "ManeuverGranted" + IntToString(i))) break;
        i++;
    }
    SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId);
}

void ListGrantedManeuvers(object oPC)
{
    int i;
    for(i = 1; i <= 4; i++)
    {
        int nMoveId = GetLocalInt(oPC, "ManeuverGranted" + IntToString(i));
        int nExpended = GetIsManeuverExpended(oPC, MANEUVER_LIST_CRUSADER, nMoveId);
        if (nMoveId > 0 && !nExpended) FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE);
    }
}

void GrantManeuvers(object oPC, int nList = MANEUVER_LIST_CRUSADER)
{
    // Only crusader level matters for this
    int nLevel = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC);
    // 2das start at Row 0
    int nGranted = StringToInt(Get2DACache(GetAMSKnownFileName(nList), "ManeuversGranted", nLevel-1));
    nGranted += GetReadiedManeuversModifier(oPC, nList);
    nGranted += GetHasFeat(FEAT_EXTRA_GRANTED_MANEUVER, oPC);

    // Counting through the local ints to determine how many are readied
    int i, nMaxReadied = GetReadiedCount(oPC, nList);
    SetLocalInt(oPC, "GrantRand#", nMaxReadied);
    for(i = 1; i <= nMaxReadied; i++)
    {
        // build temporary array for GrantRandomManeuver() function
        int nMoveId = GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i));
        if(nMoveId)
            SetLocalInt(oPC, "GrantRand#" + IntToString(i), nMoveId);
    }
    for(i = 1; i <= nGranted; i++)
    {
        GrantRandomManeuver(oPC);
    }
    ListGrantedManeuvers(oPC);    
}

void ClearReadiedManeuvers(object oPC, int nList)
{
    if(DEBUG) DoDebug("tob_inc_recovery: Clearing readied maneuvers");
    // Counting through the local ints to clear them all
    int i, nMax = GetReadiedCount(oPC, nList);
    DeleteLocalInt(oPC, "ManeuverReadied" + IntToString(nList));
    for(i = 1; i <= nMax; i++)
    {
        // Clear them all
        DeleteLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i));
    }
}

/*void GrantWithheldManeuver(object oPC, int nList, int nMoveId = -1)
{
    int i;
    string sPsiFile = GetAMSKnownFileName(nList);
    // 2das start at Row 0
    int nLevel = GetInitiatorLevel(oPC, nList);
    int nGranted = StringToInt(Get2DACache(sPsiFile, "ManeuversGranted", nLevel-1));
    int nReadied = StringToInt(Get2DACache(sPsiFile, "ManeuversReadied", nLevel-1));
    if(DEBUG) DoDebug("tob_inc_recovery: Maneuvers Granted: " + IntToString(nGranted));
    if(DEBUG) DoDebug("tob_inc_recovery: Maneuvers Readied: " + IntToString(nReadied));

    // If someone input a maneuver
    if (nMoveId > 0)
    {
        // No point in granting an expended maneuver
        if (GetIsManeuverExpended(oPC, nList, nMoveId))
            RecoverManeuver(oPC, nList, nMoveId);

        // 3 is always the number withheld
        for(i = nGranted; i < nReadied; i++)
        {
            // Making sure it gets marked properly
            int nGrantId = GetLocalInt(oPC, "ManeuverWithheld" + IntToString(i));
            // If it exists, mark it as ready and break out
            if (nMoveId == nGrantId)
            {
                if(DEBUG) DoDebug("tob_inc_recovery: Withheld Maneuver Granted: " + IntToString(nMoveId));
                DeleteLocalInt(oPC, "ManeuverWithheld" + IntToString(i));
                FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE);
                SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId);
                break;
            }
        }
    }
    else
    {
        // 3 is always the number withheld
        for(i = nGranted; i < nReadied; i++)
        {
            nMoveId = GetLocalInt(oPC, "ManeuverWithheld" + IntToString(i));
            // If it exists, mark it as ready and break out
            if (nMoveId > 0)
            {
                if(DEBUG) DoDebug("tob_inc_recovery: Withheld Maneuver Granted: " + IntToString(nMoveId));
                DeleteLocalInt(oPC, "ManeuverWithheld" + IntToString(i));
                FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE);
                SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId);
                break;
            }
        }
    }
}*/

int GetIsManeuverGranted(object oPC, int nMoveId)
{
    if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverGranted Start");
    // Counting through the local ints to determine if this one is expended
    int i, nMax = GetReadiedCount(oPC, MANEUVER_LIST_CRUSADER);
    for(i = 1; i <= nMax; i++)
    {
        // returns if the maneuver is expended
        if(GetLocalInt(oPC, "ManeuverGranted" + IntToString(i)) == nMoveId)
        {
            if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverGranted: " + IntToString(nMoveId));
            return TRUE;
        }
    }
    return FALSE;
}

void ClearGrantedWithheldManeuvers(object oPC)
{
    if(DEBUG) DoDebug("tob_inc_recovery: Clearing Granted and Withheld Maneuvers");
    // Counting through the local ints to clear them all
    int i, nMax = GetReadiedCount(oPC, MANEUVER_LIST_CRUSADER);
    for(i = 1; i <= nMax; i++)
    {
        // Clear them all
        DeleteLocalInt(oPC, "ManeuverGranted" + IntToString(i));
    }
}

void BeginCrusaderGranting(object oPC)
{
    if(DEBUG) DoDebug("BeginCrusaderGranting(): Entered Function");
    // Stops it from being called more than once.
    if(GetLocalInt(oPC, "CrusaderGrantLoop")) return;
    SetLocalInt(oPC, "CrusaderGrantLoop", TRUE);

    // Starts the granting process
    if(DEBUG) DoDebug("BeginCrusaderGranting(): DoCrusaderGranting called");
    DoCrusaderGranting(oPC, 1);
}

void DoCrusaderGranting(object oPC, int nTrip)
{
    if(DEBUG) DoDebug("DoCrusaderGranting(): Entered Function on Round #" + IntToString(nTrip));
    // First round of combat, no granting.
    // Last round of the 5, clear and recover/grant maneuvers
    if (nTrip >= 5) // Granted maneuvers empty, restart
    {
        if(DEBUG) DoDebug("DoCrusaderGranting(): RecoverExpendedManeuvers");
        RecoverExpendedManeuvers(oPC, MANEUVER_LIST_CRUSADER);
        nTrip = 1;
    }
    else if (nTrip > 1)
    {
        // Rounds 2-4, grant a single maneuver
        if(DEBUG) DoDebug("DoCrusaderGranting(): GrantWithheldManeuver");
        //GrantWithheldManeuver(oPC, MANEUVER_LIST_CRUSADER);
        GrantRandomManeuver(oPC);
        ListGrantedManeuvers(oPC);
    }

    if(DEBUG) DoDebug("DoCrusaderGranting(): Above Recursive");
    // If in combat, keep the loop going
    if (GetIsInCombat(oPC))
    {
        if(DEBUG) DoDebug("DoCrusaderGranting(): In Combat");
        DelayCommand(6.0, DoCrusaderGranting(oPC, ++nTrip)); // Increment counter
    }
    else // Recover and stop loop otherwise.
    {
        if(DEBUG) DoDebug("DoCrusaderGranting(): Out of Combat Maneuver Recovery");
        RecoverExpendedManeuvers(oPC, MANEUVER_LIST_CRUSADER);
        // Resent Int for next time out
        DeleteLocalInt(oPC, "CrusaderGrantLoop");
    }
    if(DEBUG) DoDebug("DoCrusaderGranting(): Ending");
}

int ExpendRandomManeuver(object oPC, int nList, int nDiscipline = -1)
{
    // Counting through the local ints to determine if maneuver can be expended
    int i, nMax = GetReadiedCount(oPC, nList);
    for(i = 1; i <= nMax; i++)
    {
        // If the value is valid, next step
        int nMoveId = GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i));
        if(nMoveId > 0)
        {
            // Make sure the disciplines match
            if(nDiscipline == -1 || GetDisciplineByManeuver(nMoveId) == nDiscipline)
            {
                // If not expended
                if(!GetIsManeuverExpended(oPC, nList, nMoveId))
                {
                    // Expend the damn thing and go home
                    ExpendManeuver(oPC, nList, nMoveId);
                    return TRUE;
                }
            }
        }
    }

    // If we're here, failed.
    return FALSE;
}

void RecoverPrCAbilities(object oPC)
{
    int i;
    for(i = 2; i <= 8; i++) // PrC abilities: check last seven slots
    {
        int nClass = GetClassByPosition(i, oPC);
        if(DEBUG) DoDebug("RecoverPrCAbilities" + IntToString(nClass));
        switch(nClass)
        {
            case CLASS_TYPE_INVALID:
                if(DEBUG) DoDebug("RecoverPrCAbilities: no class to recover");
                break;
            case CLASS_TYPE_JADE_PHOENIX_MAGE:
                DeleteLocalInt(oPC, "JPM_Empowering_Strike_Expended");
                DeleteLocalInt(oPC, "JPM_Quickening_Strike_Expended");
                break;
            case CLASS_TYPE_DEEPSTONE_SENTINEL:
                DeleteLocalInt(oPC, "DPST_Awaken_Stone_Dragon_Expended");
                break;
            case CLASS_TYPE_ETERNAL_BLADE:
                DeleteLocalInt(oPC, "ETBL_Eternal_Training_Expended");
                DeleteLocalInt(oPC, "ETBL_Island_In_Time_Expended");
                // Remove bonus to racial type from eternal training
                PRCRemoveEffectsFromSpell(oPC, ETBL_RACIAL_TYPE);
                break;
        }
    }
}

void VitalRecovery(object oPC)
{
    if (GetLocalInt(oPC, "VitalRecovery")) return; //Once a minute
    int nHD = GetHitDice(oPC);
    effect eHeal = EffectHeal(nHD+3); // That's it
    effect eVis = EffectVisualEffect(VFX_IMP_HEALING_M);
    ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oPC);
    ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC);
    
    SetLocalInt(oPC, "VitalRecovery", TRUE);
    DelayCommand(60.0, DeleteLocalInt(oPC, "VitalRecovery"));
}