//::///////////////////////////////////////////////
//:: Truenaming include: Misceallenous
//:: true_inc_trufunc
//::///////////////////////////////////////////////
/** @file
    Defines various functions and other stuff that
    do something related to the truenaming implementation.

    Also acts as inclusion nexus for the general
    truenaming includes. In other words, don't include
    them directly in your scripts, instead include this.

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

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

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

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


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

/**
 * Determines from what class's Utterance list the currently being truespoken
 * Utterance is truespoken from.
 *
 * @param oTrueSpeaker A creature uttering a Utterance at this moment
 * @return            CLASS_TYPE_* constant of the class
 */
int GetTruespeakingClass(object oTrueSpeaker = OBJECT_SELF);

/**
 * Determines the given creature's truespeaker level. If a class is specified,
 * then returns the truespeaker level for that class. Otherwise, returns
 * the truespeaker level for the currently active utterance.
 *
 * @param oTrueSpeaker   The creature whose truespeaker level to determine
 * @param nSpecificClass The class to determine the creature's truespeaker
 *                       level in.
 * @param nUseHD         If this is set, it returns the Character Level of the calling creature.
 *                       DEFAULT: CLASS_TYPE_INVALID, which means the creature's
 *                       truespeaker level in regards to an ongoing utterance
 *                       is determined instead.
 * @return               The truespeaker level
 */
int GetTruespeakerLevel(object oTrueSpeaker, int nSpecificClass = CLASS_TYPE_INVALID, int nUseHD = FALSE);

/**
 * Determines whether a given creature uses truenaming.
 * Requires either levels in a truenaming-related class or
 * natural truenaming ability based on race.
 *
 * @param oCreature Creature to test
 * @return          TRUE if the creature can use truenames, FALSE otherwise.
 */
int GetIsTruenamingUser(object oCreature);

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

/**
 * Determines whether a given class is a truenaming-related class or not.
 *
 * @param nClass CLASS_TYPE_* of the class to test
 * @return       TRUE if the class is a truenaming-related class, FALSE otherwise
 */
int GetIsTruenamingClass(int nClass);

/**
 * Gets the level of the Utterance being currently truespoken.
 * WARNING: Return value is not defined when a Utterance is not being truespoken.
 *
 * @param oTrueSpeaker The creature currently uttering a utterance
 * @return            The level of the Utterance being truespoken
 */
int GetUtteranceLevel(object oTrueSpeaker);

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

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

/**
 * Calculates the DC of the Utterance being currently truespoken.
 * Base value is 10 + Utterance level + ability modifier in uttering stat
 *
 * WARNING: Return value is not defined when a Utterance is not being truespoken.
 *
 */
int GetTrueSpeakerDC(object oTrueSpeaker = OBJECT_SELF);

/**
 * Determines the truespeaker's level in regards to truespeaker checks to overcome
 * spell resistance.
 *
 * WARNING: Return value is not defined when a Utterance is not being truespoken.
 *
 * @param oTrueSpeaker A creature uttering a Utterance at the moment
 * @return            The creature's truespeaker level, adjusted to account for
 *                    modifiers that affect spell resistance checks.
 */
int GetTrueSpeakPenetration(object oTrueSpeaker = OBJECT_SELF);

/**
 * Marks an utterance as active for the Law of Sequence.
 * Called from the Utterance
 *
 * @param oTrueSpeaker    Caster of the Utterance
 * @param nSpellId        SpellId of the Utterance
 * @param fDur            Duration of the Utterance
 */
void DoLawOfSequence(object oTrueSpeaker, int nSpellId, float fDur);

/**
 * Checks to see whether the law of sequence is active
 * Utterance fails if it is.
 *
 * @param oTrueSpeaker    Caster of the Utterance
 * @param nSpellId        SpellId of the Utterance
 *
 * @return True if the Utterance is active, False if it is not.
 */
int CheckLawOfSequence(object oTrueSpeaker, int nSpellId);

/**
 * Returns the name of the Utterance
 *
 * @param nSpellId        SpellId of the Utterance
 */
string GetUtteranceName(int nSpellId);

/**
 * Returns the name of the Lexicon
 *
 * @param nLexicon        LEXICON_* to name
 */
string GetLexiconName(int nLexicon);

/**
 * Returns the Lexicon the Utterance is in
 * @param nSpellId   Utterance to check
 *
 * @return           LEXICON_*
 */
int GetLexiconByUtterance(int nSpellId);

/**
 * Affects all of the creatures with Speak Unto the Masses
 *
 * @param oTrueSpeaker    Caster of the Utterance
 * @param oTarget   Original Target of Utterance
 * @param utter     The utterance structure returned by EvaluateUtterance
 */
void DoSpeakUntoTheMasses(object oTrueSpeaker, object oTarget, struct utterance utter);

/**
 * Affects all of the creatures with Speak Unto the Masses
 *
 * @param oTrueSpeaker    Caster of the Utterance
 * @param oTarget         Original Target of Utterance
 * @param utter           The utterance structure returned by EvaluateUtterance
 */
void DoWordOfNurturingReverse(object oTrueSpeaker, object oTarget, struct utterance utter);

/**
 * Affects all of the creatures with Speak Unto the Masses
 *
 * @param oTrueSpeaker    Caster of the Utterance
 * @param oTarget         Original Target of Utterance
 * @param utter           The utterance structure returned by EvaluateUtterance
 * @param nBeats          Number of rounds to fire this utterance
 * @param nDamageType     DAMAGE_TYPE_*
 */
void DoEnergyNegation(object oTrueSpeaker, object oTarget, struct utterance utter, int nBeats, int nDamageType);

/**
 * Checks to see if the chosen target of the Crafted Tool utterance is valid.
 * If it is not valid, it will search through all slots, starting with right hand weapon
 * to try and find a valid target.
 *
 * @param oTrueSpeaker    Caster of the Utterance
 * @param oTarget         Target of the utterance
 *
 * @return                Item in slot, or, if there are no valid objects on the creature, OBJECT_INVALID.
 *                        If the target is an item, it returns the item.
 */
object CraftedToolTarget(object oTrueSpeaker, object oTarget);

/**
 * Enforces the cross class cap on the Truespeech skill
 *
 * @param oTrueSpeaker  The PC whose feats to check.
 * @return              TRUE if needed to relevel, FALSE otherwise.
 */
int CheckTrueSpeechSkill(object oTrueSpeaker);

/**
 * Applies modifications to a utterance's damage that depend on some property
 * of the target.
 * Currently accounts for:
 *  - Mental Resistance
 *  - Greater Utterance Specialization
 *  - Intellect Fortress
 *
 * @param oTarget     A creature being dealt damage by a utterance
 * @param oTrueSpeaker The creature uttering the damaging utterance
 * @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 oTrueSpeaker, int nDamage,
                                     int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE);

*/

/**
 * Returns how many Cadence feats an Acolyte of the Ego has
 *
 * @param oTrueSpeaker  The PC whose feats to check.
 * @return              The count of feats
 */
int GetCadenceCount(object oTrueSpeaker);

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

#include "prc_alterations"
#include "true_inc_utter"
#include "true_inc_truknwn"
#include "true_inc_truespk"

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

int GetTruespeakingClass(object oTrueSpeaker = OBJECT_SELF)
{
    return GetLocalInt(oTrueSpeaker, PRC_TRUESPEAKING_CLASS) - 1;
}

int GetTruespeakerLevel(object oTrueSpeaker, int nSpecificClass = CLASS_TYPE_INVALID, int nUseHD = FALSE)
{
    int nLevel;
    int nAdjust = GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_ADJUSTMENT);
    // Bereft's speak syllables and use their character level.
    if (GetIsSyllable(PRCGetSpellId())) nUseHD = TRUE;

    // If this is set, return the user's HD
    if (nUseHD) return GetHitDice(oTrueSpeaker);

    // The function user needs to know the character's truespeaker level in a specific class
    // instead of whatever the character last truespoken a Utterance as
    if(nSpecificClass != CLASS_TYPE_INVALID)
    {
        if(GetIsTruenamingClass(nSpecificClass))
        {
            int nClassLevel = GetLevelByClass(nSpecificClass, oTrueSpeaker);
            if (nClassLevel > 0)
            {
            	nLevel = nClassLevel;
            }
        }
        // A character's truespeaker level gained from non-uttering classes is always a nice, round zero
        else
            return 0;
    }

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

        return GetCasterLevel(oTrueSpeaker) + nAdjust;
    }

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

        DelayCommand(1.0, DeleteLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE));
        nLevel = GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE);
    }
    else if(GetTruespeakingClass(oTrueSpeaker) != CLASS_TYPE_INVALID)
    {
        //Gets the level of the uttering class
        int nUtteringClass = GetTruespeakingClass(oTrueSpeaker);
        nLevel = GetLevelByClass(nUtteringClass, oTrueSpeaker);
    }

    // If everything else fails, use the character's first class position
    if(nLevel == 0)
    {
        if(DEBUG)             DoDebug("Failed to get truespeaker level for creature " + DebugObject2Str(oTrueSpeaker) + ", using first class slot");
        else WriteTimestampedLogEntry("Failed to get truespeaker level for creature " + DebugObject2Str(oTrueSpeaker) + ", using first class slot");

        nLevel = GetLevelByPosition(1, oTrueSpeaker);
    }

    nLevel += nAdjust;

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

    return nLevel;
}

int GetIsTruenamingUser(object oCreature)
{
    return !!(GetLevelByClass(CLASS_TYPE_TRUENAMER, oCreature) ||
              GetLevelByClass(CLASS_TYPE_BEREFT,    oCreature)
             );
}

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

	}
	
	return nHighest;
}

/* int GetHighestTrueSpeakerLevel(object oCreature)
{
    return PRCMax(PRCMax(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(1, oCreature)) : 0,
                   GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(2, oCreature)) : 0
                   ),
               GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(3, oCreature)) : 0
               );
} */

int GetIsTruenamingClass(int nClass)
{
    return (nClass == CLASS_TYPE_TRUENAMER ||
            nClass == CLASS_TYPE_BEREFT
            );
}

int GetUtteranceLevel(object oTrueSpeaker)
{
    return GetLocalInt(oTrueSpeaker, PRC_UTTERANCE_LEVEL);
}

int GetTruenameAbilityScoreOfClass(object oTrueSpeaker, int nClass)
{
    return GetAbilityScore(oTrueSpeaker, GetTruenameAbilityOfClass(nClass));
}

int GetTruenameAbilityOfClass(int nClass){
    switch(nClass)
    {
        case CLASS_TYPE_TRUENAMER:
            return ABILITY_CHARISMA;
        default:
            return ABILITY_CHARISMA;
    }

    // Technically, never gets here but the compiler does not realise that
    return -1;
}

int GetTrueSpeakerDC(object oTrueSpeaker = OBJECT_SELF)
{
    // Things we need for DC Checks
    int nSpellId = PRCGetSpellId();
    object oTarget = PRCGetSpellTargetObject();
    int nRace = MyPRCGetRacialType(oTarget);
    // DC is 10 + 1/2 Truenamer level + Ability (Charisma)
    int nClass = GetTruespeakingClass(oTrueSpeaker);
    int nDC = 10;
    nDC += GetLevelByClass(nClass, oTrueSpeaker) / 2;
    nDC += GetAbilityModifier(GetTruenameAbilityOfClass(nClass), oTrueSpeaker);
    int nFeat = -1;

    // Focused Lexicon. Bonus vs chosen racial type  //:: [PRC .35] Needs update for new racialtypes
    switch(nRace)
    {
        case RACIAL_TYPE_ABERRATION:         nFeat = FEAT_FOCUSED_LEXICON_ABERRATION;   break;
        case RACIAL_TYPE_ANIMAL:             nFeat = FEAT_FOCUSED_LEXICON_ANIMAL;       break;
        case RACIAL_TYPE_BEAST:              nFeat = FEAT_FOCUSED_LEXICON_BEAST;        break;
        case RACIAL_TYPE_CONSTRUCT:          nFeat = FEAT_FOCUSED_LEXICON_CONSTRUCT;    break;
        case RACIAL_TYPE_DRAGON:             nFeat = FEAT_FOCUSED_LEXICON_DRAGON;       break;
        case RACIAL_TYPE_DWARF:              nFeat = FEAT_FOCUSED_LEXICON_DWARF;        break;
        case RACIAL_TYPE_ELEMENTAL:          nFeat = FEAT_FOCUSED_LEXICON_ELEMENTAL;    break;
        case RACIAL_TYPE_ELF:                nFeat = FEAT_FOCUSED_LEXICON_ELF;          break;
        case RACIAL_TYPE_FEY:                nFeat = FEAT_FOCUSED_LEXICON_FEY;          break;
        case RACIAL_TYPE_GIANT:              nFeat = FEAT_FOCUSED_LEXICON_GIANT;        break;
        case RACIAL_TYPE_GNOME:              nFeat = FEAT_FOCUSED_LEXICON_GNOME;        break;
        case RACIAL_TYPE_HALFELF:            nFeat = FEAT_FOCUSED_LEXICON_HALFELF;      break;
        case RACIAL_TYPE_HALFLING:           nFeat = FEAT_FOCUSED_LEXICON_HALFLING;     break;
        case RACIAL_TYPE_HALFORC:            nFeat = FEAT_FOCUSED_LEXICON_HALFORC;      break;
        case RACIAL_TYPE_HUMAN:              nFeat = FEAT_FOCUSED_LEXICON_HUMAN;        break;
        case RACIAL_TYPE_HUMANOID_GOBLINOID: nFeat = FEAT_FOCUSED_LEXICON_GOBLINOID;    break;
        case RACIAL_TYPE_HUMANOID_MONSTROUS: nFeat = FEAT_FOCUSED_LEXICON_MONSTROUS;    break;
        case RACIAL_TYPE_HUMANOID_ORC:       nFeat = FEAT_FOCUSED_LEXICON_ORC;          break;
        case RACIAL_TYPE_HUMANOID_REPTILIAN: nFeat = FEAT_FOCUSED_LEXICON_REPTILIAN;    break;
        case RACIAL_TYPE_MAGICAL_BEAST:      nFeat = FEAT_FOCUSED_LEXICON_MAGICALBEAST; break;
        case RACIAL_TYPE_OOZE:               nFeat = FEAT_FOCUSED_LEXICON_OOZE;         break;
        case RACIAL_TYPE_OUTSIDER:           nFeat = FEAT_FOCUSED_LEXICON_OUTSIDER;     break;
        case RACIAL_TYPE_SHAPECHANGER:       nFeat = FEAT_FOCUSED_LEXICON_SHAPECHANGER; break;
        case RACIAL_TYPE_UNDEAD:             nFeat = FEAT_FOCUSED_LEXICON_UNDEAD;       break;
        case RACIAL_TYPE_VERMIN:             nFeat = FEAT_FOCUSED_LEXICON_VERMIN;       break;
    }
    if(nFeat > -1 && GetHasFeat(nFeat, oTrueSpeaker))
    {
        nDC += 1;
        nFeat = -1;
    }

    // Utterance Focus. DC Bonus for a chosen utterance
    switch(nSpellId)
    {
        case UTTER_BREATH_CLEANSING_R:      nFeat = FEAT_UTTERANCE_FOCUS_BREATH_CLEANSING;      break;
        case UTTER_BREATH_RECOVERY_R:       nFeat = FEAT_UTTERANCE_FOCUS_BREATH_RECOVERY;       break;
        case UTTER_ELDRITCH_ATTRACTION:     nFeat = FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION;   break;
        case UTTER_ELDRITCH_ATTRACTION_R:   nFeat = FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION;   break;
        case UTTER_MORALE_BOOST_R:          nFeat = FEAT_UTTERANCE_FOCUS_MORALE_BOOST;          break;
        case UTTER_PRETERNATURAL_CLARITY_R: nFeat = FEAT_UTTERANCE_FOCUS_PRETERNATURAL_CLARITY; break;
        case UTTER_SENSORY_FOCUS_R:         nFeat = FEAT_UTTERANCE_FOCUS_SENSORY_FOCUS;         break;
        case UTTER_SILENT_CASTER_R:         nFeat = FEAT_UTTERANCE_FOCUS_SILENT_CASTER;         break;
        case UTTER_SINGULAR_MIND_R:         nFeat = FEAT_UTTERANCE_FOCUS_SINGULAR_MIND;         break;
        case UTTER_TEMPORAL_SPIRAL_R:       nFeat = FEAT_UTTERANCE_FOCUS_TEMPORAL_SPIRAL;       break;
        case UTTER_TEMPORAL_TWIST_R:        nFeat = FEAT_UTTERANCE_FOCUS_TEMPORAL_TWIST;        break;
        case UTTER_WARD_PEACE_R:            nFeat = FEAT_UTTERANCE_FOCUS_WARD_PEACE;            break;
        case UTTER_SHOCKWAVE:               nFeat = FEAT_UTTERANCE_FOCUS_SHOCKWAVE;             break;
    }
    if(nFeat > -1 && GetHasFeat(nFeat, oTrueSpeaker))
        nDC += 1;

    return nDC;
}

int GetTrueSpeakPenetration(object oTrueSpeaker = OBJECT_SELF)
{
    int nPen = GetTruespeakerLevel(oTrueSpeaker);

    // According to Page 232 of Tome of Magic, Spell Pen as a feat counts, so here it is.
    if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oTrueSpeaker)) nPen += 6;
    else if(GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oTrueSpeaker)) nPen += 4;
    else if(GetHasFeat(FEAT_SPELL_PENETRATION, oTrueSpeaker)) nPen += 2;

    // Blow away SR totally, just add 9000
    // Does not work on Syllables, only utterances
    if (GetLocalInt(oTrueSpeaker, TRUE_IGNORE_SR) && !GetIsSyllable(PRCGetSpellId())) nPen += 9000;

    if(DEBUG) DoDebug("GetTrueSpeakPenetration(" + GetName(oTrueSpeaker) + "): " + IntToString(nPen));

    return nPen;
}

void DoLawOfSequence(object oTrueSpeaker, int nSpellId, float fDur)
{
    // This makes sure everything is stored using the Normal, and not the reverse
    string sSpellId = GetNormalUtterSpellId(nSpellId);
    SetLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + sSpellId, TRUE);
    DelayCommand(fDur, DeleteLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + sSpellId));
}

int CheckLawOfSequence(object oTrueSpeaker, int nSpellId)
{
	// Turns this off
	if (GetPRCSwitch(PRC_LAW_OF_SEQUENCE)) return FALSE;
    // This makes sure everything is stored using the Normal, and not the reverse
    return GetLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + GetNormalUtterSpellId(nSpellId));
}

string GetUtteranceName(int nSpellId)
{
    return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId)));
}

string GetLexiconName(int nLexicon)
{
    int nStrRef;
    switch(nLexicon)
    {
        case LEXICON_EVOLVING_MIND: nStrRef = 16828478; break;
        case LEXICON_CRAFTED_TOOL:  nStrRef = 16828479; break;
        case LEXICON_PERFECTED_MAP: nStrRef = 16828480; break;
    }

    return GetStringByStrRef(nStrRef);
}

int GetLexiconByUtterance(int nSpellId)
{
     int i, nUtter;
     for(i = 0; i < GetPRCSwitch(FILE_END_CLASS_POWER) ; i++)
     {
         nUtter = StringToInt(Get2DACache("cls_true_utter", "SpellID", i));
         if(nUtter == nSpellId)
         {
             return StringToInt(Get2DACache("cls_true_utter", "Lexicon", i));
         }
     }
     // This should never happen
     return -1;
}

void DoSpeakUntoTheMasses(object oTrueSpeaker, object oTarget, struct utterance utter)
{
	// Check for Speak Unto the Masses, exit function if not set
	if (!GetLocalInt(oTrueSpeaker, TRUE_SPEAK_UNTO_MASSES)) return;

	// Speak to the Masses affects all creatures of the same race in the AoE
	int nRacial = MyPRCGetRacialType(oTarget);
	object oSkin;

	// Loop over targets
        object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE);
        while(GetIsObjectValid(oAreaTarget))
        {
        	if(DEBUG) DoDebug("Speak Unto the Masses: While entered");
            // Skip the original target/truespeaker, its already been hit
            if (oAreaTarget != oTarget && oAreaTarget != oTrueSpeaker)
            {
		if(DEBUG) DoDebug("Speak Unto the Masses: Target check");
                // Targeting limitations
                if(MyPRCGetRacialType(oAreaTarget) == nRacial)
                {
                	if(DEBUG) DoDebug("Speak Unto the Masses: Racial Check");
                    // Only affect friends or ignore it
                    if (GetIsFriend(oAreaTarget, oTrueSpeaker) || !utter.bFriend)
                    {
                    if(DEBUG) DoDebug("Speak Unto the Masses: Friend Check");
                        // Do SR, or ignore if its a friendly utterance.
                    if (!PRCDoResistSpell(utter.oTrueSpeaker, oAreaTarget, utter.nPen) || utter.bIgnoreSR)
                    {
                    if(DEBUG) DoDebug("Speak Unto the Masses: SR Check");
                        // Saving throw, ignore it if there is no DC to check
                        if(!PRCMySavingThrow(utter.nSaveThrow, oAreaTarget, utter.nSaveDC, utter.nSaveType, OBJECT_SELF) ||
                           utter.nSaveDC == 0)
                            {
                            if(DEBUG) DoDebug("Speak Unto the Masses: Saving Throw");
                                // Itemproperty, if there is one
                                oSkin = GetPCSkin(oAreaTarget);
                                if (GetIsItemPropertyValid(utter.ipIProp1))
                                {
                                	if(DEBUG) DoDebug("Speak Unto the Masses: IProp1");
                                    IPSafeAddItemProperty(oSkin, utter.ipIProp1, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
                                }
                                // Itemproperty, if there is one
                                if (GetIsItemPropertyValid(utter.ipIProp2))
                                {
                                if(DEBUG) DoDebug("Speak Unto the Masses: IProp2");
                                    IPSafeAddItemProperty(oSkin, utter.ipIProp2, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
                                }
                                // Itemproperty, if there is one
                                if (GetIsItemPropertyValid(utter.ipIProp3))
                                {
                                if(DEBUG) DoDebug("Speak Unto the Masses: IProp3");
                                    IPSafeAddItemProperty(oSkin, utter.ipIProp3, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
                                }
                                        // Duration Effects
                                SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, utter.eLink, oAreaTarget, utter.fDur, TRUE, utter.nSpellId, utter.nTruespeakerLevel);
                                if(DEBUG) DoDebug("Speak Unto the Masses: Duration");
                                // Impact Effects
                            SPApplyEffectToObject(DURATION_TYPE_INSTANT, utter.eLink2, oAreaTarget);
                            if(DEBUG) DoDebug("Speak Unto the Masses: Instant");
                            // Utterance Specific code down here
                            DoWordOfNurturingReverse(oTrueSpeaker, oAreaTarget, utter);
                            if(DEBUG) DoDebug("Speak Unto the Masses: Word of Nurturing Reverse");
                            if (utter.nSpellId == UTTER_ENERGY_NEGATION_R)
                                DoEnergyNegation(oTrueSpeaker, oTarget, utter, FloatToInt(utter.fDur / 6.0), GetLocalInt(oTrueSpeaker, "TrueEnergyNegation"));
                        } // end if - Saving Throw
                    } // end if - Spell Resistance
                } // end if - Friend Check
                }// end if - Targeting check
            }

            // Get next target
            oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE);
	}// end while - Target loop
}

void DoWordOfNurturingReverse(object oTrueSpeaker, object oTarget, struct utterance utter)
{
	// Returns TRUE upon concentration failure
	if (GetBreakConcentrationCheck(oTrueSpeaker)) return;

	int nDamage;
	// First, find out what utterance we're using
	if (utter.nSpellId == UTTER_WORD_NURTURING_MINOR_R)    nDamage = d6();
	else if (utter.nSpellId == UTTER_WORD_NURTURING_LESSER_R)   nDamage = d6(2);
	else if (utter.nSpellId == UTTER_WORD_NURTURING_MODERATE_R) nDamage = d6(4);
	else if (utter.nSpellId == UTTER_WORD_NURTURING_POTENT_R)   nDamage = d6(6);
	else if (utter.nSpellId == UTTER_WORD_NURTURING_CRITICAL_R) nDamage = d6(8);
	else if (utter.nSpellId == UTTER_WORD_NURTURING_GREATER_R)  nDamage = d6(10);
	// Empower it
	if(utter.bEmpower) nDamage += (nDamage/2);
	// If we're using this, target has already failed SR and Saves
	effect eImp = EffectLinkEffects(EffectVisualEffect(VFX_IMP_MAGLAW), EffectDamage(nDamage));
	SPApplyEffectToObject(DURATION_TYPE_INSTANT, eImp, oTarget);
}

void DoEnergyNegation(object oTrueSpeaker, object oTarget, struct utterance utter, int nBeats, int nDamageType)
{
	int nDamage = d6(2);
	// Empower it
	if(utter.bEmpower) nDamage += (nDamage/2);
       	// Impact VFX
        utter.eLink2 = EffectLinkEffects(EffectVisualEffect(VFX_IMP_MAGVIO), EffectDamage(nDamage, nDamageType));
        // Impact Effects
        SPApplyEffectToObject(DURATION_TYPE_INSTANT, utter.eLink2, oTarget);

        nBeats -= 1;
        if (nBeats > 0)
        	DelayCommand(6.0, DoEnergyNegation(oTrueSpeaker, oTarget, utter, nBeats, nDamageType));
}

object CraftedToolTarget(object oTrueSpeaker, object oTarget)
{
	// Check to see if its a weapon or item of some sort
	// Return it if its a valid base item type
	if (GetBaseItemType(oTarget) != BASE_ITEM_INVALID) return oTarget;

	object oItem = OBJECT_INVALID;

	// These are utterances that only target weapons
	if (PRCGetSpellId() == UTTER_KEEN_WEAPON || PRCGetSpellId() == UTTER_SUPPRESS_WEAPON || PRCGetSpellId() == UTTER_TRANSMUTE_WEAPON)
	{
		// By the time we're here, it should only be creatures, not items as targets
		oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
		// Only do this for Keen
		if (PRCGetSpellId() == UTTER_KEEN_WEAPON)
		{
			// Put the bonus on the ammo rather than the bow if its ranged
			if( GetBaseItemType(oItem) == BASE_ITEM_LONGBOW || GetBaseItemType(oItem) == BASE_ITEM_SHORTBOW )
			{
			     oItem = GetItemInSlot(INVENTORY_SLOT_ARROWS, oTarget);
			}
			else if(GetBaseItemType(oItem) == BASE_ITEM_LIGHTCROSSBOW || GetBaseItemType(oItem) == BASE_ITEM_HEAVYCROSSBOW)
			{
			     oItem = GetItemInSlot(INVENTORY_SLOT_BOLTS, oTarget);
			}
			else if(GetBaseItemType(oItem) == BASE_ITEM_SLING)
			{
			     oItem = GetItemInSlot(INVENTORY_SLOT_BULLETS, oTarget);
      			}
      		}
		// If its a valid weapon, return it
		if (GetBaseItemType(oItem) != BASE_ITEM_INVALID) return oItem;
		// Check the spare hand, and make sure its not a shield
		oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget);
		// If its a valid weapon and not a shield, return it
		if (GetBaseItemType(oItem) != BASE_ITEM_INVALID     &&
		    GetBaseItemType(oItem) != BASE_ITEM_LARGESHIELD &&
                    GetBaseItemType(oItem) != BASE_ITEM_SMALLSHIELD &&
                    GetBaseItemType(oItem) != BASE_ITEM_TOWERSHIELD) return oItem;
	}// These ones target only armour
	else if (PRCGetSpellId() == UTTER_FORTIFY_ARMOUR_SNEAK || PRCGetSpellId() == UTTER_FORTIFY_ARMOUR_CRIT)
	{
		return GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget);
	}// This one targets scrolls and potions
	else if (PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_EMP || PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_EXT ||
	         PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_MAX)
	{
		oItem = GetFirstItemInInventory(oTarget);
		while(GetIsObjectValid(oItem))
		{
		        if (GetBaseItemType(oItem) == BASE_ITEM_SCROLL || GetBaseItemType(oItem) == BASE_ITEM_POTIONS)
		        {
		        	return oItem;
		        }
	        	oItem = GetNextItemInInventory(oTarget);
     		}
	}
	else // For the rest of the utterances, any item is a valid target.
	{
		// Get the PC's chosen inventory slot
		int nSlot = GetLocalInt(oTrueSpeaker, "TrueCraftedToolTargetSlot");
		oItem = GetItemInSlot(nSlot, oTarget);
		// If the chosen item isn't valid, we go into the choice progession
		// Yes, its a long chain
		if (!GetIsObjectValid(oItem))
		{
		    oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
		    if (!GetIsObjectValid(oItem))
		    {
		        oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget);
		        if (!GetIsObjectValid(oItem))
			{
			    oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget);
			    if (!GetIsObjectValid(oItem))
			    {
			        oItem = GetItemInSlot(INVENTORY_SLOT_HEAD, oTarget);
			        if (!GetIsObjectValid(oItem))
			        {
			            oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oTarget);
			            if (!GetIsObjectValid(oItem))
				    {
				        oItem = GetItemInSlot(INVENTORY_SLOT_LEFTRING, oTarget);
				        if (!GetIsObjectValid(oItem))
			                {
			                    oItem = GetItemInSlot(INVENTORY_SLOT_NECK, oTarget);
			                    if (!GetIsObjectValid(oItem))
			    		    {
			    		        oItem = GetItemInSlot(INVENTORY_SLOT_CLOAK, oTarget);
			    		        if (!GetIsObjectValid(oItem))
						{
						    oItem = GetItemInSlot(INVENTORY_SLOT_ARMS, oTarget);
						    if (!GetIsObjectValid(oItem))
						    {
							oItem = GetItemInSlot(INVENTORY_SLOT_BOOTS, oTarget);
							if (!GetIsObjectValid(oItem))
							{
							    oItem = GetItemInSlot(INVENTORY_SLOT_BELT, oTarget);
		                                        }
		                                    }
		                               }
		                            }
		                        }
		                    }
		                }
		            }
		        }
		    }
		}
	}
	return oItem;
}

int CheckTrueSpeechSkill(object oTrueSpeaker)
{
    // The max for a class skill is 3 + 1 per level. We just divide this in half for Cross Class
    int nMax = GetHitDice(oTrueSpeaker) + 3;
    nMax /= 2;
    // We want base ranks only
    int nRanks = GetSkillRank(SKILL_TRUESPEAK, oTrueSpeaker, TRUE);

    // The Truenamer class has Truespeech as a class skill, so no relevel
    if (GetLevelByClass(CLASS_TYPE_TRUENAMER, oTrueSpeaker) > 0) return FALSE;
    // Same for this class
    else if (GetLevelByClass(CLASS_TYPE_BEREFT, oTrueSpeaker) > 0) return FALSE;
    // And this one
    else if (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oTrueSpeaker) > 0) return FALSE;
    // If they have the feat, no relevel
    else if(GetHasFeat(FEAT_TRUENAME_TRAINING, oTrueSpeaker)) return FALSE;
    // Now we check the values. If they have too many ranks, relevel.
    else if (nRanks > nMax)
    {
        // Relevel
        FloatingTextStringOnCreature("You cannot have more than " + IntToString(nMax) + " in TrueSpeech.", oTrueSpeaker, FALSE);
        return TRUE;
    }

    // No relevel normally
    return FALSE;
}

/*
int GetTargetSpecificChangesToDamage(object oTarget, object oTrueSpeaker, int nDamage,
                                     int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE)
{
    // Greater Utterance Specialization - +2 damage on all HP-damaging utterances when target is within 30ft
    if(bIsHitPointDamage                                                &&
       GetHasFeat(FEAT_GREATER_Utterance_SPECIALIZATION, oTrueSpeaker)       &&
       GetDistanceBetween(oTarget, oTrueSpeaker) <= FeetToMeters(30.0f)
       )
            nDamage += 2;
    // Intellect Fortress - Halve damage dealt by utterances that allow PR. Goes before DR (-like) reductions
    if(GetLocalInt(oTarget, "PRC_Utterance_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;

    return nDamage;
}
*/
// Test main
//void main(){}

int GetCadenceCount(object oTrueSpeaker)
{
    int nClass = GetLevelByClass(CLASS_TYPE_ACOLYTE_EGO, oTrueSpeaker);
    if (GetLocalInt(oTrueSpeaker, "ResonantVoice") == TRUE)
    {
	nClass += 3; //Adds 3 to class level
    }    
    
    int nCount = nClass/2; //Get a cadence feat at 2, 4, 6, 8, 10 levels.
    
    if (nCount > 6) nCount = 6; //Can't go above 6 with Resonant Voice active
    
    // Return total
    return nCount;
}