IEnumerator HandleCommandMoveToObject(xCommand cCommand, Action onComplete)
    {
        GameObject oTarget = engine.GetCommandObjectRef(ref cCommand);
        if (oTarget != null)
        {
            Vector3 vObject = gameObject.transform.position;
            Vector3 vTarget = oTarget.transform.position;
            //Check to make sure that the object target referenced in command still exists in the scene
            GameObject ot = GameObject.Find(oTarget.name);

            float distance = Mathf.Abs(vObject.sqrMagnitude - vTarget.sqrMagnitude);
            while (distance > 0 && cBreak == EngineConstants.FALSE)
            //while (bTargetReached == EngineConstants.FALSE)
            {
                vObject = Vector3.MoveTowards(vObject, vTarget, Time.deltaTime * 3);
                gameObject.transform.position = vObject;

                if (engine.GetLocalInt(engine.GetModule(), "GAME_MODE") == EngineConstants.GM_COMBAT ||
                    engine.GetLocalInt(engine.GetModule(), "GAME_MODE") == EngineConstants.GM_EXPLORE)
                {
                    if (gameObject.GetComponent<xGameObjectUTC>().bControlled == EngineConstants.TRUE)
                        Camera.main.transform.position = new Vector3(vObject.x, Camera.main.transform.position.y, vObject.z);
                }

                distance = Mathf.Abs(vObject.sqrMagnitude - vTarget.sqrMagnitude);

                yield return null;
            }
        }
        onComplete();

    }
 void PendingCommandRef(ref xCommand cCommand)
 {
     //Wait
 }
    IEnumerator HandleCommandWait(xCommand cCommand, Action onComplete)
    {
        yield return new WaitForSeconds(engine.GetCommandFloatRef(ref cCommand));

        onComplete();
    }
    void HandleCommand(xCommand cCommand)
    {
        currentCommand.nResult = EngineConstants.COMMAND_IN_PROGRESS;
        cBreak = EngineConstants.FALSE;//reset the command break back to zero
        switch (cCommand.nType)
        {
            case EngineConstants.COMMAND_TYPE_WAIT:
                {
                    StartCoroutine(
                        HandleCommandWait(cCommand, () =>
                        {
                            UpdateCommandRef(ref cCommand);
                        })
                        );

                    break;
                }
            case EngineConstants.COMMAND_TYPE_MOVE_TO_OBJECT:
                {
                    StartCoroutine(
                        HandleCommandMoveToObject(cCommand, () =>
                        {
                            UpdateCommandRef(ref cCommand);
                        })
                        );

                    break;
                }
            case EngineConstants.COMMAND_TYPE_MOVE_TO_LOCATION:
                {
                    StartCoroutine(
                        HandleCommandMoveToLocation(cCommand, () =>
                        {
                            UpdateCommandRef(ref cCommand);
                        })
                        );

                    break;
                }
            case EngineConstants.COMMAND_TYPE_USE_OBJECT:
                {
                    StartCoroutine(
                        HandleCommandUseObject(cCommand, () =>
                        {
                            UpdateCommandRef(ref cCommand);
                        })
                        );

                    break;
                }
            case EngineConstants.COMMAND_TYPE_START_CONVERSATION:
                {
                    StartCoroutine(
                        HandleCommandStartConversation(cCommand, () =>
                        {
                            UpdateCommandRef(ref cCommand);
                        })
                        );

                    break;
                }
            case EngineConstants.COMMAND_TYPE_ATTACK:
                {
                    StartCoroutine(
                        HandleCommandAttack(cCommand, () =>
                        {
                            PendingCommandRef(ref cCommand);
                        })
                        );

                    break;
                }
            default: throw new NotImplementedException();
        }
    }
 //Function called internally by coroutines, Assume always success?
 void UpdateCommandRef(ref xCommand cCommand)
 {
     cCommand.nResult = EngineConstants.COMMAND_RESULT_SUCCESS;
 }
    IEnumerator HandleCommandStartConversation(xCommand cCommand, Action onComplete)
    {
        GameObject oTarget = engine.GetCommandObjectRef(ref cCommand);

        xEvent ev = engine.Event(EngineConstants.EVENT_TYPE_CONVERSATION);
        engine.SetEventCreatorRef(ref ev, engine.GetHero());
        engine.SetEventResourceRef(ref ev, 0, engine.GetCommandStringRef(ref cCommand));
        engine.SignalEvent(gameObject, ev);

        yield return null;
        onComplete();
    }
     public void WR_RemoveCommand(GameObject oObject, xCommand cCommand)
     {
          int nCommandType = GetCommandType(cCommand);
          string sCommand = "wrappers_h.WR_RemoveCommand" + Log_GetCommandNameById(nCommandType);

          Log_Trace_Commands(sCommand, cCommand, oObject);

          if (IsObjectValid(oObject) == EngineConstants.FALSE)
          {
               Log_Trace_Scripting_Error(sCommand, "used on invalid object");
               return;
          }

          if (nCommandType == EngineConstants.COMMAND_TYPE_INVALID)
          {
               Log_Trace_Scripting_Error(sCommand, "invalid input parameter for cCommand (EngineConstants.COMMAND_TYPE_INVALID");
               return;
          }

          /*    xCommand cCurrent = GetCurrentCommand(oObject);
                if(cCurrent == cCommand)
                  Log_Systems("*** Removing current command", EngineConstants.LOG_LEVEL_WARNING, oObject, EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
                else
                  Log_Systems("*** Removing from queue", EngineConstants.LOG_LEVEL_WARNING, oObject, EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
          */

          int nSize = GetCommandQueueSize(oObject);
          /*    if(nSize == 0)
                  Log_Systems("*** Command queue is currently empty", EngineConstants.LOG_LEVEL_DEBUG, oObject
                      , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
              else
                  Log_Systems("*** Command queue size BEFORE REMOVING COMMAND is: " + IntToString(nSize), EngineConstants.LOG_LEVEL_DEBUG, oObject
                      , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);*/

          int i;
          int nType;
          /*    for(i = 0; i < nSize; i++)
              {
                  cCurrent = GetCommandByIndex(oObject, i);
                  nType = GetCommandType(cCurrent);
                  if(cCurrent == cCommand)
                      Log_Systems("*** (TO BE REMOVED) COMMAND[" + IntToString(i) + "]= " + IntToString(nType), EngineConstants.LOG_LEVEL_DEBUG, oObject
                      , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
                  else
                      Log_Systems("*** COMMAND[" + IntToString(i) + "]= " + IntToString(nType), EngineConstants.LOG_LEVEL_DEBUG, oObject
                      , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
              }
          */

          //    Log_Systems("*** REMOVING COMMAND (" + IntToString(nCommandType) + ")", EngineConstants.LOG_LEVEL_DEBUG, oObject, EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
          RemoveCommand(oObject, cCommand);
     }
     // returns a return value for using a potion
     public xCommand _AI_GetPotionUseCommand(GameObject oItem)
     {
          xCommand cRet = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);
          // WORKS ONLY IN COMBAT!!!
          if (IsObjectValid(oItem) != EngineConstants.FALSE && GetCombatState(gameObject) != EngineConstants.FALSE)
          {
               int nAbility = GetItemAbilityId(oItem);
               if (Ability_CheckUseConditions(gameObject, gameObject, nAbility) == EngineConstants.FALSE)
                    return cRet; // failed tactic

               if (_AI_CanUseAbility(nAbility, gameObject) == EngineConstants.FALSE)
                    return cRet; // can't use specific ability

               // Command is valid to be executed on the target
               Vector3 vNul = Vector3.zero;
               cRet = CommandUseAbility(nAbility, gameObject, vNul, -1.0f, GetTag(oItem));

          }
          else
               return cRet;

          return cRet;
     }
     //////////////////////////////////////////////
     // ai_main_h
     //
     // This script includes all general AI functions
     //
     // Owner: Yaron Jakobs
     //
     /////////////////////////////////////////////

     /* @addtogroup scripting_ai2 Scripting AI handling
     *
     * Main AI interface functions
     */
     /* @{*/

     //#include "log_h"
     //#include "wrappers_h"
     //#include "ai_threat_h"
     //#include "xEvents_h"
     //#include "effects_h"
     //#include "items_h"
     //#include "ability_h"
     //#include "ai_conditions_h"
     //#include "ai_constants_h"
     //#include "ai_ballista_h"
     //#include "ai_behaviors_h"


     ////////////////////////////////////////////////////////////////////////////////
     //
     //                            FUNCTIONS DEFINITIONS
     //
     ////////////////////////////////////////////////////////////////////////////////

     /* @brief Determines the exact action to take when an GameObject is blocking the way
*
* A few examples are bashing a container or door, lockpicking and then opening a gate, etc.
*
* @param oBlockingObject the GameObject blocking the path
* @returns EngineConstants.TRUE if the AI found a solution to deal with the blocking GameObject
* @author Jose
*/
     public int AI_DeterminePathBlockedAction(GameObject oBlockingObject)
     {
          // Check that we have a valid GameObject
          if (IsObjectValid(oBlockingObject) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("AI_DeterminePathBlockedAction", "INVALID BLOCKING OBJECT");
#endif
               return EngineConstants.FALSE;
          }

          // Attempt to find an action to unblock the path
#if DEBUG
          Log_Trace_AI("AI_DeterminePathBlockedAction", "***** START ***** , blocking GameObject: " + ObjectToString(oBlockingObject));
#endif
          xCommand cPathAction = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);
          xCommand cPathActionContinue;

          // DOOR - Locked
          if (GetPlaceableState(oBlockingObject) == EngineConstants.PLC_STATE_DOOR_LOCKED)
          {
               if (IsControlled(gameObject) == EngineConstants.FALSE)
               {
                    cPathAction = _AI_DoNothing(-1, -1, EngineConstants.FALSE, EngineConstants.TRUE);
               }
               // Other interesting cases to consider are:
               //   if rogue has lockpicking
               //   if ranged combatant has shatteting shot
               //   if mage has spell for breaking a placeable
               //   ... etc
          }
          // DOOR - Unlocked
          else if (GetPlaceableState(oBlockingObject) == EngineConstants.PLC_STATE_DOOR_UNLOCKED)
          {
               cPathAction = CommandUseObject(oBlockingObject, EngineConstants.PLACEABLE_ACTION_OPEN);

               // Other interesting cases to consider are:
               //   hostile creatures might still decide to break the door to make this more aggresive?
               //   I don't see hurlocks or ogres gently pushing the door...
          }
          // Add more cases here as necessary
          // ...

          // If we found a valid way to unblock the path, add it to the AI queue
          if (GetCommandType(cPathAction) != EngineConstants.COMMAND_TYPE_INVALID)
          {
               // This action should unblock the path
               WR_AddCommand(gameObject, cPathAction, EngineConstants.FALSE, EngineConstants.FALSE, -1, EngineConstants.AI_COMMAND_TIMER);

               // After that, the character can resume the previous action
               xCommand cPreviousCommand = GetPreviousCommand(gameObject);
               WR_AddCommand(gameObject, cPreviousCommand, EngineConstants.FALSE, EngineConstants.FALSE, EngineConstants.COMMAND_ADDBEHAVIOR_DONTCLEAR, EngineConstants.AI_COMMAND_TIMER);

               return EngineConstants.TRUE;
          }
          // If not, just report a warning and return failure
          else
          {
#if DEBUG
               Log_Trace_AI("AI_DeterminePathBlockedAction", "Couldn't find a solution for dealing with the blocking GameObject", EngineConstants.LOG_SEVERITY_WARNING);
#endif
               return EngineConstants.FALSE;
          }
     }
     // returns a xCommand to wait or play a taunt animation
     public xCommand _AI_DoNothing(int nLastTacticID, int nLastCommandStatus, int nAllowTaunts, int bQuick = EngineConstants.FALSE, GameObject oTarget = null, int nClearThreat = EngineConstants.TRUE)
     {
#if DEBUG
          Log_Trace_AI("_AI_DoNothing", "START");
#endif

          int nRand;
          int nRand2;
          xCommand cRet = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);
          int nAnim = 0;

          if (IsFollower(gameObject) != EngineConstants.FALSE
              || _AI_HasAIStatus(gameObject, EngineConstants.AI_STATUS_PARALYZE) != EngineConstants.FALSE
              || _AI_HasAIStatus(gameObject, EngineConstants.AI_STATUS_DAZE) != EngineConstants.FALSE
              || _AI_HasAIStatus(gameObject, EngineConstants.AI_STATUS_STUN) != EngineConstants.FALSE)
               nAllowTaunts = EngineConstants.FALSE;

          nRand2 = Engine_Random(3) + 1;

          if (IsControlled(gameObject) != EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_DoNothing", "Controled follower - NOT WAITING");
#endif
               return cRet;
          }

          if (nClearThreat != EngineConstants.FALSE && oTarget != null && IsFollower(gameObject) == EngineConstants.FALSE)
          {
               // Lowering threat to oTarget
               AI_Threat_UpdateCantAttackTarget(gameObject, oTarget);
          }

          // If last xCommand was DoNothing and it failed (possibly by trying to play a non-existing taunt animation)
          if (nLastTacticID == EngineConstants.AI_TACTIC_ID_WAIT && nLastCommandStatus < 0)
          {
               if (bQuick != EngineConstants.FALSE)
                    cRet = CommandWait(EngineConstants.AI_DO_NOTHING_DELAY_QUICK);
               else
                    cRet = CommandWait(EngineConstants.AI_DO_NOTHING_DELAY);
#if DEBUG
               Log_Trace_AI("_AI_DoNothing", "Last DoNothing xCommand failed - trying to wait");
#endif
          }
          else if (nAllowTaunts != EngineConstants.FALSE)
          {
               nRand = Engine_Random(100) + 1;
               if (nRand <= EngineConstants.AI_TAUNT_CHANCE && GetAppearanceType(gameObject) != EngineConstants.APR_TYPE_OGRE)
               {
                    if (nRand2 == 1) nAnim = 144;
                    else if (nRand2 == 2) nAnim = 2005;
                    else if (nRand2 == 3) nAnim = 149;
                    cRet = CommandPlayAnimation(nAnim);
#if DEBUG
                    Log_Trace_AI("_AI_DoNothing", "Playing taunt animation");
#endif

               }
               if (GetCommandType(cRet) == EngineConstants.COMMAND_TYPE_INVALID)
               {
#if DEBUG
                    Log_Trace_AI("_AI_DoNothing", "failed to add taunt animation - waiting instead");
#endif
                    if (bQuick != EngineConstants.FALSE)
                         cRet = CommandWait(EngineConstants.AI_DO_NOTHING_DELAY_QUICK);
                    else
                         cRet = CommandWait(EngineConstants.AI_DO_NOTHING_DELAY);
               }
          }
          else
          {
               if (bQuick != EngineConstants.FALSE)
                    cRet = CommandWait(EngineConstants.AI_DO_NOTHING_DELAY_QUICK);
               else
                    cRet = CommandWait(EngineConstants.AI_DO_NOTHING_DELAY);
#if DEBUG
               Log_Trace_AI("_AI_DoNothing", "Waiting");
#endif
          }
          return cRet;
     }
     public xCommand _AI_GetFlyCommand(GameObject oTurnTo, int bMoveTo = EngineConstants.FALSE)
     {
          float fAngle = GetAngleBetweenObjects(gameObject, oTurnTo);
          float fMyFacing = GetFacing(gameObject);
          float fEnemyFacing = GetFacing(oTurnTo);
          float fTurnAngle = 360.0f - fAngle + fMyFacing;
          if (fTurnAngle >= 180.0f)
               fTurnAngle = fMyFacing - fAngle;
          float fDif;
          if (fTurnAngle > fMyFacing) fDif = fTurnAngle - fMyFacing;
          else fDif = fMyFacing - fTurnAngle;
          float fDistance = GetDistanceBetween(gameObject, oTurnTo);
          xCommand cFly = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);
#if DEBUG
          Log_Trace_AI("_AI_GetFlyCommand", "Object: " + GetTag(oTurnTo) + ", turn angle: " + FloatToString(fTurnAngle) + ", distance: " + FloatToString(fDistance)
              + ", angle dif: " + FloatToString(fDif));
#endif


          if (fDif < EngineConstants.AI_TURN_MIN_ANGLE && bMoveTo == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_GetFlyCommand", "Too small angle to turn - avoiding turn and returning invalid xCommand");
#endif
               return cFly;
          }
          else if (fDistance < EngineConstants.AI_FLY_MIN_DISTANCE && bMoveTo != EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_GetFlyCommand", "Too small distance to fly - avoiding fly and returning invalid xCommand. Distance: " + FloatToString(fDistance));
#endif
               return cFly;
          }


          Vector3 lLoc;
          if (bMoveTo != EngineConstants.FALSE)
          {
               if (fDistance > EngineConstants.AI_FLY_MAX_DISTANCE &&
                   GetLocalInt(gameObject, EngineConstants.CREATURE_COUNTER_3) == 0) // EngineConstants.CREATURE_COUNTER_3 used to enable/disable stomp. 1 is for disabled
               {
                    WR_ClearAllCommands(gameObject, EngineConstants.TRUE);
                    WR_SetObjectActive(gameObject, EngineConstants.FALSE);
                    xEvent eFlyDown = Event(EngineConstants.EVENT_TYPE_SET_OBJECT_ACTIVE);
                    float fFacing = GetFacing(oTurnTo);
                    SetEventFloatRef(ref eFlyDown, 0, fFacing);
                    SetEventVectorRef(ref eFlyDown, 0, GetLocation(oTurnTo));
                    SetEventIntegerRef(ref eFlyDown, 3, EngineConstants.TRUE); // tells it to call an AI function
                    DelayEvent(2.5f, gameObject, eFlyDown);

                    // putting wait xCommand as a flag to abort AI
                    cFly = CommandWait(2.5f);

               }
               else if (fDistance < EngineConstants.AI_FLY_MAX_DISTANCE)
               {
                    lLoc = Location(GetArea(gameObject), GetPosition(oTurnTo), fTurnAngle);
                    cFly = CommandFly(lLoc);
               }
          }
          else if (fDistance > EngineConstants.AI_TURN_MIN_DISTANCE)
          {
               lLoc = Location(GetArea(gameObject), GetPosition(gameObject), fTurnAngle);
               cFly = CommandFly(lLoc);
          }



          return cFly;
     }
     /* @brief Executes an attack xCommand including possible weapon switching
*
* @param oTarget the target being attacked
* @param nLastCommandStatus used to determine what to do in case of failed movement or weapon switch
* @returns an attack or weapon switch xCommand
* @author Yaron
*/
     public xCommand _AI_ExecuteAttack(GameObject oTarget, int nLastCommandStatus)
     {
          // This can include a weapon switch condition as well:
          // If current creature equips a ranged weapon and the target is within melee range -> switch to melee
          // If current creature equips a melee weapon and the target is not within melee range AND
          // the creature prefers ranged weapons (flag) AND the ranged weapon set has enough ammo -> switch to ranged weapon
          // All of the conditions above assume the creature has the appropriate weapon sets
          // If the creature decides to switch weapons then we will NOT add another melee xCommand this round



          xCommand cTacticCommand = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);
          int nTacticID = 0; // used to store the tactic that was executed, if it there is no tactic ID from a table
          int nLastTacticID = GetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC); // the last tactic being used
#if DEBUG
          Log_Trace_AI("_AI_ExecuteAttack", "*** START ***, Target: " + GetTag(oTarget) + ", Last Command Status: "
              + IntToString(nLastCommandStatus) + ", last tactic id: " + IntToString(nLastTacticID));
#endif

          float fDistance;
          int nRand;
          float fAvoidDistance;
          GameObject oCurrentWP;
          List<GameObject> arWPs;
          float fWPDistance;
          int nSize;
          int i;
          int bWPFound = EngineConstants.FALSE;
          List<GameObject> oCreaturesNearWP;
          float fNearestCreatureToWPDistance;
          List<GameObject> arPerceivedCreatures;
          GameObject oCreatureNearWP;
          GameObject oOldWP;
          int nFailMessage = 0;
          switch (nLastCommandStatus)
          {
               case EngineConstants.COMMAND_FAILED_COMMAND_CLEARED: nFailMessage = EngineConstants.UI_DEBUG_COMMAND_FAILED; break;
               case EngineConstants.COMMAND_FAILED_INVALID_DATA: nFailMessage = EngineConstants.UI_DEBUG_INVALID_DATA; break;
               case EngineConstants.COMMAND_FAILED_INVALID_PATH: nFailMessage = EngineConstants.UI_DEBUG_INVALID_PATH; break;
               case EngineConstants.COMMAND_FAILED_NO_LINE_OF_SIGHT: nFailMessage = EngineConstants.UI_DEBUG_NO_LOS; break;
               //case EngineConstants.COMMAND_FAILED_NO_SPACE_IN_MELEE_RING: nFailMessage = EngineConstants.UI_DEBUG_NO_SPACE_IN_MELEE_RING; break;
               case EngineConstants.COMMAND_FAILED_TARGET_DESTROYED: nFailMessage = EngineConstants.UI_DEBUG_TARGET_DESTROYED; break;
               case EngineConstants.COMMAND_FAILED_DISABLED: nFailMessage = EngineConstants.UI_DEBUG_MOVEMENT_DISABLED; break;
               case EngineConstants.COMMAND_FAILED_TIMEOUT: nFailMessage = EngineConstants.UI_DEBUG_COMMAND_TIMED_OUT; break;
          }
#if DEBUG
          if (nFailMessage > 0)
               UI_DisplayMessage(gameObject, nFailMessage);
#endif


          if (GetEffectsFlags(gameObject) == EngineConstants.EFFECT_FLAG_DISABLE_COMBAT)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "Can't add any combat xCommands: combat is disabled by xEffect - waiting instead");
#endif
               cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE);
               nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
          }

          // Special handling:
          // If the last xCommand failed AND the last xCommand was a part of this generic 'attack' sequence
          // dec 17, 2008 -- yaron: adding a condition that allows this part to run if the xCommand before was not a normal attack and the failure is 'movement disable'
          // feb 24, 2009 -- yaron: same as above but also for timeoutfailures
          else if (nLastCommandStatus < 0 &&
                  (nLastTacticID < 0 || (nLastTacticID >= 0 && (nLastCommandStatus == EngineConstants.COMMAND_FAILED_DISABLED || nLastCommandStatus == EngineConstants.COMMAND_FAILED_TIMEOUT))) &&
                  nLastCommandStatus != EngineConstants.COMMAND_FAILED_TARGET_DESTROYED)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "Last tactic failed AND was an Attack tactic - trying something else, error: " + IntToString(nLastCommandStatus));
#endif
               if (IsFollower(gameObject) != EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteAttack", "Command failed for follower - trying only to WAIT");
#endif
                    // Nothing much we can do in these cases except wait
                    cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE);
                    nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
               }
               else
               {
                    fDistance = GetDistanceBetween(gameObject, oTarget);

                    // timeout failure - special case
                    // (regardles of what the last action was)
                    if (nLastCommandStatus == EngineConstants.COMMAND_FAILED_TIMEOUT)
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteAttack", "Handling attack timeout");
#endif
                         if (IsFollower(gameObject) == EngineConstants.FALSE &&
                             GetLocalInt(gameObject, EngineConstants.CREATURE_HAS_TIMER_ATTACK) == 1) // first failure -> try to attack again
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteAttack", "First timer failure - try to attack again");
#endif
                              SetLocalInt(gameObject, EngineConstants.CREATURE_HAS_TIMER_ATTACK, 2);
                              cTacticCommand = CommandAttack(oTarget);
                              nTacticID = EngineConstants.AI_TACTIC_ID_ATTACK;

                         }
                         else // not first failure -> switch weapon or try to attack again
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteAttack", "Not first timer failure - trying to switch to ranged");
#endif
                              if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE &&
                                  _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED) != EngineConstants.FALSE)
                              {
#if DEBUG
                                   Log_Trace_AI("_AI_ExecuteAttack", "switching to ranged because of timer failure");
#endif
                                   cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED);
                                   nTacticID = EngineConstants.AI_TACTIC_ID_SWITCH_MELEE_TO_RANGED;
                              }
                              else // can't switch -> try to attack again (no need for wait as this is a timeout failure
                              {
#if DEBUG
                                   Log_Trace_AI("_AI_ExecuteAttack", "timer failure - can't switch - trying to attack again");
#endif
                                   _AI_ApplyTimerDifficultyEffects(oTarget);
                                   // adding speed boost for normal/hard difficulty
                                   cTacticCommand = CommandAttack(oTarget);
                                   nTacticID = EngineConstants.AI_TACTIC_ID_ATTACK;
                              }

                         }
                    }
                    else // not a timer failure
                    {

                         switch (nLastTacticID)
                         {
                              case EngineConstants.AI_TACTIC_ID_ATTACK:
                                   {
                                        // if a follower has the movement disabled GUI activated and he fails the last action
                                        if (nLastCommandStatus == EngineConstants.COMMAND_FAILED_DISABLED &&
                                                IsFollower(gameObject) != EngineConstants.FALSE &&
                                                _AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE &&
                                                _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED) != EngineConstants.FALSE &&
                                                _AI_IsTargetInMeleeRange(oTarget) == EngineConstants.FALSE)
                                        {
                                             cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED);
                                             nTacticID = EngineConstants.AI_TACTIC_ID_SWITCH_MELEE_TO_RANGED;
                                        }
                                        else if (IsFollower(gameObject) == EngineConstants.FALSE &&
                                                _AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE &&
                                                _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED) != EngineConstants.FALSE && _AI_IsTargetInMeleeRange(oTarget) == EngineConstants.FALSE)
                                        {
                                             cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED);
                                             nTacticID = EngineConstants.AI_TACTIC_ID_SWITCH_MELEE_TO_RANGED;
                                        }
                                        // if has melee but no ranged: move closer
                                        else if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE &&
                                                _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED) == EngineConstants.FALSE &&
                                                fDistance > (EngineConstants.AI_MINIMAL_MELEE_DISTANCE + 1.0f) &&
                                                nLastCommandStatus != EngineConstants.COMMAND_FAILED_DISABLED &&
                                                nLastCommandStatus != EngineConstants.COMMAND_FAILED_PATH_ACTION_REQUIRED &&
                                                nLastCommandStatus != EngineConstants.COMMAND_FAILED_INVALID_PATH)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteAttack", "Trying to move closer to target");
#endif
                                             cTacticCommand = CommandMoveToObject(oTarget, EngineConstants.TRUE, EngineConstants.AI_MINIMAL_MELEE_DISTANCE);
                                             nTacticID = EngineConstants.AI_TACTIC_ID_MOVE;
                                        }
                                        // if has ranged: change to melee
                                        else if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_RANGED &&
                                                _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_MELEE) != EngineConstants.FALSE && _AI_IsTargetInMeleeRange(oTarget) != EngineConstants.FALSE)
                                        {
                                             cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_MELEE);
                                             nTacticID = EngineConstants.AI_TACTIC_ID_SWITCH_RANGED_TO_MELEE;
                                        }
                                        // if too close and not on ring, move a little ways away from target
                                        else if (nLastCommandStatus == EngineConstants.COMMAND_FAILED_NO_SPACE_IN_MELEE_RING &&
                                                  fDistance < EngineConstants.AI_MELEE_RANGE)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteAttack", "Trying to move away from target");
#endif
                                             cTacticCommand = CommandMoveAwayFromObject(oTarget, 2.0f, EngineConstants.FALSE);
                                             nTacticID = EngineConstants.AI_TACTIC_ID_MOVE;
                                        }
                                        else
                                        {
                                             cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE, EngineConstants.FALSE, oTarget);
                                             nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
                                        }
                                        break;
                                   }
                              case EngineConstants.AI_TACTIC_ID_SWITCH_MELEE_TO_RANGED:
                              case EngineConstants.AI_TACTIC_ID_SWITCH_RANGED_TO_MELEE:
                              case EngineConstants.AI_TACTIC_ID_WAIT:
                              case EngineConstants.AI_TACTIC_ID_MOVE:
                              default:
                                   {
                                        // Nothing much we can do in these cases except wait
                                        cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE, EngineConstants.FALSE, oTarget);
                                        nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
                                        break;
                                   }
                         } // end of tactic ID failure switch
                    } // end of else (not a timer failure)
               } // end of 'not follower' if-else
          }
          else if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_RANGED &&
              (_AI_IsTargetInMeleeRange(oTarget) != EngineConstants.FALSE || _AI_GetFlag(EngineConstants.AI_FLAG_PREFERS_RANGED) == EngineConstants.FALSE) &&
              _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_MELEE) != EngineConstants.FALSE &&
              nLastTacticID != EngineConstants.AI_TACTIC_ID_SWITCH_MELEE_TO_RANGED) // so it won't try to switch to melee right after changing to ranged
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "Trying to switch into a melee weapon set");
#endif

               if (IsFollower(gameObject) != EngineConstants.FALSE && AI_BehaviorCheck_PreferRange() != EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteAttack", "Follower prefers range - switch to melee aborted");
#endif
               }
               else
               {
                    int nRandBackToMelee = Engine_Random(100) + 1;
                    if (IsFollower(gameObject) == EngineConstants.FALSE && _AI_GetFlag(EngineConstants.AI_FLAG_PREFERS_RANGED) == EngineConstants.FALSE && nRandBackToMelee > 33)
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteAttack", "Not prefering range, but random chance failed to allow changing back to melee");
#endif
                    }
                    else if (_AI_IsTargetInMeleeRange(oTarget) != EngineConstants.FALSE)
                    {
                         cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_MELEE);
                         nTacticID = EngineConstants.AI_TACTIC_ID_SWITCH_RANGED_TO_MELEE;

                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID) // failed to switch
                         {
                              cTacticCommand = CommandAttack(oTarget); // Continue attacking with ranged weapon
                              nTacticID = EngineConstants.AI_TACTIC_ID_ATTACK;
                         }
                    }
               }
          }
          else if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE &&
                 _AI_IsTargetInMeleeRange(oTarget) == EngineConstants.FALSE &&
                  (IsFollower(gameObject) != EngineConstants.FALSE || _AI_GetFlag(EngineConstants.AI_FLAG_PREFERS_RANGED) != EngineConstants.FALSE) &&
                  _AI_HasWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED) != EngineConstants.FALSE &&     // Ranged weapon set check includes ammo check
                  nLastTacticID != EngineConstants.AI_TACTIC_ID_SWITCH_RANGED_TO_MELEE) // Did not try
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "Trying to switch into a ranged weapon set");
#endif
               if (IsFollower(gameObject) != EngineConstants.FALSE && AI_BehaviorCheck_PreferMelee() != EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteAttack", "Follower prefers range - switch to range aborted");
#endif
               }
               else if (IsFollower(gameObject) != EngineConstants.FALSE && AI_BehaviorCheck_PreferRange() == EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteAttack", "Follower does NOT prefer range - switch to range aborted");
#endif
               }
               else
               {
                    cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED);
                    //cTacticCommand = _AI_SwitchWeaponSet(0);
                    nTacticID = EngineConstants.AI_TACTIC_ID_SWITCH_MELEE_TO_RANGED;

                    if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID) // failed to switch
                    {
                         cTacticCommand = CommandAttack(oTarget); // Continue attacking with melee weapon
                         nTacticID = EngineConstants.AI_TACTIC_ID_ATTACK;
                    }
               }

          }
          else// Did not switch any weapon set -> continue attacking with current.
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "NORMAL ATTACK");
#endif

               cTacticCommand = CommandAttack(oTarget);
               nTacticID = EngineConstants.AI_TACTIC_ID_ATTACK;
          }
          // ...one last ammo check!


          // One last check - in case we failed to switch a weapon:
          if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "FAILED TO FIND A VALID COMMAND - TRYING COMMAND ATTACK", EngineConstants.LOG_SEVERITY_CRITICAL);
#endif
               cTacticCommand = CommandAttack(oTarget);
               nTacticID = EngineConstants.AI_TACTIC_ID_ATTACK;
          }

          // Last check - making sure selected attack matches stationary flag
          if (IsFollower(gameObject) == EngineConstants.FALSE && GetLocalInt(gameObject, EngineConstants.AI_FLAG_STATIONARY) > 0 && GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_ATTACK)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteAttack", "Creature stationary - checking if he can execute seleted attack");
#endif
               if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE)
               {
                    // if melee weapon -> attack only if target is in stationaty range
                    fDistance = GetDistanceBetween(gameObject, oTarget);
                    if (fDistance > EngineConstants.AI_STATIONARY_RANGE)
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteAttack", "Creature stationary - too far from melee target to execute attack - WAITING");
#endif
                         cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE, EngineConstants.TRUE);
                         nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
                    }
               }
               else if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_RANGED)
               {
                    // if ranged weapon -> attack only if target is in weapon range
                    GameObject oWeapon = GetItemInEquipSlot(EngineConstants.INVENTORY_SLOT_MAIN);
                    fDistance = GetDistanceBetween(gameObject, oTarget);
                    float fWeaponRange = GetItemRange(oWeapon);

#if DEBUG
                    Log_Trace_AI("_AI_ExecuteAttack", "Weapon range check for stationary creature - weapon range: " + FloatToString(fWeaponRange));
#endif
                    if (fDistance > fWeaponRange)
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteAttack", "Creature stationary - too far from ranged target to execute attack - WAITING");
#endif
                         cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE, EngineConstants.TRUE);
                         nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
                    }
                    else // creature within range - check line of sight
                    {
                         if (CheckLineOfSightObject(gameObject, oTarget) == EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteAttack", "Creature stationary - no line of sight to target - WAITING");
#endif
                              cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.TRUE, EngineConstants.TRUE);
                              nTacticID = EngineConstants.AI_TACTIC_ID_WAIT;
                         }
                    }

               }
          }

#if DEBUG
          Log_Trace_AI("_AI_ExecuteAttack", "Setting last tactic ID to: " + IntToString(nTacticID));
#endif
          SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, nTacticID);
          return cTacticCommand;
     }
     /* @brief Switches the weapon set of the current creature to a weapon set of the specified type
*
* @param nWeaponSetType the weapon set we want to switch to
* @returns a xCommand to switch the weapons
* @author Yaron
*/
     public xCommand _AI_SwitchWeaponSet(int nWeaponSetType)
     {
#if DEBUG
          Log_Trace_AI("_AI_SwitchWeaponSet", "Switching weapons to set type: " + IntToString(nWeaponSetType));
#endif

          int nSet = -1;
          switch (nWeaponSetType)
          {
               case EngineConstants.AI_WEAPON_SET_MELEE:
                    {
                         nSet = Items_GetMeleeWeaponSet(gameObject);
                         break;
                    }
               case EngineConstants.AI_WEAPON_SET_RANGED:
                    {
                         nSet = Items_GetRangedWeaponSet(gameObject, EngineConstants.TRUE);
                         break;
                    }
          }
#if DEBUG
          Log_Trace_AI("_AI_SwitchWeaponSet", "Switching weapons to set: " + IntToString(nSet));
#endif

          xCommand cmd = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);

#if DEBUG
          if (nSet == -1)
               Log_Trace_AI("_AI_SwitchWeaponSet", "ERROR: FAILED TO FIND A SET TO SWITCH TO");
#endif

          if (nSet != -1)
          {
               cmd = CommandSwitchWeaponSet(nSet);
          }

          return cmd;
     }
     public void WR_AddCommand(GameObject oObject, xCommand cCommand, int bAddToFront = EngineConstants.FALSE, int bStatic = EngineConstants.FALSE, int nOverrideAddBehavior = -1, float fTimeout = 0.0f)
     {
          int nCommandType = GetCommandType(cCommand);

          // timeout check
          // if a timeout was selected AND this is not a follower AND we're in combat AND it's the creatures first timer command
          // then shorten the timer so if the creature moves he'll stop faster and will then have a chance to reevaluate
          // his threat towards the party
          if (fTimeout > 0.0f && IsFollower(oObject) == EngineConstants.FALSE && GetCombatState(oObject) != EngineConstants.FALSE &&
              GetLocalInt(oObject, EngineConstants.CREATURE_HAS_TIMER_ATTACK) == 0)
          {
               SetLocalInt(oObject, EngineConstants.CREATURE_HAS_TIMER_ATTACK, 1); // applied only for first timer check
               fTimeout = 1.0f; // quick timeout
          }

          if (IsObjectValid(oObject) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_Scripting_Error("WR_AddCommand()", Log_GetCommandNameById(nCommandType) + " used on invalid object.");
#endif
               return;
          }

          if (nCommandType == EngineConstants.COMMAND_TYPE_INVALID || nCommandType == 0)
          {
#if DEBUG
               Log_Trace_Scripting_Error("WR_AddCommand()", "Invalid input parameter for cCommand (EngineConstants.COMMAND_TYPE_INVALID)");
#endif
               Warning("Something is trying to add an invalid xCommand from scripting (command_type 0). Please contact georg.)");
               return;
          }

#if DEBUG
          Log_Trace_Commands("WR_AddCommand()", cCommand, oObject);
#endif

#if DEBUG
          Log_Trace(EngineConstants.LOG_CHANNEL_COMMANDS, "WR_AddCommand", "*** START, object= " + GetTag(oObject) + ". command= " + IntToString(nCommandType));
          if (bAddToFront != EngineConstants.FALSE)
               Log_Trace(EngineConstants.LOG_CHANNEL_COMMANDS, "WR_AddCommand()", "*** Adding xCommand to front");
          if (bStatic != EngineConstants.FALSE)
               Log_Trace(EngineConstants.LOG_CHANNEL_COMMANDS, "WR_AddCommand()", "*** Command is static");
          if (fTimeout > 0.0f)
               Log_Trace(EngineConstants.LOG_CHANNEL_COMMANDS, "WR_AddCommand()", "*** Timeout: " + FloatToString(fTimeout));
#endif

          xCommand cCurrent = GetCurrentCommand(oObject);
          int nCurrentType = GetCommandType(cCurrent);
          //Uncommented by DHK
          if (nCurrentType == EngineConstants.COMMAND_TYPE_INVALID)
               Log_Systems("*** No xCommand is executing currently", EngineConstants.LOG_LEVEL_DEBUG, oObject,
                   EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
          else
               Log_Systems("*** Current executing xCommand is: " + IntToString(nCurrentType), EngineConstants.LOG_LEVEL_DEBUG, oObject
                   , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);

          //Uncommented by DHK
          int nSize = GetCommandQueueSize(oObject);
          if (nSize == 0)
          {
               Log_Systems("*** Command queue is currently empty", EngineConstants.LOG_LEVEL_DEBUG, oObject
                   , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
          }
          else
          {
               Log_Systems("*** Command queue size is: " + IntToString(nSize), EngineConstants.LOG_LEVEL_DEBUG, oObject
                   , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
               int i;
               int nType;
               for (i = 0; i < nSize; i++)
               {
                    cCurrent = GetCommandByIndex(oObject, i);
                    nType = GetCommandType(cCurrent);
                    Log_Systems("*** COMMAND[" + IntToString(i) + "]= " + IntToString(nType), EngineConstants.LOG_LEVEL_DEBUG, oObject
                        , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
               }
          }

          //Uncommented by DHK
          Log_Trace(EngineConstants.LOG_CHANNEL_COMMANDS, "*** ADDING NEW COMMAND (" + IntToString(nCommandType) + ")");

          if (GetCommandType(cCommand) == EngineConstants.COMMAND_TYPE_ATTACK)
          {
               Log_Trace_Combat("wrappers_h", "AddCommand(Attack) called from " + GetCurrentScriptName() + " on " + ToString(oObject));
          }

        if (fTimeout > 0.0f && IsFollower(oObject) == EngineConstants.FALSE) // followers can't have xCommand timeout
        {
            SetCommandFloatRef(ref cCommand, fTimeout, 5);
        }

          AddCommand(oObject, cCommand, bAddToFront, bStatic, nOverrideAddBehavior);

          //Uncommented by DHK
          nCurrentType = GetCommandType(GetCurrentCommand(oObject));

          if (nCurrentType == EngineConstants.COMMAND_TYPE_INVALID)
               Log_Systems("*** VERIFY: No xCommand is executing currently", EngineConstants.LOG_LEVEL_DEBUG, oObject,
                   EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);
          else
               Log_Systems("*** VERIFY: executing xCommand is: " + IntToString(nCurrentType), EngineConstants.LOG_LEVEL_DEBUG, oObject
                   , EngineConstants.LOG_SYSTEMS_SUBTYPE_WRAPPER_FUNCTIONS);

     }
Exemple #15
0
    IEnumerator HandleCommandMoveToLocation(xCommand cCommand, Action onComplete)
    {
        Vector3 vTarget = engine.GetCommandLocationRef(ref cCommand);
        Vector3 vObject = gameObject.transform.position;

        float distance = Mathf.Abs(vObject.sqrMagnitude - vTarget.sqrMagnitude);
        while (distance > 0 && cBreak == EngineConstants.FALSE)
        {
            vObject = Vector3.MoveTowards(vObject, vTarget, Time.deltaTime * 3);
            gameObject.transform.position = vObject;

            if (engine.GetLocalInt(engine.GetModule(), "GAME_MODE") == EngineConstants.GM_COMBAT ||
                    engine.GetLocalInt(engine.GetModule(), "GAME_MODE") == EngineConstants.GM_EXPLORE)
            {
                if (gameObject.GetComponent<xGameObjectUTC>().bControlled == EngineConstants.TRUE)
                    Camera.main.transform.position = new Vector3(vObject.x, Camera.main.transform.position.y, vObject.z);
            }

            distance = Mathf.Abs(vObject.sqrMagnitude - vTarget.sqrMagnitude);

            yield return null;
        }

        onComplete();
    }
     /* @brief Determines the exact action to take for the next combat round for a controlled party member
*
* This includes minimal AI handling
*
* @param oLastTarget the target the attacker attacked last round (if valid)
* @param nLastCommand the xCommand the attacker used last round
* @param nLastCommandStatus EngineConstants.COMMAND_SUCCESSFUL or EngineConstants.COMMAND_FAILURE - used mostly to detect movement failures
* @param nLastSubCommand last sub xCommand (ability ID for use ability xCommands)
* @author Yaron
*/
     public void AI_DetermineCombatRound_Partial(GameObject oLastTarget = null, int nLastCommand = -1, int nLastCommandStatus = EngineConstants.COMMAND_SUCCESSFUL, int nLastSubCommand = -1)
     {
#if DEBUG
          Log_Trace_AI("AI_DetermineCombatRound_Partial", "START, last target: " + GetTag(oLastTarget));
#endif
          if (GetCombatState(gameObject) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("AI_DetermineCombatRound_Partial", "Follower not in combat - aborting partial AI");
#endif
               return;
          }

          xCommand cCommand = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);

          GameObject oSelectedTarget = GetAttackTarget(gameObject);
#if DEBUG
          Log_Trace_AI("AI_DetermineCombatRound_Partial", "Selected target: " + GetTag(oSelectedTarget));
#endif

          GameObject oTarget = oSelectedTarget;
          if (_AI_IsHostileTargetValid(oTarget) == EngineConstants.FALSE)
               oTarget = oLastTarget;

#if DEBUG
          if (_AI_IsHostileTargetValid(oTarget) == EngineConstants.FALSE)
               Log_Trace_AI("AI_DetermineCombatRound_Partial", "COULD NOT FIND VALID TARGET");
          else
               Log_Trace_AI("AI_DetermineCombatRound_Partial", "FINAL target: " + GetTag(oTarget));
#endif

          GameObject oCurrent;
          int i;
          float fRangeToSelected = GetDistanceBetween(gameObject, oTarget);

          // If enemy is in melee range and has melee weapon -> attack
          if (_AI_IsHostileTargetValid(oTarget) != EngineConstants.FALSE && _AI_IsTargetInMeleeRange(oTarget) != EngineConstants.FALSE && IsUsingMeleeWeapon(gameObject) != EngineConstants.FALSE)
          {
               cCommand = CommandAttack(oTarget);
          }
          // If has ranged weapon and has ammo and is within the range of my ranged weapon -> attack
          else if (_AI_IsHostileTargetValid(oTarget) != EngineConstants.FALSE && IsUsingRangedWeapon(gameObject) != EngineConstants.FALSE
                  && fRangeToSelected <= _AI_GetEquippedWeaponRange() &&
                  (nLastCommand == EngineConstants.COMMAND_TYPE_ATTACK || nLastCommand == EngineConstants.COMMAND_TYPE_USE_ABILITY))
          {
               if (AI_BehaviorCheck_AttackBack() == EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("AI_DetermineCombatRound_Partial", "Creature behavior set to NOT attack back - aborting");
#endif
               }
               else
                    cCommand = CommandAttack(oTarget);
          }

          // NOTE: there is another part of the player attack logic that does not go here, but to the attack xEvent in rules_core
          // This deals with any creatures that attack me while I don't have any target

          if (GetCommandType(cCommand) == EngineConstants.COMMAND_TYPE_INVALID)
          {
#if DEBUG
               Log_Trace_AI("AI_DetermineCombatRound_Partial", "NO VALID ACTION - DOING NOTHING (waiting)", EngineConstants.LOG_SEVERITY_CRITICAL);
#endif
               if (IsControlled(gameObject) != EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("AI_DetermineCombatRound_Partial", "Controlled follower - aborting wait - keeping queue empty", EngineConstants.LOG_SEVERITY_CRITICAL);
#endif
                    return;
               }
               cCommand = _AI_DoNothing(-1, nLastCommandStatus, EngineConstants.FALSE, EngineConstants.TRUE);
          }

          if (IsObjectValid(oTarget) != EngineConstants.FALSE && IsObjectHostile(gameObject, oTarget) != EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("AI_DetermineCombatRound_Partial", "Attacking hostile target: allowing rest of party to attack");
#endif
               AI_SetPartyAllowedToAttack(EngineConstants.TRUE);
          }

          WR_AddCommand(gameObject, cCommand);
     }
Exemple #17
0
    IEnumerator HandleCommandUseObject(xCommand cCommand, Action onComplete)
    {
        //Let's check what type of Use can we get out of it, starting with area transition
        //TO DO WorldMap
        GameObject oTarget = engine.GetCommandObjectRef(ref cCommand);

        #region Area Transition
        if (oTarget != null && oTarget.tag == "AreaTransition")
        {
            string sArea = oTarget.GetComponent<xGameObjectUTP>().PLC_AT_DEST_AREA_TAG;
            string sWP = oTarget.GetComponent<xGameObjectUTP>().PLC_AT_DEST_TAG;
            if (sArea != "" && sWP != "")
            {
                xGameObjectMOD.instance.bTransitioning = EngineConstants.TRUE;

                //engine.Warning(w.name + " was clicked");
                GameObject _player = engine.GetHero();

                //Set placeable action as area transition
                engine.UpdateGameObjectProperty(oTarget, "PLC_ACTION", EngineConstants.PLACEABLE_ACTION_AREA_TRANSITION.ToString());

                xEvent ev = engine.Event(EngineConstants.EVENT_TYPE_USE);
                engine.SetEventCreatorRef(ref ev, _player);
                engine.SignalEvent(oTarget, ev);
            }
        }
        #endregion

        yield return null;
        onComplete();
    }
     // MGB - March 9, 2009 - Exposed hash values to speed up evaluation of AI Tactics.
     //moved const int EngineConstants.HASH_TYPE                = 0x12A02374; // "Type"
     //moved const int EngineConstants.HASH_USECHANCE           = 0x36032F31; // "UseChance"
     //moved const int EngineConstants.HASH_SUBCOMMAND          = 0x20804179; // "SubCommand"
     //moved const int EngineConstants.HASH_COMMAND             = 0x0DF6E88A; // "Command"
     //moved const int EngineConstants.HASH_CONDITION           = 0x03C7F222; // "Condition"
     //moved const int EngineConstants.HASH_TARGETTYPE          = 0x0F642429; // "TargetType"
     //moved const int EngineConstants.HASH_CONDITIONBASE       = 0x56BB2EC7; // "ConditionBase"
     //moved const int EngineConstants.HASH_VALIDFORTARGET      = 0x77485DB5; // "ValidForTarget"
     //moved const int EngineConstants.HASH_CONDITIONPARAMETER  = 0x706DADFC; // "ConditionParameter"
     //moved const int EngineConstants.HASH_CONDITIONPARAMETER2 = 0xBCC74707; // "ConditionParameter2"

     /* @brief Checks if a tactic is valid and executes it if valid
*
* @param nPackageTable the package table for the specified tactic
* @param nTacticID the tactic ID that we are trying to execute
* @param nLastCommandStatus used in AI_ExecuteAttack
* @returns EngineConstants.TRUE if the tactic was executed, EngineConstants.FALSE otherwise
* @author Yaron
*/
     public int _AI_ExecuteTactic(int nPackageTable, int nTacticID, int nLastCommandStatus, int nUseGUITables)
     {
          Log_Trace_AI("_AI_ExecuteTactic", "START [Package Table: " + IntToString(nPackageTable) + "], TacticID: [" + IntToString(nTacticID) + "]");
          // read the package and retrieve the condition, target type and action

          // First checking if the tactic is enabled (followers only)
          if (nUseGUITables != 0 && IsTacticEnabled(gameObject, nTacticID) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "Tactic not enabled - moving to next tactic");
#endif
               return EngineConstants.FALSE;
          }

          // MGB - March 9, 2009
          // Only evaluate the random trigger chance if we are using an AI Package table.
          // This should be done before retrieving the other numbers to trivially reject
          // rules that have extremely low chances of being fired.
          if (nUseGUITables == 0)
          {
               int nTacticTriggerChance = GetHashedM2DAInt(nPackageTable, EngineConstants.HASH_USECHANCE, nTacticID);

               int nDifficulty = GetGameDifficulty();
               if (nDifficulty == EngineConstants.GAME_DIFFICULTY_CASUAL)
               {
                    // should not affect 100% tactics
                    if (nTacticTriggerChance < 100 && nTacticTriggerChance >= 80)
                         nTacticTriggerChance = 50;
                    else if (nTacticTriggerChance >= 50 && nTacticTriggerChance < 80)
                         nTacticTriggerChance = 25;
                    else if (nTacticTriggerChance >= 20 && nTacticTriggerChance < 50)
                         nTacticTriggerChance = 10;
                    else if (nTacticTriggerChance >= 10 && nTacticTriggerChance < 20)
                         nTacticTriggerChance = 5;
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteTactic", "Adjusted trigger chance (casual difficulty): " + IntToString(nTacticTriggerChance));
#endif
               }

               // Verifying random chance
               int nRandom = Engine_Random(100) + 1;
               Log_Trace_AI("_AI_ExecuteTactic", "RANDOM: " + IntToString(nRandom) + ", Tactic Trigger Chance:" + IntToString(nTacticTriggerChance));
               if (nRandom > nTacticTriggerChance)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteTactic", "Tactic did not pass random check");
#endif
                    return EngineConstants.FALSE;
               }
          }

          if (GetObjectActive(gameObject) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "Object Inactive - exiting");
#endif
               return EngineConstants.FALSE;
          }

          int nRet;
          int nTacticTargetType;
          int nTacticTargetBitField;
          int nTacticCondition;
          int nTacticCommand;
          int nTacticSubCommand;
          int nLastTacticID = GetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC);
          string sTacticItemTag = GetTacticCommandItemTag(gameObject, nTacticID);

          if (nUseGUITables != 0)
          {
               nTacticTargetType = GetTacticTargetType(gameObject, nTacticID);
               nTacticCondition = GetTacticCondition(gameObject, nTacticID);
               nTacticCommand = GetTacticCommand(gameObject, nTacticID);
               nTacticSubCommand = GetTacticCommandParam(gameObject, nTacticID);
          }
          else
          {
               nTacticTargetType = GetHashedM2DAInt(nPackageTable, EngineConstants.HASH_TARGETTYPE, nTacticID);
               nTacticCondition = GetHashedM2DAInt(nPackageTable, EngineConstants.HASH_CONDITION, nTacticID);
               nTacticCommand = GetHashedM2DAInt(nPackageTable, EngineConstants.HASH_COMMAND, nTacticID);
               nTacticSubCommand = GetHashedM2DAInt(nPackageTable, EngineConstants.HASH_SUBCOMMAND, nTacticID);
          }

          nTacticTargetBitField = GetHashedM2DAInt(EngineConstants.TABLE_AI_TACTICS_TARGET_TYPE, EngineConstants.HASH_TYPE, nTacticTargetType);

#if DEBUG
          string sTacticSubCommand = IntToString(nTacticSubCommand);
          string sTacticCommand = _AI_GetCommandString(nTacticCommand);

          if (nTacticCommand == EngineConstants.AI_COMMAND_USE_ABILITY || nTacticCommand == EngineConstants.AI_COMMAND_ACTIVATE_MODE || nTacticCommand == EngineConstants.AI_COMMAND_DEACTIVATE_MODE)
               sTacticSubCommand = Log_GetAbilityNameById(nTacticSubCommand);

          Log_Trace_AI("_AI_ExecuteTactic", "[" + IntToString(nTacticID) + "]" +
                                            "[Target: " + IntToString(nTacticTargetType) + "] " +
                                            "[Cond: " + IntToString(nTacticCondition) + "] " +
                                            "[" + sTacticCommand + "] " +
                                            "[" + sTacticSubCommand + "] ");

          if (sTacticItemTag != "")
               Log_Trace_AI("_AI_ExecuteTactic", "[Item Tag]: " + sTacticItemTag);
#endif

          int nTacticCondition_Base = GetHashedM2DAInt(EngineConstants.TABLE_TACTICS_CONDITIONS, EngineConstants.HASH_CONDITIONBASE, nTacticCondition);

          // Retrieve condition details
          int nTacticCondition_ValidTarget = GetHashedM2DAInt(EngineConstants.TABLE_TACTICS_BASE_CONDITIONS, EngineConstants.HASH_VALIDFORTARGET, nTacticCondition_Base);
          int nTacticCondition_Parameter = GetHashedM2DAInt(EngineConstants.TABLE_TACTICS_CONDITIONS, EngineConstants.HASH_CONDITIONPARAMETER, nTacticCondition);
          int nTacticCondition_Parameter2 = GetHashedM2DAInt(EngineConstants.TABLE_TACTICS_CONDITIONS, unchecked((int)EngineConstants.HASH_CONDITIONPARAMETER2), nTacticCondition);

          Log_Trace_AI("_AI_ExecuteTactic", "[Condition Valid for Target: " + IntToString(nTacticCondition_ValidTarget) + "] " +
                                            "[Condition Base: " + IntToString(nTacticCondition_Base) + "] " +
                                            "[Condition Parameter: " + IntToString(nTacticCondition_Parameter) + "] ");

          // Verify that the target is valid
          if ((nTacticTargetBitField & nTacticCondition_ValidTarget) == 0)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "Tactic target type is not valid for the specified condition!", EngineConstants.LOG_SEVERITY_CRITICAL);
#endif
               return EngineConstants.FALSE;
          }

          // Verify that the ability can be executed (ignoring any possible target)

          if (_AI_IsCommandValid(nTacticCommand, nTacticSubCommand, nTacticTargetType) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "Tactic xCommand can not be executed");
#endif
               return EngineConstants.FALSE;
          }

          // Handling the tactic based on the base condition

          GameObject oTarget = null; // Any tactic action will be applied to this GameObject
          Vector3 lTarget = Vector3.zero;
          GameObject oFollowerSelectedTarget = null; // for tracking follower targets
          if (IsFollower(gameObject) != EngineConstants.FALSE)
               oFollowerSelectedTarget = GetAttackTarget(gameObject); // last target - hostile or not

          if (GetHasEffects(gameObject, EngineConstants.EFFECT_TYPE_CONFUSION) != EngineConstants.FALSE && nTacticTargetType == EngineConstants.AI_TARGET_TYPE_SELF)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "Confused creature trying to target SELF - aborting tactic");
#endif
               return EngineConstants.FALSE;
          }

          switch (nTacticCondition_Base)
          {
               case EngineConstants.AI_BASE_CONDITION_HAS_EFFECT_APPLIED:
                    {
                         oTarget = _AI_Condition_GetCreatureWithAIStatus(nTacticCondition_Parameter, nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_HP_LEVEL:
                    {
                         oTarget = _AI_Condition_GetCreatureWithHPLevel(nTacticCondition_Parameter, nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_MANA_OR_STAMINA_LEVEL:
                    {
                         oTarget = _AI_Condition_GetCreatureWithManaOrStaminaLevel(nTacticCondition_Parameter, nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_MOST_DAMAGED_IN_PARTY:
                    {
                         oTarget = _AI_Condition_GetNthMostDamagedCreatureInGroup(nTacticCondition_Parameter, nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_CLUSTERED_WITH_SAME_GROUP:
                    {
                         lTarget = _AI_Condition_GetEnemyClusteredWithSameGroup(nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_MOST_HATED_ENEMY:
                    {
                         oTarget = _AI_Condition_GetMostHatedEnemy(nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_NEAREST_VISIBLE:
                    {
                         oTarget = _AI_Condition_GetNearestVisibleCreature(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_NEAREST_RACE:
                    {
                         oTarget = _AI_Condition_GetNearestVisibleCreatureByRace(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_NEAREST_CLASS:
                    {
                         oTarget = _AI_Condition_GetNearestVisibleCreatureByClass(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_NEAREST_GENDER:
                    {
                         oTarget = _AI_Condition_GetNearestVisibleCreatureByGender(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_ATTACKING_PARTY_MEMBER:
                    {
                         oTarget = _AI_Condition_GetNearestEnemyAttackingPartyMember(nTacticCommand, nTacticSubCommand, nTacticCondition_Parameter, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_HAS_ANY_BUFF_EFFECT:
                    {
                         oTarget = _AI_Condition_GetNearestEnemyWithAnyBuffEffect(nTacticCommand, nTacticSubCommand, nTacticCondition_Parameter, nTacticID, nTacticTargetType);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_FLIP_COVER_STATE:
                    {
                         oTarget = _AI_Condition_GetNearestFlipCoverByState(nTacticCondition_Parameter, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_VULNERABLE_TO_DAMAGE:
                    {
                         oTarget = _AI_Condition_GetEnemyVulnerableToDamage(nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_ANY:
                    {
                         oTarget = _AI_Condition_GetAnyTarget(nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_HAS_AMMO_LEVEL:
                    {
                         oTarget = _AI_Condition_SelfHasAmmoLevel(nTacticCondition_Parameter);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_HAS_ARMOR_TYPE:
                    {
                         oTarget = _AI_Condition_HasArmorType(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_MOST_ENEMIES_HAVE_ARMOR_TYPE:
                    {
                         oTarget = _AI_Condition_MostEnemiesHaveArmorType(nTacticCondition_Parameter);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_ALL_ENEMIES_HAVE_ARMOR_TYPE:
                    {
                         oTarget = _AI_Condition_AllEnemiesHaveArmorType(nTacticCondition_Parameter);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_TARGET_HAS_RANK:
                    {
                         oTarget = _AI_Condition_TargetHasRank(nTacticTargetType, nTacticCondition_Parameter, nTacticID, nTacticCommand, nTacticSubCommand);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_BEING_ATTACKED_BY_ATTACK_TYPE:
                    {
                         oTarget = _AI_Condition_BeingAttackedByAttackType(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_USING_ATTACK_TYPE:
               case EngineConstants.AI_BASE_CONDITION_TARGET_USING_ATTACK_TYPE_FOLLOWER:
                    {
                         oTarget = _AI_Condition_UsingAttackType(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_MOST_ENEMIES_USING_ATTACK_TYPE:
                    {
                         oTarget = _AI_Condition_MostEnemiesUsingAttackType(nTacticCondition_Parameter);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_ALL_ENEMIES_USING_ATTACK_TYPE:
                    {
                         oTarget = _AI_Condition_AllEnemiesUsingAttackType(nTacticCondition_Parameter);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_AT_LEAST_X_ENEMIES_ARE_ALIVE:
                    {
                         oTarget = _AI_Condition_AtLeastXEnemiesAreAlive(nTacticTargetType, nTacticCondition_Parameter);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_AT_LEAST_X_CREATURES_ARE_DEAD:
                    {
                         oTarget = _AI_Condition_AtLeastXCreaturesAreDead(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_AT_LEAST_X_ALLIES_ARE_ALIVE:
                    {
                         oTarget = _AI_Condition_AtLeastXAlliesAreAlive(nTacticTargetType, nTacticCondition_Parameter, nTacticCondition_Parameter2);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_ENEMY_AI_TARGET_AT_RANGE:
                    {
                         oTarget = _AI_Condition_GetTargetAtRange(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_TARGET_AT_FLANK_LOCATION:
                    {
                         oTarget = _AI_Condition_GetTargetAtFlankLocation(nTacticCondition_Parameter, nTacticTargetType);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_SURROUNDED_BY_TARGETS:
                    {
                         oTarget = _AI_Condition_SurroundedByAtLeastXEnemies(nTacticCommand, nTacticSubCommand, nTacticCondition_Parameter, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_USING_RANGED_ATTACKS_AT_RANGE:
                    {
                         oTarget = _AI_Condition_GetTargetUsingRangedWeaponsAtRange(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_PARTY_MEMBERS_TARGET:
                    {
                         oTarget = _AI_Condition_GetPartyMemberTarget(nTacticCommand, nTacticSubCommand, nTacticCondition_Parameter, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_SELF_HP_LEVEL:
                    {
                         oTarget = _AI_Condition_SelfHPLevel(nTacticCondition_Parameter, nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_SELF_MANA_STAMINA_LEVEL:
                    {
                         oTarget = _AI_Condition_SelfManaStaminaLevel(nTacticCondition_Parameter, nTacticTargetType, nTacticCommand, nTacticSubCommand, nTacticID);
                         break;
                    }
               case EngineConstants.AI_BASE_CONDITION_FOLLOWER_AI_TARGET_AT_RANGE:
                    {
                         oTarget = _AI_Condition_GetTargetAtRange(nTacticTargetType, nTacticCondition_Parameter, nTacticCommand, nTacticSubCommand);
                         break;
                    }


          }

          // If target type is SELF: fail in case was tryng to trigger an ability that tries to cure something SELF doesn't have
          // For example: casting 'remove poison' while SELF does not have any poison xEffect
          // NOTE: this is handled for allies in _AIGetAllies
          if (nTacticTargetType == EngineConstants.AI_TARGET_TYPE_SELF && nTacticCommand == EngineConstants.AI_COMMAND_USE_ABILITY)
          {
               if (_AI_IsTargetValidForBeneficialAbility(gameObject, nTacticSubCommand) == EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteTactic", "Trying to apply a beneficial ability to SELF, but SELF does not need it");
#endif
                    oTarget = null;
               }
          }

          if (IsLocationValid(lTarget) == EngineConstants.FALSE && IsObjectValid(oTarget) == EngineConstants.FALSE)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "No valid target for condition");
#endif
               return EngineConstants.FALSE;
          }
          if (IsLocationValid(lTarget) != EngineConstants.FALSE)
          {
               Vector3 vDebug = GetPositionFromLocation(lTarget);
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "VALID LOCATION: " + VectorToString(vDebug));
#endif
          }

#if DEBUG
          Log_Trace_AI("_AI_ExecuteTactic", "GOT TARGET: " + GetTag(oTarget));
#endif


          // Check if the xCommand is valid on the target and execute the xCommand
          xCommand cTacticCommand = new xCommand(EngineConstants.COMMAND_TYPE_INVALID);
          int nAbilityTargetType;
          int nStationary = GetLocalInt(gameObject, EngineConstants.AI_FLAG_STATIONARY);
          if (IsFollower(gameObject) != EngineConstants.FALSE) nStationary = EngineConstants.FALSE;

          Log_Trace_AI("_AI_ExecuteTactic", "Creature stationary state: " + IntToString(nStationary));

          switch (nTacticCommand)
          {
               case EngineConstants.AI_COMMAND_USE_HEALTH_POTION_MOST:
                    {
                         float fCurrentHealth = GetCurrentHealth(gameObject);
                         float fMaxHealth = GetMaxHealth(gameObject);
                         if (fCurrentHealth == fMaxHealth)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "health full, not using health potion");
#endif
                              return EngineConstants.FALSE;
                         }
                         GameObject oItem = _AI_GetPotionByFilter(EngineConstants.AI_POTION_TYPE_HEALTH, EngineConstants.AI_POTION_LEVEL_MOST_POWERFUL);
                         cTacticCommand = _AI_GetPotionUseCommand(oItem);

                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
                              return EngineConstants.FALSE;

                         break;
                    }
               case EngineConstants.AI_COMMAND_USE_HEALTH_POTION_LEAST:
                    {
                         float fCurrentHealth = GetCurrentHealth(gameObject);
                         float fMaxHealth = GetMaxHealth(gameObject);
                         if (fCurrentHealth == fMaxHealth)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "health full, not using health potion");
#endif
                              return EngineConstants.FALSE;
                         }
                         GameObject oItem = _AI_GetPotionByFilter(EngineConstants.AI_POTION_TYPE_HEALTH, EngineConstants.AI_POTION_LEVEL_LEAST_POWERFUL);
                         cTacticCommand = _AI_GetPotionUseCommand(oItem);

                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
                              return EngineConstants.FALSE;
                         break;
                    }
               case EngineConstants.AI_COMMAND_USE_LYRIUM_POTION_MOST:
                    {
                         float fCurrentMana = GetCurrentManaStamina(gameObject);
                         float fMaxMana = IntToFloat(GetCreatureMaxMana(gameObject));
                         if (fCurrentMana == fMaxMana)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "mana full, not using mana potion");
#endif
                              return EngineConstants.FALSE;
                         }
                         GameObject oItem = _AI_GetPotionByFilter(EngineConstants.AI_POTION_TYPE_MANA, EngineConstants.AI_POTION_LEVEL_MOST_POWERFUL);
                         cTacticCommand = _AI_GetPotionUseCommand(oItem);

                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
                              return EngineConstants.FALSE;

                         break;
                    }
               case EngineConstants.AI_COMMAND_USE_LYRIUM_POTION_LEAST:
                    {
                         float fCurrentMana = GetCurrentManaStamina(gameObject);
                         float fMaxMana = IntToFloat(GetCreatureMaxMana(gameObject));
                         if (fCurrentMana == fMaxMana)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "mana full, not using mana potion");
#endif
                              return EngineConstants.FALSE;
                         }
                         GameObject oItem = _AI_GetPotionByFilter(EngineConstants.AI_POTION_TYPE_MANA, EngineConstants.AI_POTION_LEVEL_LEAST_POWERFUL);
                         cTacticCommand = _AI_GetPotionUseCommand(oItem);

                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
                              return EngineConstants.FALSE;
                         break;
                    }
               case EngineConstants.AI_COMMAND_RUN_SCRIPT:
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteTactic", "Running custom script (custom AI xEvent)");
#endif
                         xEvent evCustomAI = Event(EngineConstants.EVENT_TYPE_HANDLE_CUSTOM_AI);
                         xCommand cLast = GetPreviousCommand(gameObject);
                         int nLastCommand = GetCommandType(cLast);
                         SendEventHandleCustomAI(gameObject, null, nLastCommand, nLastCommandStatus,
                             -1, nTacticTargetType, nTacticSubCommand, nTacticID);

                         return EngineConstants.TRUE;
                    }
               case EngineConstants.AI_COMMAND_SWITCH_TO_MELEE:
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteTactic", "Switching to melee weapon set");
#endif
                         if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_MELEE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Creature already has melee weapons equipped - aborting xCommand");
#endif
                              return EngineConstants.FALSE;
                         }
                         cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_MELEE);
                         break;
                    }
               case EngineConstants.AI_COMMAND_SWITCH_TO_RANGED:
                    {
#if DEBUG
                         Log_Trace_AI("_AI_ExecuteTactic", "Switching to ranged weapon set");
#endif
                         if (_AI_GetWeaponSetEquipped() == EngineConstants.AI_WEAPON_SET_RANGED)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Creature already has ranged weapons equipped - aborting xCommand");
#endif
                              return EngineConstants.FALSE;
                         }
                         cTacticCommand = _AI_SwitchWeaponSet(EngineConstants.AI_WEAPON_SET_RANGED);
                         break;
                    }
               case EngineConstants.AI_COMMAND_JUMP_TO_LATER_TACTIC:
                    {
                         if (nTacticSubCommand != -1 && nTacticSubCommand <= nTacticID)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Jump to later tactic: invalid value (must be greater then current tactic id)");
#endif
                              return EngineConstants.FALSE;
                         }
                         return nTacticSubCommand;
                    }
               case EngineConstants.AI_COMMAND_FLY:
                    {
                         GameObject oTurnTo = null;

                         if (_AI_CheckMoveTimer() == EngineConstants.FALSE)
                              return EngineConstants.FALSE;

                         switch (nTacticSubCommand)
                         {
                              case EngineConstants.AI_FLY_TURN_MOST_HATED:
                                   {
                                        oTurnTo = AI_Threat_GetThreatTarget(gameObject);
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_TURN_NEAREST_AI_WP:
                                   {
                                        oTurnTo = UT_GetNearestObjectByTag(gameObject, EngineConstants.AI_WP_MOVE);
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_TURN_NEAREST_ALLY:
                                   {
                                        List<GameObject> arAllies = _AI_GetAllies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oTurnTo = arAllies[0];
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_TURN_NEAREST_ENEMY:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oTurnTo = arEnemies[0];
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_APPROACH_MOST_HATED:
                                   {
                                        oTurnTo = AI_Threat_GetThreatTarget(gameObject);
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_APPROACH_NEAREST_ENEMY:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oTurnTo = arEnemies[0];
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_APPROACH_AI_WP_NEAREST_TO_MOST_HATED:
                                   {
                                        GameObject oEnemy = AI_Threat_GetThreatTarget(gameObject);
                                        oTurnTo = UT_GetNearestObjectByTag(oEnemy, EngineConstants.AI_WP_MOVE);
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_FLY_APPROACH_AI_WP_NEAREST_TO_NEAREST_ENEMY:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        GameObject oEnemy = arEnemies[0];
                                        oTurnTo = UT_GetNearestObjectByTag(oEnemy, EngineConstants.AI_WP_MOVE);
                                        cTacticCommand = _AI_GetFlyCommand(oTurnTo, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                         }
                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_WAIT)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Wait xCommand for fly/turn AI action - aborting AI instead");
#endif
                              return EngineConstants.FALSE;
                         }

                         if (IsObjectValid(oTurnTo) == EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Invalid turn target");
#endif
                              return EngineConstants.FALSE;
                         }
                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Invalid fly/turn xCommand");
#endif
                              return EngineConstants.FALSE;
                         }
                         _AI_SetMoveTimer();
                         break;
                    }
               case EngineConstants.AI_COMMAND_MOVE:
                    {
                         GameObject oMoveTo = null;
                         if (nStationary > 0)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Creature stationary - can't excute move xCommand");
#endif
                              return EngineConstants.FALSE;
                         }
                         if (nLastCommandStatus == EngineConstants.COMMAND_FAILED_TIMEOUT)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Last xCommand failed on timeout - can't excute move xCommand");
#endif
                              return EngineConstants.FALSE;
                         }
                         if (_AI_CheckMoveTimer() == EngineConstants.FALSE)
                              return EngineConstants.FALSE;

                         switch (nTacticSubCommand)
                         {
                              case EngineConstants.AI_MOVE_HATED_ENEMY:
                                   {
                                        oMoveTo = AI_Threat_GetThreatTarget(gameObject);
                                        cTacticCommand = CommandMoveToObject(oMoveTo, EngineConstants.TRUE, 0.0f, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_NEAREST_AI_WP:
                                   {
                                        oMoveTo = UT_GetNearestObjectByTag(gameObject, EngineConstants.AI_WP_MOVE);
                                        cTacticCommand = CommandMoveToObject(oMoveTo, EngineConstants.TRUE, 0.0f, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_NEAREST_ALLY:
                                   {
                                        List<GameObject> arAllies = _AI_GetAllies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oMoveTo = arAllies[0];
                                        cTacticCommand = CommandMoveToObject(oMoveTo, EngineConstants.TRUE, 0.0f, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_NEAREST_ENEMY:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oMoveTo = arEnemies[0];
                                        cTacticCommand = CommandMoveToObject(oMoveTo, EngineConstants.TRUE, 0.0f, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_RANDOM_AI_WP:
                                   {
                                        int nRand = Engine_Random(3);
                                        List<GameObject> arWPs = GetNearestObjectByTag(gameObject, EngineConstants.AI_WP_MOVE, EngineConstants.OBJECT_TYPE_WAYPOINT, 3);
                                        oMoveTo = arWPs[nRand];
                                        if (IsObjectValid(oMoveTo) == EngineConstants.FALSE) // in case there are not enough AI waypoints
                                             oMoveTo = arWPs[0];
                                        cTacticCommand = CommandMoveToObject(oMoveTo, EngineConstants.TRUE, 0.0f, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_AWAY_FROM_ENEMY_MEDIUM:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oMoveTo = arEnemies[0]; // Move away from
                                        float fDistance = GetDistanceBetween(gameObject, oMoveTo);
                                        if (fDistance >= EngineConstants.AI_MOVE_AWAY_DISTANCE_MEDIUM)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteTactic", "NOT MOVING AWAY - already far away from target");
#endif
                                             return EngineConstants.FALSE;
                                        }
                                        cTacticCommand = CommandMoveAwayFromObject(oMoveTo, EngineConstants.AI_MOVE_AWAY_DISTANCE_MEDIUM, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_AWAY_FROM_ENEMY_SHORT:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oMoveTo = arEnemies[0]; // Move away from
                                        float fDistance = GetDistanceBetween(gameObject, oMoveTo);
                                        if (fDistance >= EngineConstants.AI_MOVE_AWAY_DISTANCE_SHORT)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteTactic", "NOT MOVING AWAY - already far away from target");
#endif
                                             return EngineConstants.FALSE;
                                        }
                                        cTacticCommand = CommandMoveAwayFromObject(oMoveTo, EngineConstants.AI_MOVE_AWAY_DISTANCE_SHORT, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_AWAY_FROM_ENEMY_RANDOM:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oMoveTo = arEnemies[0]; // Move away from
                                        float fDistanceToMoveAway = RandomF(FloatToInt(EngineConstants.AI_MOVE_AWAY_DISTANCE_MEDIUM - EngineConstants.AI_MOVE_AWAY_DISTANCE_SHORT), FloatToInt(EngineConstants.AI_MOVE_AWAY_DISTANCE_SHORT));

                                        float fDistance = GetDistanceBetween(gameObject, oMoveTo);
                                        if (fDistance >= fDistanceToMoveAway)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteTactic", "NOT MOVING AWAY - already far away from target");
#endif
                                             return EngineConstants.FALSE;
                                        }
                                        cTacticCommand = CommandMoveAwayFromObject(oMoveTo, fDistanceToMoveAway, EngineConstants.TRUE);
                                        SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, EngineConstants.AI_TACTIC_ID_MOVE); // the last tactic being used
                                        break;
                                   }
                              case EngineConstants.AI_MOVE_AWAY_FROM_ENEMY_COWARD:
                                   {
                                        List<GameObject> arEnemies = _AI_GetEnemies(EngineConstants.AI_COMMAND_MOVE, nTacticSubCommand);
                                        oMoveTo = arEnemies[0]; // Move away from
                                        float fDistance = GetDistanceBetween(gameObject, oMoveTo);
                                        if (fDistance < EngineConstants.AI_MOVE_AWAY_DISTANCE_MEDIUM)
                                             // run away
                                             cTacticCommand = CommandMoveToObject(oMoveTo, EngineConstants.TRUE, EngineConstants.AI_MOVE_AWAY_DISTANCE_MEDIUM, EngineConstants.FALSE);
                                        else
                                             // cower in fear
                                             cTacticCommand = CommandPlayAnimation(602);

                                        break;
                                   }
                         }

                         if (IsObjectValid(oMoveTo) == EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Invalid movement target");
#endif
                              return EngineConstants.FALSE;
                         }
                         if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Invalid move xCommand");
#endif
                              return EngineConstants.FALSE;
                         }
                         _AI_SetMoveTimer();
                         break;
                    }
               case EngineConstants.AI_COMMAND_USE_ITEM:
                    {
                         // ASSUMING ITEMS CAN BE USED ONLY ON SELF

                         if (Ability_CheckUseConditions(gameObject, gameObject, nTacticSubCommand) == EngineConstants.FALSE)
                              return EngineConstants.FALSE; // failed tactic

                         if (_AI_CanUseAbility(nTacticSubCommand, gameObject) == EngineConstants.FALSE)
                              return EngineConstants.FALSE; // can't use specific ability

                         // Command is valid to be executed on the target
                         Vector3 vNul = Vector3.zero;
                         cTacticCommand = CommandUseAbility(nTacticSubCommand, gameObject, vNul, -1.0f, sTacticItemTag);
                         break;
                    }
               case EngineConstants.AI_COMMAND_USE_PLACEABLE:
                    {
                         // Target should be valid now
                         // At this moment the user should register an action on the placeable
                         if (nStationary > 0)
                         {
                              float fDistance = GetDistanceBetween(gameObject, oTarget);
                              if (fDistance > EngineConstants.AI_STATIONARY_RANGE)
                              {
#if DEBUG
                                   Log_Trace_AI("_AI_ExecuteTactic", "Creature stationary - placeable too far away to execute xCommand");
#endif
                                   return EngineConstants.FALSE;
                              }
                         }

                         int nCount = GetLocalInt(oTarget, EngineConstants.PLC_FLIP_COVER_USE_COUNT);
                         nCount++;
                         SetLocalInt(oTarget, EngineConstants.PLC_FLIP_COVER_USE_COUNT, nCount);

                         // Check that I'm not already using a flip cover
                         GameObject oPlaceable = GetLocalObject(gameObject, EngineConstants.AI_PLACEABLE_BEING_USED);
                         if (IsObjectValid(oPlaceable) != EngineConstants.FALSE && IsDead(oPlaceable) == EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "I'm already using a placeable!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                              return EngineConstants.FALSE;
                         }
                         SetLocalObject(gameObject, EngineConstants.AI_PLACEABLE_BEING_USED, oTarget);

                         cTacticCommand = CommandUseObject(oTarget, EngineConstants.PLACEABLE_ACTION_USE);
                         break;
                    }
               case EngineConstants.AI_COMMAND_ATTACK:
                    {
                         // If target is not a hostile creature then fail the attack
                         if (IsObjectHostile(gameObject, oTarget) == EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Non-hostile target for ATTACK action!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                              return EngineConstants.FALSE;
                         }
                         cTacticCommand = _AI_ExecuteAttack(oTarget, nLastCommandStatus);

                         break;
                    }
               case EngineConstants.AI_COMMAND_ACTIVATE_MODE:
                    {
                         if (Ability_CheckUseConditions(gameObject, oTarget, nTacticSubCommand) == EngineConstants.FALSE)
                              return EngineConstants.FALSE; // failed tactic

                         cTacticCommand = CommandUseAbility(nTacticSubCommand, gameObject, Vector3.zero);
                         break;
                    }
               case EngineConstants.AI_COMMAND_DEACTIVATE_MODE:
                    {
                         // No need to check use conditions for the ability since we are trying to deactivate it

                         cTacticCommand = CommandUseAbility(nTacticSubCommand, gameObject, Vector3.zero);
                         break;
                    }
               case EngineConstants.AI_COMMAND_WAIT:
                    {
                         int bQuick = EngineConstants.FALSE;
                         if (nTacticSubCommand == 1) // this is a wait with cooldown (used for rogues for example)
                         {
                              int nMoveStart = GetLocalInt(gameObject, EngineConstants.AI_WAIT_TIMER);
                              int nCurrentTime = GetTime();
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Wait time dif: " + IntToString(nCurrentTime - nMoveStart));
#endif
                              if (nMoveStart != 0 && nCurrentTime - nMoveStart <= EngineConstants.AI_WAIT_MIN_TIME)
                              {
#if DEBUG
                                   Log_Trace_AI("_AI_ExecuteTactic", "Last wait happened too soon");
#endif
                                   return EngineConstants.FALSE;
                              }
                              SetLocalInt(gameObject, EngineConstants.AI_WAIT_TIMER, nCurrentTime);
                              bQuick = EngineConstants.TRUE;
                         }
                         cTacticCommand = _AI_DoNothing(nLastTacticID, nLastCommandStatus, EngineConstants.FALSE, EngineConstants.TRUE);
                         break;
                    }
               case EngineConstants.AI_COMMAND_USE_ABILITY:
                    {

                         // -----------------------------------------------------------------
                         // Ability usage disabled
                         // -----------------------------------------------------------------
                         if (GetCreatureFlag(gameObject, EngineConstants.CREATURE_RULES_FLAG_AI_NO_ABILITIES) != EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "ABORT: AI_COMMAND_USE_ABILITY - EngineConstants.CREATURE_RULES_FLAG_AI_NO_ABILITIES was set.", EngineConstants.LOG_SEVERITY_WARNING);
                              Warning("ERROR! ability use disabled by a debug flag - call Yaron if you weren't using debug scripts!!!");
#endif
                              return EngineConstants.FALSE;
                         }

                         // Checking target types
                         nAbilityTargetType = Ability_GetAbilityTargetType(nTacticSubCommand, Ability_GetAbilityType(nTacticSubCommand));
                         // NOTICE: tactic target types are not exactly the same as ability target types

                         // Make sure the target type specified for the ability matches a target type that is valid for the ability
                         switch (nTacticTargetType)
                         {
                              case EngineConstants.AI_TARGET_TYPE_ENEMY:
                              case EngineConstants.AI_TARGET_TYPE_MOST_HATED:
                                   {
                                        if (nAbilityTargetType != EngineConstants.TARGET_TYPE_HOSTILE_CREATURE)
                                        {
                                             // Trying to find a target anyways
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteTactic", "Hostile target for an ability that does not support hostile targets - trying to find a new target!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                             if (nAbilityTargetType == EngineConstants.TARGET_TYPE_SELF)
                                             {
#if DEBUG
                                                  Log_Trace_AI("_AI_ExecuteTactic", "Target will now be gameObject");
#endif
                                                  oTarget = gameObject;
                                             }
                                             else if (nAbilityTargetType == EngineConstants.TARGET_TYPE_GROUND)
                                             {
#if DEBUG
                                                  Log_Trace_AI("_AI_ExecuteTactic", "Target will now be Vector3 of target");
#endif
                                                  lTarget = GetLocation(oTarget);
                                             }
                                             else
                                                  return EngineConstants.FALSE;
                                        }
                                        break;
                                   }
                              case EngineConstants.AI_TARGET_TYPE_ALLY:
                                   {
                                        if (nAbilityTargetType != EngineConstants.TARGET_TYPE_FRIENDLY_CREATURE)
                                        {
                                             if (nAbilityTargetType == EngineConstants.TARGET_TYPE_SELF)
                                             {
#if DEBUG
                                                  Log_Trace_AI("_AI_ExecuteTactic", "Ally target for an ability that does not support friendly targets - Target will now be SELF!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                                  oTarget = gameObject;
                                             }
                                             else
                                             {
#if DEBUG
                                                  Log_Trace_AI("_AI_ExecuteTactic", "Ally target for an ability that does not support friendly targets - FAILING TACTIC!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                                  return EngineConstants.FALSE;
                                             }
                                        }
                                        break;
                                   }
                              case EngineConstants.AI_TARGET_TYPE_PLACEABLE:
                                   {
                                        if (nAbilityTargetType != EngineConstants.TARGET_TYPE_PLACEABLE)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteTactic", "Placeable target for an ability that does not support placeable targets - FAILING TACTIC!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                             return EngineConstants.FALSE;
                                        }
                                        break;
                                   }
                              case EngineConstants.AI_TARGET_TYPE_SELF:
                                   {
                                        if (nAbilityTargetType != EngineConstants.TARGET_TYPE_SELF)
                                        {
#if DEBUG
                                             Log_Trace_AI("_AI_ExecuteTactic", "Self target for an ability that does not support self targets - trying to find a different target!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                             if (nAbilityTargetType == EngineConstants.TARGET_TYPE_HOSTILE_CREATURE)
                                             {
                                                  if (IsFollower(gameObject) != EngineConstants.FALSE)
                                                  {
                                                       if (_AI_IsHostileTargetValid(oFollowerSelectedTarget) != EngineConstants.FALSE)
                                                            oTarget = oFollowerSelectedTarget;
                                                       else
                                                            oTarget = _AI_Condition_GetNearestVisibleCreature(EngineConstants.AI_TARGET_TYPE_ENEMY, 1, nTacticCommand, nTacticSubCommand, nTacticID);
                                                  }
                                                  else
                                                       oTarget = _AI_Condition_GetMostHatedEnemy(1, nTacticCommand, nTacticSubCommand, nTacticID);
                                             }
                                             else if (nAbilityTargetType == EngineConstants.TARGET_TYPE_FRIENDLY_CREATURE)
                                             {
                                                  oTarget = _AI_Condition_GetAnyTarget(EngineConstants.AI_TARGET_TYPE_ALLY, nTacticCommand, nTacticSubCommand, nTacticID);
                                             }
                                             else
                                             {
#if DEBUG
                                                  Log_Trace_AI("_AI_ExecuteTactic", "Can not find a different target for this ability target type!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                                  return EngineConstants.FALSE;
                                             }

                                             if (IsObjectValid(oTarget) == EngineConstants.FALSE)
                                             {
#if DEBUG
                                                  Log_Trace_AI("_AI_ExecuteTactic", "Failed to find a secondary target for this ability!", EngineConstants.LOG_SEVERITY_WARNING);
#endif
                                                  return EngineConstants.FALSE;
                                             }

                                        }
                                        break;
                                   }
                         }

                         if (nLastCommandStatus == EngineConstants.COMMAND_FAILED_TIMEOUT && IsObjectHostile(gameObject, oTarget) != EngineConstants.FALSE)
                         {
#if DEBUG
                              Log_Trace_AI("_AI_ExecuteTactic", "Last xCommand failed on timeout and this ability targets hostiles - aborting ability use");
#endif
                              return EngineConstants.FALSE;
                         }

                         if (Ability_CheckUseConditions(gameObject, oTarget, nTacticSubCommand) == EngineConstants.FALSE)
                              return EngineConstants.FALSE; // failed tactic

                         if (_AI_CanUseAbility(nTacticSubCommand, oTarget) == EngineConstants.FALSE)
                              return EngineConstants.FALSE; // can't use specific ability

                         Vector3 vTarget = Vector3.zero;

                         if (IsLocationValid(lTarget) != EngineConstants.FALSE)
                         {
                              vTarget = GetPositionFromLocation(lTarget);
                              oTarget = null;
                         }
                         // Command is valid to be executed on the target
                         cTacticCommand = CommandUseAbility(nTacticSubCommand, oTarget, vTarget);
                         break;
                    }
          }

          if (GetCommandType(cTacticCommand) == EngineConstants.COMMAND_TYPE_INVALID)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "INVALID TACTIC COMMAND - FAILING TACTIC", EngineConstants.LOG_SEVERITY_CRITICAL);
#endif
               return EngineConstants.FALSE; ;
          }

          if (GetHasEffects(gameObject, EngineConstants.EFFECT_TYPE_CONFUSION) != EngineConstants.FALSE && oTarget == gameObject)
          {
#if DEBUG
               Log_Trace_AI("_AI_ExecuteTactic", "Confused creature trying to target SELF (second check) - aborting tactic");
#endif
               return EngineConstants.FALSE;
          }

          if (IsFollower(gameObject) != EngineConstants.FALSE && IsObjectValid(oTarget) != EngineConstants.FALSE && IsObjectHostile(gameObject, oTarget) != EngineConstants.FALSE
              && AI_GetPartyAllowedToAttack() == EngineConstants.FALSE)
          {

               if (IsControlled(gameObject) != EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteTactic", "Controlled follower attacking - clearing rest of party to target enemies");
#endif
                    AI_SetPartyAllowedToAttack(EngineConstants.TRUE);
               }
               else if (AI_BehaviorCheck_AttackOnCombatStart() == EngineConstants.FALSE)
               {
#if DEBUG
                    Log_Trace_AI("_AI_ExecuteTactic", "Non-controlled follower trying to attack a hostile before being allowed - trying to move closer to leader");
#endif
                    cTacticCommand = _AI_MoveToControlled(nLastCommandStatus);
               }
          }

          // Flagging last tactic used
          SetLocalInt(gameObject, EngineConstants.AI_LAST_TACTIC, nTacticID);
          float fTimer = EngineConstants.AI_COMMAND_TIMER;
          if (oTarget == gameObject)
               fTimer = 0.0f;
          WR_AddCommand(gameObject, cTacticCommand, EngineConstants.FALSE, EngineConstants.FALSE, -1, fTimer);

#if DEBUG
          Log_Trace_AI("_AI_ExecuteTactic", "***** TACTIC EXECUTED! *****");
#endif
          return EngineConstants.TRUE;
     }
Exemple #19
0
    IEnumerator HandleCommandAttack(xCommand cCommand, Action onComplete)
    {
        GameObject oTarget = engine.GetCommandObjectRef(ref cCommand);

        xEvent ev = engine.Event(EngineConstants.EVENT_TYPE_COMMAND_PENDING);
        engine.SetEventCreatorRef(ref ev, gameObject);
        engine.SetEventObjectRef(ref ev, 0, gameObject);
        engine.SetEventObjectRef(ref ev, 1, oTarget);
        engine.SetEventIntegerRef(ref ev, 0, EngineConstants.COMMAND_TYPE_ATTACK);
        engine.SetEventIntegerRef(ref ev, 1, EngineConstants.COMMAND_TYPE_ATTACK);
        engine.SignalEvent(gameObject, ev);

        yield return null;
        //onComplete();//Not needed here, we would update the command attack results in set command results function
    }
Exemple #20
0
     //moved public const int EngineConstants.CRITICAL_MODIFIER_MELEE  = EngineConstants.PROPERTY_ATTRIBUTE_MELEE_CRIT_MODIFIER;
     //moved public const int EngineConstants.CRITICAL_MODIFIER_MAGIC  = EngineConstants.PROPERTY_ATTRIBUTE_MAGIC_CRIT_MODIFIER;
     //moved public const int EngineConstants.CRITICAL_MODIFIER_RANGED = EngineConstants.PROPERTY_ATTRIBUTE_RANGED_CRIT_MODIFIER;

     /* ----------------------------------------------------------------------------
     * @brief (core_h) Return whether a xCommand is of higher priority than the current
     *                 command
     *
     * D E P R E C A T E D
     *
     * @param oObject      The GameObject that holds the current command
     * @param cNewCommand  The new xCommand that is tested
     *
     * @returns  EngineConstants.TRUE if cNewCommand has higher priority, EngineConstants.FALSE if not
     *
     * @author Georg Zoeller
     *  ---------------------------------------------------------------------------**/
     public int IsNewCommandHigherPriority(GameObject oObject, xCommand cNewCommand)
     {
          int bNewIsHigher = EngineConstants.FALSE;
          xCommand cCurrent = GetCurrentCommand(oObject);
          int nNewCommandPriority = GetCommandPriority(cNewCommand);
          int nCurrentCommandPriority = GetCommandPriority(cCurrent);
          int nNewCommandType = GetCommandType(cNewCommand);
          int nCurrentCommandType = GetCommandType(cCurrent);

          /*    Log_Rules("Rules_IsNewCommandHigherPriority: current command: <" +
                         IntToString(nCurrentCommandType) + "> new command: <" +
                         IntToString(nNewCommandType) + ">", EngineConstants.LOG_LEVEL_DEBUG, oObject);*/

          if (nNewCommandPriority == EngineConstants.COMMAND_PRIORITY_INVALID)
          {
               /*        Log_Rules("Rules_IsNewCommandHigherPriority: invalid priority for NEW command!",
                                 EngineConstants.LOG_LEVEL_ERROR, oObject);*/
          }
          else if (nCurrentCommandPriority == EngineConstants.COMMAND_PRIORITY_INVALID)
          {
               /*       Log_Rules("Rules_IsNewCommandHigherPriority: invalid priority for CURRENT command! (creature doing nothing?)",
                                 EngineConstants.LOG_LEVEL_ERROR, oObject);*/

               return EngineConstants.TRUE; // creature might just not doing anything at the moment - ok to interrupt
          }
          else // valid priorities for new and current commands
          {
               if (nNewCommandPriority > nCurrentCommandPriority)
               {
                    // the new xCommand is eligible to kick the current xCommand out of the queue
                    /* Log_Rules("Rules_IsNewCommandHigherPriority: new xCommand is higher priority",
                               EngineConstants.LOG_LEVEL_DEBUG, oObject);*/

                    return EngineConstants.TRUE;
               }
               else
               {
                    /*            Log_Rules("Rules_IsNewCommandHigherPriority: new xCommand is NOT higher priority",
                                           EngineConstants.LOG_LEVEL_DEBUG, oObject);*/
               }

          }
          return EngineConstants.FALSE;
     }