//::///////////////////////////////////////////////
//:: Psionics include: Manifesting
//:: psi_inc_psifunc - was psi_inc_manifest
//::///////////////////////////////////////////////
/** @file
    Defines structures and functions for handling
    manifesting a power and main nexus for
	Psi function access.
	
	Acts as inclusion nexus for the general
    psionics includes. In other words, don't include
    them directly in your scripts, instead include this.

    @author Ornedan
    @date   Created - 2005.11.19
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////

//void main (){}

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

// Constants are provided via psi_inc_core

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

/**
 * Determines if the power that is currently being attempted to be manifested
 * can in fact be manifested. Calculates PP cost and pays it. Determines
 * augmentation and metapsionics used.
 *
 * @param oManifester   A creature attempting to manifest a power at this moment.
 * @param oTarget       The target of the power, if any. For pure Area of Effect.
 *                      powers, this should be OBJECT_INVALID. Otherwise the main
 *                      target of the power as returned by PRCGetSpellTargetObject().
 * @param pap           A power augmentation profile generated by a call to
 *                      PowerAugmentationProfile(). This specifies how the power may
 *                      be augmented.
 * @param nMetaPsiFlags The metapsionics that may be used to modify this power. Any number
 *                      of METAPSIONIC_* constants ORd together using the | operator.
 *                      For example (METAPSIONIC_EMPOWER | METAPSIONIC_MAXIMIZE)
 *
 * @return              A manifestation structure that contains the data about whether
 *                      the power was successfully manifested, what augmentation and
 *                      metapsionics were used and some other commonly used data, like
 *                      the manifester's manifester level for this manifestation.
 */
struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags);

/**
 * Causes OBJECT_SELF to use the given power.
 *
 * @param nPower         The index of the power to use in spells.2da or a POWER_*
 * @param nClass         The index of the class to use the power as in classes.2da or a CLASS_TYPE_*
 * @param bIsPsiLike     Whether the power to be used is to be a normal use or a psi-like ability, which
 *                       acts somewhat differently.
 *                       Default: FALSE, meaning a normal power.
 * @param nLevelOverride An optional override to normal manifester level. This is necessary when
 *                       using the power as a psi-like ability.
 *                       Default: 0, which means the parameter is ignored.
 */
void UsePower(int nPower, int nClass, int bIsPsiLike = FALSE, int nLevelOverride = 0);

/**
 * A debugging function. Takes a manifestation structure and
 * makes a string describing the contents.
 *
 * @param manif A set of manifestation data
 * @return      A string describing the contents of manif
 */
string DebugManifestation2Str(struct manifestation manif);

/**
 * Stores a manifestation structure as a set of local variables. If
 * a structure was already stored with the same name on the same object,
 * it is overwritten.
 *
 * @param oObject The object on which to store the structure
 * @param sName   The name under which to store the structure
 * @param manif   The manifestation structure to store
 */
void SetLocalManifestation(object oObject, string sName, struct manifestation manif);

/**
 * Retrieves a previously stored manifestation structure. If no structure is stored
 * by the given name, the structure returned is empty.
 *
 * @param oObject The object from which to retrieve the structure
 * @param sName   The name under which the structure is stored
 * @return        The structure built from local variables stored on oObject under sName
 */
struct manifestation GetLocalManifestation(object oObject, string sName);

/**
 * Sets the evaluation functions to ignore constraints on manifesting.
 * Call this just prior to EvaluateManifestation() in a power script.
 * That evaluation will then ignore lacking manifestation ability score,
 * Power Points and Psionic Focuses.
 *
 * @param oManifester A creature attempting to manifest a power at this moment.
 */
void DebugIgnoreConstraints(object oManifester);

/**
 * Determines if the power that is currently being attempted to be manifested
 * can in fact be manifested. Calculates PP cost and pays it. Determines
 * augmentation and metapsionics used.
 *
 * @param oManifester   A creature attempting to manifest a power at this moment.
 * @param oTarget       The target of the power, if any. For pure Area of Effect.
 *                      powers, this should be OBJECT_INVALID. Otherwise the main
 *                      target of the power as returned by PRCGetSpellTargetObject().
 * @param nPowerLevel   The "power level" of the ability, used for determining PP cost.
 * @param pap           A power augmentation profile generated by a call to
 *                      PowerAugmentationProfile(). This specifies how the power may
 *                      be augmented.
 * @param nPowerLevel   The "power level" of the ability, used for determining PP cost.
 *
 * @return              A manifestation structure that contains the data about whether
 *                      the ability was successfully manifested, what augmentation and
 *                      metapsionics were used and some other commonly used data, like
 *                      the manifester's manifester level for this manifestation.
 */
struct manifestation EvaluateDiaDragChannel(object oManifester, object oTarget, struct power_augment_profile pap, int nPowerLevel);

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

#include "psi_inc_metapsi"
#include "psi_inc_ppoints"  //
#include "psi_inc_augment"  //
#include "psi_inc_psicraft" // Provides Psicraft identifying 
#include "psi_inc_powknown"	//


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

/** Internal function.
 * Calculates PP cost reduction from various factors. Currently accounts for:
 * - Thrallherd
 * - Shadowmind
 *
 * @param manif The manifestation data relating to this particular manifesation
 * @retrun      The manifestation data, possibly with modified costs
 */
struct manifestation _GetPPCostReduced(struct manifestation manif)
{
    int nSpell   = PRCGetSpellId();
    int nThrall  = GetLevelByClass(CLASS_TYPE_THRALLHERD, manif.oManifester);
    int nShadow  = GetLevelByClass(CLASS_TYPE_SHADOWMIND, manif.oManifester);

    if(nThrall > 0)
    {
        if(GetLocalInt(manif.oManifester, "ThrallCharm") && nSpell == POWER_CHARMPERSON)
        {
            DeleteLocalInt(manif.oManifester, "ThrallCharm");
            manif.nPPCost -= nThrall;
        }
        if(GetLocalInt(manif.oManifester, "ThrallDom") && nSpell == POWER_DOMINATE)
        {
            DeleteLocalInt(manif.oManifester, "ThrallDom");
            manif.nPPCost -= nThrall;
        }

        // Reduced cost for augmenting the Dominate power. These do not count for the DC increase
        if(nThrall >= 7 && nSpell == POWER_DOMINATE && manif.nTimesAugOptUsed_1 > 0)
        {
            manif.nPPCost -= 2;
            manif.nTimesGenericAugUsed -= 1;
        }
        if(nThrall >= 9 && nSpell == POWER_DOMINATE && manif.nTimesAugOptUsed_2 > 0)
        {
            manif.nPPCost -= 4;
            manif.nTimesGenericAugUsed -= 2;
        }

        if(manif.nPPCost < 1) manif.nPPCost = 1;
    }
    
    if (nShadow > 0)
    {
        if(GetLocalInt(manif.oManifester, "ShadowDistract") && nSpell == POWER_DISTRACT)
        {
            DeleteLocalInt(manif.oManifester, "ShadowDistract");
            manif.nPPCost -= nShadow;
        }
        if(GetLocalInt(manif.oManifester, "ShadowCloudMind") && nSpell == POWER_CLOUD_MIND)
        {
            DeleteLocalInt(manif.oManifester, "ShadowCloudMind");
            manif.nPPCost -= nShadow;
        }
        if(GetLocalInt(manif.oManifester, "ShadowCloudMindMass") && nSpell == POWER_CLOUD_MIND_MASS)
        {
            DeleteLocalInt(manif.oManifester, "ShadowCloudMindMass");
            manif.nPPCost -= nShadow;
        }
        
        if(manif.nPPCost < 1) manif.nPPCost = 1;
    }

    return manif;
}

/** Internal function.
 * A wilder that is of high enough level to posses the Volatile Mind class
 * feature causes extra cost to be applied to telepathy powers manifested
 * on it.
 *
 * @param oTarget     Target of the power being manifested at the moment
 * @param oManifester Creature manifesting the power
 * @return            Either 0 if the character does not posses the Volatile
 *                    Mind class feature or the power is not of the telepathy
 *                    discipline. Otherwise, a number determined by the target's
 *                    Wilder level.
 */
int _VolatileMind(object oTarget, object oManifester)
{
    int nWilder    = GetLevelByClass(CLASS_TYPE_WILDER, oTarget);
    int nTelepathy = GetIsTelepathyPower();
    int nCost = 0;

    if(nTelepathy   && // Only affects telepathy powers.
       nWilder >= 5 && // Only wilders need apply
       // Since the "As a standard action, a wilder can choose to lower this effect for 1 round."
       // bit is not particularly doable in NWN, we implement it so that the class feature
       // only affects powers from hostile manifesters
       GetIsEnemy(oTarget, oManifester)
       )
    {
        nCost = ((nWilder - 5) / 4) + 1;
    }

    return nCost;
}

/** Internal function.
 * Calculates the extra cost caused by the Psionic Hole feat.
 *
 * @param oTarget The target of a power currently being manifested
 * @return        If the target has the Psionic Hole feat, the greater of
 *                it's Wisdom modifier and 0. Otherwise, just 0.
 */
int _PsionicHole(object oTarget)
{
    int nCost = 0;

    if(GetHasFeat(FEAT_PSIONIC_HOLE, oTarget))
        // Psionic Hole will never decrease power cost, even if the target is lacking in wisdom bonus
        nCost = PRCMax(GetAbilityModifier(ABILITY_WISDOM, oTarget), 0);

    return nCost;
}

/** Internal function.
 * Applies Hostile Mind damage if the target posses the feat and is being
 * targeted with a telepathy power.
 *
 * @param oManifester A creature currently manifesting a power at oTarget
 * @param oTarget     The target of the power being manifested.
 */
void _HostileMind(object oManifester, object oTarget)
{
    if(GetHasFeat(FEAT_HOSTILE_MIND, oTarget) && GetIsTelepathyPower())
    {
        // Save DC is 10 + HD/2 + ChaMod
        int nDC = 10 + GetHitDice(oTarget) / 2 + GetAbilityModifier(ABILITY_CHARISMA, oTarget);
        if(!PRCMySavingThrow(SAVING_THROW_WILL, oManifester, nDC, SAVING_THROW_TYPE_NONE))
        {
            //Apply damage and some VFX
            SPApplyEffectToObject(DURATION_TYPE_INSTANT,
                                  EffectLinkEffects(EffectDamage(d6(2)), EffectVisualEffect(VFX_FNF_SPELL_FAIL_HEA)),
                                  oManifester
                                  );
        }
    }
}

/** Internal function.
 * Applies the damage caused by use of Overchannel feat.
 *
 * @param oManifester The creature currently manifesting a power
 * @param bIsPsiLike  Whether the power being manifester is a psi-like ability or not
 */
void _DoOverchannelDamage(object oManifester, int bIsPsiLike)
{
    int nOverchannel = GetLocalInt(oManifester, PRC_OVERCHANNEL);
    if(nOverchannel > 0 && !bIsPsiLike)
    {
        int nDam = d8(nOverchannel * 2 - 1);
        // Check if Talented applies
        if(GetPowerLevel(oManifester) <= 3)
        {
            if(GetLocalInt(oManifester, "TalentedActive") && UsePsionicFocus(oManifester))
                return;
            /* Should we be merciful and let the feat be "retroactively activated" if the damage were enough to kill?
            else if(GetCurrentHitPoints(oCaster) < nDam && GetHasFeat(FEAT_TALENTED, oCaster) && UsePsionicFocus(oCaster))
                return;*/
        }
        effect eDam = EffectDamage(nDam);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oManifester);
    }
}

/** Internal function.
 * If the manifester is a wilder who is using Wild Surge, rolls and
 * applies Psychic Enervation or Surging Euphoria based on the result.
 *
 * @param oManifester The creature currently manifesting a power
 */
void _SurgingEuphoriaOrPsychicEnervation(object oManifester, int nWildSurge)
{
    int nWilder    = GetLevelByClass(CLASS_TYPE_WILDER, oManifester);

    // Only Wilders need apply (at least so far)
    if(nWilder > 0 && nWildSurge)
    {
        // Psychic Enervation has a 5% chance to happen per point Wild Surged by
        if(nWildSurge >= d20())
        {
            effect eMind = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE);
            effect eDaze = EffectDazed();
            effect eLink = EffectLinkEffects(eMind, eDaze);
            eLink = ExtraordinaryEffect(eLink);

            FloatingTextStrRefOnCreature(16823620, oManifester, FALSE); // "You have become psychically enervated and lost power points"
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oManifester, RoundsToSeconds(1));

            LosePowerPoints(oManifester, nWilder);
        }
        // Need minimum wilder level 4 to be eligible for Surging Euphoria. And it only happens when there is no Enervation
        else if(nWilder >= 4)
        {
            // Euphoria is 1 at levels 4 - 11, 2 at L 12 - 19, 3 at L 20 - 27, etc.
            int nEuphoria = ((nWilder - 4) / 8) + 1;

            effect eBonAttack = EffectAttackIncrease(nEuphoria);
            effect eBonDam    = EffectDamageIncrease(nEuphoria, DAMAGE_TYPE_MAGICAL);
            effect eVis       = EffectVisualEffect(VFX_IMP_MAGIC_PROTECTION);
            effect eSave      = EffectSavingThrowIncrease(SAVING_THROW_ALL, nEuphoria, SAVING_THROW_TYPE_SPELL);
            effect eDur       = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE);
            effect eDur2      = EffectVisualEffect(VFX_DUR_MAGIC_RESISTANCE);
            effect eLink      = EffectLinkEffects(eSave, eDur);
            eLink = EffectLinkEffects(eLink, eDur2);
            eLink = EffectLinkEffects(eLink, eBonDam);
            eLink = EffectLinkEffects(eLink, eBonAttack);
            eLink = ExtraordinaryEffect(eLink);

            FloatingTextStringOnCreature(GetStringByStrRef(16823616) + ": " + IntToString(nWildSurge), oManifester, FALSE); // "Surging Euphoria: "
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oManifester, RoundsToSeconds(nWildSurge));
        }
    }
}

/** Internal function.
 * Applies the PP loss caused by the target having Mind Trap active.
 *
 * @param oManifester A creature currently manifesting a power at oTarget
 * @param oTarget     The target of the power being manifested
 */
void _DoMindTrapPPLoss(object oManifester, object oTarget)
{
    if(oManifester != oTarget                            && // Does not apply to own powers
       GetLocalInt(oTarget, "PRC_Power_MindTrap_Active") && // The target does have Mind Trap active
       GetIsTelepathyPower()                                // And the power being used is a telepathy power
       )
    {
        LosePowerPoints(oManifester, d6());
    }
}

/** Internal function.
 * Handles Spellfire absorption when a power is used on a friendly spellfire
 * user.
 */
struct manifestation _DoSpellfireFriendlyAbsorption(struct manifestation manif, object oTarget)
{
    if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") &&
       GetIsFriend(oTarget, manif.oManifester)
       )
    {
        if(CheckSpellfire(manif.oManifester, oTarget, TRUE))
        {
            PRCShowSpellResist(manif.oManifester, oTarget, SPELL_RESIST_MANTLE);
            manif.bCanManifest = FALSE;
        }
    }

    return manif;
}

/** Internal function.
 * Deletes manifestation-related local variables.
 *
 * @param oManifester The creature currently manifesting a power
 */
void _CleanManifestationVariables(object oManifester)
{
    DeleteLocalInt(oManifester, PRC_MANIFESTING_CLASS);
    DeleteLocalInt(oManifester, PRC_POWER_LEVEL);
    DeleteLocalInt(oManifester, PRC_IS_PSILIKE);
    DeleteLocalInt(oManifester, PRC_AUGMENT_OVERRIDE);
}

/** Internal function.
 * Determines whether a manifestation token exists. If one does, returns it.
 *
 * @param oManifester A creature whose manifestation token to get
 * @return            The manifestation token if it exists, OBJECT_INVALID otherwise.
 */
object _GetManifestationToken(object oManifester)
{
    object oMfToken = GetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR);

    // Special case - variable not set up yet, so this is the characters first manifestation since entering the module
/* Obsoleted by the manifestation token chest in Limbo
    if(oMfToken == OBJECT_INVALID)
    {
        object oSkin = GetPCSkin(oManifester);
        object oTest = GetFirstItemInInventory(oSkin);

        // Seek for tokens in the creature's inventory and destroy them
        while(GetIsObjectValid(oTest))
        {
            if(GetTag(oTest) == PRC_MANIFESTATION_TOKEN_NAME)
                DestroyObject(oTest);
            oTest = GetNextItemInInventory(oSkin);
        }
    }
*/
    // If the token object is no longer valid, set the variable to point at manifester
    if(!GetIsObjectValid(oMfToken))
    {
        oMfToken = oManifester;
        SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oMfToken);
    }


    // Check if there is no token
    if(oMfToken == oManifester)
        oMfToken = OBJECT_INVALID;

    return oMfToken;
}

/** Internal function.
 * Destroys the given manifestation token and sets the creature's manifestation token variable
 * to point at itself.
 *
 * @param oManifester The manifester whose token to destroy
 * @param oMfToken    The token to destroy
 */
void _DestroyManifestationToken(object oManifester, object oMfToken)
{
    DestroyObject(oMfToken);
    SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oManifester);
}

/** Internal function.
 * Destroys the previous manifestation token, if any, and creates a new one.
 *
 * @param oManifester A creature for whom to create a manifestation token
 * @return            The newly created token
 */
object _CreateManifestationToken(object oManifester)
{
    object oMfToken = _GetManifestationToken(oManifester);
    object oStore   = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oManifester);

    // Delete any previous tokens
    if(GetIsObjectValid(oMfToken))
        _DestroyManifestationToken(oManifester, oMfToken);

    // Create new token and store a reference to it
    oMfToken = CreateItemOnObject(PRC_MANIFESTATION_TOKEN_NAME, oStore);
    SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oMfToken);

    Assert(GetIsObjectValid(oMfToken), "GetIsObjectValid(oMfToken)", "ERROR: Unable to create manifestation token! Store object: " + DebugObject2Str(oStore), "psi_inc_psifunc", "_CreateManifestationToken()");

    return oMfToken;
}

/** Internal function.
 * Determines whether the given manifester is doing something that would
 * interrupt manifesting a power or affected by an effect that would do
 * the same.
 *
 * @param oManifester A creature on which _ManifestationHB() is running
 * @return            TRUE if the creature can continue manifesting,
 *                    FALSE otherwise
 */
int _ManifestationStateCheck(object oManifester)
{
    int nAction = GetCurrentAction(oManifester);
    // If the current action is not among those that could either be used to manifest the power or movement, the power fails
    if(!(nAction || ACTION_CASTSPELL     || nAction == ACTION_INVALID      ||
         nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT  ||
         nAction || ACTION_USEOBJECT     || nAction == ACTION_WAIT
       ) )
        return FALSE;

    // Affected by something that prevents one from manifesting
    effect eTest = GetFirstEffect(oManifester);
    int nEType;
    while(GetIsEffectValid(eTest))
    {
        nEType = GetEffectType(eTest);
        if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE ||
           nEType == EFFECT_TYPE_DAZED             ||
           nEType == EFFECT_TYPE_PARALYZE          ||
           nEType == EFFECT_TYPE_PETRIFY           ||
           nEType == EFFECT_TYPE_SLEEP             ||
           nEType == EFFECT_TYPE_STUNNED
           )
            return FALSE;

        // Get next effect
        eTest = GetNextEffect(oManifester);
    }

    return TRUE;
}

/** Internal function.
 * Runs while the given creature is manifesting. If they move, take other actions
 * that would cause them to interrupt manifesting the power or are affected by an
 * effect that would cause such interruption, deletes the manifestation token.
 * Stops if such condition occurs or something else destroys the token.
 *
 * @param oManifester A creature manifesting a power
 * @param lManifester The location where the manifester was when starting the manifestation
 * @param oMfToken    The manifestation token that controls the ongoing manifestation
 */
void _ManifestationHB(object oManifester, location lManifester, object oMfToken)
{
    if(DEBUG) DoDebug("_ManifestationHB() running:\n"
                    + "oManifester = " + DebugObject2Str(oManifester) + "\n"
                    + "lManifester = " + DebugLocation2Str(lManifester) + "\n"
                    + "oMfToken = " + DebugObject2Str(oMfToken) + "\n"
                    + "Distance between manifestation start location and current location: " + FloatToString(GetDistanceBetweenLocations(lManifester, GetLocation(oManifester))) + "\n"
                      );
    if(GetIsObjectValid(oMfToken))
    {
        // Continuance check
        if(GetDistanceBetweenLocations(lManifester, GetLocation(oManifester)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting
           !_ManifestationStateCheck(oManifester)                                       // Action and effect check
           )
        {
            if(DEBUG) DoDebug("_ManifestationHB(): Manifester moved or lost concentration, destroying token");
            _DestroyManifestationToken(oManifester, oMfToken);

            // Inform manifester
            FloatingTextStrRefOnCreature(16828435, oManifester, FALSE); // "You have lost concentration on the power you were attempting to manifest!"
        }
        // Schedule next HB
        else
            DelayCommand(PRC_MANIFESTATION_HB_DELAY, _ManifestationHB(oManifester, lManifester, oMfToken));
    }
}

/** Internal function.
 * Checks if the manifester is in range to use the power they are trying to use.
 * If not, queues commands to make the manifester to run into range.
 *
 * @param oManifester A creature manifesting a power
 * @param nPower      SpellID of the power being manifested
 * @param lTarget     The target location or the location of the target object
 */
void _ManifestationRangeCheck(object oManifester, int nPower, location lTarget)
{
    float fDistance   = GetDistanceBetweenLocations(GetLocation(oManifester), lTarget);
    float fRangeLimit;
    string sRange     = Get2DACache("spells", "Range", nPower);

    // Personal range powers are always in range
    if(sRange == "P")
        return;
    // Ranges according to the CCG spells.2da page
    else if(sRange == "T")
        fRangeLimit = 2.25f;
    else if(sRange == "S")
        fRangeLimit = 8.0f;
    else if(sRange == "M")
        fRangeLimit = 20.0f;
    else if(sRange == "L")
        fRangeLimit = 40.0f;

    // See if we are out of range
    if(fDistance > fRangeLimit)
    {
        // Create waypoint for the movement
        object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget);

        // Move into range, with a bit of fudge-factor
        //ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f);

        // Cleanup
        ActionDoCommand(DestroyObject(oWP));

        // Cleanup, paranoia
        AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f)));
    }
}

/** Internal function.
 * Assigns the fakecast command that is used to display the conjuration VFX when using a power.
 * Separated from UsePower() due to a bug with ActionFakeCastSpellAtObject(), which requires
 * use of ClearAllActions() to work around.
 * The problem is that if the target is an item on the ground, if the actor is out of spell
 * range when doing the fakecast, they will run on top of the item instead of to the edge of
 * the spell range. This only happens if there was a "real action" in the actor's action queue
 * immediately prior to the fakecast.
 */
void _AssignUsePowerFakeCastCommands(object oManifester, object oTarget, location lTarget, int nSpellID)
{
    // Nuke actions to prevent the fakecast action from bugging
    ClearAllActions();

    if(GetIsObjectValid(oTarget))
        ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT);
    else
        ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT);
}


/** Internal function.
 * Places the cheatcasting of the real power into the manifester's action queue.
 */
void _UsePowerAux(object oManifester, object oMfToken, int nSpellId,
                  object oTarget, location lTarget,
                  int nPower, int nClass, int bIsPsiLike, int nLevelOverride,
                  int bQuickened
                  )
{
    if(DEBUG) DoDebug("_UsePowerAux() running:\n"
                    + "oManifester = " + DebugObject2Str(oManifester) + "\n"
                    + "oMfToken = " + DebugObject2Str(oMfToken) + "\n"
                    + "nSpellId = " + IntToString(nSpellId) + "\n"
                    + "oTarget = " + DebugObject2Str(oTarget) + "\n"
                    + "lTarget = " + DebugLocation2Str(lTarget) + "\n"
                    + "nPower = " + IntToString(nPower) + "\n"
                    + "nClass = " + IntToString(nClass) + "\n"
                    + "bIsPsiLike = " + DebugBool2String(bIsPsiLike) + "\n"
                    + "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
                    + "bQuickened = " + DebugBool2String(bQuickened) + "\n"
                      );

    // Make sure nothing has interrupted this manifestation
    if(GetIsObjectValid(oMfToken))
    {
        if(DEBUG) DoDebug("_UsePowerAux(): Token was valid, queueing actual manifestation");
        // Set the class to manifest as
        SetLocalInt(oManifester, PRC_MANIFESTING_CLASS, nClass + 1);

        // Set the power's level
        SetLocalInt(oManifester, PRC_POWER_LEVEL, StringToInt(lookup_spell_innate(nSpellId)));

        // Set whether the power is to run as a psi-like ability
        SetLocalInt(oManifester, PRC_IS_PSILIKE, bIsPsiLike);

        // Set whether the power was quickened
        SetLocalInt(oManifester, PRC_POWER_IS_QUICKENED, bQuickened);

        // Queue the real manifestation
        //ActionCastSpell(nPower, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, TRUE, TRUE, oTarget);

        if(nLevelOverride != 0)
            AssignCommand(oManifester, ActionDoCommand(SetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride)));
        if(GetIsObjectValid(oTarget))
            AssignCommand(oManifester, ActionCastSpellAtObject(nPower, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
        else
            AssignCommand(oManifester, ActionCastSpellAtLocation(nPower, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
        if(nLevelOverride != 0)
            AssignCommand(oManifester, ActionDoCommand(DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE)));

        // Destroy the manifestation token for this manifestation
        _DestroyManifestationToken(oManifester, oMfToken);
    }
}


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

struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags)
{
    /* Get some data */
    int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE;
    // Manifester-related stuff
    int nManifesterLevel = GetManifesterLevel(oManifester);
    int nPowerLevel      = GetPowerLevel(oManifester);
    int nClass           = GetManifestingClass(oManifester);
    int nWildSurge       = GetWildSurge(oManifester);
    int nManifesterPP    = GetCurrentPowerPoints(oManifester);
    int bIsPsiLike       = GetLocalInt(oManifester, PRC_IS_PSILIKE);
    // Target-specific stuff
    int nVolatileMindCost = _VolatileMind(oTarget, oManifester);
    int nPsionicHoleCost  = _PsionicHole(oTarget);

    /* Initialise the manifestation structure */
    struct manifestation manif;
    manif.oManifester       = oManifester;
    manif.bCanManifest      = TRUE;                                   // Assume successfull manifestation by default
    manif.nPPCost           = nPowerLevel * 2 - 1;                    // Initialise the cost to the base cost of the power
    manif.nPsiFocUsesRemain = GetPsionicFocusesAvailable(oManifester);// Determine how many times psionic focus could be used
    manif.nManifesterLevel  = nManifesterLevel;
    manif.nSpellID          = PRCGetSpellId();

    // Run an ability score check to see if the manifester can manifest the power at all
	if (bIsPsiLike)
    {
        manif.bCanManifest = TRUE;
    }	
	else if(GetAbilityScoreOfClass(oManifester, nClass) - 10 < nPowerLevel && !bIgnoreConstraints && !bIsPsiLike && !GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && manif.nSpellID != POWER_ELAN_RESILIANCE)
    {
        FloatingTextStrRefOnCreature(16826411, oManifester, FALSE); // "You do not have a high enough ability score to manifest this power"
        manif.bCanManifest = FALSE;
    }

    // Account for metapsionics
    if(!bIsPsiLike) // Skipped for psi-like abilities
        manif = EvaluateMetapsionics(manif, nMetaPsiFlags);
        
    // This has an initial power cost of 0, all cost comes from augmentation
    if (manif.nSpellID == POWER_ELAN_RESILIANCE) manif.nPPCost = 0;

    // Account for augmentation. This factors in Wild Surge cost reduction
    manif = EvaluateAugmentation(manif, pap);

    //* APPLY COST INCREASES THAT DO NOT CAUSE ONE TO LOSE PP ON FAILURE HERE *//
    if (manif.nSpellID != POWER_ELAN_RESILIANCE)
    {
    	// Catapsi check
    	if(GetLocalInt(oManifester, "PRC_IsInCatapsi") &&                                                // Manifester is in Catapsi field
    	   !PRCMySavingThrow(SAVING_THROW_WILL, oManifester, GetLocalInt(oManifester, "PRC_Catapsi_DC"), // And fails the will save VS it
    	                     SAVING_THROW_TYPE_MIND_SPELLS, GetLocalObject(oManifester, "PRC_Catapsi_Manifester")
    	                     )
    	   )
    	{
    	   manif.nPPCost += 4;
    	}
    }	

    //* APPLY COST INCREASES THAT DO NOT CAUSE ONE TO LOSE PP ON FAILURE ABOVE *//

    // Skip paying anything if something has prevented successfull manifestation already by this point
    if(manif.bCanManifest)
    {
        /* The manifester level value includes the manifester level increase from
         * Wild Surge, but since the calculated cost already contains the augmentation
         * cost reduction provided by Wild Surge, it should not apply here.
         */
        if((nManifesterLevel - nWildSurge) >= manif.nPPCost || bIsPsiLike || bIgnoreConstraints)
        {
            // Reduced cost of manifesting a power, but does not allow you to exceed the manifester level cap
            if(!bIsPsiLike) // Skipped for psi-like abilities
                manif = _GetPPCostReduced(manif);

            //If the manifester does not have enough points before hostile modifiers, cancel power
            if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints)
            {
                FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power"
                manif.bCanManifest = FALSE;
            }
            // The manifester has enough power points that they would be able to use the power, barring extra costs
            else
            {
                //* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL HERE *//
                // Psionic Hole does not count against manifester level cap, but causes the power to fail if the manifester can't pay
                manif.nPPCost += nPsionicHoleCost;
                // Volatile Mind behaves the same
                manif.nPPCost += nVolatileMindCost;
                //* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL ABOVE *//

                if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints)
                {
                    FloatingTextStrRefOnCreature(16826413, oManifester, FALSE); // "Your target's abilities cause you to use more Power Points than you have. The power fails"
                    manif.bCanManifest = FALSE;
                }

                // Psi-like abilities ignore PP costs and metapsi
                if(!bIsPsiLike)
                {
                    // Set the power points to their new value and inform the manifester
                    LosePowerPoints(oManifester, manif.nPPCost, TRUE);

                    // Psionic focus loss from using metapsionics. Has a side effect of telling the manifester which metapsionics were actually active
                    PayMetapsionicsFocuses(manif);
                }

                //* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL MANIFESTATION HERE *//
                // Psicraft for all those who can see
                IdentifyPower(oManifester, manif.nSpellID);
                // Damage from overchanneling happens only if one actually spends PP
                _DoOverchannelDamage(oManifester, bIsPsiLike);
                // Apply Hostile Mind damage, as necessary
                _HostileMind(oManifester, oTarget);
                // Apply Wild Surge side-effects
                _SurgingEuphoriaOrPsychicEnervation(oManifester, nWildSurge);
                // Apply Mind Trap PP loss
                _DoMindTrapPPLoss(oManifester, oTarget);
                // Spellfire friendly absorption - This may set bCananifest to FALSE
                manif = _DoSpellfireFriendlyAbsorption(manif, oTarget);
                //* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL MANIFESTATION ABOVE *//
            }
        }
        // Cost was over the manifester cap
        else
        {// "Your manifester level is not high enough to spend X Power Points"
            FloatingTextStringOnCreature(GetStringByStrRef(16826410) + " " + IntToString(manif.nPPCost) + " " + GetStringByStrRef(16826409), oManifester, FALSE);
            manif.bCanManifest = FALSE;
        }
    }//end if - Something hadn't prevented successfull manifestation already before paying the power costs

    if(DEBUG) DoDebug("EvaluateManifestation(): Final result:\n" + DebugManifestation2Str(manif));

    // Initiate manifestation-related variable cleanup
    DelayCommand(0.5f, _CleanManifestationVariables(oManifester));

    return manif;
}

void UsePower(int nPower, int nClass, int bIsPsiLike = FALSE, int nLevelOverride = 0)
{
    object oManifester = OBJECT_SELF;
    object oSkin       = GetPCSkin(oManifester);
    object oTarget     = PRCGetSpellTargetObject();
    object oMfToken;
    location lTarget   = PRCGetSpellTargetLocation();
    int nSpellID       = PRCGetSpellId();
    int nManifDur      = StringToInt(Get2DACache("spells", "ConjTime", nPower)) + StringToInt(Get2DACache("spells", "CastTime", nPower));
    int bQuicken       = FALSE;
    
    
    if (GetLocalInt(oManifester, "MindStabDur")) nManifDur = 0;

    // Normally swift action powers check
    if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nPower, nClass)) == "SWIFT_ACTION") // The power is swift action to use
    {
        if (TakeSwiftAction(oManifester)) // And the Manifester can take a swift action now
            nManifDur = 0;
        else
            return;
    }
    // Quicken Power check
    else if(nManifDur <= 6000                                 && // If the power could be quickened by having manifesting time of 1 round or less
            GetLocalInt(oManifester, METAPSIONIC_QUICKEN_VAR) && // And the manifester has Quicken Power active
            GetIsPsionicallyFocused(oManifester)              && // And might be able to pay the psionic focus for it
            TakeSwiftAction(oManifester)                         // And the manifester can take a swift action
            )
    {
        // Set the manifestation time to 0 to skip VFX
        nManifDur = 0;
        // And set the Quicken Power used marker to TRUE
        bQuicken = TRUE;
    }

    if(DEBUG) DoDebug("UsePower(): Manifester is " + DebugObject2Str(oManifester) + "\n"
                    + "nPower = " + IntToString(nPower) + "\n"
                    + "nClass = " + IntToString(nClass) + "\n"
                    + "bIsPsiLike = " + DebugBool2String(bIsPsiLike) + "\n"
                    + "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
                    + "Manifestation duration = " + IntToString(nManifDur) + "ms \n"
                    + "bQuicken = " + DebugBool2String(bQuicken) + "\n"
                    //+ "Token exists = " + DebugBool2String(GetIsObjectValid(oMfToken))
                      );

    // Create the manifestation token. Deletes any old tokens and cancels corresponding manifestations as a side effect
    oMfToken = _CreateManifestationToken(oManifester);

    /// @todo Hook to the manifester's OnDamaged event for the concentration checks to avoid losing the power

    // Nuke action queue to prevent cheating with creative power stacking.
    // Probably not necessary anymore - Ornedan
    if(DEBUG) SendMessageToPC(oManifester, "Clearing all actions in preparation for second stage of the power.");
    ClearAllActions();

    // If out of range, move to range
    _ManifestationRangeCheck(oManifester, nPower, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget);

    // Start the manifestation monitor HB
    DelayCommand(nManifDur / 1000.0f, ActionDoCommand(_ManifestationHB(oManifester, GetLocation(oManifester), oMfToken)));

    // Assuming the spell isn't used as a swift action, fakecast for visuals
    if(nManifDur > 0)
    {
        // Hack. Workaround of a bug with the fakecast actions. See function comment for details
        ActionDoCommand(_AssignUsePowerFakeCastCommands(oManifester, oTarget, lTarget, nSpellID));
    }

    // Action queue the function that will cheatcast the actual power
    DelayCommand(nManifDur / 1000.0f, AssignCommand(oManifester, ActionDoCommand(_UsePowerAux(oManifester, oMfToken, nSpellID, oTarget, lTarget, nPower, nClass, bIsPsiLike, nLevelOverride, bQuicken))));
}

string DebugManifestation2Str(struct manifestation manif)
{
    string sRet;

    sRet += "oManifester = " + DebugObject2Str(manif.oManifester) + "\n";
    sRet += "bCanManifest = " + DebugBool2String(manif.bCanManifest) + "\n";
    sRet += "nPPCost = "           + IntToString(manif.nPPCost) + "\n";
    sRet += "nPsiFocUsesRemain = " + IntToString(manif.nPsiFocUsesRemain) + "\n";
    sRet += "nManifesterLevel = "  + IntToString(manif.nManifesterLevel) + "\n";

    sRet += "nTimesAugOptUsed_1 = " + IntToString(manif.nTimesAugOptUsed_1) + "\n";
    sRet += "nTimesAugOptUsed_2 = " + IntToString(manif.nTimesAugOptUsed_2) + "\n";
    sRet += "nTimesAugOptUsed_3 = " + IntToString(manif.nTimesAugOptUsed_3) + "\n";
    sRet += "nTimesAugOptUsed_4 = " + IntToString(manif.nTimesAugOptUsed_4) + "\n";
    sRet += "nTimesAugOptUsed_5 = " + IntToString(manif.nTimesAugOptUsed_5) + "\n";
    sRet += "nTimesGenericAugUsed = " + IntToString(manif.nTimesGenericAugUsed) + "\n";

    sRet += "bChain    = " + DebugBool2String(manif.bChain)    + "\n";
    sRet += "bEmpower  = " + DebugBool2String(manif.bEmpower)  + "\n";
    sRet += "bExtend   = " + DebugBool2String(manif.bExtend)   + "\n";
    sRet += "bMaximize = " + DebugBool2String(manif.bMaximize) + "\n";
    sRet += "bSplit    = " + DebugBool2String(manif.bSplit)    + "\n";
    sRet += "bTwin     = " + DebugBool2String(manif.bTwin)     + "\n";
    sRet += "bWiden    = " + DebugBool2String(manif.bWiden)    + "\n";
    sRet += "bQuicken  = " + DebugBool2String(manif.bQuicken);//    + "\n";

    return sRet;
}

void SetLocalManifestation(object oObject, string sName, struct manifestation manif)
{
    //SetLocal (oObject, sName + "_", );
    SetLocalObject(oObject, sName + "_oManifester", manif.oManifester);

    SetLocalInt(oObject, sName + "_bCanManifest",      manif.bCanManifest);
    SetLocalInt(oObject, sName + "_nPPCost",           manif.nPPCost);
    SetLocalInt(oObject, sName + "_nPsiFocUsesRemain", manif.nPsiFocUsesRemain);
    SetLocalInt(oObject, sName + "_nManifesterLevel",  manif.nManifesterLevel);
    SetLocalInt(oObject, sName + "_nSpellID",          manif.nSpellID);

    SetLocalInt(oObject, sName + "_nTimesAugOptUsed_1", manif.nTimesAugOptUsed_1);
    SetLocalInt(oObject, sName + "_nTimesAugOptUsed_2", manif.nTimesAugOptUsed_2);
    SetLocalInt(oObject, sName + "_nTimesAugOptUsed_3", manif.nTimesAugOptUsed_3);
    SetLocalInt(oObject, sName + "_nTimesAugOptUsed_4", manif.nTimesAugOptUsed_4);
    SetLocalInt(oObject, sName + "_nTimesAugOptUsed_5", manif.nTimesAugOptUsed_5);
    SetLocalInt(oObject, sName + "_nTimesGenericAugUsed", manif.nTimesGenericAugUsed);

    SetLocalInt(oObject, sName + "_bChain",    manif.bChain);
    SetLocalInt(oObject, sName + "_bEmpower",  manif.bEmpower);
    SetLocalInt(oObject, sName + "_bExtend",   manif.bExtend);
    SetLocalInt(oObject, sName + "_bMaximize", manif.bMaximize);
    SetLocalInt(oObject, sName + "_bSplit",    manif.bSplit);
    SetLocalInt(oObject, sName + "_bTwin",     manif.bTwin);
    SetLocalInt(oObject, sName + "_bWiden",    manif.bWiden);
    SetLocalInt(oObject, sName + "_bQuicken",  manif.bQuicken);
}

struct manifestation GetLocalManifestation(object oObject, string sName)
{
    struct manifestation manif;
    manif.oManifester = GetLocalObject(oObject, sName + "_oManifester");

    manif.bCanManifest      = GetLocalInt(oObject, sName + "_bCanManifest");
    manif.nPPCost           = GetLocalInt(oObject, sName + "_nPPCost");
    manif.nPsiFocUsesRemain = GetLocalInt(oObject, sName + "_nPsiFocUsesRemain");
    manif.nManifesterLevel  = GetLocalInt(oObject, sName + "_nManifesterLevel");
    manif.nSpellID          = GetLocalInt(oObject, sName + "_nSpellID");

    manif.nTimesAugOptUsed_1 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_1");
    manif.nTimesAugOptUsed_2 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_2");
    manif.nTimesAugOptUsed_3 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_3");
    manif.nTimesAugOptUsed_4 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_4");
    manif.nTimesAugOptUsed_5 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_5");
    manif.nTimesGenericAugUsed = GetLocalInt(oObject, sName + "_nTimesGenericAugUsed");

    manif.bChain    = GetLocalInt(oObject, sName + "_bChain");
    manif.bEmpower  = GetLocalInt(oObject, sName + "_bEmpower");
    manif.bExtend   = GetLocalInt(oObject, sName + "_bExtend");
    manif.bMaximize = GetLocalInt(oObject, sName + "_bMaximize");
    manif.bSplit    = GetLocalInt(oObject, sName + "_bSplit");
    manif.bTwin     = GetLocalInt(oObject, sName + "_bTwin");
    manif.bWiden    = GetLocalInt(oObject, sName + "_bWiden");
    manif.bQuicken  = GetLocalInt(oObject, sName + "_bQuicken");

    return manif;
}

void DebugIgnoreConstraints(object oManifester)
{
    SetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS, TRUE);
    DelayCommand(0.0f, DeleteLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS));
}

//Below is a modification of Evaluate Manifestation for the Diamond Dragon abilities
struct manifestation EvaluateDiaDragChannel(object oManifester, object oTarget, struct power_augment_profile pap, int nPowerLevel)
{
    /* Get some data */
    int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE;
    // Manifester-related stuff
    int nClass           = GetPrimaryPsionicClass(oManifester);
    int nWildSurge       = GetWildSurge(oManifester);
    //When Manifester level is retrieved, remove Wildsurge and Overchannel bonuses as they don't apply
    int nManifesterLevel = GetManifesterLevel(oManifester, nClass);
    int nManifesterPP    = GetCurrentPowerPoints(oManifester);

    /* Initialise the manifestation structure */
    struct manifestation manif;
    manif.oManifester       = oManifester;
    manif.bCanManifest      = TRUE;                                   // Assume successfull manifestation by default
    manif.nPPCost           = nPowerLevel * 2 - 1;                    // Initialise the cost to the base cost of the power
    manif.nPsiFocUsesRemain = 0;                                      // Not needed
    manif.nManifesterLevel  = nManifesterLevel;
    manif.nSpellID          = PRCGetSpellId();

    // Run an ability score check to see if the manifester can manifest the power at all
    if(GetAbilityScoreOfClass(oManifester, nClass) - 10 < nPowerLevel && (!bIgnoreConstraints))
    {
        FloatingTextStrRefOnCreature(16826411, oManifester, FALSE); // "You do not have a high enough ability score to manifest this power"
        manif.bCanManifest = FALSE;
    }

    // Account for augmentation. This factors in Wild Surge cost reduction
    manif = EvaluateAugmentation(manif, pap);

    // Skip paying anything if something has prevented successfull manifestation already by this point
    if(manif.bCanManifest)
    {
        //Remove the Wild Surge cost reduction
        manif.nPPCost += nWildSurge;
        /* The manifester level value includes the manifester level increase from
         * Wild Surge, but since the calculated cost already contains the augmentation
         * cost reduction provided by Wild Surge, it should not apply here.
         */
        if((nManifesterLevel) >= manif.nPPCost || bIgnoreConstraints)
        {

            //If the manifester does not have enough points before hostile modifiers, cancel power
            if(manif.nPPCost > nManifesterPP && !bIgnoreConstraints)
            {
                FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power"
                manif.bCanManifest = FALSE;
            }
            // The manifester has enough power points that they would be able to use the power, barring extra costs
            else
            {
                //* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL ABOVE *//

                if(manif.nPPCost > nManifesterPP && !bIgnoreConstraints)
                {
                    FloatingTextStrRefOnCreature(16826413, oManifester, FALSE); // "Your target's abilities cause you to use more Power Points than you have. The power fails"
                    manif.bCanManifest = FALSE;
                }

                // Set the power points to their new value and inform the manifester
                LosePowerPoints(oManifester, manif.nPPCost, TRUE);

            }
        }
        // Cost was over the manifester cap
        else
        {// "Your manifester level is not high enough to spend X Power Points"
            FloatingTextStringOnCreature(GetStringByStrRef(16826410) + " " + IntToString(manif.nPPCost) + " " + GetStringByStrRef(16826409), oManifester, FALSE);
            manif.bCanManifest = FALSE;
        }
    }//end if - Something hadn't prevented successfull manifestation already before paying the power costs

    if(DEBUG) DoDebug("EvaluateManifestation(): Final result:\n" + DebugManifestation2Str(manif));

    // Initiate manifestation-related variable cleanup
    DelayCommand(0.5f, _CleanManifestationVariables(oManifester));

    return manif;
}

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