//::///////////////////////////////////////////////
//:: Psionics include: Psionic Core Files
//:: psi_inc_core
//::///////////////////////////////////////////////
/** @file
    Core functions removed from
	
	psi_inc_psifunc
	psi_inc_focus (depreciated)
	
	as they are required by many of the other psi groups

    @author Ornedan/ElgarL
    @date   Created - 2005.11.10/23.07.2010
 */
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////

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

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

// Included here to provide the values for the constants below
#include "prc_class_const"

const int POWER_LIST_PSION          = CLASS_TYPE_PSION;
const int POWER_LIST_WILDER         = CLASS_TYPE_WILDER;
const int POWER_LIST_PSYWAR         = CLASS_TYPE_PSYWAR;
const int POWER_LIST_PSYROG         = CLASS_TYPE_PSYCHIC_ROGUE;
const int POWER_LIST_FIST_OF_ZUOKEN = CLASS_TYPE_FIST_OF_ZUOKEN;
const int POWER_LIST_WARMIND        = CLASS_TYPE_WARMIND;

#include "psi_inc_const"

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

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

/**
 * Attempts to use psionic focus. If the creature was focused, it
 * loses the focus. If it has Epic Psionic Focus feats, it will
 * be able to use the focus for a number of times equal to the
 * number of those feats it has during the next 0.5s
 *
 * @param oUser Creature expending it's psionic focus
 * @return      TRUE if the creature was psionically focus or had
 *              Epic Psionic Focus uses remaining. FALSE otherwise.
 */
int UsePsionicFocus(object oUser = OBJECT_SELF);

/**
 * Sets psionic focus active and triggers feats dependant
 * on it.
 *
 * Feats currently keyed to activity of psionic focus:
 *  Psionic Dodge
 *  Speed of Thought
 *
 * @param oGainee Creature gaining psionic focus.
 */
void GainPsionicFocus(object oGainee = OBJECT_SELF);

/**
 * Gets the number of psionic focus uses the creature has available
 * to it at this moment. If the creature is psionically focused,
 * the number equal to GetPsionicFocusUsesPerExpenditure(), otherwise
 * it is however many focus uses the creature still has remaining of
 * that number.
 *
 * @param oCreature Creaute whose psionic focus use count to evaluate
 * @return          The total number of times UsePsionicFocus() will
 *                  return TRUE if called at this moment.
 */
int GetPsionicFocusesAvailable(object oCreature = OBJECT_SELF);

/**
 * Calculates the number of times a creature may use it's psionic focus when expending it.
 * Base is 1.
 * In addition, 1 more use for each Epic Psionic Focus feat the creature has.
 *
 * @param oCreature Creaute whose psionic focus use count to evaluate
 * @return          The total number of times UsePsionicFocus() will return
 *                  TRUE for a single expending of psionic focus.
 */
int GetPsionicFocusUsesPerExpenditure(object oCreature = OBJECT_SELF);

/**
 * Sets the given creature's psionic focus off and deactivates all feats keyed to it.
 *
 * @param oLoser Creature losing it's psionic focus
 */
void LosePsionicFocus(object oLoser = OBJECT_SELF);

/**
 * Checks whether the given creature is psionically focused.
 *
 * @param oCreature Creature whose psionic focus's state to examine
 * @return          TRUE if the creature is psionically focused, FALSE
 *                  otherwise.
 */
int GetIsPsionicallyFocused(object oCreature = OBJECT_SELF);

/**
 * Determines the number of feats that would use psionic focus
 * when triggered the given creature has active.
 *
 * Currently accounts for:
 *  Talented
 *  Power Specialization
 *  Power Penetration
 *  Psionic Endowment
 *  Chain Power
 *  Empower Power
 *  Extend Power
 *  Maximize Power
 *  Split Psionic Ray
 *  Twin Power
 *  Widen Power
 *  Quicken Power
 *
 * @param oCreature Creature whose feats to examine
 * @return          How many of the listed feats are active
 */
int GetPsionicFocusUsingFeatsActive(object oCreature = OBJECT_SELF);

/**
 * Calculates the DC of the power being currently manifested.
 * Base value is 10 + power level + ability modifier in manifesting stat
 *
 * WARNING: Return value is not defined when a power is not being manifested.
 *
 */
int GetManifesterDC(object oManifester = OBJECT_SELF);

/**
 * Determines the spell school matching a discipline according to the
 * standard transparency rules.
 * Disciplines which have no matching spell school are matched with
 * SPELL_SCHOOL_GENERAL.
 *
 * @param nDiscipline Discipline to find matching spell school for
 * @return            SPELL_SCHOOL_* of the match
 */
int DisciplineToSpellSchool(int nDiscipline);

/**
 * Determines the discipline matching a spell school according to the
 * standard transparency rules.
 * Spell schools that have no matching disciplines are matched with
 * DISCIPLINE_NONE.
 *
 * @param nSpellSchool Spell schools to find matching discipline for
 * @return             DISCIPLINE_* of the match
 */
int SpellSchoolToDiscipline(int nSpellSchool);

/**
 * Determines the discipline of a power, using the School column of spells.2da.
 *
 * @param nSpellID The spellID of the power to determine the discipline of
 * @return         DISCIPLINE_* constant. DISCIPLINE_NONE if the power's
 *                 School designation does not match any of the discipline's.
 */
int GetPowerDiscipline(int nSpellID);

/**
 * Determines whether a given power is a power of the Telepahty discipline.
 *
 * @param nSpellID The spells.2da row of the power. If left to default,
 *                 PRCGetSpellId() is used.
 * @return         TRUE if the power's discipline is Telepathy, FALSE otherwise
 */
int GetIsTelepathyPower(int nSpellID = -1);

/**
 * Checks whether the PC possesses the feats the given feat has as it's
 * prerequisites. Possession of a feat is checked using GetHasFeat().
 *
 *
 * @param nFeat The feat for which determine the possession of prerequisites
 * @param oPC   The creature whose feats to check
 * @return      TRUE if the PC possesses the prerequisite feats AND does not
 *              already posses nFeat, FALSE otherwise.
 */
int CheckPowerPrereqs(int nFeat, object oPC);

/**
 * Determines the manifester's level in regards to manifester checks to overcome
 * spell resistance.
 *
 * WARNING: Return value is not defined when a power is not being manifested.
 *
 * @param oManifester A creature manifesting a power at the moment
 * @return            The creature's manifester level, adjusted to account for
 *                    modifiers that affect spell resistance checks.
 */
int GetPsiPenetration(object oManifester = OBJECT_SELF);

/**
 * Determines whether a given creature possesses the Psionic subtype.
 * Ways of possessing the subtype:
 * - Being of a naturally psionic race
 * - Having class levels in a psionic class
 * - Possessing the Wild Talent feat
 *
 * @param oCreature Creature to test
 * @return          TRUE if the creature is psionic, FALSE otherwise.
 */
int GetIsPsionicCharacter(object oCreature);

/**
 * Creates the creature weapon for powers like Bite of the Wolf and Claws of the
 * Beast. If a creature weapon of the correct type is already present, it is
 * used instead of a new one.
 *
 * Preserving existing weapons may cause problems, if so, make the function instead delete the old object - Ornedan
 *
 * @param oCreature     Creatue whose creature weapons to mess with.
 * @param sResRef       Resref of the creature weapon. Assumed to be one of the
 *                      PRC creature weapons, so this considered to is also be
 *                      the tag.
 * @param nIventorySlot Inventory slot where the creature weapon is to be equipped.
 * @param fDuration     If a new weapon is created, it will be destroyed after
 *                      this duration.
 *
 * @return              The newly created creature weapon. Or an existing weapon,
 *                      if there was one.
 */
object GetPsionicCreatureWeapon(object oCreature, string sResRef, int nInventorySlot, float fDuration);

/**
 * Applies modifications to a power's damage that depend on some property
 * of the target.
 * Currently accounts for:
 *  - Mental Resistance
 *  - Greater Power Specialization
 *  - Intellect Fortress
 *
 * @param oTarget     A creature being dealt damage by a power
 * @param oManifester The creature manifesting the damaging power
 * @param nDamage     The amount of damage the creature would be dealt
 *
 * @param bIsHitPointDamage Is the damage HP damage or something else?
 * @param bIsEnergyDamage   Is the damage caused by energy or something else? Only relevant if the damage is HP damage.
 *
 * @return The amount of damage, modified by oTarget's abilities
 */
int GetTargetSpecificChangesToDamage(object oTarget, object oManifester, int nDamage,
                                     int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE);

/**
 * Gets the manifester level adjustment from the Practiced Manifester feats.
 *
 * @param oManifester           The creature to check
 * @param iManifestingClass     The CLASS_TYPE* that the power was cast by.
 * @param iManifestingLevels    The manifester level for the power calculated so far 
 *                              ie. BEFORE Practiced Manifester.
 */
int PracticedManifesting(object oManifester, int iManifestingClass, int iManifestingLevels);

/**
 * Determines the given creature's manifester level. If a class is specified,
 * then returns the manifester level for that class. Otherwise, returns
 * the manifester level for the currently active manifestation.
 *
 * @param oManifester    The creature whose manifester level to determine
 * @param nSpecificClass The class to determine the creature's manifester
 *                       level in.
 *                       DEFAULT: CLASS_TYPE_INVALID, which means the creature's
 *                       manifester level in regards to an ongoing manifestation
 *                       is determined instead.
 * @param nMaxPowerLevel For learning powers. Practiced Manifester is breaking things otherwise.
 * @return               The manifester level
 */
int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE);

/**
 * Determines the given creature's highest undmodified manifester level among it's
 * manifesting classes.
 *
 * @param oCreature Creature whose highest manifester level to determine
 * @return          The highest unmodified manifester level the creature can have
 */
int GetHighestManifesterLevel(object oCreature);

/**
 * Gets the level of the power being currently manifested.
 * WARNING: Return value is not defined when a power is not being manifested.
 *
 * @param oManifester The creature currently manifesting a power
 * @return            The level of the power being manifested
 */
int GetPowerLevel(object oManifester);

/**
 * Determines a creature's ability score in the manifesting ability of a given
 * class.
 *
 * @param oManifester Creature whose ability score to get
 * @param nClass      CLASS_TYPE_* constant of a manifesting class
 */
int GetAbilityScoreOfClass(object oManifester, int nClass);

/**
 * Determines from what class's power list the currently being manifested
 * power is manifested from.
 *
 * @param oManifester A creature manifesting a power at this moment
 * @return            CLASS_TYPE_* constant of the class
 */
int GetManifestingClass(object oManifester = OBJECT_SELF);

/**
 * Determines the manifesting ability of a class.
 *
 * @param nClass CLASS_TYPE_* constant of the class to determine the manifesting stat of
 * @return       ABILITY_* of the manifesting stat. ABILITY_CHARISMA for non-manifester
 *               classes.
 */
int GetAbilityOfClass(int nClass);

/**
 * Determines which of the character's classes is their highest or first psionic
 * manifesting class, if any. This is the one which gains manifester level raise
 * benefits from prestige classes.
 *
 * @param oCreature Creature whose classes to test
 * @return          CLASS_TYPE_* of the first psionic manifesting class,
 *                  CLASS_TYPE_INVALID if the creature does not posses any.
 */
int GetPrimaryPsionicClass(object oCreature = OBJECT_SELF);

/**
 * Calculates how many manifester levels are gained by a given creature from
 * it's levels in prestige classes.
 *
 * @param oCreature Creature to calculate added manifester levels for
 * @return          The number of manifester levels gained
 */
int GetPsionicPRCLevels(object oCreature);

/**
 * Determines the position of a creature's first psionic manifesting class, if any.
 *
 * @param oCreature Creature whose classes to test
 * @return          The position of the first psionic class {1, 2, 3} or 0 if
 *                  the creature possesses no levels in psionic classes.
 */
int GetFirstPsionicClassPosition(object oCreature = OBJECT_SELF);

/**
 * Determines whether a given class is a psionic class or not. A psionic
 * class is defined as one that gives base manifesting.
 *
 * @param nClass CLASS_TYPE_* of the class to test
 * @return       TRUE if the class is a psionic class, FALSE otherwise
 */
int GetIsPsionicClass(int nClass);

/**
 * Gets the amount of manifester levels the given creature is Wild Surging by.
 *
 * @param oManifester The creature to test
 * @return            The number of manifester levels added by Wild Surge. 0 if
 *                    Wild Surge is not active.
 */
int GetWildSurge(object oManifester);

/**
 * Gets the highest power level the creature should know
 *
 * @param oManifester The creature to test
 * @return            Power level.
 */
int GetMaxPowerLevel(object oManifester);

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

#include "prc_inc_unarmed"
						
//////////////////////////////////////////////////
/*          Global Structures                   */
//////////////////////////////////////////////////

// These are used in psi_inc_psifunc and psi_inc_augment

/**
 * A structure that contains common data used during power manifestation.
 */
struct manifestation{
    /* Generic stuff */
    /// The creature manifesting the power
    object oManifester;
    /// Whether the manifestation is successfull or not
    int bCanManifest;
    /// How much Power Points the manifestation costs
    int nPPCost;
    /// How many psionic focus uses the manifester would have remaining at a particular point in the manifestation
    int nPsiFocUsesRemain;
    /// The creature's manifester level in regards to this power
    int nManifesterLevel;
    /// The power's spell ID
    int nSpellID;

    /* Augmentation */
    /// How many times the first augmentation option of the power is used
    int nTimesAugOptUsed_1;
    /// How many times the second augmentation option of the power is used
    int nTimesAugOptUsed_2;
    /// How many times the third augmentation option of the power is used
    int nTimesAugOptUsed_3;
    /// How many times the fourth augmentation option of the power is used
    int nTimesAugOptUsed_4;
    /// How many times the fifth augmentation option of the power is used
    int nTimesAugOptUsed_5;
    /// How many times the PP used for augmentation triggered the generic augmentation of the power
    int nTimesGenericAugUsed;

    /* Metapsionics */
    /// Whether Chain Power was used with this manifestation
    int bChain;
    /// Whether Empower Power was used with this manifestation
    int bEmpower;
    /// Whether Extend Power was used with this manifestation
    int bExtend;
    /// Whether Maximize Power was used with this manifestation
    int bMaximize;
    /// Whether Split Psionic Ray was used with this manifestation
    int bSplit;
    /// Whether Twin Power was used with this manifestation
    int bTwin;
    /// Whether Widen Power was used with this manifestation
    int bWiden;
    /// Whether Quicken Power was used with this manifestation
    int bQuicken;
};

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



/*					Begin PSI FOCUS					*/
//////////////////////////////////////////////////////

int UsePsionicFocus(object oUser = OBJECT_SELF)
{
    int bToReturn = FALSE;
    // This does not expend your psionic focus, but rather the item's
    if (GetLocalInt(oUser, "SimpleBow_Focus"))
    {
        DeleteLocalInt(oUser, "SimpleBow_Focus");
        return TRUE;
    }
    // Next, check if we have focus on
    else if(GetLocalInt(oUser, PSIONIC_FOCUS))
    {
        SetLocalInt(oUser, "PsionicFocusUses", GetPsionicFocusUsesPerExpenditure(oUser) - 1);
        DelayCommand(0.5f, DeleteLocalInt(oUser, "PsionicFocusUses"));
        SendMessageToPCByStrRef(oUser, 16826414); // "You have used your Psionic Focus"

        bToReturn = TRUE;
    }
    // We don't. Check if there are uses remaining
    else if(GetLocalInt(oUser, "PsionicFocusUses"))
    {
        SetLocalInt(oUser, "PsionicFocusUses", GetLocalInt(oUser, "PsionicFocusUses") - 1);

        bToReturn = TRUE;
    }

    // Lose focus if it was used
    if(bToReturn) LosePsionicFocus(oUser);

    return bToReturn;
}

void GainPsionicFocus(object oGainee = OBJECT_SELF)
{
    SetLocalInt(oGainee, PSIONIC_FOCUS, TRUE);

    // Speed Of Thought
    if(GetHasFeat(FEAT_SPEED_OF_THOUGHT, oGainee))
    {
        // Check for heavy armor before adding the bonus now
        if(GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oGainee)) < 6)
            AssignCommand(oGainee, ActionCastSpellAtObject(SPELL_FEAT_SPEED_OF_THOUGHT_BONUS, oGainee, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));

        // Schedule a script to remove the bonus should they equip heavy armor
        AddEventScript(oGainee, EVENT_ONPLAYEREQUIPITEM, "psi_spdfthgt_oeq", TRUE, FALSE);
        // Schedule another script to add the bonus back if the unequip the armor
        AddEventScript(oGainee, EVENT_ONPLAYERUNEQUIPITEM, "psi_spdfthgt_ueq", TRUE, FALSE);
    }
    // Psionic Dodge
    if(GetHasFeat(FEAT_PSIONIC_DODGE, oGainee))
        SetCompositeBonus(GetPCSkin(oGainee), "PsionicDodge", 1, ITEM_PROPERTY_AC_BONUS);

    //Strength of Two - Kalashtar racial feat
    if(GetHasFeat(FEAT_STRENGTH_OF_TWO, oGainee))
    {
        SetCompositeBonus(GetPCSkin(oGainee), "StrengthOfTwo", 1, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_WILL);
    }
    // Danger Sense abilities for Psychic Rogue
    if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 5)
        ExecuteScript("psi_psyrog_dngr", oGainee);
    if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 7) // Uncanny Dodge
        IPSafeAddItemProperty(GetPCSkin(oGainee), PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE1), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); 
    if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 9) // Improved Uncanny Dodge
        IPSafeAddItemProperty(GetPCSkin(oGainee), PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE2), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); 
    if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_1d6, oGainee))
    {
        int nPsySneak = 1;
        if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_2d6, oGainee))
            nPsySneak += 2;
        if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_3d6, oGainee))
            nPsySneak += 3; 
        
        SetLocalInt(oGainee, "PsyRogueSneak",nPsySneak);
        DelayCommand(0.1, ExecuteScript("prc_sneak_att", oGainee));
    }    
}

int GetPsionicFocusesAvailable(object oCreature = OBJECT_SELF)
{
    // If the creature has a psionic focus active, return the maximum
    if(GetLocalInt(oCreature, PSIONIC_FOCUS))
        return GetPsionicFocusUsesPerExpenditure(oCreature);
    // Otherwise, return the amount currently remaining
    else
        return GetLocalInt(oCreature, "PsionicFocusUses");
}

int GetPsionicFocusUsesPerExpenditure(object oCreature = OBJECT_SELF)
{
    int nFocusUses = 1;
    int i;
    for(i = FEAT_EPIC_PSIONIC_FOCUS_1; i <= FEAT_EPIC_PSIONIC_FOCUS_10; i++)
        if(GetHasFeat(i, oCreature)) nFocusUses++;

    return nFocusUses;
}

void LosePsionicFocus(object oLoser = OBJECT_SELF)
{
    // Only remove focus if it's present
    if(GetLocalInt(oLoser, PSIONIC_FOCUS))
    {
        SetLocalInt(oLoser, PSIONIC_FOCUS, FALSE);

        // Loss of Speed of Thought effects
        PRCRemoveSpellEffects(SPELL_FEAT_SPEED_OF_THOUGHT_BONUS, oLoser, oLoser);
        RemoveEventScript(oLoser, EVENT_ONPLAYEREQUIPITEM, "psi_spdfthgt_oeq", TRUE);
        RemoveEventScript(oLoser, EVENT_ONPLAYERUNEQUIPITEM, "psi_spdfthgt_ueq", TRUE);
        // Loss of Psionic Dodge effects
        SetCompositeBonus(GetPCSkin(oLoser), "PsionicDodge", 0, ITEM_PROPERTY_AC_BONUS);
        // Loss of Strength of Two effects
        SetCompositeBonus(GetPCSkin(oLoser), "StrengthOfTwo", 0, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_WILL);
        
        if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 5)
            PRCRemoveSpellEffects(POWER_DANGERSENSE, oLoser, oLoser);   
        if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 7)            
            RemoveItemProperty(GetPCSkin(oLoser), ItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE1));
        if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 9)            
            RemoveItemProperty(GetPCSkin(oLoser), ItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE2));

        // Inform oLoser about the event
        FloatingTextStrRefOnCreature(16826415, oLoser, FALSE); // "You have lost your Psionic Focus"
        if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_1d6, oLoser))
        {
            DeleteLocalInt(oLoser, "PsyRogueSneak");
            DelayCommand(0.1, ExecuteScript("prc_sneak_att", oLoser));
        }        
    }
}

int GetIsPsionicallyFocused(object oCreature = OBJECT_SELF)
{
    return GetLocalInt(oCreature, PSIONIC_FOCUS);
}

int GetPsionicFocusUsingFeatsActive(object oCreature = OBJECT_SELF)
{
    int nFeats;

    if(GetLocalInt(oCreature, "TalentedActive"))            nFeats++;
    if(GetLocalInt(oCreature, "PowerSpecializationActive")) nFeats++;
    if(GetLocalInt(oCreature, "PowerPenetrationActive"))    nFeats++;
    if(GetLocalInt(oCreature, "PsionicEndowmentActive"))    nFeats++;

    if(GetLocalInt(oCreature, METAPSIONIC_CHAIN_VAR))       nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_EMPOWER_VAR))     nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_EXTEND_VAR))      nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_MAXIMIZE_VAR))    nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_SPLIT_VAR))       nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_TWIN_VAR))        nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_WIDEN_VAR))       nFeats++;
    if(GetLocalInt(oCreature, METAPSIONIC_QUICKEN_VAR))     nFeats++;

    return nFeats;
}

//////////////////////////////////////////////////////
/*					END PSI FOCUS					*/
//////////////////////////////////////////////////////

int GetManifesterDC(object oManifester = OBJECT_SELF)
{
    int nClass = GetManifestingClass(oManifester);
    int nDC = 10;
    nDC += GetPowerLevel(oManifester);
    nDC += GetAbilityModifier(GetAbilityOfClass(nClass), oManifester);

    // Stuff that applies only to powers, not psi-like abilities goes inside
    if(!GetLocalInt(oManifester, PRC_IS_PSILIKE))
    {
        if (GetLocalInt(oManifester, "PsionicEndowmentActive") == TRUE && UsePsionicFocus(oManifester))
        {
            nDC += GetHasFeat(FEAT_GREATER_PSIONIC_ENDOWMENT, oManifester) ? 4 : 2;
        }
    }

    // Needed to do some adjustments here.
    object oTarget = PRCGetSpellTargetObject();
    int nPower     = PRCGetSpellId();

    // Other DC adjustments
    // Soul Manifester
    nDC += Soulcaster(oManifester, PRCGetSpellId());
    // Charming Veil meld
    if(GetHasSpellEffect(MELD_CHARMING_VEIL, oManifester) && (GetIsOfSubschool(nPower, SUBSCHOOL_CHARM) || GetIsOfSubschool(nPower, SUBSCHOOL_COMPULSION))) nDC += GetEssentiaInvested(oManifester, MELD_CHARMING_VEIL)+1;
    // Soul Eater
    nDC += (GetLocalInt(oManifester, "PRC_SoulEater_HasDrained") && GetLevelByClass(CLASS_TYPE_SOUL_EATER, oManifester) >= 10) ? 2 : 0;
    // Closed Mind
    if(GetHasFeat(FEAT_CLOSED_MIND, oTarget)) nDC -= 2;
    // Strong Mind
    if(GetHasFeat(FEAT_STRONG_MIND, oTarget)) nDC -= 3;
    // Fist of Dal Quor
    if(GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oTarget) >= 4) nDC -= 2;

    return nDC;
}

int DisciplineToSpellSchool(int nDiscipline)
{
    int nSpellSchool = SPELL_SCHOOL_GENERAL;

    switch(nDiscipline)
    {
        case DISCIPLINE_CLAIRSENTIENCE:   nSpellSchool = SPELL_SCHOOL_DIVINATION;    break;
        case DISCIPLINE_METACREATIVITY:   nSpellSchool = SPELL_SCHOOL_CONJURATION;   break;
        case DISCIPLINE_PSYCHOKINESIS:    nSpellSchool = SPELL_SCHOOL_EVOCATION;     break;
        case DISCIPLINE_PSYCHOMETABOLISM: nSpellSchool = SPELL_SCHOOL_TRANSMUTATION; break;
        case DISCIPLINE_TELEPATHY:        nSpellSchool = SPELL_SCHOOL_ENCHANTMENT;   break;

        default: nSpellSchool = SPELL_SCHOOL_GENERAL; break;
    }

    return nSpellSchool;
}

int SpellSchoolToDiscipline(int nSpellSchool)
{
    int nDiscipline = DISCIPLINE_NONE;

    switch(nSpellSchool)
    {
        case SPELL_SCHOOL_GENERAL:       nDiscipline = DISCIPLINE_NONE;             break;
        case SPELL_SCHOOL_ABJURATION:    nDiscipline = DISCIPLINE_NONE;             break;
        case SPELL_SCHOOL_CONJURATION:   nDiscipline = DISCIPLINE_METACREATIVITY;   break;
        case SPELL_SCHOOL_DIVINATION:    nDiscipline = DISCIPLINE_CLAIRSENTIENCE;   break;
        case SPELL_SCHOOL_ENCHANTMENT:   nDiscipline = DISCIPLINE_TELEPATHY;        break;
        case SPELL_SCHOOL_EVOCATION:     nDiscipline = DISCIPLINE_PSYCHOKINESIS;    break;
        case SPELL_SCHOOL_ILLUSION:      nDiscipline = DISCIPLINE_NONE;             break;
        case SPELL_SCHOOL_NECROMANCY:    nDiscipline = DISCIPLINE_NONE;             break;
        case SPELL_SCHOOL_TRANSMUTATION: nDiscipline = DISCIPLINE_PSYCHOMETABOLISM; break;

        default: nDiscipline = DISCIPLINE_NONE;
    }

    return nDiscipline;
}

int GetPowerDiscipline(int nSpellID)
{
    string sSpellSchool = Get2DACache("spells", "School", nSpellID);//lookup_spell_school(nSpellID);
    int nDiscipline;

    if      (sSpellSchool == "A") nDiscipline = DISCIPLINE_NONE;
    else if (sSpellSchool == "C") nDiscipline = DISCIPLINE_METACREATIVITY;
    else if (sSpellSchool == "D") nDiscipline = DISCIPLINE_CLAIRSENTIENCE;
    else if (sSpellSchool == "E") nDiscipline = DISCIPLINE_TELEPATHY;
    else if (sSpellSchool == "V") nDiscipline = DISCIPLINE_PSYCHOKINESIS;
    else if (sSpellSchool == "I") nDiscipline = DISCIPLINE_NONE;
    else if (sSpellSchool == "N") nDiscipline = DISCIPLINE_NONE;
    else if (sSpellSchool == "T") nDiscipline = DISCIPLINE_PSYCHOMETABOLISM;
    else if (sSpellSchool == "G") nDiscipline = DISCIPLINE_PSYCHOPORTATION;

    return nDiscipline;
}

int GetIsTelepathyPower(int nSpellID = -1)
{
    if(nSpellID == -1) nSpellID = PRCGetSpellId();

    return GetPowerDiscipline(nSpellID) == DISCIPLINE_TELEPATHY;
}

int CheckPowerPrereqs(int nFeat, object oPC)
{
    // Having the power already automatically disqualifies one from taking it again
    if(GetHasFeat(nFeat, oPC))
    return FALSE;
    // We assume that the 2da is correctly formatted, and as such, a prereq slot only contains
    // data if the previous slots in order also contains data.
    // ie, no PREREQFEAT2 if PREREQFEAT1 is empty
    if(Get2DACache("feat", "PREREQFEAT1", nFeat) != "")
    {
        if(!GetHasFeat(StringToInt(Get2DACache("feat", "PREREQFEAT1", nFeat)), oPC))
        return FALSE;
        if(Get2DACache("feat", "PREREQFEAT2", nFeat) != ""
        && !GetHasFeat(StringToInt(Get2DACache("feat", "PREREQFEAT2", nFeat)), oPC))
        return FALSE;
    }

    if(Get2DACache("feat", "OrReqFeat0", nFeat) != "")
    {
        if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat0", nFeat)), oPC))
            return FALSE;
        if(Get2DACache("feat", "OrReqFeat1", nFeat) != "")
        {
            if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat1", nFeat)), oPC))
                return FALSE;
            if(Get2DACache("feat", "OrReqFeat2", nFeat) != "")
            {
                if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat2", nFeat)), oPC))
                    return FALSE;
                if(Get2DACache("feat", "OrReqFeat3", nFeat) != "")
                {
                    if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat3", nFeat)), oPC))
                        return FALSE;
                    if(Get2DACache("feat", "OrReqFeat4", nFeat) != "")
                    {
                        if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat4", nFeat)), oPC))
                            return FALSE;
    }   }   }   }   }

    //if youve reached this far then return TRUE
    return TRUE;
}

int GetPsiPenetration(object oManifester = OBJECT_SELF)
{
    int nPen = GetManifesterLevel(oManifester);

    // The stuff inside applies only to normal manifestation, not psi-like abilities
    if(!GetLocalInt(oManifester, PRC_IS_PSILIKE))
    {
        // Check for Power Pen feats being used
        if(GetLocalInt(oManifester, "PowerPenetrationActive") == TRUE && UsePsionicFocus(oManifester))
        {
            nPen += GetHasFeat(FEAT_GREATER_POWER_PENETRATION, oManifester) ? 8 : 4;
        }
    }

    return nPen;
}

int GetIsPsionicCharacter(object oCreature)
{
    return !!(GetLevelByClass(CLASS_TYPE_PSION,          oCreature) ||
              GetLevelByClass(CLASS_TYPE_PSYWAR,         oCreature) ||
              GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE,  oCreature) ||
              GetLevelByClass(CLASS_TYPE_WILDER,         oCreature) ||
              GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature) ||
              GetLevelByClass(CLASS_TYPE_WARMIND,        oCreature) ||
              GetHasFeat(FEAT_WILD_TALENT,               oCreature) ||
              GetHasFeat(FEAT_KALASHTAR_PP,              oCreature) ||
              GetHasFeat(FEAT_NATPSIONIC_1,              oCreature) ||
              GetHasFeat(FEAT_NATPSIONIC_2,              oCreature) ||
              GetHasFeat(FEAT_NATPSIONIC_3,              oCreature)
              // Racial psionicity signifying feats go here
             );
}

void LocalCleanExtraFists(object oCreature)
{
    int iIsCWeap, iIsEquip;

    object oClean = GetFirstItemInInventory(oCreature);

    while (GetIsObjectValid(oClean))
    {
        iIsCWeap = GetIsPRCCreatureWeapon(oClean);

        iIsEquip = oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_L) ||
                   oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_R) ||
                   oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_B);

        if (iIsCWeap && !iIsEquip)
        {
            DestroyObject(oClean);
        }

        oClean = GetNextItemInInventory(oCreature);
    }
}
object GetPsionicCreatureWeapon(object oCreature, string sResRef, int nInventorySlot, float fDuration)
{
    int bCreatedWeapon = FALSE;
    object oCWeapon = GetItemInSlot(nInventorySlot, oCreature);

    RemoveUnarmedAttackEffects(oCreature);
    // Make sure they can actually equip them
    UnarmedFeats(oCreature);

    // Determine if a creature weapon of the proper type already exists in the slot
    if(!GetIsObjectValid(oCWeapon)                                       ||
       GetStringUpperCase(GetTag(oCWeapon)) != GetStringUpperCase(sResRef) // Hack: The resref's and tags of the PRC creature weapons are the same
       )
    {
        if (GetHasItem(oCreature, sResRef))
        {
            oCWeapon = GetItemPossessedBy(oCreature, sResRef);
            SetIdentified(oCWeapon, TRUE);
            //AssignCommand(oCreature, ActionEquipItem(oCWeapon, INVENTORY_SLOT_CWEAPON_L));
            ForceEquip(oCreature, oCWeapon, nInventorySlot);
        }
        else
        {
            oCWeapon = CreateItemOnObject(sResRef, oCreature);
            SetIdentified(oCWeapon, TRUE);
            //AssignCommand(oCreature, ActionEquipItem(oCWeapon, INVENTORY_SLOT_CWEAPON_L));
            ForceEquip(oCreature, oCWeapon, nInventorySlot);
            bCreatedWeapon = TRUE;
        }
    }


    // Clean up the mess of extra fists made on taking first level.
    DelayCommand(6.0f, LocalCleanExtraFists(oCreature));

    // Weapon finesse or intuitive attack?
    SetLocalInt(oCreature, "UsingCreature", TRUE);
    ExecuteScript("prc_intuiatk", oCreature);
    DelayCommand(1.0f, DeleteLocalInt(oCreature, "UsingCreature"));

    // Add OnHitCast: Unique if necessary
    if(GetHasFeat(FEAT_REND, oCreature))
        IPSafeAddItemProperty(oCWeapon, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);

    // This adds creature weapon finesse
    ApplyUnarmedAttackEffects(oCreature);

    // Destroy the weapon if it was created by this function
    if(bCreatedWeapon)
        DestroyObject(oCWeapon, (fDuration + 6.0));

    return oCWeapon;
}

int GetTargetSpecificChangesToDamage(object oTarget, object oManifester, int nDamage,
                                     int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE)
{
    // Greater Power Specialization - +2 damage on all HP-damaging powers when target is within 30ft
    if(bIsHitPointDamage                                                &&
       GetHasFeat(FEAT_GREATER_POWER_SPECIALIZATION, oManifester)       &&
       GetDistanceBetween(oTarget, oManifester) <= FeetToMeters(30.0f)
       )
            nDamage += 2;
    // Intellect Fortress - Halve damage dealt by powers that allow PR. Goes before DR (-like) reductions
    if(GetLocalInt(oTarget, "PRC_Power_IntellectFortress_Active")    &&
       Get2DACache("spells", "ItemImmunity", PRCGetSpellId()) == "1"
       )
        nDamage /= 2;
    // Mental Resistance - 3 damage less for all non-energy damage and ability damage
    if(GetHasFeat(FEAT_MENTAL_RESISTANCE, oTarget) && !bIsEnergyDamage)
        nDamage -= 3;

    // Reasonable return values only
    if(nDamage < 0) nDamage = 0;
    
    if (GetIsMeldBound(oManifester, MELD_PSYCHIC_FOCUS) == CHAKRA_THROAT && nDamage > 0 && !GetLocalInt(oManifester, "PsychicFocusMeld") && bIsHitPointDamage)
    {
    	SetLocalInt(oManifester, "PsychicFocusMeld", TRUE);
    	DelayCommand(6.0, DeleteLocalInt(oManifester, "PsychicFocusMeld"));
       	int nClass = GetMeldShapedClass(oManifester, MELD_PSYCHIC_FOCUS);
		int nDC = GetMeldshaperDC(oManifester, nClass, MELD_PSYCHIC_FOCUS);
    	if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE))
    		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oTarget, 6.0);
    }

    return nDamage;
}

int PracticedManifesting(object oManifester, int iManifestingClass, int iManifestingLevels)
{
    int nFeat;
    int iAdjustment = GetHitDice(oManifester) - iManifestingLevels;
    iAdjustment = iAdjustment > 4 ? 4 : iAdjustment < 0 ? 0 : iAdjustment;

    switch(iManifestingClass)
    {
        case CLASS_TYPE_PSION:          nFeat = FEAT_PRACTICED_MANIFESTER_PSION;          break;
        case CLASS_TYPE_PSYWAR:         nFeat = FEAT_PRACTICED_MANIFESTER_PSYWAR;         break;
        case CLASS_TYPE_PSYCHIC_ROGUE:  nFeat = FEAT_PRACTICED_MANIFESTER_PSYROG;         break;
        case CLASS_TYPE_WILDER:         nFeat = FEAT_PRACTICED_MANIFESTER_WILDER;         break;
        case CLASS_TYPE_WARMIND:        nFeat = FEAT_PRACTICED_MANIFESTER_WARMIND;        break;
        case CLASS_TYPE_FIST_OF_ZUOKEN: nFeat = FEAT_PRACTICED_MANIFESTER_FIST_OF_ZUOKEN; break;
        default: nFeat = -1;
    }

    if(GetHasFeat(nFeat, oManifester))
        return iAdjustment;

    return 0;
}

int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE)
{
    int nLevel;
    int nAdjust = GetLocalInt(oManifester, PRC_CASTERLEVEL_ADJUSTMENT);
    nAdjust -= GetLocalInt(oManifester, "WoLManifPenalty");

    // The function user needs to know the character's manifester level in a specific class
    // instead of whatever the character last manifested a power as
    if(nSpecificClass != CLASS_TYPE_INVALID)
    {
        if(GetIsPsionicClass(nSpecificClass))
        {
            nLevel = GetLevelByClass(nSpecificClass, oManifester);
            // Add levels from +ML PrCs only for the first manifesting class
            if(nSpecificClass == GetPrimaryPsionicClass(oManifester))
                nLevel += GetPsionicPRCLevels(oManifester);
                
			// Psionic vestiges are tucked in here to override things.
			// This assumes that there will never be a psion with this spell effect manifesting things
            if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel)
            {
            	nLevel = GetLocalInt(oManifester, "AretePsion");
            	nMaxPowerLevel = TRUE;
            }  
            if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !nMaxPowerLevel)
            {
            	nLevel = GetLocalInt(oManifester, "TheTriadPsion");
            	nMaxPowerLevel = TRUE;
            }
            if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && !nMaxPowerLevel)
            {
            	nLevel = GetLocalInt(oManifester, "AbysmPsion");
            	nMaxPowerLevel = TRUE;
            }            
                
            // This is for learning powers, we need to ignore some adjustments    
            if (nMaxPowerLevel) return nLevel;   

            nLevel += PracticedManifesting(oManifester, nSpecificClass, nLevel); //gotta be the last one

            return nLevel + nAdjust;
        }
        // A character's manifester level gained from non-manifesting classes is always a nice, round zero
        else
            return 0;
    }

    // Item Spells
    if(GetItemPossessor(GetSpellCastItem()) == oManifester)
    {
        if(DEBUG) SendMessageToPC(oManifester, "Item casting at level " + IntToString(GetCasterLevel(oManifester)));

        return GetCasterLevel(oManifester) + nAdjust;
    }

    // For when you want to assign the caster level.
    else if(GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE) != 0)
    {
        if(DEBUG) SendMessageToPC(oManifester, "Forced-level manifesting at level " + IntToString(GetCasterLevel(oManifester)));

        DelayCommand(1.0, DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE));
        nLevel = GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE);
    }
    else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID)
    {
        //Gets the manifesting class
        int nManifestingClass = GetManifestingClass(oManifester);
//        if(DEBUG) DoDebug("Manifesting class: " + IntToString(nManifestingClass), oManifester);
        nLevel = GetLevelByClass(nManifestingClass, oManifester);
        // Add levels from +ML PrCs only for the first manifesting class
        nLevel += nManifestingClass == GetPrimaryPsionicClass(oManifester) ? GetPsionicPRCLevels(oManifester) : 0;
        
		// Psionic vestiges are tucked in here to override things.
		// This assumes that there will never be a psion with this spell effect manifesting things
        if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel)
        {
        	nLevel = GetLocalInt(oManifester, "AretePsion");
        	nMaxPowerLevel = TRUE;
        }  
        if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !nMaxPowerLevel)
        {
        	nLevel = GetLocalInt(oManifester, "TheTriadPsion");
        	nMaxPowerLevel = TRUE;
        }        
        if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && !nMaxPowerLevel)
        {
        	nLevel = GetLocalInt(oManifester, "AbysmPsion");
        	nMaxPowerLevel = TRUE;
        }         
        
        // This is for learning powers, we need to ignore some adjustments    
        if (nMaxPowerLevel) return nLevel;   

        nLevel += PracticedManifesting(oManifester, nManifestingClass, nLevel); //gotta be the last one
//        if(DEBUG) DoDebug("Level gotten via GetLevelByClass: " + IntToString(nLevel), oManifester);
    }
    
    // If you have a primary psionic class and no manifester level yet, get levels based on that
    if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) 
    {
    	int nClass = GetPrimaryPsionicClass(oManifester);
    	nLevel = GetLevelByClass(nClass, oManifester);
        nLevel += GetPsionicPRCLevels(oManifester);
        nLevel += PracticedManifesting(oManifester, nClass, nLevel); //gotta be the last one
    }

    // If everything else fails, you are not a manifester
    if(nLevel == 0)
    {
        if(DEBUG)             DoDebug("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot");
        //else WriteTimestampedLogEntry("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot");

        return 0;
    }


    // The bonuses inside only apply to normal manifestation
    if(!GetLocalInt(oManifester, PRC_IS_PSILIKE))
    {
        //Adding wild surge
        int nSurge = GetWildSurge(oManifester);
        if (nSurge > 0) nLevel += nSurge;

        // Adding overchannel
        int nOverchannel = GetLocalInt(oManifester, PRC_OVERCHANNEL);
        if(nOverchannel > 0) nLevel += nOverchannel;
        
        // Adding Soul Manifester
        nLevel += Soulcaster(oManifester, PRCGetSpellId());
    }

    nLevel += nAdjust;

    // This spam is technically no longer necessary once the manifester level getting mechanism has been confirmed to work
//    if(DEBUG) FloatingTextStringOnCreature("Manifester Level: " + IntToString(nLevel), oManifester, FALSE);

    return nLevel;
}

int GetHighestManifesterLevel(object oCreature)
{
	int n = 0;
	int nHighest;
	int nTemp;
	
    while(n <= 8)
	{
		if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID)
		{
			nTemp = GetManifesterLevel(oCreature, GetClassByPosition(n, oCreature));
			
			if(nTemp > nHighest) 
				nHighest = nTemp;
		}
	n++;

	}
	
	return nHighest;
}

/* int GetHighestManifesterLevel(object oCreature)
{
    return max(max(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(1, oCreature)) : 0,
                   GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(2, oCreature)) : 0
                   ),
               GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(3, oCreature)) : 0
               );
} */

int GetPowerLevel(object oManifester)
{
    return GetLocalInt(oManifester, PRC_POWER_LEVEL);
}

int GetAbilityScoreOfClass(object oManifester, int nClass)
{
    return GetAbilityScore(oManifester, GetAbilityOfClass(nClass));
}

int GetManifestingClass(object oManifester = OBJECT_SELF)
{
    return GetLocalInt(oManifester, PRC_MANIFESTING_CLASS) - 1;
}

int GetAbilityOfClass(int nClass)
{
    switch(nClass)
    {
        case CLASS_TYPE_DIAMOND_DRAGON:
        case CLASS_TYPE_PSION:
        case CLASS_TYPE_PSYCHIC_ROGUE:
            return ABILITY_INTELLIGENCE;
        case CLASS_TYPE_PSYWAR:
        case CLASS_TYPE_FIST_OF_ZUOKEN:
        case CLASS_TYPE_WARMIND:
            return ABILITY_WISDOM;
        case CLASS_TYPE_WILDER:
            return ABILITY_CHARISMA;
    }

    // If there's no class, it's racial. Use Charisma
    return ABILITY_CHARISMA;
}

int GetPrimaryPsionicClass(object oCreature = OBJECT_SELF)
{
    int nClass;

    if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE))
    {
        int iPsionicPos = GetFirstPsionicClassPosition(oCreature);
        if (!iPsionicPos) return CLASS_TYPE_INVALID; // no Psionic casting class

        nClass = GetClassByPosition(iPsionicPos, oCreature);
    }
    else
    {
        int nClassLvl;
        int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8;
        int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl;

        nClass1 = GetClassByPosition(1, oCreature);
        nClass2 = GetClassByPosition(2, oCreature);
        nClass3 = GetClassByPosition(3, oCreature);
        nClass4 = GetClassByPosition(4, oCreature);
        nClass5 = GetClassByPosition(5, oCreature);
        nClass6 = GetClassByPosition(6, oCreature);
		nClass7 = GetClassByPosition(7, oCreature);
        nClass8 = GetClassByPosition(8, oCreature);

        if(GetIsPsionicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature);
        if(GetIsPsionicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature);
        if(GetIsPsionicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature);		
        if(GetIsPsionicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature);
        if(GetIsPsionicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature);
        if(GetIsPsionicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature);
        if(GetIsPsionicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature);
        if(GetIsPsionicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature);

		
        nClass = nClass1;
        nClassLvl = nClass1Lvl;
		
        if(nClass2Lvl > nClassLvl)
        {
            nClass = nClass2;
            nClassLvl = nClass2Lvl;
        }
        if(nClass3Lvl > nClassLvl)
        {
            nClass = nClass3;
            nClassLvl = nClass3Lvl;
        }
		if(nClass4Lvl > nClassLvl)
        {
            nClass = nClass4;
            nClassLvl = nClass4Lvl;
        }
        if(nClass5Lvl > nClassLvl)
        {
            nClass = nClass5;
            nClassLvl = nClass5Lvl;
        }
		if(nClass6Lvl > nClassLvl)
        {
            nClass = nClass6;
            nClassLvl = nClass6Lvl;
        }
        if(nClass7Lvl > nClassLvl)
        {
            nClass = nClass7;
            nClassLvl = nClass7Lvl;
        }		
        if(nClass8Lvl > nClassLvl)
        {
            nClass = nClass8;
            nClassLvl = nClass8Lvl;
        }		
		
        if(nClassLvl == 0)
            nClass = CLASS_TYPE_INVALID;
    }

    return nClass;
}

//:: [PRC .35] This needs to be updated for marker feats.
int GetPsionicPRCLevels(object oCreature)
{
    int nLevel = 0;

    // Cerebremancer and Psychic Theurge add manifester levels on each level
    nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature);
    nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature);
    if (GetFirstPsionicClassPosition(oCreature)) nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature);

    // No manifester level boost at level 1 and 10 for Thrallherd
    if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature))
    {
        nLevel += GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) - 1;
        if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) >= 10) nLevel -= 1;
    }
    // No manifester level boost at levels 2, 5 and 8 for Shadow Mind
    if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature))
    {
        nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature);
        if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1;
        if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1;
        if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1;
    }
    // No manifester level boost at level 1 and 6 for Iron Mind
    if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature))
    {
        nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1;
        if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1;
    }
    // No manifester level boost at level 1 and 6 for Diamond Dragon
    if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature))
    {
        nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1;
        if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1;
    }
    // No manifester level boost at level 1 for Sanctified Mind
    if(GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature))
    {
        nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1;
    }

    return nLevel;
}

int GetFirstPsionicClassPosition(object oCreature = OBJECT_SELF)
{
    if (GetIsPsionicClass(GetClassByPosition(1, oCreature)))
        return 1;
    if (GetIsPsionicClass(GetClassByPosition(2, oCreature)))
        return 2;
    if (GetIsPsionicClass(GetClassByPosition(3, oCreature)))
        return 3;
    if (GetIsPsionicClass(GetClassByPosition(4, oCreature)))
        return 4;
    if (GetIsPsionicClass(GetClassByPosition(5, oCreature)))
        return 5;
    if (GetIsPsionicClass(GetClassByPosition(6, oCreature)))
        return 6;
    if (GetIsPsionicClass(GetClassByPosition(7, oCreature)))
        return 7;
    if (GetIsPsionicClass(GetClassByPosition(8, oCreature)))
        return 8;
	
    return 0;
}

int GetIsPsionicClass(int nClass)
{
    return (nClass==CLASS_TYPE_PSION
          || nClass==CLASS_TYPE_PSYWAR
          || nClass==CLASS_TYPE_PSYCHIC_ROGUE
          || nClass==CLASS_TYPE_WILDER
          || nClass==CLASS_TYPE_FIST_OF_ZUOKEN
          || nClass==CLASS_TYPE_WARMIND
            );
}

int GetWildSurge(object oManifester)
{
    int nWildSurge = GetLocalInt(oManifester, PRC_IS_PSILIKE) ?
                      0 :                                       // Wild Surge does not apply to psi-like abilities
                      GetLocalInt(oManifester, PRC_WILD_SURGE);

    if(DEBUG) DoDebug("GetWildSurge():\n"
                    + "oManifester = " + DebugObject2Str(oManifester) + "\n"
                    + "nWildSurge = " + IntToString(nWildSurge) + "\n"
                      );

    return nWildSurge;
}

int GetMaxPowerLevel(object oManifester)
{
	int nClass = GetPrimaryPsionicClass(oManifester);
    string sFile = GetAMSKnownFileName(nClass);
    int nLevel = GetHighestManifesterLevel(oManifester);
    int nMax = StringToInt(Get2DACache(sFile, "MaxPowerLevel", nLevel));
    if (DEBUG) DoDebug("GetMaxPowerLevel is "+IntToString(nMax));
    return nMax;
}