/*

    Henchman Inventory And Battle AI

    This file contains functions used in the default On* scripts
    for combat. It acts as filter preventing the main hench_o0_ai
    from being called more than it needs to. (Usually only to start
    combat, heartbeat, or end combat round.)

*/

#include "hench_i0_generic"
#include "hench_i0_options"
#include "hench_i0_melee"


// void main() {    }


// Sets the location of an unheard or unseen enemy
// Either moved out of range or did attack while not detected
void SetEnemyLocation(object oEnemy);

// Clears the last unheard, unseen enemy location
void ClearEnemyLocation();

// Moves to the last perceived enemy location
void MoveToLastSeenOrHeard();

// Cleans all temporary values used during combat
void CleanCombatVars();

// New determinecombatround call. Determines if call should cause
// main AI script to run
void HenchDetermineCombatRound(object oIntruder = OBJECT_INVALID, int bForceInterrupt = FALSE);

// Simplified combat start, used by commoners
void HenchStartAttack(object oIntruder);

// modified TalentFlee routine
int HenchTalentFlee(object oIntruder = OBJECT_INVALID);

// modified GetNearestSeenOrHeardEnemy to not get dead creatures
object GetNearestSeenOrHeardEnemyNotDead(int bCheckIsPC = FALSE);

// modified DetermineSpecialBehavior, calls HenchDetermineCombatRound instead
void HenchDetermineSpecialBehavior(object oIntruder = OBJECT_INVALID, int bForceInterrupt = FALSE);

// used to attack non creature items (placeables and doors)
void HenchAttackObject(object oTarget);

// associate (henchman, etc.) determines if enemy is perceived or not
int HenchGetIsEnemyPerceived();

// modified TalentAdvancedBuff routine
int HenchTalentAdvancedBuff(float fDistance);

// get nearest ally that is higher in hit dice
object GetNearestTougherFriend(object oSelf, object oPC);

// main stealth and wandering code
void DoStealthAndWander();

// code for bashing placeables
int HenchBashDoorCheck(object oIntruder = OBJECT_INVALID);



void SetEnemyLocation(object oEnemy)
{
    SetLocalInt(OBJECT_SELF, "LastSeenOrHeard", TRUE);
    
    location enemyLocation = GetLocation(oEnemy);
    SetLocalLocation(OBJECT_SELF, "LastSeenOrHeard", enemyLocation);
    
    // Get the area, position and facing
    object   oArea       = GetAreaFromLocation (enemyLocation);
    vector   vPosition   = GetPositionFromLocation (enemyLocation);
    float    fFacing     = GetFacingFromLocation (enemyLocation);
    
    vPosition.z += 100.0;

    // Build a new location, which faces in the opposite direction
    enemyLocation = Location (oArea, vPosition, fFacing);

    object oInvisTarget = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_invisobj", enemyLocation); 
    SetLocalObject(OBJECT_SELF, "LastSeenOrHeard", oInvisTarget);
}


void ClearEnemyLocation()
{
    DeleteLocalInt(OBJECT_SELF, "LastSeenOrHeard");
    DeleteLocalLocation(OBJECT_SELF, "LastSeenOrHeard");
    
    object oInvisTarget = GetLocalObject(OBJECT_SELF, "LastSeenOrHeard");
    if (GetIsObjectValid(oInvisTarget))
    {    
        DestroyObject(oInvisTarget);
        DeleteLocalObject(OBJECT_SELF, "LastSeenOrHeard");
    }
}


void MoveToLastSeenOrHeard()
{
    location moveToLoc = GetLocalLocation(OBJECT_SELF, "LastSeenOrHeard");
    if (GetDistanceBetweenLocations(GetLocation(OBJECT_SELF), moveToLoc) <= 5.0)
    {
        ClearEnemyLocation();
        // go to search
        // TODO add spells to help search
        if (GetDetectMode(OBJECT_SELF) != DETECT_MODE_ACTIVE)
        {
            ActionUseSkill(SKILL_SEARCH, OBJECT_SELF);
        }
    }
    else
    {
        object oInvisTarget = GetLocalObject(OBJECT_SELF, "LastSeenOrHeard");
        ActionMoveToObject(oInvisTarget, TRUE);
    }
}


void CleanCombatVars()
{
    DeleteLocalInt(OBJECT_SELF, "tkCombatRoundCount");
    DeleteLocalInt(OBJECT_SELF, "tkLastDragonBreath");
    DeleteLocalInt(OBJECT_SELF, "nRoundsSkipped");
    DeleteLocalInt(OBJECT_SELF, "tkFirstSpellLoc");
    DeleteLocalInt(OBJECT_SELF, "tkNoAttackSpellTalents");
    DeleteLocalInt(OBJECT_SELF, "tkNoEnhanceSpellTalents");
    DeleteLocalInt(OBJECT_SELF, "tkNoConditionalSpellTalents");
    DeleteLocalInt(OBJECT_SELF, "tkNoPotions");
    DeleteLocalInt(OBJECT_SELF, "CloseRangeEnhanced");
    DeleteLocalObject(OBJECT_SELF, "LastTarget");
}


void HenchDetermineCombatRound(object oIntruder = OBJECT_INVALID, int bForceInterrupt = FALSE)
{
//    Jug_Debug(GetName(OBJECT_SELF) + " det com round called with " + GetName(oIntruder) + " force = " + IntToString(bForceInterrupt));
    string sAIScript = GetLocalString(OBJECT_SELF, "AIScript");
    if (sAIScript == "")
    {
        sAIScript = "hench_o0_ai";  
    }
    
    SetLocalObject(OBJECT_SELF, "AIIntruderObj", oIntruder);
    SetLocalInt(OBJECT_SELF, "AIIntruderForce", bForceInterrupt);    
    
    if (bForceInterrupt)
    {
        SetLocalInt(OBJECT_SELF, "AIIntruder", 1);
        ExecuteScript(sAIScript, OBJECT_SELF);
        return;
    }
    
    // check if we have to actually determine to rerun ai
    int iAIIntruderLevel = GetLocalInt(OBJECT_SELF, "AIIntruder");

    if (iAIIntruderLevel == 0)
    {
        // first run of HenchDetermineCombatRound
        SetLocalInt(OBJECT_SELF, "AIIntruder", 1);
        ExecuteScript(sAIScript, OBJECT_SELF);
        return;        
    }
    object oLastTarget = GetLocalObject(OBJECT_SELF, "LastTarget");

    if(!GetIsObjectValid(oLastTarget) || GetIsDead(oLastTarget) || !GetIsEnemy(oLastTarget) || GetLocalInt(oLastTarget,"RunningAway"))
    {
        oLastTarget = OBJECT_INVALID;
    }
    else if (!GetObjectSeen(oLastTarget) && !GetObjectHeard(oLastTarget))
    {
        oLastTarget = OBJECT_INVALID;
    }
    if (!GetIsObjectValid(oLastTarget))
    {
        // prevent too many calls to main script if already moving to an unseen and
        // unheard monster
        if (!GetLocalInt(OBJECT_SELF, "LastSeenOrHeard") || GetObjectSeen(oIntruder) || GetObjectHeard(oIntruder))
        {
            ExecuteScript(sAIScript, OBJECT_SELF);
        }
    }
    else if (GetIsObjectValid(oIntruder))
    {
        if (GetDistanceToObject(oIntruder) <= 5.0 && GetDistanceToObject(oLastTarget) > 5.0)
        {
            ExecuteScript(sAIScript, OBJECT_SELF);
        }
        else if (GetObjectSeen(oIntruder) && !GetObjectSeen(oLastTarget))
        {
            ExecuteScript(sAIScript, OBJECT_SELF);
        }
    }
}


void HenchStartAttack(object oIntruder)
{    
    SetLocalObject(OBJECT_SELF, "AIIntruderObj", oIntruder);
    ExecuteScript("hench_o0_att", OBJECT_SELF);
}


// FLEE COMBAT AND HOSTILES
int HenchTalentFlee(object oIntruder = OBJECT_INVALID)
{
    object oTarget = oIntruder;
    if(!GetIsObjectValid(oIntruder))
    {
        oTarget = GetLastHostileActor();
        if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget))
        {
            oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
            float fDist = GetDistanceBetween(OBJECT_SELF, oTarget);
            if(!GetIsObjectValid(oTarget))
            {
                return FALSE;
            }
        }
    }
    ClearAllActions();
    //Look at this to remove the delay
    ActionMoveAwayFromObject(oTarget, TRUE, 10.0f);
    DelayCommand(4.0, ClearAllActions());
    return TRUE;
}


object GetNearestSeenOrHeardEnemyNotDead(int bCheckIsPC = FALSE)
{
    object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
    if(!GetIsObjectValid(oTarget) && (GetGeneralOptions(HENCH_GENAI_ENABLEHEARING) & HENCH_GENAI_ENABLEHEARING))
    {
        int curCount = 1;
        while (curCount == 1 || GetIsObjectValid(oTarget))
        {            
            oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, curCount, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);            
            if (bCheckIsPC && !GetIsPC(GetTopMaster(oTarget)))
            {
                oTarget = OBJECT_INVALID;
            }
            curCount++;
        }
    }
    return oTarget;
}


//::///////////////////////////////////////////////
//:: Determine Special Behavior
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    Determines the special behavior used by the NPC.
    Generally all NPCs who you want to behave differently
    than the defualt behavior.
    For these behaviors, passing in a valid object will
    cause the creature to become hostile the the attacker.
    
    Modified by Tony K to call HenchDetermineCombatRound
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Dec 14, 2001
//:://////////////////////////////////////////////

void HenchDetermineSpecialBehavior(object oIntruder = OBJECT_INVALID, int bForceInterrupt = FALSE)
{
    object oTarget = GetNearestSeenOrHeardEnemyNotDead(HENCH_MONSTER_DONT_CHECK_HEARD_MONSTER);
    if(GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE))
    {
        int bAttack = FALSE;
        if(!GetIsObjectValid(oIntruder))
        {
            if(!GetIsObjectValid(GetAttemptedAttackTarget()) &&
               !GetIsObjectValid(GetAttemptedSpellTarget()) &&
               !GetIsObjectValid(GetAttackTarget()))
            {
                if(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 5.0)
                {
                    if(!GetIsFriend(oTarget))
                    {
                        if(GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0)
                        {
                            SetIsTemporaryEnemy(oTarget, OBJECT_SELF, FALSE, 20.0);
                            bAttack = TRUE;
                            HenchDetermineCombatRound(oTarget);
                        }
                    }
                }
            }
        }
        else if(!IsInConversation(OBJECT_SELF))
        {
            bAttack = TRUE;
            HenchDetermineCombatRound(oIntruder);
        }
        
        // * if not attacking, the wander
        if (bAttack == FALSE)
        {
            ClearAllActions();
            ActionRandomWalk();
            return;
        }
    }
    else if(GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE))
    {
        if(!GetIsObjectValid(GetAttemptedAttackTarget()) &&
           !GetIsObjectValid(GetAttemptedSpellTarget()) &&
           !GetIsObjectValid(GetAttackTarget()))
        {
            if(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 6.0)
            {
                if(!GetIsFriend(oTarget))
                {
                    if(GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0)
                    {
                        HenchTalentFlee(oTarget);
                    }
                }
            }
        }
        else if(!IsInConversation(OBJECT_SELF))
        {
            ClearAllActions();
            ActionRandomWalk();
            return;
        }
    }
}


void HenchAttackObject(object oTarget)
{
    if (GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)))
    {
        ActionAttack(oTarget);                        
    }
    else if(GetHasFeat(FEAT_IMPROVED_POWER_ATTACK))
    {
        ActionUseFeat(FEAT_IMPROVED_POWER_ATTACK, oTarget);
    }
    else if(GetHasFeat(FEAT_POWER_ATTACK))
    {
        ActionUseFeat(FEAT_POWER_ATTACK, oTarget);
    }
    else
    {
        ActionAttack(oTarget);
    }
}


int HenchGetIsEnemyPerceived()
{
    object oClosestSeen = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
                    OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
                    CREATURE_TYPE_IS_ALIVE, TRUE);

    if (GetIsObjectValid(oClosestSeen))
    {
        return TRUE;
    }
    if (GetGeneralOptions(HENCH_GENAI_ENABLEHEARING) & HENCH_GENAI_ENABLEHEARING)
    {
        object oClosestHeard = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
                        OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN,
                        CREATURE_TYPE_IS_ALIVE, TRUE);
        if (GetIsObjectValid(oClosestHeard))
        {
            if (GetDistanceToObject(oClosestHeard) <= fHenchHearingDistance)
            {        
                return TRUE;
            }
            object oRealMaster = GetRealMaster();
            if (GetIsObjectValid(oRealMaster))
            {
                if (GetDistanceBetween(oRealMaster, oClosestHeard) <= fHenchMasterHearingDistance)
                {        
                    return TRUE;
                }
            }
        }
    }
    return FALSE;
}


// FAST BUFF SELF
int HenchTalentAdvancedBuff(float fDistance)
{
    object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
    if(GetIsObjectValid(oPC) && GetDistanceToObject(oPC) <= fDistance)
    {
        if(!GetIsFighting(OBJECT_SELF))
        {
            ClearAllActions();
            //Combat Protections
            if(GetHasSpell(SPELL_PREMONITION))
            {
                ActionCastSpellAtObject(SPELL_PREMONITION, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_GREATER_STONESKIN))
            {
                ActionCastSpellAtObject(SPELL_GREATER_STONESKIN, OBJECT_SELF, METAMAGIC_NONE, 0, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_STONESKIN))
            {
                ActionCastSpellAtObject(SPELL_STONESKIN, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            //Visage Protections
            if(GetHasSpell(SPELL_SHADOW_SHIELD))
            {
                ActionCastSpellAtObject(SPELL_SHADOW_SHIELD, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_ETHEREAL_VISAGE))
            {
                ActionCastSpellAtObject(SPELL_ETHEREAL_VISAGE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_GHOSTLY_VISAGE))
            {
                ActionCastSpellAtObject(SPELL_GHOSTLY_VISAGE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            //Mantle Protections
            if(GetHasSpell(SPELL_GREATER_SPELL_MANTLE))
            {
                ActionCastSpellAtObject(SPELL_GREATER_SPELL_MANTLE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SPELL_MANTLE))
            {
                ActionCastSpellAtObject(SPELL_SPELL_MANTLE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_LESSER_SPELL_BREACH))
            {
                ActionCastSpellAtObject(SPELL_LESSER_SPELL_BREACH, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            // Globes
            if(GetHasSpell(SPELL_GLOBE_OF_INVULNERABILITY))
            {
                ActionCastSpellAtObject(SPELL_GLOBE_OF_INVULNERABILITY, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_MINOR_GLOBE_OF_INVULNERABILITY))
            {
                ActionCastSpellAtObject(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }

            //Misc Protections
            if(GetHasSpell(SPELL_ELEMENTAL_SHIELD))
            {
                ActionCastSpellAtObject(SPELL_ELEMENTAL_SHIELD, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }

            //Elemental Protections
            if(GetHasSpell(SPELL_PROTECTION_FROM_ELEMENTS))
            {
                ActionCastSpellAtObject(SPELL_PROTECTION_FROM_ELEMENTS, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_RESIST_ELEMENTS))
            {
                ActionCastSpellAtObject(SPELL_RESIST_ELEMENTS, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_ENDURE_ELEMENTS))
            {
                ActionCastSpellAtObject(SPELL_ENDURE_ELEMENTS, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }

            //Mental Protections
            if(GetHasSpell(SPELL_MIND_BLANK))
            {
                ActionCastSpellAtObject(SPELL_MIND_BLANK, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_LESSER_MIND_BLANK))
            {
                ActionCastSpellAtObject(SPELL_LESSER_MIND_BLANK, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_CLARITY))
            {
                ActionCastSpellAtObject(SPELL_CLARITY, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            //Summon Ally
            // TODO add gate!!!!
            if(GetHasSpell(SPELL_ELEMENTAL_SWARM))
            {
                ActionCastSpellAtLocation(SPELL_ELEMENTAL_SWARM, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_IX))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_IX, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_CREATE_GREATER_UNDEAD))
            {
                ActionCastSpellAtLocation(SPELL_CREATE_GREATER_UNDEAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_GREATER_PLANAR_BINDING))
            {
                ActionCastSpellAtLocation(SPELL_GREATER_PLANAR_BINDING, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_VIII))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_VIII, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_MORDENKAINENS_SWORD))
            {
                ActionCastSpellAtLocation(SPELL_MORDENKAINENS_SWORD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_VII))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_VII, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_CREATE_UNDEAD))
            {
                ActionCastSpellAtLocation(SPELL_CREATE_UNDEAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_PLANAR_BINDING))
            {
                ActionCastSpellAtLocation(SPELL_PLANAR_BINDING, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_VI))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_VI, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_V))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_V, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELLABILITY_SUMMON_SLAAD))
            {
                ActionCastSpellAtLocation(SPELLABILITY_SUMMON_SLAAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELLABILITY_SUMMON_TANARRI))
            {
                ActionCastSpellAtLocation(SPELLABILITY_SUMMON_TANARRI, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELLABILITY_SUMMON_MEPHIT))
            {
                ActionCastSpellAtLocation(SPELLABILITY_SUMMON_MEPHIT, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELLABILITY_SUMMON_CELESTIAL))
            {
                ActionCastSpellAtLocation(SPELLABILITY_SUMMON_CELESTIAL, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_ANIMATE_DEAD))
            {
                ActionCastSpellAtLocation(SPELL_ANIMATE_DEAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_IV))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_IV, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_III))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_III, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_II))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_II, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            else if(GetHasSpell(SPELL_SUMMON_CREATURE_I))
            {
                ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_I, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
            }
            return TRUE;
        }
    }
    return FALSE;
}


object GetNearestTougherFriend(object oSelf, object oPC)
{
    int i = 0;

    object oFriend = oSelf;

    int nEqual = 0;
    int nNear = 0;
    while (GetIsObjectValid(oFriend)) {
      if (GetDistanceBetween(oSelf,oFriend) < 40.0 && oFriend != oSelf) {
        ++nNear;
        if (GetHitDice(oFriend) > GetHitDice(oSelf))
                return oFriend;
        if (GetHitDice(oFriend) == GetHitDice(oSelf))
            ++nEqual;
      }
      ++i;
      oFriend =  GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
                                         oSelf, i);
    }

    SetLocalInt(OBJECT_SELF,"LocalBoss",FALSE);
    if (nEqual == 0)
        if (nNear > 0 || GetHitDice(oPC)-GetHitDice(OBJECT_SELF) < 2) {
            SetLocalInt(OBJECT_SELF,"LocalBoss",TRUE);
        }

    return OBJECT_INVALID;
}


int CheckStealth()
{
    int nStealthCheck = GetLocalInt(OBJECT_SELF, "canStealth");
    if (nStealthCheck == 0)
    {
        int nDexCheck = GetAbilityModifier(ABILITY_DEXTERITY);
        if (nDexCheck < 0) { nDexCheck = 0; }
        int nHideCheck = GetSkillRank(SKILL_HIDE, OBJECT_SELF) - nDexCheck;
        int nMoveSilentCheck = GetSkillRank(SKILL_MOVE_SILENTLY, OBJECT_SELF) - nDexCheck;
        nStealthCheck = ((nHideCheck > 0) || (nMoveSilentCheck > 0)) ? 1 : 2;
        SetLocalInt(OBJECT_SELF, "canStealth", nStealthCheck);
    }
    return nStealthCheck == 1;
}


    // Pausanias: monsters try to find you.
void DoStealthAndWander()
{
    if (GetIsObjectValid(GetLastHostileActor()))
    {
        return;
    }
    if (GetIsInCombat())
    {
        return;
    }
    if (GetIsFighting(OBJECT_SELF))
    {
        return;
    }
    if(GetSpawnInCondition(NW_FLAG_DAY_NIGHT_POSTING))
    {
        int nDay = FALSE;
        if(GetIsDay() || GetIsDawn())
        {
            nDay = TRUE;
        }
        if(GetLocalInt(OBJECT_SELF, "NW_GENERIC_DAY_NIGHT") != nDay)
        {
            if(nDay == TRUE)
            {
                SetLocalInt(OBJECT_SELF, "NW_GENERIC_DAY_NIGHT", TRUE);
            }
            else
            {
                SetLocalInt(OBJECT_SELF, "NW_GENERIC_DAY_NIGHT", FALSE);
            }
            WalkWayPoints();
        }
    }

    int nStealthAndWander = GetMonsterOptions(HENCH_MONAI_STEALTH | HENCH_MONAI_WANDER);
    // Auldar: and they now stealth if they have some skill points (and not marked with plot flag)
    if (nStealthAndWander && !GetPlotFlag(OBJECT_SELF))
    {
        object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
        object oNearestHostile = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);

        if (!(GetIsObjectValid(oNearestHostile) && GetIsObjectValid(oPC) && GetIsEnemy(oPC)))
        {
            nStealthAndWander = 0;
        }

        if (!(GetIsObjectValid(oNearestHostile) && GetIsObjectValid(oPC) && GetIsEnemy(oPC)))
        {
            nStealthAndWander = 0;
        }
        int bActionsCleared = FALSE;
        if (nStealthAndWander & HENCH_MONAI_STEALTH)
        {
            // Auldar: Checking if the NPC is hostile to the PC and has skill points in Hide
            // or move silently, and it not marked Plot. If so, go stealthy even if not flagged
            // by the module creator as Stealth on spawn, as long as the PC or associate is hostile and close.
            int bStealthCheck = !GetPlotFlag(OBJECT_SELF) && CheckStealth(); 

// Auldar: Configurable distance at which to "hide", if the PC, or PC's Associate is within that distance.
            // TK 40 doesn't seem to be far enough
            float distThresh = 80.0;

// Auldar: Check how far away the nearest hostile Creature is
            float enemyDistance = GetDistanceToObject(oNearestHostile);

// Auldar: and check if it's a PC, or a PC's associate.
            object oRealHostileMaster = GetRealMaster(oNearestHostile);
            if (((oPC == oNearestHostile) || (GetIsObjectValid(oRealHostileMaster)
                && GetIsPC(oRealHostileMaster) && GetIsEnemy(oRealHostileMaster)))
                && (enemyDistance <= distThresh) && (enemyDistance != -1.0)
                && bStealthCheck
                && (GetStealthMode(OBJECT_SELF) == STEALTH_MODE_DISABLED))
            {
                ClearAllActions();
                bActionsCleared = TRUE;
                ActionUseSkill(SKILL_HIDE, OBJECT_SELF);
            }
// Auldar: here ends Auldar's NPC stealth code. Back to Paus' work :)
        }
// Auldar: Reducing the distance from 40.0 to 25.0 to reduce the "bloodbath" effect
// requested by FoxBat.
        if ((nStealthAndWander & HENCH_MONAI_WANDER) &&
            GetDistanceToObject(oPC) < 25.0)
        {
            int ScoutMode = GetLocalInt(OBJECT_SELF,"ScoutMode");
            if (ScoutMode == 0)
            {
               ScoutMode = d2();
               SetLocalInt(OBJECT_SELF,"ScoutMode",ScoutMode);
            }
            object oTarget = GetNearestTougherFriend(OBJECT_SELF,oPC);
            if (!GetLocalInt(OBJECT_SELF,"LocalBoss"))
            {
                int fDist = 15;
                if (!GetIsObjectValid(oTarget) || ScoutMode == 1)
                {
                    fDist = 10;
                    oTarget = oPC;
                    if (d10() > 5) fDist = 25;
                }

                location lNew;
                if (GetLocalInt(OBJECT_SELF,"OpenedDoor"))
                {
                    lNew = GetLocalLocation(OBJECT_SELF,"ScoutZone");
                    SetLocalInt(OBJECT_SELF,"OpenedDoor",FALSE);
                }
                else
                {
                    vector vLoc = GetPosition(oTarget);
                    vLoc.x += fDist-IntToFloat(Random(2*fDist+1));
                    vLoc.y += fDist-IntToFloat(Random(2*fDist+1));
                    vLoc.z += fDist-IntToFloat(Random(2*fDist+1));
                    lNew = Location(GetArea(oTarget),vLoc,0.);
                    SetLocalLocation(OBJECT_SELF,"ScoutZone",lNew);
                }
                if (!bActionsCleared)
                {
                    ClearAllActions();
                }
                ActionMoveToLocation(lNew);
                return;
            }
        }
    }
}


// BARD SONG
int HenchTalentBardSong()
{
    //BARD SONG CONSTANT PENDING
    if(!GetHasSpellEffect(411))
    {
        if(GetHasFeat(FEAT_BARD_SONGS))
        {
            ActionUseFeat(FEAT_BARD_SONGS, OBJECT_SELF);
            return TRUE;
        }
    }
    return FALSE;
}


// Pausanias's version of the last: float GetEnemyChallenge()
// My forumla: Total Challenge of Enemy = log ( Sum (2**challenge) )
// Auldar: Changed to 1.5 at Paus' request to better mirror the 3E DMG
float GetEnemyChallenge(object oRelativeTo=OBJECT_SELF)
{
    float fChallenge = 0.;

    object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 20.0, GetLocation(OBJECT_SELF), TRUE);
    while(GetIsObjectValid(oTarget))
    {
         if (GetIsEnemy(oTarget))  {
            if (GetObjectSeen(oTarget))
                fChallenge += pow(1.5,GetChallengeRating(oTarget));
         }
         oTarget = GetNextObjectInShape(SHAPE_SPHERE, 20.0, GetLocation(OBJECT_SELF), TRUE);
    }

    return (log(fChallenge)/log(1.5))-IntToFloat(GetHitDice(oRelativeTo));
}


//::///////////////////////////////////////////////
//:: Bash Doors
//:: Copyright (c) 2002 Bioware Corp.
//:://////////////////////////////////////////////
/*
    Used in DetermineCombatRound to keep a
    henchmen bashing doors.
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: April 4, 2002
//:://////////////////////////////////////////////

int HenchBashDoorCheck(object oIntruder = OBJECT_INVALID)
{
    int bDoor = FALSE;
    //This code is here to make sure that henchmen keep bashing doors and placables.
    object oDoor = GetLocalObject(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH");
    
    if(GetIsObjectValid(oDoor))
    {
        int nDoorMax = GetMaxHitPoints(oDoor);
        int nDoorNow = GetCurrentHitPoints(oDoor);
        int nCnt = GetLocalInt(OBJECT_SELF,"NW_GENERIC_DOOR_TO_BASH_HP");
        if(GetLocked(oDoor) || GetIsTrapped(oDoor))
        {
            if(nDoorMax == nDoorNow)
            {
                nCnt++;
                SetLocalInt(OBJECT_SELF,"NW_GENERIC_DOOR_TO_BASH_HP", nCnt);
            }
            if(nCnt <= 2)
            {
                bDoor = TRUE;                
                HenchAttackObject(oDoor);
            }
        }
        if(bDoor == FALSE)
        {
            DeleteLocalObject(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH");
            DeleteLocalInt(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH_HP");
            VoiceCuss();
            ActionDoCommand(HenchEquipAppropriateWeapons(OBJECT_SELF, -5., FALSE));
        }
    }
    return bDoor;
}
