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); }
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); }
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; }
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 }
//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; }