/*//////////////////////////////////////////////////////////////////////////////
// Script Name: 0i_combat
////////////////////////////////////////////////////////////////////////////////
 Include scripts for combat scripts.
*///////////////////////////////////////////////////////////////////////////////
// Programmer: Philos
////////////////////////////////////////////////////////////////////////////////
#include "0i_messages"
#include "0i_items"
#include "0i_spells"
// This structure is used to represent the number and type of
// enemies that a creature is facing, divided into four main
// categories: FIGHTERS, CLERICS, MAGES, MONSTERS.
struct stClasses
{
    int FIGHTERS;
    int FIGHTER_LEVELS;
    int CLERICS;
    int CLERIC_LEVELS;
    int MAGES;
    int MAGE_LEVELS;
    int MONSTERS;
    int MONSTER_LEVELS;
    int TOTAL;
    int TOTAL_LEVELS;
};
struct stTarget
{
    object oTarget;
    int nValue;
    int nBestValue;
    int nBestSecondaryValue;
    float fNearestRange;
    float fNearestSecondaryRange;
    int nIndex;
    int nSecondaryIndex;
    string sTargetType;
};
//******************************************************************************
//************ GET TARGETS USING THE OBJECT SEARCH FUNCTIONS *******************
//******************************************************************************

// Returns the nearest enemy that is not disabled from oCreature.
// You may pass in any of the CREATURE_TYPE_* constants
// used in GetNearestCreature as nCType1 & nCType2, with
// corresponding values for nCValue1 & nCValue2.
// NOTE: CREATURE_TYPE_PERCEPTION = 7, PERCEPTION_SEEN = 7.
// bDisabled = TRUE will also return any disabled targets that are not dead.
object ai_GetNearestEnemy(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1, int bDisabled = FALSE);
// Returns the nearest ally from oCreature.
// You may pass in any of the CREATURE_TYPE_* constants
// used in GetNearestCreature as nCType1 & nCType2, with
// corresponding values for nCValue1 & nCValue2.
// NOTE: CREATURE_TYPE_PERCEPTION = 7, PERCEPTION_SEEN = 7.
object ai_GetNearestAlly(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1);
// Returns the number of alive enemies grouped near oCreature within fDistance.
int ai_GetNumOfEnemiesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE);
// Returns the number of alive allies grouped near oCreature within fDistance.
int ai_GetNumOfAlliesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE);
// Returns the number of creatures of nRacial_Type within fDistance that can be seen by oCreature.
int ai_GetRacialTypeCount(object oCreature, int nRacial_Type, float fDistance = AI_RANGE_PERCEPTION);
// Returns the weakest attacker that is in melee or is attacking oCreature's master.
object ai_GetLowestCRAttackerOnMaster(object oCreature);

//******************************************************************************
//******************** SET/CLEAR COMBAT STATE FUNCTIONS ************************
//******************************************************************************

// Sets oCreatures's combat state by setting variables for AI_ALLIES and AI_ENEMIES.
// Returns the nearest visible enemy.
object ai_SetCombatState(object oCreature);
// Clears all variables that were define for the current round for oCreature.
void ai_ClearCombatState(object oCreature);
// Clears just the Talent variables so we can recalculate the Talents.
void ai_ClearTalents(object oCreature);

//******************************************************************************
//*************** GET TARGETS USING COMBAT STATE FUNCTIONS *********************
//******************************************************************************
// These functions will find a target or an index to a target based on the
// combat state variables created by the function ai_SetCombatState.

// ******************************** Target Checks ******************************
// ** Used to get creatures within fMaxRange as a target.                     **
// ** These checks that do basic checks to find a creature.                   **
// ** Used mostly in spell checks.                                            **
// *****************************************************************************
// Returns the Index of the nearest creature seen within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the nearest creature seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetNearestTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the index of the nearest creature seen with the lowest combat rating
// within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the nearest creature seen with the lowest combat rating within fMaxRange
// in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetLowestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the index of the nearest creature seen with the highest combat rating
// within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the nearest creature seen with the highest combat rating within fMaxRange
// in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetHighestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);

// *************************** Not in an AOE Target Checks *********************
// ** Used to get creatures within fMaxRange as a target.                     **
// ** These checks that do basic checks to find a creature that is not in a   **
// ** harmful AOE. Used in spell checks to target unaffected cratures first.  **
// *****************************************************************************
// Returns the index of the nearest creature seen that is busy attacking an ally
// within fMaxRange in the combat state.
// If none is found then it will return 0.
int ai_GetNearestIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the nearest combat creature seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetNearestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the index of the nearest creature seen with the lowest combat rating
// that is not in a dangerous area of effect within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetLowestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the lowest combat creature seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetLowestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the index of the nearest creature seen with the highest combat rating
// that is not in a dangerous area of effect within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetHighestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the highest combat creature seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetHighestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);

// ******************************** Physical Target Checks *********************
// ** Used in melee checks to make sure the attacker can damage oCreature     **
// ** as well as stay within range of their master.                           **
// *****************************************************************************
// Returns the Index of the nearest creature seen within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetNearestPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the nearest creature seen within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetNearestPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the index of the nearest creature seen with the lowest combat rating
// within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetLowestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the nearest creature seen with the lowest combat rating within fMaxRange
// in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetLowestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the index of the nearest creature seen with the highest combat rating
// within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetHighestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the nearest creature seen with the highest combat rating within fMaxRange
// in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetHighestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);

// *************************** Not in an AOE Target Checks *********************
// ** Used to get creatures within fMaxRange as a target.                     **
// ** These checks that do basic checks to find a creature that is not in a   **
// ** harmful AOE. Used in spell checks to target unaffected cratures first.  **
// *****************************************************************************
// Returns the index of the nearest creature seen that is busy attacking an ally
// within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// If none is found then it will return 0.
int ai_GetNearestPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the nearest combat creature seen within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetNearestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the index of the nearest creature seen with the lowest combat rating
// that is not in a dangerous area of effect within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetLowestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the lowest combat creature seen within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetLowestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the index of the nearest creature seen with the highest combat rating
// that is not in a dangerous area of effect within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetHighestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the highest combat creature seen within fMaxRange in the combat state.
// This also checks if the caller can do damage to oCreature with current weapon.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
object ai_GetHighestCRPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);

// ******************************** Group Target Checks ************************
// ** These get the largest group of enemies in 5' with a minimal number of   **
// ** allies within fMaxRange. Used for AOE spells.                           **
// *****************************************************************************
// Returns the index of the creature seen with the lowest enemies to oCreature
// that are in a group minus the number of allies in that group within
// fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetLowestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the index of the creature seen with the most enemies to the caller
// that are in a group minus the number of allies in that group within
// fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetHighestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns a creature of sTargetType where they have the least number of
// allies and the most number of enemies within fMaxRange in the combat state.
// Returns OBJECT_INVALID if there is not a good creature to select.
// sTargetType is either AI_ENEMY, or AI_ALLY.
object ai_GetGroupedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns the index of the creature seen with the most enemies to oCreature that
// they are in melee with minus the number of allies to oCreature they are in
// melee with that is not in a dangerous area of effect within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetHighestGroupIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);
// Returns a creature of sTargetType where they have the least number of
// allies and the most number of enemies within fMaxRange that are not in a
// dangerous area of effect in the combat state.
// Returns OBJECT_INVALID if there is not a good creature to select.
// sTargetType is either AI_ENEMY, or AI_ALLY.
object ai_GetGroupTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY);

// ***************************** Specific Target Checks ************************
// ** Used in melee checks to make sure the attacker can damage oCreature     **
// ** as well as stay within range of their master.                           **
// *****************************************************************************
// Returns the index of the nearest creature with the least % of hitpoints within
// fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_GetMostWoundedIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the creature with the lowest health seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
object ai_GetMostWoundedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the index of the nearest ally with the least % of hitpoints within
// fMaxRange in the combat state.
// This also filters for AI_MODE_PARTY_HEALING_OFF and AI_MODE_SELF_HEALING_OFF.
// If no ally is found then it will return an index of 0.
int ai_GetAllyToHealIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the ally with the lowest health seen within fMaxRange in the combat state.
// This also filters for AI_MODE_PARTY_HEALING_OFF and AI_MODE_SELF_HEALING_OFF.
// Returns OBJECT_INVALID if no creature is found.
object ai_GetAllyToHealTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the creature with the lowest fortitude save seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
object ai_GetLowestFortitudeSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the creature with the lowest reflex save seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
object ai_GetLowestReflexSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the creature with the lowest will save seen within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
object ai_GetLowestWillSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the creature with the lowest save based on nSpell save type seen
// within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
object ai_GetSpellTargetBasedOnSaves(object oCreature, int nSpell, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the index of the nearest creature seen that is busy attacking an ally
// within fMaxRange in the combat state.
// If none is found then it will return 0.
int ai_GetSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE);
// Returns the nearest creature seen of nClassType within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetNearestClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the creature with the lowest combat rating seen of nClassType within
// fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetLowestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the creature with the highest combat rating seen of nClassType within
// fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetHighestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the nearest creature seen of nRacialType within fMaxRange in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetNearestRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the creature with the lowest combat rating seen of nRacialType within
// fMaxRange in the combat state. Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetLowestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the creature with the highest combat rating seen of nRacialType within
// fMaxRange in the combat state. Returns OBJECT_INVALID if no creature is found.
// sTargetType is either AI_ENEMY or AI_ALLY.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetHighestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the nearest enemy seen that is attacking an ally with the least
// number of enemies on them within fMaxRange in the combat state.
// If none is found then it will return 0.
object ai_GetFlankTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE);
// Returns the nearest enemy creature seen wihtin fMaxRange that is a favored enemy
// of the caller in the combat state.
// Returns OBJECT_INVALID if no creature is found.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetNearestFavoredEnemyTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE);
// Returns the best target for melee combat based if we are in melee or not.
// If not in melee it will get the nearest target that is not in a dangerous
// area of effect for us to attack in the combat state.
// If in melee it will get the weakest target.
// If it returns OBJECT_INVALID then we should stop the attack. The only way
// to not get a target is if we have been told not to attack strong opponents.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetBestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE);
// Returns the nearest target for melee combat based if we are in melee or not.
// If not in melee it will get the nearest target that is not in a dangerous
// area of effect for us to attack in the combat state.
// If it returns OBJECT_INVALID then we should stop the attack. The only way
// to not get a target is if we have been told not to attack strong opponents.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetNearestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE);
// Returns the target with the lowest combat rating for melee combat based if
// we are in melee or not. If not in melee it will get the nearest target that
// is not in a dangerous area of effect for us to attack in the combat state.
// If it returns OBJECT_INVALID then we should stop the attack. The only way
// to not get a target is if we have been told not to attack strong opponents.
// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies.
object ai_GetLowestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE);
// Returns the target with the highest combat rating for melee combat based if
// we are in melee or not. If not in melee it will get the nearest target that
// is not in a dangerous area of effect for us to attack in the combat state.
// If it returns OBJECT_INVALID then we should stop the attack.
object ai_GetHighestCRTargetForMeleeCombat(object oCreature, int nInMelee);
// Returns the Index of the nearest creature seen within fMaxRange in the combat state.
// If no creature is found then it will return an index of 0.
// sTargetType is either AI_ENEMY or AI_ALLY.
int ai_MonsterGetNearestIndex(object oMonster, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE);
// Returns the index of the nearest enemy creature that can see oCreature.
int ai_GetNearestIndexThatSeesUs(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION);
// Returns the nearest creature attacking the caller within fMaxRange in the combat state.
// Returns OBJECT_INVALID if oCreature is not being attacked.
object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE);
// Returns the nearest creature attacking oAlly from oCreature within fMaxRange
// in the combat state.
// Returns OBJECT_INVALID if oAlly is not being attacked.
object ai_GetEnemyAttackingMyAlly(object oCreature, object oAlly, float fMaxRange = AI_RANGE_MELEE);
// Returns the number of enemies within fMaxRange of the caller in the combat state.
int ai_GetNumOfEnemiesInRange(object oCreature, float fMaxRange = AI_RANGE_MELEE);
// Returns the best ally target withing fMaxRange for nSpell to be cast on.
// Uses the ai_spells.2da file to pick a target.
object ai_GetAllyBuffTarget(object oCreature, int nSpell, float fMaxRange = AI_RANGE_BATTLEFIELD);

//******************************************************************************
//********************  OTHER COMBAT FUNCTIONS  ********************************
//******************************************************************************

// Returns the current round that oCreature is in for this combat.
int ai_GetCurrentRound(object oCreature);
// Returns the difficulty of the battle based on the combat state.
// nDifficulty is Enemy level - Ally level + 20 + Player adjustment.
//    20+    : Impossible     - Cannot win.
// 17 to  19 : Overpowering   - Use all of our powers.
// 15 to  16 : Very Difficult - Use all of our power (Highest level spells).
// 11 to  14 : Challenging    - Use most of our power (Higher level powers).
//  8 to  10 : Moderate       - Use half of our power (Mid level powers and less).
//  5 to   7 : Easy           - Use our weaker powers (Lowest level powers).
//  2 to   4 : Effortless     - Don't waste spells and powers on this.
//  1 or less: Pointless      - We probably should ignore these dangers.
int ai_GetDifficulty(object oCreature);
// Returns oCreatures Combat rating.
//(BAB + AC - 10) / 2
int ai_GetMyCombatRating(object oCreature);
// Returns the last creature oCreature attacked.
// bPhysical checks for creatures attacked in melee or range with a weapon.
// bSpell will look for creatures attacked by a spell.
object ai_GetAttackedTarget(object oCreature, int bPhysical = TRUE, int bSpell = FALSE);
// Returns TRUE if oCreature is of nClassType;
// May also check for general Class types with
// AI_CLASS_TYPE_ARCANE, AI_CLASS_TYPE_DIVINE, AI_CLASS_TYPE_CASTER, AI_CLASS_TYPE_WARRIOR.
int ai_CheckClassType(object oCreature, int nClassType);
// Returns TRUE if oCreature is of nRacialType;
// May also check for general racial types with
// AI_RACIAL_TYPE_ANIMAL_BEAST
int ai_CheckRacialType(object oCreature, int nRacialType);
// Saves oCreatures Normal appearance if they are not polymorphed and it has
// not already been saved.
void ai_SetNormalAppearance(object oCreature);
// Returns the normal appearance of oCreature.
int ai_GetNormalAppearance(object oCreature);
// Return the number and levels of all creatures within fMaxRange.
// They are grouped into Fighters, Clerics, Mages, and Monsters.
struct stClasses ai_GetFactionsClasses(object oCreature, int bEnemy = TRUE, float fMaxRange = AI_RANGE_BATTLEFIELD);
// This will return the class with the most levels.
// Returns a string of "FIGHTER", "CLERIC", "MAGE", or "MONSTER".
// Execute with GetFactionsClasses.
string ai_GetMostDangerousClass(struct stClasses stCount);
// Equips the best weapon, ranged or melee.
// Returns TRUE if equiped, FALSE if not.
// oTarget is the creature the caller is targeting.
void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID);
// Equips a melee weapon AND checks for shield, two weapons, two handed, etc.
// Returns TRUE if equiped, FALSE if not.
// oTarget is the creature the caller is targeting.
int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID);
// Equips a ranged weapon AND checks for ammo.
// Returns TRUE if equiped, FALSE if not.
// oTarget is the creature the caller is targeting.
int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID);
// Equips the best weapon for a monk character.
// Returns TRUE if equiped, FALSE if not.
// oTarget is the creature the caller is targeting.
int ai_EquipBestMonkMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID);
// Returns TRUE if oCreature is in a Dangerous Area of Effect in fMaxRange.
// bMove will attempt to move oCreature out of the Dangerous AOE if needed.
int ai_IsInADangerousAOE(object oCreature, float fMaxRange = AI_RANGE_BATTLEFIELD, int bMove = FALSE);
// Returns 1 if oHidden has an Invisiblity effect, Can't be spotted but can be heard.
// Returns 2 if oHidden has a Darkness effect. Can't be spotted but can be heard.
// Returns 3 if oHidden has a Sanctuary effect, Can't be spotted or heard.
// Returns 4 if oHidden is in stealth mode, Can be spotted and heard.
int ai_GetIsHidden(object oHidden);
// Returns TRUE if if oCaster has a good chance of effecting oCreature with nSpell.
int ai_CastOffensiveSpellVsTarget(object oCaster, object oCreature, int nSpell);
// Gets the base DC for a dragon.
int ai_GetDragonDC(object oCreature);
// Set oCreature's ai scripts based on its first class or the variable "AI_DEFAULT_SCRIPT".
void ai_SetCreatureAIScript(object oCreature);
// Returns TRUE if oTarget is immune to sneak attacks.
int ai_IsImmuneToSneakAttacks(object oCreature, object oTarget);
// Returns TRUE if iIndex target has a higher combat rating than oCreature.
int ai_IsStrongerThanMe(object oCreature, int nIndex);
// Returns TRUE if oTarget's CR is within nAdj of oCreature's level, otherwise FALSE.
int ai_StrongOpponent(object oCreature, object oTarget, int nAdj = 2);
// Returns TRUE if attacking oTarget with Power attack is a good option.
int ai_PowerAttackGood(object oCreature, object oTarget, float fAdj);
// Returns TRUE if oTarget's AC - oCreature Atk - nAtkAdj can hit within 25% to 75%.
int ai_AttackPenaltyOk(object oCreature, object oTarget, float fAtkAdj);
// Returns TRUE if oCreature AC - oTarget's Atk is less than 20.
int ai_ACAdjustmentGood(object oCreature, object oTarget, float fACAdj);
// Checks oCreatures melee weapon to see if they can kill oTarget in one hit.
int ai_WillKillInOneHit(object oCreature, object oTarget);
// Returns TRUE if oCreature has Mobility, SpringAttack, or a high Tumble.
int ai_CanIMoveInCombat(object oCreature);
// Returns TRUE if oCreature can safely fire a ranged weapon.
int ai_CanIUseRangedWeapon(object oCreature, int nInMelee);
// Returns TRUE if oCreature moves before the action. FALSE if they do not move.
// and -1 if the action is canceled.
// Checks current combat state to see if oCreature needs to move before using an action.
int ai_CheckCombatPosition(object oCreature, object oTarget, int nInMelee, int nAction, int nBaseItemType = 0);
//:: [PRC8] Calculates total Shield AC bonuses from all sources
int ai_GetTotalShieldACBonus(object oCreature);
//:: [PRC8] Helper fucntion for CanIGrapple()
int GetSpecialGrappleSizeMod(int nSize);
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Tripping
int ai_CanITrip(object oCreature, object oTarget);
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Grappling
int ai_CanIGrapple(object oCreature, object oTarget);
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Awesome Blow
int ai_CanIAwesomeBlow(object oCreature, object oTarget);
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Overrun
int ai_CanIOverrun(object oCreature, object oTarget);
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Bullrush
int ai_CanIBullrush(object oCreature, object oTarget);

//******************************************************************************
//************ GET TARGETS USING THE OBJECT SEARCH FUNCTIONS *******************
//******************************************************************************
object ai_GetNearestEnemy(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1, int bDisabled = FALSE)
{
    object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
                                        oCreature, nNth, nCType1, nCValue1, nCType2, nCValue2);
    if(bDisabled)
    {
        while(oTarget != OBJECT_INVALID && GetIsDead(oTarget))
        {
            oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
                                         oCreature, ++nNth, nCType1, nCValue1, nCType2, nCValue2);
        }
    }
    else
    {
        while(oTarget != OBJECT_INVALID && ai_Disabled(oTarget))
        {
            oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
                                         oCreature, ++nNth, nCType1, nCValue1, nCType2, nCValue2);
        }
    }
    return oTarget;
}
object ai_GetNearestAlly(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1)
{
    return GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
                              oCreature, ++nNth, nCType1, nCValue1, nCType2, nCValue2);
}
int ai_GetNumOfEnemiesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE)
{
    int nCnt;
    location lLocation = GetLocation(oCreature);
    object oEnemy = GetFirstObjectInShape(SHAPE_SPHERE, fDistance, lLocation);
    while(oEnemy != OBJECT_INVALID)
    {
        if(GetIsEnemy(oEnemy, oCreature) && !GetIsDead(oEnemy)) nCnt++;
        oEnemy = GetNextObjectInShape(SHAPE_SPHERE, fDistance, lLocation);
    }
    return nCnt;
}
int ai_GetNumOfAlliesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE)
{
    int nCnt;
    location lLocation = GetLocation(oCreature);
    object oAlly = GetFirstObjectInShape(SHAPE_SPHERE, fDistance, lLocation);
    while(oAlly != OBJECT_INVALID)
    {
        if(GetReputation(oCreature, oAlly) > 89 && oAlly != oCreature && !GetIsDead(oAlly))
        {
            nCnt++;
        }
        oAlly = GetNextObjectInShape(SHAPE_SPHERE, fDistance, lLocation);
    }
    return nCnt;
}
int ai_GetRacialTypeCount(object oCreature, int nRacial_Type, float fDistance = AI_RANGE_PERCEPTION)
{
    int nCnt = 1;
    int nCount = 0;
    object oEnemy = ai_GetNearestEnemy(oCreature, nCnt,
                                      CREATURE_TYPE_PERCEPTION,
                                      PERCEPTION_SEEN,
                                      CREATURE_TYPE_RACIAL_TYPE,
                                      nRacial_Type);
    while(oEnemy != OBJECT_INVALID && GetDistanceBetween(oEnemy, oCreature) <= fDistance)
    {
        if(!ai_GetHasEffectType(oEnemy, EFFECT_TYPE_TURNED)) nCount++;
        nCnt++;
        oEnemy = ai_GetNearestEnemy(oCreature, nCnt,
                                   CREATURE_TYPE_PERCEPTION,
                                   PERCEPTION_SEEN,
                                   CREATURE_TYPE_RACIAL_TYPE,
                                   nRacial_Type);
    }
    return nCount;
}
object ai_GetLowestCRAttackerOnMaster(object oCreature)
{
    object oTarget = OBJECT_INVALID, oMaster = GetMaster(oCreature);
    if(AI_DEBUG) ai_Debug("0i_combat", "419", "Checking for weakest attacker on " + GetName(oMaster));
    int nEnemyCombatRating, nWeakestCombatRating, nCntr = 1;
    float fNearest = AI_RANGE_MELEE + 1.0f;
    // Get the weakest opponent in melee with our master.
    object oEnemy = ai_GetNearestEnemy(oMaster, nCntr, 7, 7);
    float fDistance = GetDistanceBetween(oMaster, oEnemy);
    while (oEnemy != OBJECT_INVALID && fDistance <= AI_RANGE_MELEE)
    {
        nEnemyCombatRating = ai_GetMyCombatRating(oEnemy);
        if(AI_DEBUG) ai_Debug("0i_combat", "428", GetName(oEnemy) + " nECR: " + IntToString(nEnemyCombatRating));
        if (nEnemyCombatRating < nWeakestCombatRating ||
            nEnemyCombatRating == nWeakestCombatRating && fDistance < fNearest)
        {
            fNearest = fDistance;
            nWeakestCombatRating = nEnemyCombatRating;
            oTarget = oEnemy;
        }
        oEnemy = ai_GetNearestEnemy(oMaster, ++nCntr, 7, 7);
    }
    // No targets in melee with our master, lets see if there is a ranged attacker.
    if (oTarget == OBJECT_INVALID) oTarget = GetLastHostileActor(oMaster);
    return oTarget;
}

//******************************************************************************
//******************** SET/CLEAR COMBAT STATE FUNCTIONS ************************
//******************************************************************************

object ai_SetCombatState(object oCreature)
{
    if(AI_DEBUG) ai_Counter_Start();
    object oMaster = GetMaster();
    if(oMaster == OBJECT_INVALID) oMaster = oCreature;
    int nEnemyNum, nEnemyPower, nAllyNum, nAllyPower, nInMelee, nMagic;
    int nHealth, nNth, nAllies, nPower, nDisabled, bThreat,nObjects;
    int nEnemyHighestPower, nAllyHighestPower;
    float fNearest = AI_RANGE_BATTLEFIELD;
    float fDistance;
    float fMaxRange = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE);
    if(fMaxRange == 0.0) fMaxRange = AI_RANGE_PERCEPTION;
    string sCnt, sDebugText;
    location lLocation = GetLocation(oMaster);
    object oMelee, oNearestEnemy = OBJECT_INVALID;
    if(AI_DEBUG) ai_Debug("0i_combat", "491", "************************************************************");
    if(AI_DEBUG) ai_Debug("0i_combat", "492", "******************* CREATING COMBAT DATA *******************");
    if(AI_DEBUG) ai_Debug("0i_combat", "493", GetName(oCreature));
    // We want to include ourselves in the combat state.
    object oObject = GetFirstObjectInShape(SHAPE_SPHERE, AI_RANGE_BATTLEFIELD, lLocation);
    // Get all creatures within 40 meters(5 meters beyond our perception of 35).
    // Centered on either the creature or their master.
    while(oObject != OBJECT_INVALID)
    {
        // Process all enemies.
        if(GetIsEnemy(oObject, oCreature))
        {
            if(GetObjectSeen(oObject, oCreature) || GetObjectHeard(oObject, oCreature))
            {
                fDistance = GetDistanceBetween(oObject, oCreature);
                if(fDistance <= fMaxRange)
                {
                    // ********** Get the Total levels of the Enemy **********
                    nPower = ai_GetCharacterLevels(oObject);
                    if(nPower < 1) nPower = 1;
                    if(nEnemyHighestPower < nPower) nEnemyHighestPower = nPower;
                    nEnemyPower += nPower;
                    // ********** Check if the Enemy is disabled **********
                    bThreat = TRUE;
                    nDisabled = ai_Disabled(oObject);
                    if(nDisabled)
                    {
                        if(AI_DEBUG) sDebugText += "**** DISABLED(" + IntToString(nDisabled) + ") ****";
                        // Decide if they are still a threat: 1 - dead, 2 - Bleeding.
                        if(nDisabled == 1 || nDisabled == 2 ||
                           //nDisabled == EFFECT_TYPE_CONFUSED ||
                           //nDisabled == EFFECT_TYPE_FRIGHTENED ||
                           //nDisabled == EFFECT_TYPE_PARALYZE ||
                           nDisabled == EFFECT_TYPE_CHARMED ||
                           nDisabled == EFFECT_TYPE_PETRIFY)
                        {
                            bThreat = FALSE;
                            if(AI_DEBUG) ai_Debug("0i_combat", "527", "Enemy: " + GetName(oObject) + sDebugText);
                        }
                    }
                    // If they are using the coward ai then treat them as frightened.
                    // we place it here as an else so we don't overwrite another disabled effect.
                    else if(GetLocalString(oObject, AI_COMBAT_SCRIPT) == "ai_coward")
                    {
                        nDisabled = EFFECT_TYPE_FRIGHTENED;
                        // !!!! For /DEBUG CODE !!!!
                        if(AI_DEBUG) sDebugText += "**** DISABLED(" + IntToString(nDisabled) + ") ****";
                    }
                    if(bThreat)
                    {
                        sCnt = IntToString(++nEnemyNum);
                        // ********** Set if the Enemy is disabled **********
                        SetLocalInt(oCreature, AI_ENEMY_DISABLED + sCnt, nDisabled);
                        // ********** Set the Enemy Object **********
                        SetLocalObject(oCreature, AI_ENEMY + sCnt, oObject);
                        // ********** Set the Enemy Combat Rating **********
                        SetLocalInt(oCreature, AI_ENEMY_COMBAT + sCnt, ai_GetMyCombatRating(oObject));
                        // ********** Set the Enemy Health Percentage **********
                        nHealth = ai_GetPercHPLoss(oObject);
                        SetLocalInt(oCreature, AI_ENEMY_HEALTH + sCnt, nHealth);
                        // ********** Set the number of enemies near the enemy **********
                        nInMelee = 0;
                        nNth = 1;
                        oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, nNth);
                        while(oMelee != OBJECT_INVALID && !GetIsDead(oMelee) &&
                              GetDistanceBetween(oMelee, oObject) < AI_RANGE_MELEE)
                        {
                            // We add an enemy to the group.
                            if(GetIsEnemy(oMelee, oCreature)) nInMelee++;
                            oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, ++nNth);
                        }
                        SetLocalInt(oCreature, AI_ENEMY_MELEE + sCnt, nInMelee);
                        // ********** Set the Enemies distance **********
                        fDistance = GetDistanceBetween(oObject, oCreature);
                        SetLocalFloat(oCreature, AI_ENEMY_RANGE + sCnt, fDistance);
                        // ********** Set if the Enemy is perceived **********
                        if(GetObjectSeen(oObject, oCreature) ||
                          (GetObjectHeard(oObject, oCreature) && fDistance <= AI_RANGE_MELEE &&
                          ai_GetIsHidden(oObject)))
                        {
                            SetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt, TRUE);
                            if(AI_DEBUG) sDebugText += "**** PERCEIVED Seen: " +
                                         IntToString(GetObjectSeen(oObject, oCreature)) +
                                         " Heard: " + IntToString(GetObjectHeard(oObject, oCreature)) + " ****";
                        }
                        else SetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt, FALSE);
                        // ********** Set the Nearest Enemy seen **********
                        if(fDistance < fNearest)
                        {
                            fNearest = fDistance;
                            oNearestEnemy = oObject;
                        }
                    }
                }
                // !!! Debug code !!!
                if(AI_DEBUG && fDistance < AI_RANGE_MELEE) sDebugText += "**** MELEE ****";
                if(AI_DEBUG) ai_Debug("0i_combat", "587", "Enemy(" + IntToString(nEnemyNum) + "): " +
                         GetName(oObject) + sDebugText);
                if(AI_DEBUG) ai_Debug("0i_combat", "589", "nHealth: " + IntToString(nHealth) +
                         " nInMelee: " + IntToString(nInMelee) +
                         " fDistance: " + FloatToString(fDistance, 0, 2) +
                         " nNum: " + IntToString(nEnemyNum) +
                         " nPower: " + IntToString(nEnemyPower / 2));
            }
            else
            {
                // ********** Also add the levels of Unknown Enemies ***********
                nPower = FloatToInt(ai_GetCharacterLevels(oObject) / 1.5);
                if(nPower < 1) nPower = 1;
                nEnemyPower += nPower;
                if(AI_DEBUG) ai_Debug("0i_combat", "601", "Enemy(NOT PERCEIVED): " +
                         GetName(oObject) + " fDistance: " +
                         FloatToString(GetDistanceBetween(oObject, oCreature), 0, 2) +
                         " nPower: " + IntToString(nEnemyPower));
            }
        }
        // Process all Allies.
        else if(GetFactionEqual(oObject, oCreature))
        {
            // ********** Set if the Ally is disabled **********
            nDisabled = ai_Disabled(oObject);
            if(nDisabled)
            {
                sDebugText += "**** DISABLED(" + IntToString(nDisabled) + ") ****";
                SetLocalInt(oCreature, AI_ALLY_DISABLED + sCnt, nDisabled);
            }
            if(nDisabled != 1)
            {
                sCnt = IntToString(++nAllyNum);
                // ********** Set the Ally Object **********
                SetLocalObject(oCreature, AI_ALLY + sCnt, oObject);
                // ********** Set the Ally Combat Rating **********
                SetLocalInt(oCreature, AI_ALLY_COMBAT + sCnt, ai_GetMyCombatRating(oObject));
                // ********** Set the Ally Health Percentage **********
                nHealth = ai_GetPercHPLoss(oObject);
                SetLocalInt(oCreature, AI_ALLY_HEALTH + sCnt, nHealth);
                // ********** Set the number of enemies near the ally **********
                nInMelee = 0;
                nNth = 1;
                oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, nNth);
                while(oMelee != OBJECT_INVALID && !GetIsDead(oMelee) &&
                      GetDistanceBetween(oMelee, oObject) < AI_RANGE_MELEE)
                {
                    if(GetIsEnemy(oMelee, oCreature)) nInMelee++;
                    //else nInMelee--;
                    oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, ++nNth);
                }
                SetLocalInt(oCreature, AI_ALLY_MELEE + sCnt, nInMelee);
                // ********** Set the Allies distance **********
                SetLocalFloat(oCreature, AI_ALLY_RANGE + sCnt, GetDistanceBetween(oObject, oCreature));
                // ********** All allies are considered to be seen **********
                SetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCnt, TRUE);
                // ********** Get the Total levels of the Allies **********
                nPower = ai_GetCharacterLevels(oObject);
                if(nAllyHighestPower < nPower) nAllyHighestPower = nPower;
                nAllyPower +=(nPower * nHealth) / 100;
                if(AI_DEBUG) ai_Debug("0i_combat", "647", "Ally(" + IntToString(nAllyNum) + "): " +
                       GetName(oObject) + sDebugText);
                if(AI_DEBUG) ai_Debug("0i_combat", "649", "nHealth: " + IntToString(nHealth) +
                       " nInMelee: " + IntToString(nInMelee) +
                       " fDistance: " + FloatToString(GetDistanceToObject(oObject), 0, 2) +
                       " nNum: " + IntToString(nAllyNum) +
                       " nPower: " + IntToString(nAllyPower / 2));
            }
        }
        if(AI_DEBUG) sDebugText = "";
        oObject = GetNextObjectInShape(SHAPE_SPHERE, AI_RANGE_BATTLEFIELD, lLocation);
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "659", "Nearest Enemy: " + GetName(oNearestEnemy));
    if(AI_DEBUG) ai_Debug("0i_combat", "660", "****************** FINISHED COMBAT DATA  *******************");
    if(AI_DEBUG) ai_Debug("0i_combat", "661", "************************************************************");
    // Lets save processing by only clearing previous enemy data we don't overwrite.
    int nPreviousEnd = GetLocalInt(oCreature, AI_ENEMY_NUMBERS);
    int nCnt = nEnemyNum + 1;
    if(AI_DEBUG) ai_Debug("0i_combat", "665", "Clearing Enemy Combat Data: nPreviousEnd: " +
             IntToString(nPreviousEnd) + " nCurrentEnd: " + IntToString(nCnt - 1));
    while(nPreviousEnd >= nCnt)
    {
        sCnt = IntToString(nCnt);
        if(AI_DEBUG) ai_Debug("0i_combat", "670", "Clearing Enemy Combat Data: " + sCnt + " " +
                 GetName(GetLocalObject(oCreature, AI_ENEMY + sCnt)));
        DeleteLocalObject(oCreature, AI_ENEMY + sCnt);
        DeleteLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt);
        DeleteLocalFloat(oCreature, AI_ENEMY_RANGE + sCnt);
        DeleteLocalInt(oCreature, AI_ENEMY_COMBAT + sCnt);
        DeleteLocalInt(oCreature, AI_ENEMY_MELEE + sCnt);
        DeleteLocalInt(oCreature, AI_ENEMY_HEALTH + sCnt);
        nCnt ++;
    }
    // Lets save processing by only clearing previous ally data we don't overwrite.
    nPreviousEnd = GetLocalInt(oCreature, AI_ALLY_NUMBERS);
    nCnt = nAllyNum + 1;
    if(AI_DEBUG) ai_Debug("0i_combat", "683", "Clearing Ally Combat Data: nPreviousEnd: " +
             IntToString(nPreviousEnd) + " nCurrentEnd: " + IntToString(nCnt - 1));
    while(nPreviousEnd >= nCnt)
    {
        sCnt = IntToString(nCnt);
        if(AI_DEBUG) ai_Debug("0i_combat", "688", "Clearing Ally Combat Data: " + sCnt + " " +
                 GetName(GetLocalObject(oCreature, AI_ENEMY + sCnt)));
        DeleteLocalObject(oCreature, AI_ALLY + sCnt);
        DeleteLocalInt(oCreature, AI_ALLY_PERCEIVED + sCnt);
        DeleteLocalFloat(oCreature, AI_ALLY_RANGE + sCnt);
        DeleteLocalInt(oCreature, AI_ALLY_COMBAT + sCnt);
        DeleteLocalInt(oCreature, AI_ALLY_MELEE + sCnt);
        DeleteLocalInt(oCreature, AI_ALLY_HEALTH + sCnt);
        nCnt ++;
    }
    // Finally set all group states.
    SetLocalInt(oCreature, AI_ENEMY_NUMBERS, nEnemyNum);
    // Total enemy power is half the levels of all enemies + the total levels
    // of the highest level enemy.
    nEnemyPower = (nEnemyPower / 2) + nEnemyHighestPower;
    SetLocalInt(oCreature, AI_ENEMY_POWER, nEnemyPower);
    SetLocalObject(oCreature, AI_ENEMY_NEAREST, oNearestEnemy);
    SetLocalInt(oCreature, AI_ALLY_NUMBERS, nAllyNum);
    // Total ally power is half the levels of all allies + the total levels
    // of the highest level ally, only used by associates.
    nAllyPower = (nAllyPower / 2) + nAllyHighestPower;
    SetLocalInt(oCreature, AI_ALLY_POWER, nAllyPower);
    if(AI_DEBUG) ai_Debug("0i_combat", "710", "nEnemyPower: " + IntToString(nEnemyPower) +
             " nEnemyHighestPower: " + IntToString(nEnemyHighestPower) +
             " nAllyPower: " + IntToString(nAllyPower) +
             " nAllyHighestPower: " + IntToString(nAllyHighestPower));
    if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + " has finished the Combat State");
    return oNearestEnemy;
}
void ai_ClearCombatState(object oCreature)
{
    int bEnemyDone, bAllyDone, nCnt = 1;
    int nEnemyNum = GetLocalInt(oCreature, AI_ENEMY_NUMBERS);
    int nAllyNum = GetLocalInt(oCreature, AI_ALLY_NUMBERS);
    if(AI_DEBUG) ai_Debug("0i_combat", "722", "Clearing " + GetName(oCreature) + "'s combat state." +
             " nEnemyNum: " + IntToString(nEnemyNum) + " nAllyNum: " + IntToString(nAllyNum));
    string sCnt;
    while(!bEnemyDone || !bAllyDone)
    {
        sCnt = IntToString(nCnt);
        if(nCnt <= nEnemyNum)
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "730", "Clearing " + GetName(GetLocalObject(oCreature, AI_ENEMY + sCnt)) + ".");
            DeleteLocalObject(oCreature, AI_ENEMY + sCnt);
            DeleteLocalInt(oCreature, AI_ENEMY_DISABLED + sCnt);
            DeleteLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt);
            DeleteLocalFloat(oCreature, AI_ENEMY_RANGE + sCnt);
            DeleteLocalInt(oCreature, AI_ENEMY_COMBAT + sCnt);
            DeleteLocalInt(oCreature, AI_ENEMY_MELEE + sCnt);
            DeleteLocalInt(oCreature, AI_ENEMY_HEALTH + sCnt);
        }
        else bEnemyDone = TRUE;
        if(nCnt <= nAllyNum)
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "742", "Clearing " + GetName(GetLocalObject(oCreature, AI_ALLY + sCnt)) + ".");
            DeleteLocalObject(oCreature, AI_ALLY + sCnt);
            DeleteLocalInt(oCreature, AI_ALLY_DISABLED + sCnt);
            DeleteLocalInt(oCreature, AI_ALLY_PERCEIVED + sCnt);
            DeleteLocalFloat(oCreature, AI_ALLY_RANGE + sCnt);
            DeleteLocalInt(oCreature, AI_ALLY_COMBAT + sCnt);
            DeleteLocalInt(oCreature, AI_ALLY_MELEE + sCnt);
            DeleteLocalInt(oCreature, AI_ALLY_HEALTH + sCnt);
        }
        else bAllyDone = TRUE;
        nCnt++;
    }
    DeleteLocalObject(oCreature, AI_ENEMY_NEAREST);
    DeleteLocalInt(oCreature, AI_ENEMY_NUMBERS);
    DeleteLocalInt(oCreature, AI_ENEMY_POWER);
    DeleteLocalInt(oCreature, AI_ALLY_NUMBERS);
    DeleteLocalObject(oCreature, AI_ALLY_POWER);
    // Also clear these combat variables at the end of combat.
    DeleteLocalObject(oCreature, AI_ATTACKED_PHYSICAL);
    DeleteLocalObject(oCreature, AI_ATTACKED_SPELL);
    // Remove Talent variables.
    DeleteLocalJson(oCreature, AI_TALENT_CURE);
    DeleteLocalJson(oCreature, AI_TALENT_HEALING);
    DeleteLocalJson(oCreature, AI_TALENT_ENHANCEMENT);
    DeleteLocalJson(oCreature, AI_TALENT_PROTECTION);
    DeleteLocalJson(oCreature, AI_TALENT_SUMMON);
    DeleteLocalJson(oCreature, AI_TALENT_DISCRIMINANT_AOE);
    DeleteLocalJson(oCreature, AI_TALENT_INDISCRIMINANT_AOE);
    DeleteLocalJson(oCreature, AI_TALENT_RANGED);
    DeleteLocalJson(oCreature, AI_TALENT_TOUCH);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_HEALING);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_ENHANCEMENT);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_PROTECTION);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_SUMMON);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_DISCRIMINANT_AOE);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_INDISCRIMINANT_AOE);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_RANGED);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_TOUCH);
    DeleteLocalInt(oCreature, AI_AM_I_SEARCHING);
    DeleteLocalInt(oCreature, AI_TRIED_TO_HIDE);
    DeleteLocalObject(oCreature, AI_IS_INVISIBLE);
    DeleteLocalInt(oCreature, sLastActionVarname);
    DeleteLocalInt(oCreature, AI_TALENTS_SET);
    DeleteLocalInt(oCreature, AI_ROUND);
    DeleteLocalInt(oCreature, sIPHasHasteVarname);
    DeleteLocalInt(oCreature, sIPImmuneVarname);
    DeleteLocalInt(oCreature, sIPResistVarname);
    DeleteLocalInt(oCreature, sIPReducedVarname);
    DeleteLocalJson(oCreature, AI_TALENT_IMMUNITY);
    ai_EndCombatRound(oCreature);
}
void ai_ClearTalents(object oCreature)
{
    // Remove Talent variables.
    DeleteLocalJson(oCreature, AI_TALENT_CURE);
    DeleteLocalJson(oCreature, AI_TALENT_HEALING);
    DeleteLocalJson(oCreature, AI_TALENT_ENHANCEMENT);
    DeleteLocalJson(oCreature, AI_TALENT_PROTECTION);
    DeleteLocalJson(oCreature, AI_TALENT_SUMMON);
    DeleteLocalJson(oCreature, AI_TALENT_DISCRIMINANT_AOE);
    DeleteLocalJson(oCreature, AI_TALENT_INDISCRIMINANT_AOE);
    DeleteLocalJson(oCreature, AI_TALENT_RANGED);
    DeleteLocalJson(oCreature, AI_TALENT_TOUCH);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_HEALING);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_ENHANCEMENT);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_PROTECTION);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_SUMMON);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_DISCRIMINANT_AOE);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_INDISCRIMINANT_AOE);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_RANGED);
    DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_TOUCH);
}
//******************************************************************************
//*********************** GET TARGETS INTERNAL FUNCTIONS ***********************
//******************************************************************************
// These functions are used by the Get Index/ Get Target functions below.

int ai_TargetIsInRangeofCreature(object oCreature, string sTargetType, string sCounter, float fMaxRange)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "952", "fMaxRange: " + FloatToString(fMaxRange, 0, 2) +
                          " fTargetRange: " + FloatToString(GetLocalFloat(oCreature, sTargetType + "_RANGE" + sCounter), 0, 2));
    return fMaxRange >= GetLocalFloat(oCreature, sTargetType + "_RANGE" + sCounter);
}
int ai_TargetIsInRangeofMaster(object oCreature, object oTarget)
{
    object oMaster = GetMaster();
    if(oMaster == OBJECT_INVALID) return TRUE;
    float fMaxRange = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE);
    if(fMaxRange == 0.0) fMaxRange = 20.0;
    float fTargetRangefromMaster = GetDistanceBetween(oTarget, oMaster);
    if(AI_DEBUG) ai_Debug("0i_combat", "963", "fMaxRangefromMaster: " + FloatToString(fMaxRange, 0, 2) +
                          " fTargetRangefromMaster: " + FloatToString(fTargetRangefromMaster, 0, 2));
    return fMaxRange >= fTargetRangefromMaster;
}
struct stTarget ai_CheckForNearestTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "969", "Getting nearest index: " + sIndex +
                          " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) +
                          " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) +
                          " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2));
    // Lets put any disabled targets and associates if set in a secondary group.
    if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) ||
      (ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(sTarget.oTarget)))
    {
        if(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestSecondaryRange)
        {
            sTarget.fNearestSecondaryRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
            sTarget.nSecondaryIndex = nIndex;
        }
    }
    else if(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange)
    {
        sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
        sTarget.nIndex = nIndex;
    }
    return sTarget;
}
struct stTarget ai_CheckForLowestValueTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "992", "Getting lowest value index: " + sIndex +
                          " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) +
                          " fNearestRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) +
                          " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) +
                          " sTarget.nValue: " + IntToString(sTarget.nValue) +
                          " sTarget.nBestValue: " + IntToString(sTarget.nBestValue) +
                          " sTarget.nBestSecondaryValue: " + IntToString(sTarget.nBestSecondaryValue));
    // Lets put any disabled targets and associates if set in a secondary group.
    if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) ||
      (ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(sTarget.oTarget)))
    {
        if(sTarget.nValue < sTarget.nBestSecondaryValue ||
          (sTarget.nValue == sTarget.nBestSecondaryValue &&
           GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestSecondaryRange))
        {
            sTarget.fNearestSecondaryRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
            sTarget.nBestSecondaryValue = sTarget.nValue;
            sTarget.nSecondaryIndex = nIndex;
        }
    }
    // Has less value or equal value and is closer.
    else if(sTarget.nValue < sTarget.nBestValue ||
           (sTarget.nBestValue == sTarget.nValue &&
           GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange))
    {
        sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
        sTarget.nBestValue = sTarget.nValue;
        sTarget.nIndex = nIndex;
    }
    return sTarget;
}
struct stTarget ai_CheckForHighestValueTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1025", "Getting highest value index: " + sIndex +
                          " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) +
                          " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) +
                          " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) +
                          " sTarget.nValue: " + IntToString(sTarget.nValue) +
                          " sTarget.nBestValue: " + IntToString(sTarget.nBestValue) +
                          " sTarget.nBestSecondaryValue: " + IntToString(sTarget.nBestSecondaryValue));
    // Lets put any disabled targets and associates if set in a secondary group.
    if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) ||
      (ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(sTarget.oTarget)))
    {
        if(sTarget.nValue > sTarget.nBestSecondaryValue ||
          (sTarget.nValue == sTarget.nBestSecondaryValue &&
          GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestSecondaryRange))
        {
            sTarget.fNearestSecondaryRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
            sTarget.nBestSecondaryValue = sTarget.nValue;
            sTarget.nSecondaryIndex = nIndex;
        }
    }
    // Has less value or equal value and is closer.
    else if(sTarget.nValue > sTarget.nBestValue ||
           (sTarget.nBestValue == sTarget.nValue &&
            GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange))
    {
        sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
        sTarget.nBestValue = sTarget.nValue;
        sTarget.nIndex = nIndex;
    }
    return sTarget;
}
struct stTarget ai_CheckForNearestAllTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1153", "Getting nearest (not disabled) index: " + sIndex +
                          " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) +
                          " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2));
    // If we are ignoring associates set then ignore them.
    // Has lower value or equal value and is closer. Familiars/Companions/Summons/Dominated.
    if(AI_DEBUG) ai_Debug("0i_combat", "1158", "Don't Ignore Associate: " + IntToString(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES)) +
                   " Not an Associate? " + IntToString(GetAssociateType(sTarget.oTarget) < 2));
    if((!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) || GetAssociateType(sTarget.oTarget) < 2) &&
       GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange)
    {
       sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
       sTarget.nIndex = nIndex;
    }
    return sTarget;
}
struct stTarget ai_CheckForLowestValueAllTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1170", "Getting lowest value index: " + sIndex +
                          " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) +
                          " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) +
                          " sTarget.nValue: " + IntToString(sTarget.nValue) +
                          " sTarget.nBestValue: " + IntToString(sTarget.nBestValue));
    // Has less value or equal value and is closer. Ignoring only Familiars/Companions/Summons/Dominated.
    if(AI_DEBUG) ai_Debug("0i_combat", "1176", "Don't Ignore Associate: " + IntToString(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES)) +
                   " Not an Associate? " + IntToString(GetAssociateType(sTarget.oTarget) < 2));
    if((!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) || GetAssociateType(sTarget.oTarget) < 2) &&
      sTarget.nValue < sTarget.nBestValue ||
      (sTarget.nBestValue == sTarget.nValue &&
      GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange))
    {
        sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex);
        sTarget.nBestValue = sTarget.nValue;
        sTarget.nIndex = nIndex;
    }
    return sTarget;
}

//******************************************************************************
//************ GET INDEX/TARGETs USING COMBAT STATE FUNCTIONS ******************
//******************************************************************************
// These functions will find a target based on the combat state variables created
// by the function ai_SetCombatState for associates.

int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY))
    {
        return ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType);
    }
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1211", "Getting the nearest index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1229", "Found nearest [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetNearestTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1234", "Getting the nearest target.");
    string sIndex = IntToString(ai_GetNearestIndex(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1251", "Getting the lowest CR index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1270", "Found lowest CR [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetLowestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1275", "Getting the lowest CR target.");
    string sIndex = IntToString(ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1292", "Getting the highest CR index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1311", "Found highest CR [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetHighestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1316", "Getting the highest CR target.");
    string sIndex = IntToString(ai_GetHighestCRIndex(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetNearestIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY))
    {
        ai_GetLowestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType);
    }
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1335", "Getting the nearest not in AOE index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1353", "Found nearest not in AOE Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetNearestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1358", "Getting the nearest not in AOE target.");
    string sIndex = IntToString(ai_GetNearestIndexNotInAOE(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetLowestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1375", "Getting the lowest CR not in AOE index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1394", "Found lowest CR not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetLowestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1399", "Getting the lowest cr not in AOE target.");
    string sIndex = IntToString(ai_GetLowestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetHighestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1416", "Getting the highest CR not in AOE index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1435", "Found highest CR not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetHighestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1440", "Getting the highest cr not in AOE target.");
    string sIndex = IntToString(ai_GetHighestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetNearestPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY))
    {
        return ai_GetLowestCRPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk);
    }
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1459", "Getting the nearest physical index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1466", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                  " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                GetIsWeaponEffective(sTarget.oTarget) &&
                ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
                ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1479", "Found nearest [" + sTargetType + "] Physical Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetNearestPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1484", "Getting the nearest physcial target.");
    string sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetLowestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1501", "Getting the lowest CR physical index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1508", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                  " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                GetIsWeaponEffective(sTarget.oTarget) &&
                ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
                ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1522", "Found lowest CR [" + sTargetType + "] Physical Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetLowestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1527", "Getting the lowest CR physical target.");
    string sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetHighestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1544", "Getting the highest CR physical index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1551", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                  " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                GetIsWeaponEffective(sTarget.oTarget) &&
                ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
                ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1565", "Found highest CR [" + sTargetType + "] Physical Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetHighestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1570", "Getting the highest CR physical target.");
    string sIndex = IntToString(ai_GetHighestCRPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetNearestPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY))
    {
        ai_GetLowestCRPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk);
    }
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1589", "Getting the nearest physical index not in AOE: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1596", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                  " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                GetIsWeaponEffective(sTarget.oTarget) &&
                ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
                ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1609", "Found nearest Physcial Index not in AOE: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetNearestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1614", "Getting the nearest physical target not in AOE.");
    string sIndex = IntToString(ai_GetNearestPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetLowestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1631", "Getting the lowest CR physical index not in AOE: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1551", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                  " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                GetIsWeaponEffective(sTarget.oTarget) &&
                ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
                ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1652", "Found lowest CR Physical Index not in AOE [" + sTargetType + "]: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetLowestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1657", "Getting the lowest cr physcial target not in AOE.");
    string sIndex = IntToString(ai_GetLowestCRPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetHighestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1674", "Getting the highest CR physical index not in AOE: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1681", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                  " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                GetIsWeaponEffective(sTarget.oTarget) &&
                ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
                ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1695", "Found highest CR Physical Index not in AOE [" + sTargetType + "]: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetHighestCRPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1700", "Getting the highest cr physical target not in AOE.");
    string sIndex = IntToString(ai_GetHighestCRPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetLowestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1717", "Getting the lowest group index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_MELEE" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1736", "Found lowest Group Index [" + sTargetType + "]: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
int ai_GetHighestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1752", "Getting the highest group index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_MELEE" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1771", "Found highest Group Index [" + sTargetType + "]: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetGroupTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1776", "Getting the highest group target.");
    string sIndex = IntToString(ai_GetHighestGroupIndex(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetHighestGroupIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1793", "Getting the highest group index not in AOE: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_MELEE" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1812", "Found highest Group Index not in AOE [" + sTargetType + "]: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetGroupTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1818", "Getting the highest group target not in AOE.");
    string sIndex = IntToString(ai_GetHighestGroupIndexNotInAOE(oCreature, fMaxRange, sTargetType));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetMostWoundedIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 200;
    sTarget.nBestSecondaryValue = 200;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1834", "Getting the most wounded index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "1841", "bAlwaysAtk: " + IntToString(bAlwaysAtk));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_HEALTH" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1855", "Found most wounded [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetMostWoundedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1860", "Getting the most wounded target.");
    string sIndex = IntToString(ai_GetMostWoundedIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk));
    return GetLocalObject(oCreature, sTargetType + sIndex);
}
int ai_GetAllyToHealIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.nBestValue = 200;
    sTarget.sTargetType = AI_ALLY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTarget.sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1875", "Getting the most wounded ally to heal index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, AI_ALLY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, AI_ALLY_HEALTH + sCounter);
                sTarget = ai_CheckForLowestValueAllTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ALLY + sCounter);
    }
    // If we do not have a normal target then we are done..
    if(AI_DEBUG) ai_Debug("0i_combat", "1893", "Found most wounded ally to heal Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetAllyToHealTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "1898", "Getting the most wounded ally to heal target.");
    string sIndex = IntToString(ai_GetAllyToHealIndex(oCreature, fMaxRange));
    return GetLocalObject(oCreature, AI_ALLY + sIndex);
}
object ai_GetLowestFortitudeSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 200;
    sTarget.nBestSecondaryValue = 200;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1915", "Getting the lowest fortitude save index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetFortitudeSavingThrow(sTarget.oTarget);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1934", "Found lowest fortitude save Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex));
}
object ai_GetLowestReflexSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 200;
    sTarget.nBestSecondaryValue = 200;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1950", "Getting the lowest reflex save index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetReflexSavingThrow(sTarget.oTarget);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "1969", "Found lowest reflex save Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex));
}
object ai_GetLowestWillSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 200;
    sTarget.nBestSecondaryValue = 200;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "1985", "Getting the lowest will save index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetWillSavingThrow(sTarget.oTarget);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2004", "Found lowest will save Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex));
}
object ai_GetSpellTargetBasedOnSaves(object oCreature, int nSpell, float fMaxRange = AI_RANGE_PERCEPTION)
{
    // Check the spells save type in "ai_spells.2da" and find the weakest
    // creature based on that save.
    string sSaveType = Get2DAString("ai_spells", "SaveType", nSpell);
    if(sSaveType == "Reflex") return ai_GetLowestReflexSaveTarget(oCreature, fMaxRange);
    if(sSaveType == "Fortitude") return ai_GetLowestFortitudeSaveTarget(oCreature, fMaxRange);
    if(sSaveType == "Will") return ai_GetLowestWillSaveTarget(oCreature, fMaxRange);
    // If there is no save then lets see if we can find an enemy with the lowest health.
    return ai_GetMostWoundedTarget(oCreature, fMaxRange);
}
int ai_GetNearestIndexThatSeesUs(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2029", "Getting the nearest creature that sees us index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                if(AI_DEBUG) ai_Debug("0i_combat", "2039", GetName(sTarget.oTarget) + " can see us? " +
                            IntToString(GetObjectSeen(oCreature, sTarget.oTarget)));
                if(GetObjectSeen(oCreature, sTarget.oTarget))
                {
                    sTarget = ai_CheckForNearestAllTarget(oCreature, sTarget, nCounter, sCounter);
                }
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(AI_DEBUG) ai_Debug("0i_combat", "2051", "Found nearest creature that sees us Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
int ai_GetBestSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    object oAttacking;
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2066", "Getting the best sneak attack index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget) &&
           !ai_IsImmuneToSneakAttacks(oCreature, sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2074", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                oAttacking = ai_GetAttackedTarget(sTarget.oTarget);
                if(AI_DEBUG) ai_Debug("0i_combat", "2082", "oTarget: " + GetName(sTarget.oTarget) +
                                      " is attacking " + GetName(oAttacking));
                // They are attacking someone besides us or we are hidden?
                if((oAttacking != OBJECT_INVALID && oAttacking != oCreature) ||
                   GetActionMode(oCreature, ACTION_MODE_STEALTH))
                {
                    sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
                }
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2097", "Found best sneak attack Index: " + IntToString(sTarget.nIndex));
    return sTarget.nIndex;
}
object ai_GetNearestClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY))
    {
        ai_GetLowestCRClassTarget(oCreature, nClassType, fMaxRange, sTargetType, bAlwaysAtk);
    }
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2115", "Getting the nearest class index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && ai_CheckClassType(sTarget.oTarget, nClassType))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2122", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2137", "Found nearest class Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex));
}
object ai_GetLowestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2153", "Getting the lowest CR class index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && ai_CheckClassType(sTarget.oTarget, nClassType))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2160", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2176", "Found lowest CR class [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex));
}
object ai_GetHighestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2192", "Getting the highest CR class index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && ai_CheckClassType(sTarget.oTarget, nClassType))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2199", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2215", "Found highest CR class [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex));
}
object ai_GetNearestRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY))
    {
        ai_GetLowestCRRacialTarget(oCreature, nRacialType, fMaxRange, sTargetType, bAlwaysAtk);
    }
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2233", "Getting the nearest race index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2240", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2255", "Found nearest race Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex));
}
object ai_GetLowestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = 100;
    sTarget.nBestSecondaryValue = 100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2271", "Getting the lowest CR race index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2278", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2294", "Found lowest CR race [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex));
}
object ai_GetHighestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = sTargetType;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2310", "Getting the highest CR race index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) &&
           !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2317", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter);
                sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter);
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2333", "Found highest CR race [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex));
}
object ai_GetNearestFavoredEnemyTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.nBestValue = -100;
    sTarget.nBestSecondaryValue = -100;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    int nRace, nRacialType;
    while(nRace < 24)
    {
        // Find which favored enemies we have.
        if(nRace < 1 && GetHasFeat(FEAT_FAVORED_ENEMY_ABERRATION, oCreature))
        {
            nRace = 1;
            nRacialType = RACIAL_TYPE_ABERRATION;
        }
        else if(nRace < 2 && GetHasFeat(FEAT_FAVORED_ENEMY_ANIMAL, oCreature))
        {
            nRace = 2;
            nRacialType = RACIAL_TYPE_ANIMAL;
        }
        else if(nRace < 3 && GetHasFeat(FEAT_FAVORED_ENEMY_BEAST, oCreature))
        {
            nRace = 3;
            nRacialType = RACIAL_TYPE_BEAST;
        }
        else if(nRace < 4 && GetHasFeat(FEAT_FAVORED_ENEMY_CONSTRUCT, oCreature))
        {
            nRace = 4;
            nRacialType = RACIAL_TYPE_CONSTRUCT;
        }
        else if(nRace < 5 && GetHasFeat(FEAT_FAVORED_ENEMY_DRAGON, oCreature))
        {
            nRace = 5;
            nRacialType = RACIAL_TYPE_DRAGON;
        }
        else if(nRace < 6 && GetHasFeat(FEAT_FAVORED_ENEMY_DWARF, oCreature))
        {
            nRace = 6;
            nRacialType = RACIAL_TYPE_DWARF;
        }
        else if(nRace < 7 && GetHasFeat(FEAT_FAVORED_ENEMY_ELEMENTAL, oCreature))
        {
            nRace = 7;
            nRacialType = RACIAL_TYPE_ELEMENTAL;
        }
        else if(nRace < 8 && GetHasFeat(FEAT_FAVORED_ENEMY_ELF, oCreature))
        {
            nRace = 8;
            nRacialType = RACIAL_TYPE_ELF;
        }
        else if(nRace < 9 && GetHasFeat(FEAT_FAVORED_ENEMY_FEY, oCreature))
        {
            nRace = 9;
            nRacialType = RACIAL_TYPE_FEY;
        }
        else if(nRace < 10 && GetHasFeat(FEAT_FAVORED_ENEMY_GIANT, oCreature))
        {
            nRace = 10;
            nRacialType = RACIAL_TYPE_GIANT;
        }
        else if(nRace < 11 && GetHasFeat(FEAT_FAVORED_ENEMY_GNOME, oCreature))
        {
            nRace = 11;
            nRacialType = RACIAL_TYPE_GNOME;
        }
        else if(nRace < 12 && GetHasFeat(FEAT_FAVORED_ENEMY_GOBLINOID, oCreature))
        {
            nRace = 12;
            nRacialType = RACIAL_TYPE_HUMANOID_GOBLINOID;
        }
        else if(nRace < 13 && GetHasFeat(FEAT_FAVORED_ENEMY_HALFELF, oCreature))
        {
            nRace = 13;
            nRacialType = RACIAL_TYPE_HALFELF;
        }
        else if(nRace < 14 && GetHasFeat(FEAT_FAVORED_ENEMY_HALFLING, oCreature))
        {
            nRace = 14;
            nRacialType = RACIAL_TYPE_HALFLING;
        }
        else if(nRace < 15 && GetHasFeat(FEAT_FAVORED_ENEMY_HALFORC, oCreature))
        {
            nRace = 15;
            nRacialType = RACIAL_TYPE_HALFORC;
        }
        else if(nRace < 16 && GetHasFeat(FEAT_FAVORED_ENEMY_HUMAN, oCreature))
        {
            nRace = 16;
            nRacialType = RACIAL_TYPE_HUMAN;
        }
        else if(nRace < 17 && GetHasFeat(FEAT_FAVORED_ENEMY_MAGICAL_BEAST, oCreature))
        {
            nRace = 17;
            nRacialType = RACIAL_TYPE_MAGICAL_BEAST;
        }
        else if(nRace < 18 && GetHasFeat(FEAT_FAVORED_ENEMY_MONSTROUS, oCreature))
        {
            nRace = 18;
            nRacialType = RACIAL_TYPE_HUMANOID_MONSTROUS;
        }
        else if(nRace < 19 && GetHasFeat(FEAT_FAVORED_ENEMY_ORC, oCreature))
        {
            nRace = 19;
            nRacialType = RACIAL_TYPE_HUMANOID_ORC;
        }
        else if(nRace < 20 && GetHasFeat(FEAT_FAVORED_ENEMY_OUTSIDER, oCreature))
        {
            nRace = 20;
            nRacialType = RACIAL_TYPE_OUTSIDER;
        }
        else if(nRace < 21 && GetHasFeat(FEAT_FAVORED_ENEMY_REPTILIAN, oCreature))
        {
            nRace = 21;
            nRacialType = RACIAL_TYPE_HUMANOID_REPTILIAN;
        }
        else if(nRace < 22 && GetHasFeat(FEAT_FAVORED_ENEMY_SHAPECHANGER, oCreature))
        {
            nRace = 22;
            nRacialType = RACIAL_TYPE_SHAPECHANGER;
        }
        else if(nRace < 23 && GetHasFeat(FEAT_FAVORED_ENEMY_UNDEAD, oCreature))
        {
            nRace = 23;
            nRacialType = RACIAL_TYPE_UNDEAD;
        }
        else if(nRace < 24 && GetHasFeat(FEAT_FAVORED_ENEMY_VERMIN, oCreature))
        {
            nRace = 24;
            nRacialType = RACIAL_TYPE_VERMIN;
        }
        else nRace = 25;
        if(nRace < 25)
        {
            sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
            while(sTarget.oTarget != OBJECT_INVALID)
            {
                if(AI_DEBUG) ai_Debug("0i_combat", "2476", "Getting the nearest favored race index: " +
                                      sCounter + " " + GetName(sTarget.oTarget) +
                                      " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                                      " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
                if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
                   !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType))
                {
                    if(AI_DEBUG) ai_Debug("0i_combat", "2483", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                                 " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
                    if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
                       GetIsWeaponEffective(sTarget.oTarget) &&
                       ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
                       ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
                    {
                        sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
                    }
                }
                sCounter = IntToString(++nCounter);
                sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
            }
        }
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2500", "Found nearest favored race Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex));
}
object ai_GetFlankTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE)
{
    int nCnt = 1, nInMelee, nHighestMelee;
    string sCnt = "1";
    float fAllyRange;
    object oTarget, oAlly = GetLocalObject(oCreature, AI_ALLY + sCnt);
    while(oAlly != OBJECT_INVALID)
    {
        fAllyRange = GetLocalFloat(oCreature, AI_ALLY_RANGE + sCnt);
        if(AI_DEBUG) ai_Debug("0i_combat", "2512", "Getting Ally being Flanked Index: " + sCnt + " " +
                 GetName(oAlly) + " fAllyRange: " + FloatToString(fAllyRange, 0, 2) +
                 " fMaxRange: " + FloatToString(fMaxRange, 0, 2));
        if(fAllyRange <= fMaxRange)
        {
            nInMelee = GetLocalInt(oCreature, AI_ALLY_MELEE + sCnt);
            if(AI_DEBUG) ai_Debug("0i_combat", "2519", "nInMelee: " + IntToString(nInMelee));
            if(!GetIsDead(oAlly) && nInMelee > nHighestMelee)
            {
                if(AI_DEBUG) ai_Debug("0i_combat", "2521", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                             " Weapon Effective: " + IntToString(GetIsWeaponEffective(oTarget)));
                if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCnt)) &&
                   GetIsWeaponEffective(oTarget))
                {
                    oTarget = ai_GetEnemyAttackingMyAlly(oCreature, oAlly, fMaxRange);
                    if(oTarget != OBJECT_INVALID) nHighestMelee = nInMelee;
                }
            }
        }
        sCnt = IntToString(++nCnt);
        oAlly = GetLocalObject(oCreature, AI_ALLY + sCnt);
    }
    // If we do not have a good target then lets see if there are more targets.
    if(oTarget == OBJECT_INVALID)
    {
        // If we just checked within melee then lets check what we can see if
        // we can move around in combat.
        if(fMaxRange == AI_RANGE_MELEE && ai_CanIMoveInCombat(oCreature))
        {
            oTarget = ai_GetFlankTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk);
        }
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "2544", "oTarget " + GetName(oTarget) +
               " is attacking " + GetName(oAlly));
    return oTarget;
}
object ai_GetRangedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE)
{
    struct stTarget sTarget;
    sTarget.fNearestRange = fMaxRange + 1.0;
    sTarget.fNearestSecondaryRange = sTarget.fNearestRange;
    sTarget.sTargetType = AI_ENEMY;
    int nCounter = 1;
    string sCounter = "1";
    sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    while(sTarget.oTarget != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2559", "Getting the nearest ranged index: " +
                              sCounter + " " + GetName(sTarget.oTarget) +
                              " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) +
                              " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget)));
        if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) &&
           !GetIsDead(sTarget.oTarget))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "2566", "bAlwaysAtk: " + IntToString(bAlwaysAtk) +
                         " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget)));
            if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) &&
               GetIsWeaponEffective(sTarget.oTarget) &&
               ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) &&
               ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget))
            {
                if(ai_GetIsRangeWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, sTarget.oTarget)))
                {
                    sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter);
                }
            }
        }
        sCounter = IntToString(++nCounter);
        sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter);
    }
    // If we do not have a normal target then use our best secondary target.
    if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex;
    if(AI_DEBUG) ai_Debug("0i_combat", "2584", "Found nearest ranged Index: " + IntToString(sTarget.nIndex));
    return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex));
}
object ai_GetBestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE)
{
    object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET);
    if(oPCTarget != OBJECT_INVALID) return oPCTarget;
    string sIndex;
    // Are we in melee? If so try to get the weakest enemy in melee.
    if(nInMelee > 0)
    {
        if(ai_CanIMoveInCombat(oCreature))
        {
            sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_MELEE));
        }
        else sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_MELEE));
    }
    // If not then lets go find someone to attack!
    else
    {
        // If we are not in melee then we should get the nearest enemy.
        sIndex = IntToString(ai_GetNearestPhysicalIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk));
        // If we didn't get a target then get any target within range.
        if(sIndex == "0")
        {
            sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk));
        }
    }
    object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex);
    // We might not have a target this is fine as sometimes we don't want to attack!
    if(AI_DEBUG) ai_Debug("0i_combat", "2614", GetName(oTarget) + " is the best target for melee combat!");
    return oTarget;
}
object ai_GetNearestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE)
{
    object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET);
    if(oPCTarget != OBJECT_INVALID) return oPCTarget;
    string sIndex;
    // Are we in melee? If so try to get the nearest enemy in melee.
    if(nInMelee > 0) sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_MELEE));
    // If not then lets go find someone to attack!
    else
    {
        // Get the nearest enemy.
        sIndex = IntToString(ai_GetNearestPhysicalIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk));
        // If we didn't get a target then get any target within range.
        if(sIndex == "0")
        {
            sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk));
        }
    }
    object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex);
    // If we don't have a target lets try one last time and see if we can find one.
    if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk);
    // We might not have a target this is fine as sometimes we don't want to attack!
    if(AI_DEBUG) ai_Debug("0i_combat", "2637", GetName(oTarget) + " is the nearest target for melee combat!");
    return oTarget;
}
object ai_GetLowestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE)
{
    object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET);
    if(oPCTarget != OBJECT_INVALID) return oPCTarget;
    string sIndex;
    // Are we in melee? If so try to get the weakest enemy in melee.
    if(nInMelee > 0) sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_MELEE));
    // If not then lets go find someone to attack!
    else
    {
        // Get the weakest combat rated enemy.
        sIndex = IntToString(ai_GetLowestCRPhysicalIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk));
        // If we didn't get a target then get any target within range.
        if(sIndex == "0")
        {
            sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk));
        }
    }
    object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex);
    // If we don't have a target lets try one last time and see if we can find one.
    if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk);
    // We might not have a target this is fine as sometimes we don't want to attack!
    if(AI_DEBUG) ai_Debug("0i_combat", "2660", GetName(oTarget) + " is the weakest target for melee combat!");
    return oTarget;
}
object ai_GetHighestCRTargetForMeleeCombat(object oCreature, int nInMelee)
{
    object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET);
    if(oPCTarget != OBJECT_INVALID) return oPCTarget;
    string sIndex;
    // Are we in melee? If so try to get the weakest enemy in melee.
    if(nInMelee > 0) sIndex = IntToString(ai_GetHighestCRPhysicalIndex(oCreature, AI_RANGE_MELEE));
    // If not then lets go find someone to attack!
    else
    {
        // Get the weakest combat rated enemy.
        sIndex = IntToString(ai_GetHighestCRPhysicalIndexNotInAOE(oCreature));
        // If we didn't get a target then get any target within range.
        if(sIndex == "0") sIndex = IntToString(ai_GetHighestCRPhysicalIndex(oCreature));
    }
    object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex);
    // If we don't have a target lets try one last time and see if we can find one.
    if(oTarget == OBJECT_INVALID) oTarget = ai_GetHighestCRPhysicalTarget(oCreature);
    // We might not have a target this is fine as sometimes we don't want to attack!
    if(AI_DEBUG) ai_Debug("0i_combat", "2680", GetName(oTarget) + " is the strongest target for melee combat!");
    return oTarget;
}
object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE)
{
    int nCtr = 1;
    float fDistance;
    string sCtr = "1";
    object oAttacked;
    object oEnemy = GetLocalObject(oCreature, AI_ENEMY + "1");
    while(oEnemy != OBJECT_INVALID)
    {
        if(!ai_Disabled(oEnemy))
        {
            fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sCtr);
            if(AI_DEBUG) ai_Debug("0i_combat", "2695", "Getting Enemy Attacking Me: " + sCtr + " " +
                             GetName(oEnemy) + " fTargetRange: " + FloatToString(fDistance, 0, 2) +
                             " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " Attacking: " +
                             GetName(ai_GetAttackedTarget(oEnemy)));
            if(fDistance <= fMaxRange)
            {
                oAttacked = ai_GetAttackedTarget(oEnemy);
                // If an enemy isn't attacking someone we must assume we are next!
                if(oAttacked == oCreature || oAttacked == OBJECT_INVALID)
                {
                    if(AI_DEBUG) ai_Debug("0i_combat", "2705", "Enemy attacking me: " + GetName(oEnemy) + " has attacked: " + GetName(ai_GetAttackedTarget(oEnemy)));
                    return oEnemy;
                }
            }
        }
        sCtr = IntToString(++nCtr);
        oEnemy = GetLocalObject(oCreature, AI_ENEMY + sCtr);
    }
    return OBJECT_INVALID;
}
object ai_GetEnemyAttackingMyAlly(object oCreature, object oAlly, float fMaxRange = AI_RANGE_MELEE)
{
    int nCtr = 1, nIndex, nDIndex;
    int bIngnoreAssociates = ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES);
    float fEnemyRange, fNearestEnemyRange = fMaxRange + 1.0;
    float fNearestDEnemyRange = fMaxRange + 1.0;
    string sCtr = "1";
    object oAttacked;
    object oEnemy = GetLocalObject(oCreature, AI_ENEMY + "1");
    while(oEnemy != OBJECT_INVALID)
    {
        fEnemyRange = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sCtr);
        if(AI_DEBUG) ai_Debug("0i_combat", "2727", "Getting Enemy Attacking Ally:" +
                         GetName(oAlly) + ": " + sCtr + " InMelee:" +
                         GetName(oEnemy) + " fEnemyRange: " + FloatToString(fEnemyRange, 0, 2) +
                         " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " Attacking: " +
                         GetName(ai_GetAttackedTarget(oEnemy)));
        if(fEnemyRange <= fMaxRange)
        {
            oAttacked = ai_GetAttackedTarget(oEnemy);
            if(AI_DEBUG) ai_Debug("0i_combat", "2735", "Enemy attacking " +
                       GetName(oAlly) + ": " + GetName(oEnemy) +
                       " has attacked: " + GetName(ai_GetAttackedTarget(oEnemy)));
            // If an enemy isn't attacking someone we must assume we are next!
            if(oAttacked == oAlly)
            {
               // Lets put any disabled targets in its own group, if we
               // ignore associates lets put them here as well.
                if(GetLocalInt(oCreature, AI_ENEMY_DISABLED + sCtr) ||
                   (bIngnoreAssociates && GetAssociateType(oEnemy)))
                {
                    if(fEnemyRange < fNearestDEnemyRange)
                    {
                        fNearestDEnemyRange = fEnemyRange;
                        nDIndex = nCtr;
                    }
                }
                else if(fEnemyRange < fNearestEnemyRange)
                {
                    fNearestEnemyRange = fEnemyRange;
                    nIndex = nCtr;
                }
            }
        }
        sCtr = IntToString(++nCtr);
        oEnemy = GetLocalObject(oCreature, AI_ENEMY + sCtr);
    }
    // If we do not have a good target then lets see if there are more targets.
    if(nIndex == 0 && nDIndex != 0)
    {
        // If we just checked within melee then lets check what we can see.
        if (fMaxRange == AI_RANGE_MELEE) return ai_GetEnemyAttackingMyAlly(oCreature, oAlly, AI_RANGE_PERCEPTION);
        else nIndex = nDIndex;
    }
    return GetLocalObject(oCreature, AI_ENEMY + IntToString(nIndex));
}
int ai_GetNumOfEnemiesInRange(object oCreature, float fMaxRange = AI_RANGE_MELEE)
{
    int nNumOfEnemies, nCnt = 1;
    float fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + "1");
    while(fDistance != 0.0)
    {
        if(fDistance < fMaxRange) nNumOfEnemies ++;
        fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + IntToString(++nCnt));
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "2780", IntToString (nNumOfEnemies) + " enemies within " + FloatToString(fMaxRange, 0, 2) + " meters.");
    return nNumOfEnemies;
}
object ai_GetAllyBuffTarget(object oCreature, int nSpell, float fMaxRange = AI_RANGE_BATTLEFIELD)
{
    // Make sure we don't over extend our movement running across the
    // battlefield to cast a spell on someone does not look good.
    float fNearestEnemy = GetDistanceBetween(oCreature, GetLocalObject(oCreature, AI_ENEMY_NEAREST)) - 3.0f;
    // If we are in melee then extend to melee incase an ally is just past the enemy.
    if(fNearestEnemy <= AI_RANGE_MELEE) fNearestEnemy = AI_RANGE_MELEE;
    if(fMaxRange > fNearestEnemy) fMaxRange = fNearestEnemy;
    // Now lets get the best target based on the spell data in ai_spells.2da
    string sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell);
    if(AI_DEBUG) ai_Debug("0i_combat", "2793", "sBuffTarget: " + sBuffTarget + " fMaxRange: " + FloatToString(fMaxRange, 0, 2));
    if(sBuffTarget == "0") return oCreature;
    if(sBuffTarget == "1")
        return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_STRENGTH, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "2")
        return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_DEXTERITY, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "3")
        return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_CONSTITUTION, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "4")
        return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_INTELLIGENCE, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "5")
        return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_WISDOM, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "6")
        return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_CHARISMA, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "7")
        return ai_BuffLowestACTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "8")
        return ai_BuffLowestACWithOutACBonus(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "9")
        return ai_BuffHighestAttackTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "10")
        return ai_BuffMostWoundedTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "11")
        return ai_BuffLowestFortitudeSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "12")
        return ai_BuffLowestReflexSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "13")
        return ai_BuffLowestWillSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    else if(sBuffTarget == "14")
        return ai_BuffLowestSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY);
    return OBJECT_INVALID;
}

//******************************************************************************
//********************  OTHER COMBAT FUNCTIONS  ********************************
//******************************************************************************

int ai_GetCurrentRound(object oCreature)
{
    int nRound = GetLocalInt(oCreature, AI_ROUND) + 1;
    SetLocalInt(oCreature, AI_ROUND, nRound);
    if(AI_DEBUG) ai_Debug("0i_combat", "2834", "nRound: " + IntToString(nRound));
    return nRound;
}
int ai_GetDifficulty(object oCreature)
{
    int nAdjustment = GetLocalInt(oCreature, AI_DIFFICULTY_ADJUSTMENT);
    int nDifficulty = GetLocalInt(oCreature, AI_ENEMY_POWER) - GetLocalInt(oCreature, AI_ALLY_POWER) + 13 + nAdjustment;
    if(nDifficulty < 1) nDifficulty = 1;
    if(AI_DEBUG) ai_Debug("0i_combat", "2842", "(Difficulty: Enemy Power: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_POWER)) +
             " - Ally Power: " + IntToString(GetLocalInt(oCreature, AI_ALLY_POWER)) +
             ") + 13 + nAdj: " + IntToString(nAdjustment) +
             " = " + IntToString(nDifficulty) + "(Min of 1)");
    return nDifficulty;
}
int ai_GetMyCombatRating(object oCreature)
{
    object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature);
    int nAtkBonus = GetBaseAttackBonus(oCreature);
    if(GetHasFeat(FEAT_WEAPON_FINESSE, oCreature) && ai_GetIsFinesseWeapon(oCreature, oWeapon))
    {
        nAtkBonus += GetAbilityModifier(ABILITY_DEXTERITY, oCreature);
    }
    else nAtkBonus += GetAbilityModifier(ABILITY_STRENGTH, oCreature);
    if(ai_GetIsMeleeWeapon(oWeapon)) nAtkBonus += ai_GetWeaponAtkBonus(oWeapon);
    if(AI_DEBUG) ai_Debug("0i_combat", "2858", "GetMyCombatRating (nAtkBonus: " + IntToString(nAtkBonus) +
             " nAC: " + IntToString(GetAC(oCreature)) + " - 10) / 2 = " +
             IntToString((nAtkBonus + GetAC(oCreature) - 10) / 2));
    return(nAtkBonus + GetAC(oCreature) - 10) / 2;
}
object ai_GetAttackedTarget(object oCreature, int bPhysical = TRUE, int bSpell = FALSE)
{
    object oTarget = GetAttackTarget(oCreature);
    if(!GetIsObjectValid(oTarget) && bPhysical) oTarget = GetLocalObject(oCreature, AI_ATTACKED_PHYSICAL);
    if(!GetIsObjectValid(oTarget) && bSpell) oTarget = GetLocalObject(oCreature, AI_ATTACKED_SPELL);
    if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) return OBJECT_INVALID;
    return oTarget;
}
int ai_CheckClassType(object oTarget, int nClassType)
{
    int nCnt = 1, nClass = GetClassByPosition(1, oTarget);
    // We check for the group class types.
    if(nClassType < 0)
    {
        while(nCnt <= AI_MAX_CLASSES_PER_CHARACTER)
        {
            int nCaster = StringToInt(Get2DAString("classes", "SpellCaster", nClass));
            if(nClassType == AI_CLASS_TYPE_WARRIOR && !nCaster) return TRUE;
            else if(nClassType == AI_CLASS_TYPE_CASTER && nCaster) return TRUE;
            int nSpellType = StringToInt(Get2DAString("classes", "Arcane", nClass));
            if(nClassType == AI_CLASS_TYPE_ARCANE && nSpellType) return TRUE;
            else if(nClassType == AI_CLASS_TYPE_DIVINE && !nSpellType) return TRUE;
            nClass = GetClassByPosition(++nCnt, oTarget);
        }
    }
    // Checks for normal classes.
    else
    {
        while(nCnt <= AI_MAX_CLASSES_PER_CHARACTER)
        {
            if(nClass == nClassType) return TRUE;
            nClass = GetClassByPosition(++nCnt, oTarget);
        }
    }
    return FALSE;
}
int ai_CheckRacialType(object oTarget, int nRacialType)
{
    int nRace = GetRacialType(oTarget);
    if(nRacialType == nRace) return TRUE;
    else if(nRacialType == AI_RACIAL_TYPE_ANIMAL_BEAST)
    {
        if(nRace == RACIAL_TYPE_ANIMAL ||
           nRace == RACIAL_TYPE_BEAST ||
           nRace == RACIAL_TYPE_MAGICAL_BEAST) return TRUE;
    }
    else if(nRacialType == AI_RACIAL_TYPE_HUMANOID)
    {
        switch (nRace)
        {
            case RACIAL_TYPE_DWARF :
            case RACIAL_TYPE_ELF :
            case RACIAL_TYPE_GNOME :
            case RACIAL_TYPE_HALFELF :
            case RACIAL_TYPE_HALFLING :
            case RACIAL_TYPE_HALFORC :
            case RACIAL_TYPE_HUMAN :
            case RACIAL_TYPE_HUMANOID_GOBLINOID :
            case RACIAL_TYPE_HUMANOID_MONSTROUS :
            case RACIAL_TYPE_HUMANOID_ORC :
            case RACIAL_TYPE_HUMANOID_REPTILIAN :
                return TRUE;
       }
    }
    return FALSE;
}
void ai_SetNormalAppearance(object oCreature)
{
    if(!ai_GetHasEffectType(oCreature, EFFECT_TYPE_POLYMORPH))
    {
        int nForm = GetAppearanceType(oCreature);
        if(AI_DEBUG) ai_Debug("0i_combat", "2835", GetName(oCreature) + " form: " + IntToString(nForm));
        SetLocalInt(oCreature, AI_NORMAL_FORM, nForm + 1);
    }
}
int ai_GetNormalAppearance(object oCreature)
{
    int nForm = GetLocalInt(oCreature, AI_NORMAL_FORM) - 1;
    if(nForm == -1)
    {
        ai_SetNormalAppearance(oCreature);
        nForm = GetLocalInt(oCreature, AI_NORMAL_FORM) - 1;
    }
    return nForm;
}
struct stClasses ai_GetFactionsClasses(object oCreature, int bEnemy = TRUE, float fMaxRange = AI_RANGE_BATTLEFIELD)
{
    struct stClasses sCount;
    int nCnt = 1, nPosition, nClass, nLevels;
    object oTarget;
    if(bEnemy) oTarget = ai_GetNearestEnemy(oCreature, 1, 7, 7);
    else oTarget = ai_GetNearestAlly(oCreature, 1, 7, 7);
    while(oTarget != OBJECT_INVALID && GetDistanceBetween(oTarget, oCreature) <= fMaxRange)
    {
        for(nPosition = 1; nPosition <= AI_MAX_CLASSES_PER_CHARACTER; nPosition++)
        {
            nClass = GetClassByPosition(nPosition, oTarget);
            nLevels = GetLevelByPosition(nPosition, oTarget);
            if(nClass == CLASS_TYPE_ANIMAL ||
               nClass == CLASS_TYPE_BARBARIAN ||
               nClass == CLASS_TYPE_COMMONER ||
               nClass == CLASS_TYPE_CONSTRUCT ||
               nClass == CLASS_TYPE_ELEMENTAL ||
               nClass == CLASS_TYPE_FIGHTER ||
               nClass == CLASS_TYPE_GIANT ||
               nClass == CLASS_TYPE_HUMANOID ||
               nClass == CLASS_TYPE_MONSTROUS ||
               nClass == CLASS_TYPE_PALADIN ||
               nClass == CLASS_TYPE_RANGER ||
               nClass == CLASS_TYPE_ROGUE ||
               nClass == CLASS_TYPE_VERMIN ||
               nClass == CLASS_TYPE_MONK ||
               nClass == CLASS_TYPE_SHAPECHANGER)
            {
                sCount.FIGHTERS += 1;
                sCount.FIGHTER_LEVELS += nLevels;
            }
            else if(nClass == CLASS_TYPE_CLERIC ||
                    nClass == CLASS_TYPE_DRUID)
            {
                sCount.CLERICS += 1;
                sCount.CLERIC_LEVELS += nLevels;
            }
            else if(nClass == CLASS_TYPE_BARD ||
                    nClass == CLASS_TYPE_FEY ||
                    nClass == CLASS_TYPE_SORCERER ||
                    nClass == CLASS_TYPE_WIZARD)
            {
               sCount.MAGES += 1;
               sCount.MAGE_LEVELS += nLevels;
            }
            else if(nClass == CLASS_TYPE_ABERRATION ||
                    nClass == CLASS_TYPE_DRAGON ||
                    nClass == 29 || //oozes
                    nClass == CLASS_TYPE_MAGICAL_BEAST ||
                    nClass == CLASS_TYPE_OUTSIDER)
            {
               sCount.MONSTERS += 1;
               sCount.MONSTER_LEVELS += nLevels;
            }
            sCount.TOTAL_LEVELS += nLevels;
        }
        sCount.TOTAL += 1;
        if(bEnemy) oTarget = ai_GetNearestEnemy(oCreature, ++nCnt, 7, 7);
        else oTarget = ai_GetNearestAlly(oCreature, ++nCnt, 7, 7);
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "2910", "Enemy: " + IntToString(bEnemy) + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) +
              " CLERICS: " + IntToString(sCount.CLERICS) + "(" + IntToString(sCount.CLERIC_LEVELS) +
              ") FIGHTERS: " +IntToString(sCount.FIGHTERS) + "(" + IntToString(sCount.FIGHTER_LEVELS) +
              ") MAGES: " +IntToString(sCount.MAGES) + "(" + IntToString(sCount.MAGE_LEVELS) +
              ") MONSTERS: " +IntToString(sCount.MONSTERS) + "(" + IntToString(sCount.MONSTER_LEVELS) +
              ") TOTALS: " +IntToString(sCount.TOTAL) + "(" + IntToString(sCount.TOTAL_LEVELS));
    return sCount;
}
string ai_GetMostDangerousClass(struct stClasses stCount)
{
    string sClass;
    // Lets weight the fighter levels 30% higher.
    int nFighter =((stCount.FIGHTER_LEVELS) * 13)/10;
    if(nFighter >= stCount.CLERIC_LEVELS)
    {
        if(nFighter >= stCount.MAGE_LEVELS)
        {
            if(nFighter >= stCount.MONSTER_LEVELS) return "FIGHTER";
            else return "MONSTER";
        }
        else if(stCount.MAGE_LEVELS >= stCount.MONSTER_LEVELS) return "MAGE";
        else return "MONSTER";
    }
    else if(stCount.CLERIC_LEVELS >= stCount.MAGE_LEVELS)
    {
        if(stCount.CLERIC_LEVELS >= stCount.MONSTER_LEVELS) return "CLERIC";
        else return "MONSTER";
    }
    else if(stCount.MAGE_LEVELS >= stCount.MONSTER_LEVELS) return "MAGE";
    else return "MONSTER";
    return "";
}
void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID)
{
    // Lets not check for weapons on creatures that can't use them!
    int nRacialType = GetRacialType(oCreature);
    if(nRacialType == RACIAL_TYPE_ANIMAL ||
        nRacialType == RACIAL_TYPE_DRAGON ||
        nRacialType == RACIAL_TYPE_MAGICAL_BEAST ||
        nRacialType == RACIAL_TYPE_OOZE ||
        nRacialType == RACIAL_TYPE_VERMIN) return;
    //if(Polymorphed()) return;
    if(AI_DEBUG) ai_Debug("0i_combat", "2952", GetName(OBJECT_SELF) + " is equiping best weapon!");
    // Determine if I am wielding a ranged weapon, melee weapon, or none.
    int bIsWieldingRanged = ai_HasRangedWeaponWithAmmo(oCreature);
    int bIsWieldingMelee = ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND));
    if(AI_DEBUG) ai_Debug("0i_combat", "2956", "bIsWieldingRanged: " + IntToString(bIsWieldingRanged) +
             " bIsWieldingMelee: " + IntToString(bIsWieldingMelee));
    // If we are hidden then change to a melee weapon so we can move in to attack.
    if(ai_GetIsHidden(oCreature))
    {
        // Equip a melee weapon unless we already have one.
        if(!bIsWieldingMelee) ai_EquipBestMeleeWeapon(oCreature, oTarget);
        return;
    }
    // Equip the appropriate weapon for the distance of the enemy.
    int nEnemyGroup = ai_GetNumOfEnemiesInGroup(oCreature);
    if(AI_DEBUG) ai_Debug("0i_combat", "2967", GetName(oCreature) + " has " + IntToString(nEnemyGroup) + " enemies within 5.0f them! PointBlank: " +
             IntToString(GetHasFeat(FEAT_POINT_BLANK_SHOT, oCreature)));
    // We are in melee combat.
    if(nEnemyGroup > 0)
    {
        if(bIsWieldingRanged)
        {
            // We have the point blank shot feat or there are more than one enemy on us.
            // Note: Point Blank shot feat is bad once we have more than one enemy on us.
            if(!GetHasFeat(FEAT_POINT_BLANK_SHOT, oCreature) || nEnemyGroup > 1)
            {
                // If I'm not using a melee weapon.
                if(!bIsWieldingMelee)
                {
                    ai_EquipBestMeleeWeapon(oCreature);
                    if(AI_DEBUG) ai_Debug("0i_combat", "2982", GetName(oCreature) + " is equiping melee weapon due to close enemies!");
                }
            }
        }
    }
    // We are not in melee range.
    else
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "2990", GetName(oCreature) + " is not in melee combat with an enemy!");
        // If are at range with the enemy then equip a ranged weapon.
        if(!bIsWieldingRanged)
        {
            ai_EquipBestRangedWeapon(oTarget);
            // Make sure that they equiped a range weapon.
            bIsWieldingRanged = ai_HasRangedWeaponWithAmmo(oCreature);
            bIsWieldingMelee = ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature));
            if(AI_DEBUG) ai_Debug("0i_combat", "2998", GetName(oCreature) + " is attempting to equip a ranged weapon: " + IntToString(bIsWieldingRanged));
            // If we equiped a ranged weapon then drop out.
        }
    }
    // We don't have a weapon out so equip one! We are in combat!
    if(!bIsWieldingRanged && !bIsWieldingMelee) ai_EquipBestMeleeWeapon(OBJECT_INVALID);
}
int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID)
{
    if(ai_GetAIMode(oCreature, AI_MODE_EQUIP_WEAPON_OFF)) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "2992", GetName(oCreature) + " is equiping best melee weapon!");
    float fItemPower, fOffItemPower, fRightPower, fLeftPower, f2HandedPower;
    int nItemPower, nShieldPower, nShieldValue, nItemValue, nRightValue;
    int n2HandedValue, nLeftValue, bTwoWeaponUser;
    int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature));
    if(AI_DEBUG) ai_Debug("0i_combat", "2997", "nMaxItemValue: " + IntToString(nMaxItemValue));
    bTwoWeaponUser = GetHasFeat(374/*FEAT_DUAL_WIELD*/, oCreature) || GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oCreature);
    object oShield = OBJECT_INVALID;
    object oRight = OBJECT_INVALID;
    object oLeft = OBJECT_INVALID;
    object o2Handed = OBJECT_INVALID;
    object o2HandedHand = OBJECT_INVALID;
    object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
    if(oRightHand != OBJECT_INVALID)
    {
        // Setup the item in our right hand's avg dmg and gold value as our base.
        if(ai_GetIsTwoHandedWeapon(oRightHand, oCreature))
        {
            if(ai_GetIsDoubleWeapon(oRightHand))
            {
                f2HandedPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, TRUE, FALSE, oRightHand);
            }
            else f2HandedPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, TRUE);
            n2HandedValue = GetGoldPieceValue(oRightHand);
            if(AI_DEBUG) ai_Debug("0i_combat", "3016", " 2Handed oRightHand: " + GetName(oRightHand) +
                                  " f2HandPower: " + FloatToString(f2HandedPower, 0, 2) +
                                  " n2HandedValue: " + IntToString(n2HandedValue));
        }
        else if(ai_GetIsSingleHandedWeapon(oRightHand, oCreature))
        {
            fRightPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand);
            nRightValue = GetGoldPieceValue(oRightHand);
            if(AI_DEBUG) ai_Debug("0i_combat", "3024", " 1Handed oRightHand: " + GetName(oRightHand) +
                                  " fRightPower: " + FloatToString(fRightPower, 0, 2) +
                                  " nRightValue: " + IntToString(nRightValue));
        }
    }
    object oLeftHand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature);
    if(oLeftHand != OBJECT_INVALID)
    {
        // Setup the item in our left hand's Shield AC and gold value as our base.
        if(ai_GetIsShield(oLeftHand))
        {
            nShieldPower = ai_SetShieldAC(oCreature, oLeftHand);
            nShieldValue = GetGoldPieceValue(oLeftHand);
            if(AI_DEBUG) ai_Debug("0i_combat", "3037", " Shield oLeftHand: " + GetName(oLeftHand) +
                                  " fShieldPower: " + IntToString(nShieldPower) +
                                  " nShieldValue: " + IntToString(nShieldValue));
        }
        // Setup the item in our left hand's avg dmg and gold value as our base.
        else
        {
            fLeftPower = ai_GetMeleeWeaponAvgDmg(oCreature, oLeftHand, FALSE, TRUE);
            nLeftValue = GetGoldPieceValue(oLeftHand);
            if(AI_DEBUG) ai_Debug("0i_combat", "3046", " 1Handed oLeftHand: " + GetName(oLeftHand) +
                                  " fLeftPower: " + FloatToString(fLeftPower, 0, 2) +
                                  " nLeftValue: " + IntToString(nLeftValue));
        }
    }
    int nWeaponSize, nType, nCreatureSize = GetCreatureSize(oCreature);
    // Get the best weapons they have in their inventory.
    object oItem = GetFirstItemInInventory(oCreature);
    // If they don't have any items then lets stop, we can't equip a weapon/shield.
    if(oItem == OBJECT_INVALID) return FALSE;
    while(oItem != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3058", GetName(oItem) + " MeleeWeapon: " +
                              IntToString(ai_GetIsMeleeWeapon(oItem)) + " Proficient: " +
                              IntToString(ai_GetIsProficientWith(oCreature, oItem)) +
                              " Identified: " + IntToString(GetIdentified(oItem)));
        if(ai_GetIsProficientWith(oCreature, oItem) &&
           GetIdentified(oItem) && ai_CheckIfCanUseItem(oCreature, oItem))
        {
            nItemValue = GetGoldPieceValue(oItem);
            if(AI_DEBUG) ai_Debug("0i_combat", "3066", " nItemValue: " + IntToString(nItemValue));
            if(!GetLocalInt(GetModule(), AI_RULE_ILR) || nMaxItemValue >= nItemValue)
            {
                if(ai_GetIsShield(oItem))
                {
                    nItemPower = ai_SetShieldAC(oCreature, oItem);
                    if(nItemPower > nShieldPower ||
                      (nItemPower == nShieldPower && nItemValue > nShieldValue))
                    { oShield = oItem; nShieldPower = nItemPower; nShieldValue = nItemValue; }
                }
                else if(ai_GetIsMeleeWeapon(oItem))
                {
                    // Make sure the creature and weapon are close enough in size.
                    // Can wield a weapon up to one size larger than their size.
                    // Can wield a weapon down to two sizes smaller than their size.
                    nType = GetBaseItemType(oItem);
                    nWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nType));
                    if(nWeaponSize >=  nCreatureSize - 2 && nWeaponSize <= nCreatureSize + 1)
                    {
                        // Get item avg damage based on if it is 2handed or 1handed.
                        if(ai_GetIsSingleHandedWeapon(oItem, oCreature))
                        {
                            fItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem);
                            fOffItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem, FALSE, TRUE);
                            // If the new weapon is better than the weapon in our right hand.
                            if(fItemPower > fRightPower ||
                              (fItemPower == fRightPower && nItemValue > nRightValue))
                            {
                                // We need to check if the weapon in the right hand is
                                // better than the weapon in the left hand since we are
                                // replacing our right hand weapon.
                                // Note: we must find out if we have selected a weapon for the
                                // right hand i.e. oRight or the best weapon is in our
                                // right hand i.e. oRightHand!
                                fOffItemPower = 0.0;
                                if(oRight != OBJECT_INVALID && ai_GetIsSingleHandedWeapon(oRight, oCreature))
                                {
                                    fOffItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRight, FALSE, TRUE);
                                }
                                else if(oRightHand != OBJECT_INVALID && ai_GetIsSingleHandedWeapon(oRightHand, oCreature))
                                {
                                    fOffItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, FALSE, TRUE);
                                }
                                // If the right hand weapon is better than the weapon in our left hand.
                                if(fOffItemPower > fLeftPower || (fOffItemPower > 0.0 &&
                                   fOffItemPower == fLeftPower && nRightValue > nLeftValue))
                                {
                                    if(oRight != OBJECT_INVALID) oLeft = oRight;
                                    else oLeft = oRightHand;
                                    fLeftPower = fOffItemPower;
                                    nLeftValue = nRightValue;
                                }
                                oRight = oItem;
                                fRightPower = fItemPower;
                                nRightValue = nItemValue;
                            }
                            // If the new weapon is better than the weapon in our left hand.
                            else if(fOffItemPower > fLeftPower ||
                                   (fOffItemPower == fLeftPower && nItemValue > nLeftValue))
                            { oLeft = oItem; fLeftPower = fOffItemPower; nLeftValue = nItemValue; }
                        }
                        else if(ai_GetIsTwoHandedWeapon(oItem, oCreature))
                        {
                            if(ai_GetIsDoubleWeapon(oItem))
                            {
                                fItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem, TRUE, FALSE, oItem);
                            }
                            else fItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem, TRUE);
                            // If the new weapon is better than the selected weapon.
                            if(fItemPower > f2HandedPower ||
                              (fItemPower == f2HandedPower && nItemValue > n2HandedValue))
                            {
                                o2Handed = oItem;
                                f2HandedPower = fItemPower;
                                n2HandedValue = nItemValue;
                            }
                        }
                    }
                }
            }
        }
        oItem = GetNextItemInInventory();
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "3149", "oRight: " + GetName(oRight) + " oLeft:" +
                          GetName(oLeft) + " oShield: " + GetName(oShield) +
                          "o2Handed: " + GetName(o2Handed));
    // First check for two weapons first.
    if(bTwoWeaponUser && oRight != OBJECT_INVALID && oLeft != OBJECT_INVALID)
    {
        fRightPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRight, FALSE, FALSE, oLeft);
        fRightPower += ai_GetMeleeWeaponAvgDmg(oCreature, oLeft, FALSE, TRUE);
        if(AI_DEBUG) ai_Debug("0i_combat", "3157", " Right/Left Power: " +
                          FloatToString(fRightPower, 0, 2) + " 2HandedPower: " +
                          FloatToString(f2HandedPower, 0, 2));
        if(fRightPower > f2HandedPower)
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "3162", GetName(oCreature) + " is equiping " +
                    GetName(oRight) + " in the right hand and " + GetName(oLeft) +
                    " in the left hand.");
            ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND);
            ActionEquipItem(oLeft, INVENTORY_SLOT_LEFTHAND);
            return TRUE;
        }
    }
    if(f2HandedPower > fRightPower && o2Handed != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3172", GetName(oCreature) + " is equiping " +
                GetName(o2Handed) + " in both hands.");
        ActionEquipItem(o2Handed, INVENTORY_SLOT_RIGHTHAND);
        return TRUE;
    }
    // Now lets just equip the best weapon for the right hand.
    if(oRight != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3180", GetName(oCreature) + " is equiping " +
                 GetName(oRight) + " in the right hand. ");
        ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND);
    }
    // Make sure we are not equiping a 2handed weapon and
    // If not can we equip a shield?
    if((oRight == OBJECT_INVALID || ai_GetIsSingleHandedWeapon(oRight, oCreature) ||
       !ai_GetIsTwoHandedWeapon(oRightHand, oCreature)) &&
       oShield != OBJECT_INVALID && GetHasFeat(FEAT_SHIELD_PROFICIENCY, oCreature))
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3190", GetName(oCreature) + " is equiping " +
                              GetName(oShield) + " in the left hand.");
        ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
        return TRUE;
    }
    // Finally if we don't have a weapon to equip so check to see if we are
    // holding a bow.
    else if(oRight == OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3199", GetName(oCreature) + " did not equip a melee weapon");
        // We couldn't find a melee weapon but we are looking to go into melee
        // I'm holding a ranged weapon! We better put it up.
        if(GetWeaponRanged(oRightHand))
        {
            if(AI_DEBUG) ai_Debug("0i_combat", "3204", GetName(oCreature) + " is unequiping " + GetName(oRightHand));
            ActionUnequipItem(oRightHand);
            return TRUE;
        }
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "3209", GetName(oCreature) + " is not equiping a weapon!");
    return FALSE;
}
int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID)
{
    if(ai_GetAIMode(oCreature, AI_MODE_EQUIP_WEAPON_OFF)) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3215", GetName(oCreature) + " is looking for best ranged weapon!");
    int nAmmo, nAmmoSlot, nBestType1, nBestType2, nType, nFeat, nItemValue, nRangedValue;
    int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature));
    string sAmmo;
    object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature);
    if(oRightHand != OBJECT_INVALID && ai_GetIsRangeWeapon(oRightHand))
    {
        // Setup the item in our right hand as our base gold value to check against.
        if(ai_GetIsRangeWeapon(oRightHand)) nRangedValue = GetGoldPieceValue(oRightHand);
    }
    object oRanged = OBJECT_INVALID, oAmmo = OBJECT_INVALID;
    // Find the best type of ranged weapon for this player.
    if(GetHasFeat(FEAT_WEAPON_FOCUS_LONGBOW, oCreature))
    { nBestType1 = BASE_ITEM_LONGBOW; nAmmo = BASE_ITEM_ARROW; nAmmoSlot = INVENTORY_SLOT_ARROWS; sAmmo = "arrow";}
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORTBOW, oCreature))
    { nBestType1 = BASE_ITEM_SHORTBOW; nAmmo = BASE_ITEM_ARROW; nAmmoSlot = INVENTORY_SLOT_ARROWS; sAmmo = "arrow";}
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW, oCreature))
    { nBestType1 = BASE_ITEM_HEAVYCROSSBOW; nAmmo = BASE_ITEM_BOLT; nAmmoSlot = INVENTORY_SLOT_BOLTS; sAmmo = "bolt";}
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW, oCreature))
    { nBestType1 = BASE_ITEM_LIGHTCROSSBOW; nAmmo = BASE_ITEM_BOLT; nAmmoSlot = INVENTORY_SLOT_BOLTS; sAmmo = "bolt";}
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_SLING, oCreature))
    { nBestType1 = BASE_ITEM_SLING; nAmmo = BASE_ITEM_BULLET; nAmmoSlot = INVENTORY_SLOT_BULLETS; sAmmo = "bullet";}
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_DART, oCreature))
    { nBestType1 = BASE_ITEM_DART; }
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_SHURIKEN, oCreature))
    { nBestType1 = BASE_ITEM_SHURIKEN; }
    else if(GetHasFeat(FEAT_WEAPON_FOCUS_THROWING_AXE, oCreature))
    { nBestType1 = BASE_ITEM_THROWINGAXE; }
    // These feats require a bow.
    else if(GetHasFeat(FEAT_RAPID_SHOT, oCreature))
    { nBestType1 = BASE_ITEM_LONGBOW; nBestType2 = BASE_ITEM_SHORTBOW;
      nAmmo = BASE_ITEM_ARROW; nAmmoSlot = INVENTORY_SLOT_ARROWS; sAmmo = "arrow"; }
    // This feat requires a xbow.
    else if(GetHasFeat(FEAT_RAPID_RELOAD, oCreature))
    { nBestType1 = BASE_ITEM_HEAVYCROSSBOW; nBestType2 = BASE_ITEM_LIGHTCROSSBOW;
      nAmmo = BASE_ITEM_BOLT; nAmmoSlot = INVENTORY_SLOT_BOLTS; sAmmo = "bolt"; }
    if(AI_DEBUG) ai_Debug("0i_combat", "3251", "nBestType1: " + IntToString(nBestType1) + " nBestType2: " + IntToString(nBestType2) +
           " nAmmo: " + IntToString(nAmmo));
    int nCreatureSize = GetCreatureSize(oCreature) + 1;
    // Cycle through the inventory looking for a ranged weapon.
    object oItem = GetFirstItemInInventory(oCreature);
    while(oItem != OBJECT_INVALID)
    {
        nType = GetBaseItemType(oItem);
        if(AI_DEBUG) ai_Debug("0i_combat", "3259", "oItem: " + GetName(oItem) +
                 " Identified: " + IntToString(GetIdentified(oItem)) +
                 " Ranged Weapon: " + Get2DAString("baseitems", "RangedWeapon", nType));
        // Make sure it is identified and it is a ranged weapon.
        if(GetIdentified(oItem) && Get2DAString("baseitems", "RangedWeapon", nType) != "")
        {
           if(AI_DEBUG) ai_Debug("0i_combat", "3265", " Proficient: " +
                    IntToString(ai_GetIsProficientWith(oCreature, oItem)) +
                    " nMaxItemValue: " + IntToString(nMaxItemValue));
            if(ai_GetIsProficientWith(oCreature, oItem))
            {
                if(ai_CheckIfCanUseItem(oCreature, oItem))
                {
                    nItemValue = GetGoldPieceValue(oItem);
                    if(AI_DEBUG) ai_Debug("0i_combat", "3273", "nItemValue: " + IntToString(nItemValue));
                    if(!GetLocalInt(GetModule(), AI_RULE_ILR) || nMaxItemValue >= nItemValue)
                    {
                        if(AI_DEBUG) ai_Debug("0i_combat", "3276", " Creature Size: " + IntToString(nCreatureSize) +
                               " Weapon Size: " + Get2DAString("baseitems", "WeaponSize", nType));
                        // Make sure they are large enough to use it.
                        if(StringToInt(Get2DAString("baseitems", "WeaponSize", nType)) <= nCreatureSize)
                        {
                            if(AI_DEBUG) ai_Debug("0i_combat", "3281", "nItemValue: " + IntToString(nItemValue) +
                                     " nRangedValue: " + IntToString(nRangedValue) + " nType: " + IntToString(nType));
                            // Is it of the best range weapon type? 0 is any range weapon.
                            // Also grab any range weapon until we have a best type.
                            if(nType == nBestType1 || nType == nBestType2 ||
                                nBestType1 == 0 || oRanged == OBJECT_INVALID)
                            {
                                if(nItemValue > nRangedValue)
                                {
                                    if(ai_GetHasItemProperty(oItem, ITEM_PROPERTY_UNLIMITED_AMMUNITION))
                                    {
                                        oRanged = oItem; nRangedValue = nItemValue;
                                        if(AI_DEBUG) ai_Debug("0i_combat", "3293", "Selecting oRanged: " + GetName(oRanged) +
                                                 " nRangedValue: " + IntToString(nRangedValue) + " and doesn't need ammo!");
                                    }
                                    else
                                    {
                                        if(nBestType1 == 0)
                                        {
                                            if(nType == BASE_ITEM_LONGBOW || nType == BASE_ITEM_SHORTBOW)
                                            { nAmmo = BASE_ITEM_ARROW; sAmmo = "arrow"; nAmmoSlot = INVENTORY_SLOT_ARROWS; }
                                            else if(nType == BASE_ITEM_HEAVYCROSSBOW || nType == BASE_ITEM_LIGHTCROSSBOW)
                                            { nAmmo = BASE_ITEM_BOLT; sAmmo = "bolt"; nAmmoSlot = INVENTORY_SLOT_BOLTS; }
                                            else if(nType == BASE_ITEM_SLING)
                                            { nAmmo = BASE_ITEM_BULLET; sAmmo = "bullet"; nAmmoSlot = INVENTORY_SLOT_BULLETS; }
                                            else nAmmo = 0;
                                        }
                                        // Now do we have ammo for it?
                                        if(AI_DEBUG) ai_Debug("0i_combat", "3309", "nAmmo: " + IntToString(nAmmo));
                                        if(nAmmo > 0)
                                        {
                                            if(nAmmo == BASE_ITEM_ARROW ||
                                                nAmmo == BASE_ITEM_BOLT ||
                                                nAmmo == BASE_ITEM_BULLET) oAmmo = GetItemInSlot(nAmmoSlot);
                                            if(oAmmo == OBJECT_INVALID)
                                            {
                                                // We don't have ammo equiped so lets see if we have any in our inventory.
                                                oAmmo = GetFirstItemInInventory();
                                                while(oAmmo != OBJECT_INVALID)
                                                {
                                                    if(GetBaseItemType(oAmmo) == nAmmo) break;
                                                    oAmmo = GetNextItemInInventory();
                                                }
                                                if(oAmmo != OBJECT_INVALID) ActionEquipItem(oAmmo, nAmmoSlot);
                                            }
                                        }
                                        if(oAmmo != OBJECT_INVALID)
                                        {
                                            oRanged = oItem; nRangedValue = nItemValue;
                                            if(AI_DEBUG) ai_Debug("0i_combat", "3330", "Selecting oRanged: " + GetName(oRanged) +
                                                     " nRangedValue: " + IntToString(nRangedValue));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        oItem = GetNextItemInInventory(oCreature);
    }
    // They don't have a range weapon so lets break out.
    if(oRanged == OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3346", GetName(oCreature) + " did not equip a ranged weapon!");
        return FALSE;
    }
    ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND);
    return TRUE;
}
int ai_EquipBestMonkMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "3354", GetName(OBJECT_SELF) + " is equiping best monk melee weapon!");
    int nValue, nRightValue;
    int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature));
    object oRight = OBJECT_INVALID;
    object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature);
    if(oRightHand != OBJECT_INVALID)
    {
        nRightValue = GetGoldPieceValue(oRightHand);
    }
    // Get the best kama they have in their inventory.
    object oItem = GetFirstItemInInventory(oCreature);
    // If they don't have any kamas then lets stop, we can't equip a weapon.
    if(oItem == OBJECT_INVALID) return FALSE;
    while(oItem != OBJECT_INVALID)
    {
        nValue = GetGoldPieceValue(oItem);
        // Make sure they are high enough level to equip this item.
        if(nMaxItemValue >= nValue && nValue > 1)
        {
            // Is it a single handed weapon?
            if(GetBaseItemType(oItem) == BASE_ITEM_KAMA)
            {
                // Replace the lowest value right weapon.
                if(nValue > nRightValue)
                {
                    oRight = oItem; nRightValue = nValue;
                }
            }
        }
        oItem = GetNextItemInInventory(oCreature);
    }
    // Finally lets just equip the kama if we have one.
    if(oRight == OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3388", GetName(oCreature) + " did not equip a melee weapon!");
        return FALSE;
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "3391", GetName(oCreature) + " is equiping " + GetName(oRight) + " in the right hand.");
    ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND);
    return TRUE;
}
int ai_IsInADangerousAOE(object oCreature, float fMaxRange = AI_RANGE_BATTLEFIELD, int bMove = FALSE)
{
    int bDangerous, nSpell, nCnt = 1;
    string sAOEType;
    object oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oCreature, nCnt);
    float fRadius, fDistance = GetDistanceBetween(oCreature, oAOE);
    while(oAOE != OBJECT_INVALID && fDistance <= fMaxRange)
    {
        // AOE's have the tag set to the "LABEL" in vfx_persistent.2da
        // I check vs those labels to see if the AOE is offensive.
        // Below is the list of Offensive AOE effects.
        sAOEType = GetTag(oAOE);
        if(sAOEType == "VFX_PER_WEB") { fRadius = 6.7; nSpell = SPELL_WEB; }
        else if(sAOEType == "VFX_PER_ENTANGLE") { fRadius = 5.0; nSpell = SPELL_ENTANGLE; }
        else if(sAOEType == "VFX_PER_GREASE") { fRadius = 6.0; nSpell = SPELL_GREASE; }
        else if(sAOEType == "VFX_PER_EVARDS_BLACK_TENTACLES")
             { fRadius = 5.0; nSpell = SPELL_EVARDS_BLACK_TENTACLES; }
        //else if(sAOEType == "VFX_PER_DARKNESS") { fRadius = 6.7; nSpell = SPELL_DARKNESS; }
        //else if(sAOEType == "VFX_MOB_SILENCE") { fRadius = 4.0; nSpell = SPELL_SILENCE; }
        else if(sAOEType == "VFX_PER_FOGSTINK") { fRadius = 6.7; nSpell = SPELL_STINKING_CLOUD; }
        else if(sAOEType == "VFX_PER_FOGFIRE") { fRadius = 5.0; nSpell = SPELL_INCENDIARY_CLOUD; }
        else if(sAOEType == "VFX_PER_FOGKILL") { fRadius = 5.0; nSpell = SPELL_CLOUDKILL; }
        else if(sAOEType == "VFX_PER_FOGMIND") { fRadius = 5.0; nSpell = SPELL_MIND_FOG; }
        else if(sAOEType == "VFX_PER_CREEPING_DOOM") { fRadius = 6.7; nSpell = SPELL_CREEPING_DOOM; }
        else if(sAOEType == "VFX_PER_FOGACID") { fRadius = 5.0; nSpell = SPELL_ACID_FOG; }
        else if(sAOEType == "VFX_PER_FOGBEWILDERMENT") { fRadius = 5.0; nSpell = SPELL_CLOUD_OF_BEWILDERMENT; }
        else if(sAOEType == "VFX_PER_WALLFIRE") { fRadius = 10.0; nSpell = SPELL_WALL_OF_FIRE; }
        else if(sAOEType == "VFX_PER_WALLBLADE") { fRadius = 10.0; nSpell = SPELL_BLADE_BARRIER; }
        else if(sAOEType == "VFX_PER_DELAY_BLAST_FIREBALL") { fRadius = 2.0; nSpell = SPELL_DELAYED_BLAST_FIREBALL; }
        else if(sAOEType == "VFX_PER_GLYPH") { fRadius = 2.5; nSpell = SPELL_GLYPH_OF_WARDING; }
        else fRadius = 0.0;
        if(AI_DEBUG) ai_Debug("0i_combat", "3426", GetName(oCreature) + " distance from AOE is " + FloatToString(fDistance, 0, 2) +
                " AOE Radius: " + FloatToString(fRadius, 0, 2) +
                " AOE Type: " + GetTag(oAOE));
        // fRadius > 0.0 keeps them from tiggering that they are in a dangerous
        // AOE due to having an AOE on them.
        if(fRadius > 0.0 && fDistance <= fRadius &&
           !ai_CreatureImmuneToEffect(GetAreaOfEffectCreator(oAOE), oCreature, nSpell))
        {
            bDangerous = TRUE;
            if(nSpell == SPELL_WEB || nSpell == SPELL_ENTANGLE)
            {
                if(ai_HasRangedWeaponWithAmmo(oCreature)) bDangerous = FALSE;
                if(GetReflexSavingThrow(oCreature) + GetAbilityModifier(ABILITY_DEXTERITY, oCreature) >= ai_GetCharacterLevels(oCreature))
                    bDangerous = FALSE;
            }
            break;
        }
        oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oCreature, ++nCnt);
        fDistance = GetDistanceBetween(oCreature, oAOE);
    }
    if(bDangerous && bMove)
    {
        location lLocation;
        object oTarget;
        if(ai_GetIsInCombat(oCreature))
        {
            object oMaster = GetMaster(oCreature);
            // If we have a ranged weapon then backout and use that.
            if(ai_HasRangedWeaponWithAmmo(oCreature))
            {
                lLocation = GetRandomLocation(GetArea(oCreature), oCreature, fRadius + 1.0);
            }
            else // we must find a target out of the AOE or fight in the AOE.
            {
                oTarget = ai_GetNearestPhysicalTargetNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, TRUE);
                if(oTarget != OBJECT_INVALID) lLocation = GetLocation(oTarget);
            }
        }
        else lLocation = GetRandomLocation(GetArea(oCreature), oCreature, fRadius + 1.0);
        ai_ClearCreatureActions();
        if(AI_DEBUG) ai_Debug("0i_combat", "3466", GetName(oCreature) + " is moving out of area of effect!");
        ActionMoveToLocation(lLocation, TRUE);
        return TRUE;
    }
    else if(bDangerous) return TRUE;
    return FALSE;
}
int ai_GetIsHidden(object oHidden)
{
    int nEffectType;
    effect eEffect = GetFirstEffect(oHidden);
    while(GetIsEffectValid(eEffect))
    {
        nEffectType = GetEffectType(eEffect);
        if(nEffectType == EFFECT_TYPE_INVISIBILITY) return 1;
        else if(nEffectType == EFFECT_TYPE_IMPROVEDINVISIBILITY) return 1;
        else if(nEffectType == EFFECT_TYPE_DARKNESS) return 2;
        else if(nEffectType == EFFECT_TYPE_SANCTUARY) return 3;
        else if(nEffectType == EFFECT_TYPE_ETHEREAL) return 3;
        eEffect = GetNextEffect(oHidden);
    }
    if(GetActionMode(oHidden, ACTION_MODE_STEALTH)) return 4;
   return FALSE;
}
int ai_CastOffensiveSpellVsTarget(object oCaster, object oCreature, int nSpell)
{
    // Check saves.
    string sSave = Get2DAString("ai_spells", "SaveType", nSpell);
    // There is no save!
    if(sSave == "") return TRUE;
    // Get the level of the spell.
    int nSpellLvl = StringToInt(Get2DAString("spells", "Innate", nSpell));
    // Randomize our check.
    nSpellLvl += Random(AI_SPELL_CHECK_DIE) + AI_SPELL_CHECK_BONUS;
    // Check feats that might increase our DC.
    string sSchool = Get2DAString("spells", "School", nSpell);
    if(sSchool == "V")
    {
        if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_EVOCATION, oCaster)) nSpellLvl += 4;
        else if(GetHasFeat(FEAT_SPELL_FOCUS_EVOCATION, oCaster)) nSpellLvl += 2;
    }
    else if(sSchool == "C")
    {
        if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_CONJURATION, oCaster)) nSpellLvl += 4;
        else if(GetHasFeat(FEAT_SPELL_FOCUS_CONJURATION, oCaster)) nSpellLvl += 2;
    }
    else if(sSchool == "N")
    {
        if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_NECROMANCY, oCaster)) nSpellLvl += 4;
        else if(GetHasFeat(FEAT_SPELL_FOCUS_NECROMANCY, oCaster)) nSpellLvl += 2;
    }
    else if(sSchool == "E")
    {
        if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oCaster)) nSpellLvl += 4;
        else if(GetHasFeat(FEAT_SPELL_FOCUS_ENCHANTMENT, oCaster)) nSpellLvl += 2;
    }
    else if(sSchool == "I")
    {
        if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ILLUSION, oCaster)) nSpellLvl += 4;
        else if(GetHasFeat(FEAT_SPELL_FOCUS_ILLUSION, oCaster)) nSpellLvl += 2;
    }
    else if(sSave == "Reflex")
    {
        string sImmunityType = Get2DAString("ai_spells", "ImmunityType", nSpell);
        // Give a bonus to our check for half dmg spells unless they can dodge it!
        if((sImmunityType == "Fire" || sImmunityType == "Electricity" || sImmunityType == "Acid" ||
            sImmunityType == "Cold" || sImmunityType == "Sonic") &&
            !GetHasFeat(FEAT_IMPROVED_EVASION, oCreature)) nSpellLvl += AI_SPELL_CHECK_NO_EVASION_BONUS;
        if(AI_DEBUG) ai_Debug("0i_combat", "3050", " nSpellLvl: " + IntToString(nSpellLvl) +
                 " > nMagic: " + IntToString(GetReflexSavingThrow(oCreature)));
        return (nSpellLvl > GetReflexSavingThrow(oCreature));
    }
    else if(sSave == "Fortitude") return (nSpellLvl > GetFortitudeSavingThrow(oCreature));
    else if(sSave == "Will") return (nSpellLvl > GetWillSavingThrow(oCreature));
    return TRUE;
}
int ai_GetDragonDC(object oCreature)
{
    int nDC, nHitDice = GetHitDice(oCreature);
    if(nHitDice < 4) { nDC = 12; }
    else if(nHitDice < 7) { nDC = 13; }
    else if(nHitDice < 10) { nDC = 14; }
    else if(nHitDice < 13) { nDC = 16; }
    else if(nHitDice < 16) { nDC = 18; }
    else if(nHitDice < 19) { nDC = 20; }
    else if(nHitDice < 22) { nDC = 22; }
    else if(nHitDice < 25) { nDC = 24; }
    else if(nHitDice < 28) { nDC = 26; }
    else if(nHitDice < 31) { nDC = 28; }
    else if(nHitDice < 34) { nDC = 30; }
    else if(nHitDice < 37) { nDC = 32; }
    else if(nHitDice < 39) { nDC = 34; }
    else { nDC = 36; }
    string sTag = GetTag(oCreature);
    if(sTag == "gold_dragon") nDC += 5;
    if(sTag == "red_dragon" || sTag == "silver_dragon")  return nDC + 4;
    else if(sTag == "black_dragon" || sTag == "brass_dragon") return nDC + 3;
    else if(sTag == "green_dragon" || sTag == "copper_dragon")  return nDC + 2;
    else if(sTag == "blue_dragon" || sTag == "bronze_dragon")  return nDC + 1;
    //else if(sTag == "white_dragon") nDC += 0;
    return nDC;
}
void ai_SetCreatureAIScript(object oCreature)
{
    string sCombatAI = GetLocalString(oCreature, AI_DEFAULT_SCRIPT);
    // Non-Hostile NPC's do not need to use special tactics by default.
    if(sCombatAI == "" && GetLocalInt(GetModule(), AI_RULE_AMBUSH) && d100() < 34)
    {
        // They should have skill ranks equal to their level + 1 to use a special AI.
        int nSkillNeeded = GetHitDice(oCreature) + 1;
        /*/ Ambusher: requires either Improved Invisibility or Invisibility.
        if(GetHasSpell(SPELL_IMPROVED_INVISIBILITY, oCreature) ||
           GetHasSpell(SPELL_INVISIBILITY, oCreature))
        {
            int bCast = ai_TryToCastSpell(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature);
            if(!bCast) bCast = ai_TryToCastSpell(oCreature, SPELL_INVISIBILITY, oCreature);
            if(bCast) sCombatAI = "ai_ambusher";
        } */
        if(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature, TRUE))
        {
            sCombatAI = "ai_flanker";
        }
        // Ambusher: Requires a Hide and Move silently skill equal to your level + 1.
        else if(GetSkillRank(SKILL_HIDE, oCreature) >= nSkillNeeded &&
                GetSkillRank(SKILL_MOVE_SILENTLY, oCreature) >= nSkillNeeded)
        {
            sCombatAI = "ai_ambusher";
        }
        // Defensive : requires Parry skill equal to your level or Expertise.
        else if(GetSkillRank(SKILL_PARRY, oCreature) >= nSkillNeeded ||
                GetHasFeat(FEAT_EXPERTISE, oCreature) ||
                GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature))
        {
            sCombatAI = "ai_defensive";
        }
        /* Needs looked at as it will soft lock some monsters!
        else if(GetHasSpell(SPELL_LESSER_DISPEL, oCreature) ||
                GetHasSpell(SPELL_DISPEL_MAGIC, oCreature) || GetHasSpell(SPELL_GREATER_DISPELLING, oCreature))
        {
            sCombatAI = "ai_cntrspell";
        }*/
        else if(ai_CheckClassType(oCreature, AI_CLASS_TYPE_ARCANE) &&
               ai_GetCharacterLevels(oCreature) > 4) sCombatAI = "ai_ranged";
        else if(ai_EquipBestRangedWeapon(oCreature)) sCombatAI = "ai_ranged";
        else if(GetSkillRank(SKILL_TAUNT, oCreature) >= nSkillNeeded) sCombatAI = "ai_taunter";
    }
    if(sCombatAI == "")
    {
        int nAssociateType = GetAssociateType(oCreature);
        if (nAssociateType == ASSOCIATE_TYPE_FAMILIAR)
        {
            sCombatAI = "ai_default";
        }
        else
        {
            // Select the best ai for this henchmen based on class.
            int nClass = GetClassByPosition(1, oCreature);
            // If they have more than one class use the default ai.
            if(GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID) sCombatAI = "ai_default";
            else if(nClass == CLASS_TYPE_BARBARIAN) sCombatAI = "ai_barbarian";
            else if(nClass == CLASS_TYPE_BARD) sCombatAI = "ai_bard";
            else if(nClass == CLASS_TYPE_CLERIC) sCombatAI = "ai_cleric";
            else if(nClass == CLASS_TYPE_DRUID) sCombatAI = "ai_druid";
            else if(nClass == CLASS_TYPE_FIGHTER) sCombatAI = "ai_fighter";
            else if(nClass == CLASS_TYPE_MONK) sCombatAI = "ai_monk";
            else if(nClass == CLASS_TYPE_PALADIN) sCombatAI = "ai_paladin";
            else if(nClass == CLASS_TYPE_RANGER) sCombatAI = "ai_ranger";
            else if(nClass == CLASS_TYPE_ROGUE) sCombatAI = "ai_rogue";
            else if(nClass == CLASS_TYPE_SORCERER) sCombatAI = "ai_sorcerer";
            else if(nClass == CLASS_TYPE_WIZARD) sCombatAI = "ai_wizard";
            //else if(nClass == CLASS_TYPE_ABERRATION) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_ANIMAL) sCombatAI = "ai_animal";
            //else if(nClass == CLASS_TYPE_CONSTRUCT) sCombatAI = "ai_animal";
            else if(nClass == CLASS_TYPE_DRAGON) sCombatAI = "ai_dragon";
            //else if(nClass == CLASS_TYPE_ELEMENTAL) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_FEY) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_GIANT) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_HUMANOID) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_MAGICAL_BEAST) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_MONSTROUS) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_OOZE) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_OUTSIDER) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_UNDEAD) sCombatAI = "ai_default";
            //else if(nClass == CLASS_TYPE_VERMIN) sCombatAI = "ai_animal";
            else sCombatAI = "ai_default";
        }
    }
    if(AI_DEBUG) ai_Debug("0i_combat", "3740", GetName(oCreature) + " is setting AI to " + sCombatAI);
    SetLocalString(oCreature, AI_DEFAULT_SCRIPT, sCombatAI);
    SetLocalString(oCreature, AI_COMBAT_SCRIPT, sCombatAI);
}
int ai_IsImmuneToSneakAttacks(object oCreature, object oTarget)
{
    if(GetHasFeat(FEAT_UNCANNY_DODGE_2, oTarget) &&
       GetLevelByClass(CLASS_TYPE_ROGUE, oCreature) + 3 < GetLevelByClass(CLASS_TYPE_ROGUE, oTarget)) return TRUE;
    if(GetIsImmune(oTarget, IMMUNITY_TYPE_SNEAK_ATTACK)) return TRUE;
    object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTarget);
    if(ai_GetHasItemProperty(oSkin, ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS, IP_CONST_IMMUNITYMISC_BACKSTAB)) return TRUE;
    return FALSE;
}
int ai_IsStrongerThanMe(object oCreature, int nIndex)
{
    int nEnemyCombat = GetLocalInt(oCreature, AI_ENEMY_COMBAT + IntToString(nIndex));
    int nCreatureCombat = ai_GetMyCombatRating(oCreature);
    if(AI_DEBUG) ai_Debug("0i_combat", "3955", "IsStrongerThanMe: nCreatureCombat: " +
           IntToString(nCreatureCombat) + " nEnemyCombat: " + IntToString(nEnemyCombat));
    return (nEnemyCombat > nCreatureCombat);
}
int ai_StrongOpponent(object oCreature, object oTarget, int nAdj = 2)
{
    int nLevel = GetHitDice(oCreature);
    if(AI_DEBUG) ai_Debug("0i_combat", "3220", "ai_StrongOpponent");
    nAdj = nAdj *((nAdj + nLevel) / 10);
    if(AI_DEBUG) ai_Debug("0i_combat", "3222", "Is the opponent strong? Target CR >= Our level - nAdj(" +
              FloatToString(GetChallengeRating(oTarget), 0, 2) + " >= " + IntToString(nLevel - nAdj) + ")");
    return (FloatToInt(GetChallengeRating(oTarget)) >= nLevel - nAdj);
}
int ai_PowerAttackGood(object oCreature, object oTarget, float fAdj)
{
    int nAvgDmg = ai_GetWeaponDamage(oCreature, 2);
    if(AI_DEBUG) ai_Debug("0i_combat", "3412", "PowerAttack: (nAvgDmg: " + IntToString(nAvgDmg) +
             " > Target HP: " + IntToString(GetCurrentHitPoints(oTarget)) +
             ") Skip: " + IntToString(nAvgDmg > GetCurrentHitPoints(oTarget)));
    if(nAvgDmg > GetCurrentHitPoints(oTarget)) return FALSE;
    float fAvgDmg = IntToFloat(nAvgDmg);
    float fTargetAC = IntToFloat(GetAC(oTarget));
    float fCreatureAtk = IntToFloat(ai_GetCreatureAttackBonus(oCreature));
    float fNormalChance = (21.0 - (fTargetAC - fCreatureAtk)) / 20.0;
    // Our chance to hit is already minimum of 5% so this doesn't hurt our chance!
    if(fNormalChance <= 0.05) return TRUE;
    float fAdjDamage = (fAvgDmg + fAdj) * ((21.0-(fTargetAC - fCreatureAtk + fAdj))/20);
    if(AI_DEBUG) ai_Debug("0i_combat", "3420", "fNormalDamage: " + FloatToString(fNormalChance * fAvgDmg, 0, 2) +
             " < fAdjDamage: " + FloatToString(fAdjDamage, 0, 2) + " = " + IntToString(fNormalChance * fAvgDmg < fAdjDamage));
    return fNormalChance * fAvgDmg < fAdjDamage;
}
int ai_AttackPenaltyOk(object oCreature, object oTarget, float fAtkAdj)
{
    float fTargetAC = IntToFloat(GetAC(oTarget));
    float fCreatureAtk = IntToFloat(ai_GetCreatureAttackBonus(oCreature));
    float fNormalChance = (21.0-(fTargetAC - fCreatureAtk))/20.0;
    if(AI_DEBUG) ai_Debug("0i_combat", "3431", "Normal Avg Chance: " + FloatToString(fNormalChance, 0, 2) + " <= 0.05");
    // We already need a 20 to hit so this doesn't hurt our chances!
    if(fNormalChance <= 0.05) return TRUE;
    float fAdjChance = (21.0-(fTargetAC - fCreatureAtk + fAtkAdj))/20.0;
    if(AI_DEBUG) ai_Debug("0i_combat", "3435", "Adjusted Avg Chance: " + FloatToString(fAdjChance, 0, 2) + " > 0.55");
    // if our chance is 55% or better to hit then use it.
    return fAdjChance > 0.55;
}
int ai_AttackBonusGood(object oCreature, object oTarget, float fAtkAdj)
{
    float fTargetAC = IntToFloat(GetAC(oTarget));
    float fCreatureAtk = IntToFloat(ai_GetCreatureAttackBonus(oCreature));
    float fNormalChance = (21.0-(fTargetAC - fCreatureAtk))/20.0;
    if(AI_DEBUG) ai_Debug("0i_combat", "3450", "Normal Avg Chance: " + FloatToString(fNormalChance, 0, 2) + " > 0.99");
    // We already hit them with any roll so this will not help.
    if(fNormalChance > 0.99) return FALSE;
    float fAdjChance = (21.0-(fTargetAC - fCreatureAtk - fAtkAdj))/20.0;
    if(AI_DEBUG) ai_Debug("0i_combat", "3454", "Adjusted Avg Chance: " + FloatToString(fAdjChance, 0, 2) + " < 0.0");
    // if our chance increases our to hit then this is good.
    return fAdjChance > 0.0;
}
int ai_ACAdjustmentGood(object oCreature, object oTarget, float fACAdj)
{
    float fCreatureAC = IntToFloat(GetAC(oCreature));
    float fTargetAtk = IntToFloat(ai_GetCreatureAttackBonus(oTarget));
    float fNormalChance = (21.0-(fCreatureAC - fTargetAtk))/20.0;
    if(AI_DEBUG) ai_Debug("0i_combat", "3444", "Normal Chance To Hit: " + FloatToString(fNormalChance, 0, 2) + " <= 0.05");
    // They already need a 20 to hit so adding more AC is worthless.
    if(fNormalChance <= 0.05) return FALSE;
    float fAdjChance = (21.0-(fCreatureAC - fTargetAtk + fACAdj))/20.0;
    if(AI_DEBUG) ai_Debug("0i_combat", "3448", "Adjusted Chance To Hit: " + FloatToString(fAdjChance, 0, 2) + " < 1.00");
    // Anything less than 1 helps are AC!
    return fAdjChance < 1.00;
}
int ai_ACAdjustmentPenalty(object oCreature, object oTarget, float fACAdj)
{
    float fCreatureAC = IntToFloat(GetAC(oCreature));
    float fTargetAtk = IntToFloat(ai_GetCreatureAttackBonus(oTarget));
    float fToHitChance = (21.0-(fCreatureAC - fTargetAtk + fACAdj))/20.0;
    float fRoll = IntToFloat(d100());
    if(AI_DEBUG) ai_Debug("0i_combat", "3380", "Adjusted Chance To Hit: " + FloatToString(fToHitChance, 0, 2) + " < " +
                          FloatToString(fRoll, 0, 2) + " return TRUE");
    // Roll % chance to see if we should ignore the penalty and go for it!
    return fToHitChance < fRoll;
}
int ai_CanIMoveInCombat(object oCreature)
{
    // DC 15 tumble check is required to not give attacks of opportunity.
    return (GetHasFeat(FEAT_MOBILITY, oCreature) || GetHasFeat(FEAT_SPRING_ATTACK, oCreature) ||
            GetSkillRank(SKILL_TUMBLE, oCreature) > 9);
}
int ai_CanIUseRangedWeapon(object oCreature, int nInMelee)
{
    return (!nInMelee || ai_GetEnemyAttackingMe(oCreature) == OBJECT_INVALID);
}
int ai_CheckRangedCombatPosition(object oCreature, object oTarget, int nAction)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "3559", "Ranged attack: See oTarget? " +
               IntToString(GetObjectSeen(oTarget, oCreature)) + " Line of Sight? " +
               IntToString(LineOfSightObject(oCreature, oTarget)));
    if(nAction == AI_LAST_ACTION_RANGED_ATK)
    {
        // Watch the nearest enemy instead of our target as they could move toward us.
        object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST);
        float fDistance = GetDistanceBetween(oCreature, oNearestEnemy);
        if(AI_DEBUG) ai_Debug("0i_combat", "3337", "oNearestEnemy: " + GetName(oNearestEnemy) +
                 " fDistance: " + FloatToString(fDistance, 0, 2));
        // If we have sneak attack then we want to be within 30'.
        if(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature))
        {
            if(fDistance > AI_RANGE_CLOSE)
            {
                // We check this because if the enemy is moving or has not
                // started acting then we don't want to move up on them as they
                // might move towards us. Just attack! Only sneak attack if they are busy.
                int nAction = GetCurrentAction(oNearestEnemy);
                if(AI_DEBUG) ai_Debug("0i_combat", "3353", GetName(oNearestEnemy) + " current action: " + IntToString(nAction));
                if(nAction == ACTION_MOVETOPOINT ||
                   nAction == ACTION_INVALID ||
                   nAction == ACTION_RANDOMWALK) return FALSE;
                // If they are attacking make sure it is in melee?
                // If not then don't move since they might be moving toward us.
                if(nAction == ACTION_ATTACKOBJECT)
                {
                    if(!ai_GetNumOfEnemiesInRange(oNearestEnemy)) return FALSE;
                }
                if(AI_DEBUG) ai_Debug("0i_combat", "3355", GetName(oCreature) + " is moving closer [8.0] to " +
                         GetName(oNearestEnemy) + " to sneak attack with a ranged weapon.");
                ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE);
                ActionMoveToObject(oNearestEnemy, TRUE, AI_RANGE_CLOSE);
                ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature));
                return TRUE;
            }
        }
        else if(fDistance < AI_RANGE_LONG)
        {
            // Lets move back a little, too far and we miss attacks!
            if(AI_DEBUG) ai_Debug("0i_combat", "3374", GetName(oCreature) + " is moving away from " +
                     GetName(oNearestEnemy) + "[2.0] to use a ranged weapon.");
            ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE);
            ActionMoveAwayFromObject(oNearestEnemy, TRUE, 2.0);
            ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature));
            return TRUE;
        }
    }
    // If we are casting a hostile spell then check positioning.
    else if(nAction > -1 && Get2DAString("ai_spells", "HostileSetting", nAction) == "1")
    {
        // We are out of melee and casting a spell on an ally so don't move.
        if(GetReputation(oCreature, oTarget) > 89) return FALSE;
        float fSpellRange = ai_GetSpellRange(nAction);
        float fTargetRange = GetDistanceBetween(oCreature, oTarget);
        if(AI_DEBUG) ai_Debug("0i_combat", "3389", "fSpellRange: " + FloatToString(fSpellRange, 0, 2) +
                 " fTargetRange: " + FloatToString(fTargetRange, 0, 2));
        // Adjust the ranges to see if we are too close.
        if(fSpellRange == 5.0) fSpellRange = 4.5f;
        //else if(fSpellRange == 8.0) fSpellRange = 8.0f;
        else if(fSpellRange > 10.0f) fSpellRange = 10.0f;
        if(AI_DEBUG) ai_Debug("0i_combat", "3395", "Adjusted spell range is " +
                 FloatToString(fSpellRange, 0, 2) + " : " + GetName(oTarget) + " range is " +
                 FloatToString(fTargetRange, 0, 2) + ".");
        // We are closer than we have to be to cast our spell.
        if(fTargetRange < fSpellRange)
        {
            // Lets move back a little, too far and we miss attacks!
            if(AI_DEBUG) ai_Debug("0i_combat", "3402", GetName(oCreature) + " is moving away from " +
                     GetName(oTarget) + "[2.0] to cast a spell.");
            ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE);
            ActionMoveAwayFromObject(oTarget, FALSE, 2.0);
            ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature));
            return TRUE;
        }
}
    return FALSE;
}
int ai_CheckMeleeCombatPosition(object oCreature, object oTarget, int nAction, int nBaseItemType = 0)
{
    // If we are not being attacked then we might want to back out of combat.
    if(ai_GetEnemyAttackingMe(oCreature) != OBJECT_INVALID)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3417", "I am being attacked so stand my ground!");
        return FALSE;
    }
    object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST);
    float fDistance = GetDistanceBetween(oCreature, oNearestEnemy);
    if(AI_DEBUG) ai_Debug("0i_combat", "3422", "oNearestEnemy: " + GetName(oNearestEnemy) + " fDistance " + FloatToString(fDistance, 0, 2));
    if(nAction == AI_LAST_ACTION_RANGED_ATK)
    {
        if(AI_DEBUG) ai_Debug("0i_combat", "3425", GetName(oCreature) + " is moving away from " + GetName(oNearestEnemy) +
                 "[" + FloatToString(AI_RANGE_MELEE - fDistance + 1.0, 0, 2) + "]" + " to use a ranged weapon.");
        ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE);
        // Lets move just out of melee range!
        int bRun = ai_CanIMoveInCombat(oCreature);
        ActionMoveAwayFromObject(oNearestEnemy, bRun, AI_RANGE_MELEE - fDistance + 1.0);
        ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature));
        return TRUE;
    }
    // If we want to cast a spell this round then back away!
    else if(nAction > -1)
    {
        // Some items we don't need to move on such as wands, staves, and rods.
        if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND ||
           nBaseItemType == BASE_ITEM_MAGICWAND ||
           nBaseItemType == BASE_ITEM_MAGICSTAFF ||
           nBaseItemType == BASE_ITEM_MAGICROD) return FALSE;
        float fSpellRange = ai_GetSpellRange(nAction);
        // A Touch spell means we should not move if we are not the target.
        if(fSpellRange <= 5.0 && oCreature != oTarget) return FALSE;
        if(AI_DEBUG) ai_Debug("0i_combat", "3446", GetName(oCreature) + " is moving away from " +
                 GetName(oTarget) + "[" + FloatToString(AI_RANGE_MELEE - fDistance + 1.0, 0, 2) + "] to cast a spell.");
        ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE);
        SetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST, FALSE);
        // Lets move just out of melee range!
        int bRun = ai_CanIMoveInCombat(oCreature);
        ActionMoveAwayFromObject(oNearestEnemy, bRun, AI_RANGE_MELEE - fDistance + 1.0);
        ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature));
        return TRUE;
    }
    return FALSE;
}
int ai_CheckCombatPosition(object oCreature, object oTarget, int nInMelee, int nAction, int nBaseItemType = 0)
{
    if(AI_DEBUG) ai_Debug("0i_combat", "3460", "|-----> Checking position in combat: " +
             GetName(oCreature) + " nMelee: " + IntToString(nInMelee) +
             " Action: " + IntToString(nAction) +
             " Hold mode: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) +
             " Use Advanced Movement: " + IntToString(GetLocalInt(GetModule(), AI_RULE_ADVANCED_MOVEMENT)));
    // We don't want to move around in combat if we were told to hold.
    if(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) return FALSE;
    if(!GetLocalInt(GetModule(), AI_RULE_ADVANCED_MOVEMENT)) return FALSE;
    if(ai_CompareLastAction(oCreature, AI_LAST_ACTION_MOVE)) return FALSE;
    // We are not in melee combat so lets see how close we should get.
    if(!nInMelee) return ai_CheckRangedCombatPosition(oCreature, oTarget, nAction);
    // If we are in melee we might need to move out of combat.
    return ai_CheckMeleeCombatPosition(oCreature, oTarget, nAction, nBaseItemType);
}
//:: Calculates total Shield AC bonuses from all sources
int ai_GetTotalShieldACBonus(object oCreature)
{
    int nShieldBonus = 0;
    object oItem;
    // Check left hand for shield
    oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature);
    if (GetIsObjectValid(oItem))
    {
        int nBaseItem = GetBaseItemType(oItem);
        if (nBaseItem == BASE_ITEM_SMALLSHIELD ||
            nBaseItem == BASE_ITEM_LARGESHIELD ||
            nBaseItem == BASE_ITEM_TOWERSHIELD)
        {
            nShieldBonus += GetItemACValue(oItem);
                     if(AI_DEBUG) ai_Debug("0i_combat", "3534","ai_GetTotalShieldACBonus: Found Shield AC, bonus = " + IntToString(nShieldBonus)+".");
        }
    }
    // Check creature weapon slots for shield AC bonus
    oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature);
    if(GetIsObjectValid(oItem)) nShieldBonus += GetItemACValue(oItem);
    oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCreature);
    if(GetIsObjectValid(oItem)) nShieldBonus += GetItemACValue(oItem);
    oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCreature);
    if(GetIsObjectValid(oItem)) nShieldBonus += GetItemACValue(oItem);
    // Add shield AC bonuses from magical effects
       effect eEffect = GetFirstEffect(oCreature);
    while(GetIsEffectValid(eEffect))
    {
              int nACType = GetEffectInteger(eEffect, 0);
              int nACAmount = GetEffectInteger(eEffect, 1);
              if(GetEffectType(eEffect) == EFFECT_TYPE_AC_INCREASE && nACType == AC_SHIELD_ENCHANTMENT_BONUS)
                {
                     if(AI_DEBUG) ai_Debug("0i_combat", "3560", "ai_GetTotalShieldACBonus: Found Shield AC effect, bonus = " + IntToString(nACAmount)+".");
                     nShieldBonus += nACAmount;
                }
        eEffect = GetNextEffect(oCreature);
        }
    return nShieldBonus;
}
//:: [PRC8] Returns special grapple size modifier
int ai_GetSpecialGrappleSizeMod(int nSize)
{
    switch(nSize)
    {
        case -1 /* CREATURE_SIZE_FINE */:        return -16;
        case 0 /* CREATURE_SIZE_DIMINUTIVE */:  return -12;
        case CREATURE_SIZE_TINY:        return -8;
        case CREATURE_SIZE_SMALL:       return -4;
        case CREATURE_SIZE_MEDIUM:      return 0;
        case CREATURE_SIZE_LARGE:       return 4;
        case CREATURE_SIZE_HUGE:        return 8;
        case 6 /* CREATURE_SIZE_GARGANTUAN */:  return 12;
        case 7 /* CREATURE_SIZE_COLOSSAL */:    return 16;
    }
    return 0;
}
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Tripping
int ai_CanITrip(object oCreature, object oTarget)
{
    //:: Sanity check
    if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE;
    if(!GetHasFeat(2822, oCreature))
          return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3594", "ai_CanITrip: Sanity Checks PASSED!");
    //:: Get stats
    int nStrTripper = GetAbilityModifier(ABILITY_STRENGTH, oCreature);
    int nStrTarget  = GetAbilityModifier(ABILITY_STRENGTH, oTarget);
    int nDexTarget  = GetAbilityModifier(ABILITY_DEXTERITY, oTarget);
    //:: Defender uses best of STR or DEX
    int nDefenderMod = (nDexTarget > nStrTarget) ? nDexTarget : nStrTarget;
    //:: Tripper bonuses
    if (GetHasFeat(2807, oCreature))  //:: FEAT_IMPROVED_TRIP
        {  // +4 from Improved Trip
        nStrTripper += 4;
        }
    //:: Get sizes
    int nSizeTripper = GetCreatureSize(oCreature);
    int nSizeTarget  = GetCreatureSize(oTarget);
    //:: Size mod = 4 * (size - medium)
    nStrTripper += (nSizeTripper - CREATURE_SIZE_MEDIUM) * 4;
    nDefenderMod += (nSizeTarget - CREATURE_SIZE_MEDIUM) * 4;
    int nStability = 0;
    //:: Extra Stability if creature is dwarf or quadruped
    if(GetRacialType(oTarget) == RACIAL_TYPE_DWARF ||
             GetRacialType(oTarget) == RACIAL_TYPE_ANIMAL ||
       GetRacialType(oTarget) == RACIAL_TYPE_MAGICAL_BEAST ||
       GetRacialType(oTarget) == RACIAL_TYPE_VERMIN)
    {
        nStability = 4;
    }
    nDefenderMod += nStability;
    //:: If effective trip mod is too much lower, don't bother
    if(nStrTripper + 2 < nDefenderMod) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3632", "ai_CanITrip: Size Checks PASSED!");
    if(AI_DEBUG) ai_Debug("0i_combat", "3634", "ai_CanITrip: Can use Trip on oTarget.");
       return TRUE;
}
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Awesome Blow
int ai_CanIAwesomeBlow(object oCreature, object oTarget)
{
    //:: Sanity check
       if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3642", "ai_CanIAwesomeBlow: Sanity check PASSED!");
    //:: Can't Awesome Blow incorporeal creatures
    if(GetLocalInt(oTarget, "X2_L_IS_INCORPOREAL")) return FALSE;
    if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_ETHEREAL)) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3650", "ai_CanIAwesomeBlow: Incorporealty check PASSED!");
    //:: Get sizes
    int nSizeCreature   = GetCreatureSize(oCreature);
    int nSizeTarget     = GetCreatureSize(oTarget);
    //:: Must be larger than target
    if (nSizeCreature <= nSizeTarget) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3660", "ai_CanIAwesomeBlow: Size check PASSED!");
       //:: Get target's AC
       int nTargetAC    = GetAC(oTarget);
    //:: Awesome Blow applies -4 penalty
       int nAttackBonus = GetBaseAttackBonus(oCreature) - 4;
    //:: Calculate average hit chance.
       int nHitChance = 10 + nAttackBonus - nTargetAC;
    //:: If hit chance <= 50%, don’t bother
    if (nHitChance <= 5) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3674", "ai_CanIAwesomeBlow: Attack check PASSED!");
    if(AI_DEBUG) ai_Debug("0i_combat", "3676", "ai_CanIAwesomeBlow: Can use Awesome Blow on oTarget.");
          return TRUE;
}
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Overrun
int ai_CanIOverrun(object oCreature, object oTarget)
{
    //:: FEAT_OVERRUN (2811)
       //:: FEAT_IMPROVED_OVERRUN (2805)
    //:: Sanity check
    if (!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "1281", "ai_CanIOverrun: Sanity Check PASSED!");
    //:: Get stats
    int nStrRunner = GetAbilityModifier(ABILITY_STRENGTH, oCreature);
    int nStrTarget  = GetAbilityModifier(ABILITY_STRENGTH, oTarget);
    int nDexTarget  = GetAbilityModifier(ABILITY_DEXTERITY, oTarget);
    //:: Defender uses best of STR or DEX
    int nDefenderMod = (nDexTarget > nStrTarget) ? nDexTarget : nStrTarget;
    //:: Overruner bonuses
    if (GetHasFeat(2805, oCreature))
        {  // +4 from Improved Overrun
        nStrRunner += 4;
        }
    //:: Get sizes
    int nSizeRunner = GetCreatureSize(oCreature);
    int nSizeTarget  = GetCreatureSize(oTarget);
    //:: Size mod = 4 * (size - medium)
    nStrRunner += (nSizeRunner - CREATURE_SIZE_MEDIUM) * 4;
    nDefenderMod += (nSizeTarget - CREATURE_SIZE_MEDIUM) * 4;
    int nStability = 0;
    //:: Extra Stability if creature is dwarf or quadruped
    if(GetRacialType(oTarget) == RACIAL_TYPE_DWARF ||
             GetRacialType(oTarget) == RACIAL_TYPE_ANIMAL ||
       GetRacialType(oTarget) == RACIAL_TYPE_MAGICAL_BEAST ||
       GetRacialType(oTarget) == RACIAL_TYPE_VERMIN)
    {
        nStability = 4;
    }
    nDefenderMod += nStability;
    //:: If effective STR mod is too much lower, don't bother
    if(nStrRunner + 2 < nDefenderMod) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3731", "ai_CanIOverrun: Size & Stabilty Check PASSED!");
    if(AI_DEBUG) ai_Debug("0i_combat", "3733", "ai_CanIOverrun: Using Overrun on oTarget.");
    return TRUE;
}
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Bullrush
int ai_CanIBullrush(object oCreature, object oTarget)
{
    //:: FEAT_BULLRUSH (2810)
    //:: FEAT_IMPROVED_BULLRUSH (2806)
    //:: FEAT_BULLRUSH_CHARGE (3413)
    if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3746", "ai_CanIBullrush: Sanity Check PASSED!");
    //:: Get movement speed
    //int nSpeed = StringToInt(Get2DAString("racialtypes", "Endurance", GetRacialType(oCreature)));
       float fSpeed = IntToFloat(10 * GetMovementRate(oCreature));
    //:: If target is too far away to reach in one round, return
    if(GetDistanceBetween(oCreature, oTarget) > 1.5 * fSpeed) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3755", "ai_CanIBullrush: Distance Check PASSED!");
    //:: Get attacker Strength modifier
    int nStrRusher = GetAbilityModifier(ABILITY_STRENGTH, oCreature);
    //:: Defender uses best of STR or DEX
    int nStrTarget = GetAbilityModifier(ABILITY_STRENGTH, oTarget);
    int nDexTarget = GetAbilityModifier(ABILITY_DEXTERITY, oTarget);
    int nDefenderMod = (nDexTarget > nStrTarget) ? nDexTarget : nStrTarget;
    //:: Improved Bull Rush bonus
    if(GetHasFeat(2806 /* FEAT_IMPROVED_BULLRUSH */, oCreature)) nStrRusher += 4;
    //:: Size modifiers
    int nSizeRusher = GetCreatureSize(oCreature);
    int nSizeTarget = GetCreatureSize(oTarget);
    nStrRusher     += (nSizeRusher - CREATURE_SIZE_MEDIUM) * 4;
    nDefenderMod   += (nSizeTarget - CREATURE_SIZE_MEDIUM) * 4;

    //:: Stability bonus (+4) for dwarves, quadrupeds, etc.
    int nRace = GetRacialType(oTarget);
    if (nRace == RACIAL_TYPE_DWARF ||
        nRace == RACIAL_TYPE_ANIMAL ||
        nRace == RACIAL_TYPE_MAGICAL_BEAST ||
        nRace == RACIAL_TYPE_VERMIN)
    {
        nDefenderMod += 4;
    }
    //:: Require at least +2 margin to attempt
    if (nStrRusher + 2 < nDefenderMod) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3789", "ai_CanIBullrush: Size & Stability Check PASSED!");
    if(AI_DEBUG) ai_Debug("0i_combat", "3791", "ai_CanIBullrush: Can use Bullrush on oTarget.");
    return TRUE;
}
//:: [PRC8] Returns TRUE if oTarget is a good candidate for Grappling
int ai_CanIGrapple(object oCreature, object oTarget)
{
    //:: FEAT_CM_GRAPPLE (3414)
       //:: FEAT_IMPROVED_GRAPPLE (2804)
       //:: FEAT_DEVASTATING_GRAPPLE (4246)
    //:: Sanity checks
    if (!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE;
    if (!GetHasFeat(3414, oCreature)) return FALSE;
    //:: Can't grapple incorporeal or gaseous creatures
    if (GetLocalInt(oTarget, "X2_L_IS_INCORPOREAL") || ai_GetHasEffectType(oTarget, EFFECT_TYPE_ETHEREAL)) return FALSE;
    int nSizeCreature = GetCreatureSize(oCreature);
    int nSizeTarget   = GetCreatureSize(oTarget);
    //:: Automatically fail if target is two or more size categories larger
    if (nSizeTarget - nSizeCreature >= 2) return FALSE;
    //:: Get BAB and STR for grapple calculation
    int nBAB = GetBaseAttackBonus(oCreature);
    int nStrMod = GetAbilityModifier(ABILITY_STRENGTH, oCreature);
    int nSizeMod = ai_GetSpecialGrappleSizeMod(nSizeCreature);
    int nBonusGrapple = 0;
    if (GetHasFeat(2804 /*Improved Grapple*/, oCreature)) nBonusGrapple += 2;
    if (GetHasFeat(4246 /*Devastating Grapple*/, oCreature)) nBonusGrapple += 5;
    int nGrappleScore = nBAB + nStrMod + nSizeMod + nBonusGrapple;
    //:: Estimate target's grapple score defensively
    int nTargetStrMod = GetAbilityModifier(ABILITY_STRENGTH, oTarget);
    int nTargetSizeMod = ai_GetSpecialGrappleSizeMod(nSizeTarget);
    int nTargetBAB = GetBaseAttackBonus(oTarget);
    int nTargetGrapple = nTargetBAB + nTargetStrMod + nTargetSizeMod;
    int nWinChance = 10 + nGrappleScore - nTargetGrapple;
    //:: If chance to win grapple check < 60%, don’t bother
    if (nWinChance < 6) return FALSE;
    if(AI_DEBUG) ai_Debug("0i_combat", "3743", "Can use Grapple on oTarget."); return TRUE;
}
