//---------------------------------------------------------------------------------------
//  FILE:    XComIdleAnimationStateMachine.uc
//  AUTHOR:  Ryan McFall  --  11/08/2012
//  PURPOSE: This actor manages the animation state of an XGUnit while it is not performing
//           an action. This class is native so that XGUnit native
//           functions can access its members / functions. This class replaces the
//           'ConstantCombat' system which was a large source of timing related
//           bugs and animation issues in X:EU
//           
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------
class XComIdleAnimationStateMachine extends Actor native(Animation)
	config(Animation);

//Cached values we receive when we are created
var private XGUnit                  Unit;
var private XGUnitNativeBase        UnitNative;
var private XComUnitPawn            UnitPawn;
var private XComUnitPawnNativeBase  UnitPawnNative;

//******** General Purpose Variables **********
var private int                 NumVisibleEnemies;      //Temp var to hold the result from the visibility mgr in state code
var private bool                bWaitingForStanceEval;  //This is set if the system gets a stance eval request but cannot fufill it immediately
var private XGUnit              LastAttacker;           //Set by the targeting action, when a unit selects this unit as a target 
var private Vector              TargetLocation;         //The location of the target for this unit
var private Actor               TargetActor;            //The current target of the unit. Can be NULL if the target is just a location.
var private bool                bDeferredGotoIdle;      //This is set to true if the Resume() function is called while the state machine is in a state that cannot be interrupted
var private bool                bDeferredGotoDormant;   //This is set to true if the GoDormant() function is called while the state machine is in a state that cannot be interrupted
var private Actor               kDormantLock;           //Actor that requests GoDormant() must also be the same to request Resume()
var private bool                bInLatentTurn;          //This flag is true during the 'TurnTowardsPosition' latent function
var private bool                bIsCivilian;            //This flat is true if the unit this state machine belongs to is using the civilian animation set
var			bool				bIsTurret;				//This flag is true if the unit this state machine belongs to is a turret
var private float               fIdleAnimRate;          //A random scale to the idle animation speed so that our soldiers don't look like a vaudville routine when the map starts
var private bool                bRestartIdle;           //If the idle state is entered and the unit is not already performing an idle, restart the idle anim
var         bool                bLoadInitStrangle;      //Resume strangle from load - skip initial strangle start-up animation
var         bool                bTargeting;             // Set this so we know to trigger targeting
var private CustomAnimParams    AnimParams;             //Params to start animations with
var         AnimNodeSequence    TurningSequence;        //If we are turning this is the sequence doing it
var			Name				PersistentEffectIdleName; // Go to the Persistent state and play this idle anim
var private XComGameStateVisualizationMgr VisualizationMgr;
var private XGUnit				PodTalker;
var			bool				TalkerHasSomeoneToTalkTo;
var private config float		PodIdleFidgetPercentage;
var private config float		PodTalkFidgetPercentage;
var privatewrite config float	LeftHandIKBlendTime;	//Length of time in seconds for the left hand to IK to its target position when left hand IK is turned on.
var private XComGameState_Unit	UnitState;
var private int					VisualizedHistoryIndex;
var			bool				bShowDebugInfo;			//Display debug information about the idle state machine
//****************************************

//******** Peek State Variables **********
var private config float                PeekTimeoutMin;
var private config float				PeekTimeoutMax;
var private float						PeekTimeoutCurrent;
//****************************************

//******** Signal State Variables **********
var public Name							SignalAnimationName; // The animation to play in the signal state
var public vector						SignalFaceLocation; // Turn this direction before playing the animation
//*******************************************************

//******** EvaluateStance State Variables ****************
var private CachedCoverAndPeekData		CurrentCoverPeekData;	//Stores information on the tile that the unit is standing in
var int									DesiredCoverIndex;	//The cover facing/direction that the unit should use (And sometimes the previous cover index)
var UnitPeekSide						DesiredPeekSide;	//The peek side that the unit should use (And sometimes the previous peek side)
var private bool                        bForceDesiredCover;	//This flag is set by 'ForceStance', guides the end behavior for the EvalutateStance state
var private bool                        bForceTurnTarget;	//This flag indicates that TempFaceLocation should be used for a turn target in place of TargetLocation.
var private vector                      TempFaceLocation;	//Temporary used by the state code - holds the location the unit should face
var private name                        ReturnToState;	    //This is the state that EvaluateStance will return to unless the bDeferredGotoXXX flags are set
var private bool                        bIsExitFromCover;   //Used by evaluate stance to exit cover
var private float                       ExitCoverAnimTimer; //We don't need to play the entire get out of cover anim, it can blend into the run anim
var private float                       ExitCoverAnimTime;  //We don't need to play the entire get out of cover anim, it can blend into the run anim - Sets the timeout
var private EIdleTurretState			CurrentTurretState; //State corresponding to currently-playing turret idle animation.
var private bool						StoredUpdateSkelWhenNotRendered; // Store off the bUpdateSkelWhenNotRendered so we can restore it after evaluate stance
//****************************************

//******** Fire State Variables **************************
var private float                       m_fAimTimer;
var private bool                        m_bRecenterAim;
var private Vector2D                    m_TmpAimOffset;
var private float                       FireState_AimingTimeout;
//****************************************

//******** Crouch State Variables ********
var private bool bContinueCrouching;
//****************************************

//******** Panicked State Variables ********
var private bool bStartedPanick;
//****************************************

cpptext
{
	/* Latent Function Declarations */
	DECLARE_FUNCTION(execPollTurnTowardsPosition);
}

//******** Public Interface Functions **************
//Call immediately after spawning, or as soon as OwningUnit becomes available (MP)
function Initialize(XGUnit OwningUnit)
{
	Unit = OwningUnit;
	UnitNative = Unit;
	UnitPawn = UnitNative.GetPawn();
	UnitPawnNative = UnitPawn;

	bIsCivilian = Unit.IsCivilianChar();
	bIsTurret = Unit.IsTurret();
	
	fIdleAnimRate = GetNextIdleRate();
	VisualizationMgr = `XCOMVISUALIZATIONMGR;

	AddHUDOverlayActor();
}

function bool IsStateInterruptible()
{
	local name StateName;

	StateName = GetStateName();
	return StateName == 'Idle' || StateName == 'Dormant' || StateName == 'PeekStart' || StateName == 'TargetingStart' || StateName == 'TargetingHold' || StateName == 'Panicked' || StateName == 'TurretIdle';
}

//This function allows external systems to request an examination of the present cover state 
//and facing of the unit. The 'EvaluateStance' state decides whether the unit should remain 
//in its present cover state or not. Examples of an events that could cause a unit to perform 
//this evaluation would be 1. the destruction of a piece of cover the unit was using and
//2. Changing a unit's target while in weapon aim mode
function CheckForStanceUpdate()
{
	if (IsStateInterruptible())
	{		
		`log("*********************** GOTO EvaluateStance ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name),'XCom_Anim');
		GotoState('EvaluateStance');
	}
	else
	{
		`log("*********************** CheckForStanceUpdate() bWaitingForStanceEval = true ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name),'XCom_Anim');
		bWaitingForStanceEval = true; //The system is otherwise occupied, the next time the idle state is entered the eval state will trigger
	}
}

//Instructs the state machine to do an automatic stance check the as soon as it returns to the idle state
native function CheckForStanceUpdateOnIdle();

//This is a specialized function that smoothly removes a unit from cover without turning the unit towards a target. Notes that this is used specifically in situations
//where the unit needs to exit cover to perform an action or animation, but it is NOT used when firing. There is a specialized action: XGAction_ExitCover that it used
//when firing a weapon.
function ExitFromCover(optional vector DesiredFaceDirection)
{
		bForceTurnTarget = true;
		bIsExitFromCover = true;
		//TempFaceLocation will be the target location for our turn node animation
		if(VSizeSq(DesiredFaceDirection) == 0.0f)
		{
			TempFaceLocation = Unit.Location + (Unit.GetCoverDirection(DesiredCoverIndex) * 1000.0f);
		}
		else
		{

			TempFaceLocation = Unit.Location + (Normal(DesiredFaceDirection) * 1000.0f);
		}
		ForceStance(-1, eNoPeek); //Force us out of cover
}

//This is a specialized function that will orient the character towards their current cover direction. This may be necessary if the unit has been 
//oriented away during an action ( such as firing )
function ForceHeading(vector DesiredRotationVector, optional bool FromExitCover = false)
{
		bForceTurnTarget = true;
		bIsExitFromCover = FromExitCover;
		TempFaceLocation = Unit.Location + (DesiredRotationVector * 1000.0f);//TempFaceLocation will be the target location for our turn node animation
		UnitPawn.SetFocalPoint(TempFaceLocation);
		ForceStance(Unit.CurrentCoverDirectionIndex, eNoPeek); //This will lead to a latent turn
}

//This function is used to direct the unit into a particular stance (ignoring targeting information). An example of 
//when this might be used would be for when a unit is directed to open a door and is not facing the door.
function ForceStance(int ForceCoverIndex, UnitPeekSide ForcePeekSide)
{
	local name Statename;	

	//This function can only be called while the IdleStateMachine is dormant. If this rule needs to change talk to Ryan
	Statename = GetStateName();
	if( Statename == 'Dormant' )
	{
		bForceDesiredCover = true;
		DesiredCoverIndex = ForceCoverIndex;
		DesiredPeekSide = ForcePeekSide;
		`log("*********************** GOTO EvaluateStance ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		GotoState('EvaluateStance');
	}
	else
	{
		`log("ForceStance attemped on "@Unit@" in state "@Statename@"! ForceStance can only be called in the Dormant state!", , 'XCom_Anim');
	}
}

//Use to determine if the unit's stance is being evaluated ( this state should not be interrupted! )
function bool IsEvaluatingStance()
{
	local name Statename;

	Statename = GetStateName();
	if( bWaitingForStanceEval && Statename == 'Idle' )
	{
		`log("*********************** GOTO EvaluateStance ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		GotoState('EvaluateStance');
		return true;
	}

	return Statename == 'EvaluateStance';
}

simulated function bool IsDormant()
{
	return GetStateName() == 'Dormant';
}

function bool IsUnitSuppressing()
{
	return Unit.m_bSuppressing;
}

function bool IsUnitSuppressingArea()
{
	return Unit.m_bSuppressingArea;
}

//Activates the state machine, used when the unit returns to an idle action/state from a specialized one that overrides animations
simulated function Resume(optional Actor UnlockResume=none)
{
	local name Statename;	

	if (kDormantLock != none && kDormantLock != UnlockResume)
	{
		`log("*********************** IdleStateMachine Resume() ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Locked by Actor" @ kDormantLock @ "but Resume request was made by" @ UnlockResume @ ", cannot Resume");
		return;     //  cannot resume with this actor
	}
	kDormantLock = none;

	Statename = GetStateName();
	
	if( Statename == 'Dormant' )
	{	
		`log("*********************** IdleStateMachine Resume() ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		if( ReturnToState == 'TargetingStart' || ReturnToState == 'TargetingHold' )
		{
			bWaitingForStanceEval = true;
			GotoState('TargetingStop');
		}
		else if( ReturnToState == 'PeekStart' )
		{
			bWaitingForStanceEval = true;
			GotoState('PeekStop');
		}
		else
		{
			GotoState('Idle');
		}			
	}
	else if( Statename != 'HunkeredDown' && Statename != 'NeuralDampingStunned' ) //These are equivalent to dormant - except that evaluate stance cannot be called from them
	{
		bDeferredGotoDormant = false;
		bDeferredGotoIdle = true;
	}
}

//De-activates the state machine, used when the unit intends to perform an action that will override the unit's animations. The force flag permits external users
//to make the system allow going dormant from states such as 'HunkeredDown'. In this situation, the caller is responsible for restoring the proper state when its business is done.
simulated function GoDormant(optional Actor LockResume=none, optional bool Force=false, optional bool bDisableWaitingForEval=false)
{
	local name Statename;	

	Statename = GetStateName();
	if (bDisableWaitingForEval)
		bWaitingForStanceEval = false;

	if (kDormantLock != none && kDormantLock != LockResume)
	{
		`log("*********************** IdleStateMachine GoDormant() ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Dormant state is locked by" @ kDormantLock @ "but new GoDormant request was made by" @ LockResume @ ", cannot GoDormant");
		return;     //  don't override existing lock
	}
	kDormantLock = LockResume;
	
	if( Statename != 'EvaluateStance' )
	{
		if( Force || (Statename != 'HunkeredDown' && Statename != 'Strangling' && Statename != 'Strangled' && Statename != 'NeuralDampingStunned')) //These are equivalent to dormant - except that evaluate stance cannot be called from them
		{			
			`log("*********************** IdleStateMachine GoDormant() ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("Dormant now locked by" @ LockResume,  `CHEATMGR.MatchesXComAnimUnitName(Unit.Name) && LockResume != none, 'XCom_Anim');
			`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			
			GotoState('Dormant');
		}
	}
	else
	{
		`log("*********************** IdleStateMachine Requested GoDormant() - bDeferredGotoDormant = true ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		bDeferredGotoIdle = false;
		bDeferredGotoDormant = true;
	}
}
//****************************************

//******** Private Functions **************
//Runs on a timer initiated by the Idle state.
function PerformPeek()
{
	local XComPresentationLayer Pres;
	local UITacticalHUD TacticalHUD;

	UnitState = UnitNative.GetVisualizedGameState();
	
	//Make sure the conditions for peeking are still valid
	if( !bIsCivilian &&
		GetStateName() == 'Idle' &&
		UnitState.GetNumVisibleEnemyUnits() > 0 && 
		UnitNative.m_eCoverState != eCS_None )
	{
		// don't do normal peeks when somebody is being targeted. Otherwise they can bob their heads in front
		// of the user's view. Only the special hold peek on the target and shooter should be happening.
		Pres = `PRES;
		TacticalHUD = Pres != none ? Pres.GetTacticalHUD() : none;
		if(TacticalHUD == none || TacticalHUD.GetTargetingMethod() == none)
		{
			GotoState('PeekStart');
		}
	}
}

function PerformFlinch()
{
	if( IsStateInterruptible() && UnitPawnNative.GetAnimTreeController().GetAllowNewAnimations() )
	{
		GotoState('Flinch');
	}
}

function PerformCrouch()
{
	if( IsStateInterruptible() )
	{
		GotoState('Crouch');
	}
}

function EndCrouch()
{
	bContinueCrouching = false;
}

function SetPodTalker(XGUnit TalkingUnit)
{
	PodTalker = TalkingUnit;
}

function bool FacePod(out vector FacingTarget, optional XGAIGroup AIGroup = None)
{
	local XComGameStateHistory History;
	local XComWorldData WorldData;
	local XComGameState_Unit UnitStateToFace;
	local int ListenerIndex;
	local int ListenersChecked;
	local bool bFacesAwayFromPod;
	local bool bListenerFacesAwayFromPod;
	local vector AwayFromTarget;

	if( PodTalker == none )
	{
		return false;
	}
	
	if( AIGroup == None )
	{
		AIGroup = Unit.m_kBehavior.m_kPatrolGroup;
	}

	if( AIGroup == none || AIGroup.m_arrUnitIDs.Length <= 1 || Unit.GetAlertLevel() != eAL_Green )
	{
		return false;
	}

	History = `XCOMHISTORY;

	bFacesAwayFromPod = Unit.GetVisualizedGameState().GetMyTemplate().bFacesAwayFromPod;

	// face the location of the units. Since we might hit idle before the rest of our pod has arrived, we
	// need to be sure to face the location where they will end their move, and not where they actually are
	if(PodTalker.ObjectID != Unit.ObjectID)
	{
		// we are not the pod talker, so face the talkers location 
		UnitStateToFace = XComGameState_Unit(History.GetGameStateForObjectID(PodTalker.ObjectID,, VisualizationMgr.LastStateHistoryVisualized));
	}
	else
	{
		// we are the talker, select a random other person in this pod to talk to
		// pick a random index to start at, and then choose the first one that isn't us
		ListenerIndex = rand(AIGroup.m_arrUnitIDs.Length);
		for( ListenersChecked = 0; ListenersChecked < AIGroup.m_arrUnitIDs.Length; ListenersChecked++ )
		{
			if( AIGroup.m_arrUnitIDs[ListenerIndex] != Unit.ObjectID )
			{
				UnitStateToFace = XComGameState_Unit(History.GetGameStateForObjectID(AIGroup.m_arrUnitIDs[ListenerIndex], , VisualizationMgr.LastStateHistoryVisualized));
				bListenerFacesAwayFromPod = UnitStateToFace.GetMyTemplate().bFacesAwayFromPod;
				if( !bListenerFacesAwayFromPod || AIGroup.NumUnitsForPodTalking <= 1 )
				{
					break;
				}
				else
				{
					UnitStateToFace = None;
				}
			}
			
			ListenerIndex = (ListenerIndex + 1) % AIGroup.m_arrUnitIDs.Length;
		}
	}

	`assert(UnitStateToFace != none); // the only way this is possible is if the patrol unit array is bad, and is really serious (Or AdventMec logic is wrong)

	WorldData = `XWORLD;
	FacingTarget = WorldData.GetPositionFromTileCoordinates(UnitStateToFace.TileLocation);

	// If you face outward || you are the only unit to face inward
	if( bFacesAwayFromPod || AIGroup.NumUnitsForPodTalking == 1 )
	{
		// Face 180 from the talker
		AwayFromTarget = Unit.Location - FacingTarget;
		FacingTarget = Unit.Location + AwayFromTarget;
	}

	return true;
}

// Anywhere Idle should start call this instead
function PlayIdleAnim(bool ResetPodAnimation = false)
{
	local bool bAliensPresent;
	local EAlertLevel AlertLevel;
	local float PodRandom;
	local AnimNodeSequence PlayingSequence;
	UnitState = UnitNative.GetVisualizedGameState();

	bAliensPresent = UnitState.GetNumVisibleEnemyUnits(true, false, false, VisualizationMgr.LastStateHistoryVisualized) > 0;

	if(bIsTurret)
	{
		AnimParams.Looping = true;
		if( CurrentTurretState == eITS_None )
		{ 
			// Update turret state if this has never been set.  (For turrets dropped in via cheat command DropUnit)
			CurrentTurretState = UnitState.IdleTurretState;
		}
		GetTurretIdleAnim(AnimParams.AnimName, CurrentTurretState, bAliensPresent);
	}
	else
	{
		AlertLevel = Unit.GetVisualAlertLevel();

		AnimParams = default.AnimParams;
		AnimParams.PlayRate = fIdleAnimRate;
		AnimParams.Looping = true;
		if( AlertLevel == eAL_Green )
		{
			PodRandom = FRand();
			if( PodTalker != None && PodTalker == Unit && TalkerHasSomeoneToTalkTo )
			{
				if( PodRandom < PodTalkFidgetPercentage && UnitPawnNative.GetAnimTreeController().CanPlayAnimation('POD_TalkFidget') )
				{
					AnimParams.AnimName = 'POD_TalkFidget';
				}
				else
				{
					AnimParams.AnimName = 'POD_TalkIdle';
				}

				if( UnitPawnNative != None && UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
				{
					// If we are already playing a pod talk animation and we haven't finished it then let the animation play
					if( !UnitPawnNative.GetAnimTreeController().IsPlayingCurrentAnimation('POD_Talk') || ResetPodAnimation )
					{
						PlayingSequence = UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);

						// Since we want to randomize how the green idle works set a timer to start this animation again
						SetTimer(PlayingSequence.AnimSeq.SequenceLength / fIdleAnimRate, false, nameof(ResetPodIdleAnimation), self);
					}
					
					return;
				}
			}
			
			if( PodRandom < PodIdleFidgetPercentage && UnitPawnNative.GetAnimTreeController().CanPlayAnimation('POD_Fidget') )
			{
				AnimParams.AnimName = 'POD_Fidget';
			}
			else
			{
				AnimParams.AnimName = 'POD_Idle';
			}
				
			if( UnitPawnNative != None && UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) == false )
			{
				// If you can't play pod anims you go yellow
				AlertLevel = eAL_Yellow;
			}
			else if(UnitPawnNative != None)
			{
				if( (!UnitPawnNative.GetAnimTreeController().IsPlayingCurrentAnimation('POD_Fidget') && !UnitPawnNative.GetAnimTreeController().IsPlayingCurrentAnimation('POD_Idle'))
				   || ResetPodAnimation )
				{
					// Since we want to randomize how the green idle works set a timer to start this animation again
					PlayingSequence = UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
					SetTimer(PlayingSequence.AnimSeq.SequenceLength / fIdleAnimRate, false, nameof(ResetPodIdleAnimation), self);
				}
				
				return;
			}
 		}

		// Anyone is red if they see an enemy
		if( bAliensPresent && !bIsCivilian )
		{
			AlertLevel = eAL_Red;
		}
		
		if( AlertLevel == eAL_Yellow )
		{
			switch( Unit.m_eCoverState )
			{
			case eCS_LowLeft:
			case eCS_LowRight:
				AnimParams.AnimName = 'LL_IdleAlert';
				break;
			case eCS_HighLeft:
			case eCS_HighRight:
				AnimParams.AnimName = 'HL_IdleAlert';
				break;
			case eCS_None:
				AnimParams.AnimName = 'NO_IdleAlertGunDwn';
				break;
			}
		}
		else if( AlertLevel == eAL_Red )
		{
			switch( Unit.m_eCoverState )
			{
			case eCS_LowLeft:
			case eCS_LowRight:
				AnimParams.AnimName = 'LL_Idle';
				break;
			case eCS_HighLeft:
			case eCS_HighRight:
				AnimParams.AnimName = 'HL_Idle';
				break;
			case eCS_None:
				AnimParams.AnimName = 'NO_IdleGunUp';
				break;
			}
		}
	}

	if( UnitPawnNative != None )
	{
		if(AnimParams.AnimName == '')
		{
			`Redscreen("No appropriate idle could be found! Falling back to a default. Visual Alert Level: " @ String(AlertLevel) @ ". Talk to Watson.");
			AnimParams.AnimName = 'NO_IdleAlertGunDwn';
		}

		AnimParams.BlendTime = UnitPawnNative.Into_Idle_Blend;
		UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
	}
}

function ResetPodIdleAnimation()
{
	if( GetStateName() != 'Dormant' && GetStateName() != 'Panicked')
	{
		PlayIdleAnim(true);
	}
}

function GetTurretIdleAnim(out name AnimName_out, EIdleTurretState eTurretState, bool bEnemiesVisible)
{
	`Assert(eTurretState != eITS_None);
	switch (eTurretState)
	{
		case eITS_AI_Inactive:				// Stunned state, on team AI
			AnimName_out = 'NO_StunnedIdle_Advent';
			break; 
		case eITS_XCom_Inactive:		// Stunned state, on team XCom.
			AnimName_out = 'NO_StunnedIdle_Xcom';
			break;
		case eITS_AI_ActiveTargeting:	// AI team active state, has targets visible.
		case eITS_AI_ActiveAlerted:		// AI team active state, has no targets visible.
			if(bEnemiesVisible)
			{
				AnimName_out = 'NO_IdleGunUp_Advent';
			}
			else
			{
				AnimName_out = 'NO_IdleAlertGunUpA_Advent';
			}
			break;
		case eITS_XCom_ActiveTargeting:	// XCom-controlled active state, has targets visible.
		case eITS_XCom_ActiveAlerted:	// XCom-controlled active state, has no targets visible.
			if(bEnemiesVisible)
			{
				AnimName_out = 'NO_IdleGunUp_Xcom';
			}
			else
			{
				AnimName_out = 'NO_IdleAlertGunUpA_Xcom';
			}
			break;
	}
}

function bool RequiresTurretTransitionAnim(EIdleTurretState NewTurretState)
{
	return GetTurretTransitionAnim(NewTurretState) != '';
}

// Only possible transition anims are now for stun.  All other hacking options transition directly from one team control to the next.
function name GetTurretTransitionAnim( EIdleTurretState NewTurretState )
{
	local name AnimName;
	if(CurrentTurretState != NewTurretState)
	{
		switch(CurrentTurretState)
		{
		// Turret down states to Turret Up states.
		case eITS_None:					// Initial state 
		case eITS_AI_Inactive:			// Stunned on team AI.
		case eITS_XCom_Inactive:		// Stunned on team XCom.
			if(NewTurretState == eITS_AI_ActiveAlerted
			   || NewTurretState == eITS_AI_ActiveTargeting)
			{
				AnimName = 'NO_StunnedStop_Advent';
			}
			else if( NewTurretState == eITS_XCom_ActiveAlerted
					|| NewTurretState == eITS_AI_ActiveTargeting )
			{
				AnimName = 'NO_StunnedStop_Xcom';
			}
			break;

			// Turret Up state to Turret Down states.
		case eITS_AI_ActiveTargeting:	// AI team active state, has targets visible.
		case eITS_AI_ActiveAlerted:		// AI team active state, has no targets visible.
		case eITS_XCom_ActiveTargeting:	// XCom-controlled active state, has targets visible.
		case eITS_XCom_ActiveAlerted:	// XCom-controlled active state, has no targets visible.
			if( NewTurretState == eITS_XCom_Inactive )
			{
				AnimName = 'NO_StunnedStart_Xcom';
			}
			else if( NewTurretState == eITS_AI_Inactive )
			{
				AnimName = 'NO_StunnedStart_Advent';
			}
			break;
		}
	}
	return AnimName;
}
//Returns true if a target has been found. Used by the EvaluateStance state
event bool SetTargetUnit()
{		
	local X2Action_ExitCover ExitCoverAction;
	local Vector NewTargetLocation;
	local Actor  NewTargetActor;
	local XGUnit TempUnit;
	local int CoverIndex;
	local int BestCoverIndex;
	local float CoverScore;
	local float BestCoverScore;	
	local bool bEnemiesVisible;
	local bool bFoundTarget;	
	local bool bCurrentlyTargeting;
	local GameRulesCache_VisibilityInfo OutVisibilityInfo;
	local XComGameStateHistory History;
	local X2TargetingMethod TargetingMethod;
	local UITacticalHUD TacticalHUD;
	local bool bActorFromTargetingMethod;
	local int HasEnemiesOnLeftPeek;
	local int HasEnemiesOnRightPeek;
	local UnitPeekSide PeekSide;
	local int CanSeeFromDefault;
	local int RequiresLean;
	
	History = `XCOMHISTORY;
	UnitState = UnitNative.GetVisualizedGameState();

	if( UnitState == None )
	{
		return false;
	}

	NewTargetActor = TargetActor;
	NewTargetLocation = TargetLocation;	
	bFoundTarget = false; //return value, indicates whether there were any targets for this unit, if there were none we clear the target info	
	bEnemiesVisible = UnitState.GetNumVisibleEnemyUnits() > 0;

	`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	`log("*** Processing SetTargetUnit ***", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

//******** Find the current values for NewTargetActor and NewTargetLocation **********	
	TacticalHUD = `PRES.GetTacticalHUD();
	if (TacticalHUD != none)
		TargetingMethod = TacticalHUD.GetTargetingMethod();
	else
		TargetingMethod = none;

	bCurrentlyTargeting = TargetingMethod != none;
	
	//If targeting is happening, and we are the shooter
	// Can also happen if the source unit is doing a multi turn ability
	bActorFromTargetingMethod = bCurrentlyTargeting && (TargetingMethod.Ability.OwnerStateObject.ObjectID == UnitState.ObjectID);
	if ( bActorFromTargetingMethod || (UnitState.m_MultiTurnTargetRef.ObjectID > 0) )
	{
		if( bActorFromTargetingMethod )
		{
			NewTargetActor = TargetingMethod.GetTargetedActor();
		}
		else
		{
			NewTargetActor = History.GetVisualizer(UnitState.m_MultiTurnTargetRef.ObjectID);
		}

		// can't Target yourself, so just early out to focus on the shooter
		if (NewTargetActor == Unit)
		{
			NewTargetActor = LastAttacker; 
			TargetActor = NewTargetActor;
			if( TargetActor != None )
			{
				TargetLocation = TargetActor.Location;
			}
			return TargetActor != none;
		}
		else if ( (bActorFromTargetingMethod && TargetingMethod.GetCurrentTargetFocus(NewTargetLocation)) || (NewTargetActor != None) )
		{
			bFoundTarget = true;
		}
	}
	else if( Unit.CurrentExitAction != none ) //If we are performing a fire sequence
	{	
		`log("     Unit is performing a targeting action:", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		ExitCoverAction = Unit.CurrentExitAction;
			
		bFoundTarget = true;
		NewTargetActor = ExitCoverAction.PrimaryTarget;
		if( ExitCoverAction.PrimaryTarget != none )
		{
			if( ExitCoverAction.PrimaryTarget == Unit )
			{
				`log("          *ExitCover action has a target, it is ourselves. Do Nothing.", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				bFoundTarget = false;
			}
			else
			{
				`log("          *ExitCover action has a target, aiming at"@NewTargetActor, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				NewTargetLocation = ExitCoverAction.AimAtLocation;
			}
		}
		else
		{
			`log("          *ExitCover action has no primary target, aiming at"@ExitCoverAction.TargetLocation, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			NewTargetLocation = ExitCoverAction.AimAtLocation;
		}			
	}
	else if( bEnemiesVisible ) //If enemies are visible but we are not in a targeting action
	{
		`log("     Unit is idle and there are visible enemies:", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');		
		//Face enemies that are attacking us (and visible) first, but not if they are dead or dying
		if (LastAttacker != none && 
			LastAttacker.IsAlive() &&
			!LastAttacker.GetVisualizedGameState().IsIncapacitated() &&
			Unit.GetDirectionInfoForTarget(LastAttacker, CoverIndex, PeekSide, CanSeeFromDefault, RequiresLean) )
		{			
			`log("          *LastAttacker is not none, aiming at LastAttacker:"@LastAttacker, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			NewTargetActor = LastAttacker;
		}
		else //Fall back to facing the closest enemy
		{
			TestEnemyUnitsForPeekSides(DesiredCoverIndex, HasEnemiesOnLeftPeek, HasEnemiesOnRightPeek);
			if( (DesiredPeekSide == ePeekLeft && HasEnemiesOnLeftPeek == 1) || (DesiredPeekSide == ePeekRight && HasEnemiesOnRightPeek == 1) )
			{
				NewTargetActor = GetClosestEnemyForPeekSide(DesiredCoverIndex, DesiredPeekSide);
			}
			else if( class'X2TacticalVisibilityHelpers'.static.GetClosestVisibleEnemy(UnitNative.ObjectID, OutVisibilityInfo, VisualizationMgr.LastStateHistoryVisualized) )
			{
				NewTargetActor = XGUnit( History.GetVisualizer(OutVisibilityInfo.TargetID) );
			}

			`log("          *LastAttacker is none, aiming at closest visible enemy:"@NewTargetActor, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		}

		if( NewTargetActor != none )
		{
			bFoundTarget = true;			
			NewTargetLocation = NewTargetActor.Location;
			TempUnit = XGUnit(NewTargetActor);
			if( TempUnit != None )
			{
				NewTargetLocation = TempUnit.GetPawn().GetHeadshotLocation();
			}
		}
	}
	else //In the absence of a valid target, we should choose a valid cover direction and use the cover
	{
		`log("     No targets were found, clearing targeting state", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		ClearTargetInformation();
		
		BestCoverScore = 0.0f;
		BestCoverIndex = -1;

		if( UnitNative.CanUseCover() )
		{
			CurrentCoverPeekData = UnitNative.GetCachedCoverAndPeekData(VisualizationMgr.LastStateHistoryVisualized);
			for (CoverIndex = 0; CoverIndex < 4; CoverIndex++)
			{
				if( CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].bHasCover == 1 )
				{
					CoverScore = 1.0f;
					if( CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].LeftPeek.bHasPeekaround == 1 ||
						CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].RightPeek.bHasPeekaround == 1 )
					{
						CoverScore += 1.0f;
					}

					if( DesiredCoverIndex == CoverIndex )
					{
						CoverScore += 0.1f;
					}

					if( CoverScore > BestCoverScore )
					{
						BestCoverIndex = CoverIndex;
						BestCoverScore = CoverScore;
					}
				}
			}		

			bFoundTarget = true;
			NewTargetActor = none;
			if( BestCoverIndex > -1 )
			{
				NewTargetLocation = Unit.Location + (CurrentCoverPeekData.CoverDirectionInfo[BestCoverIndex].CoverDirection * 1000.0f);
			}
			else
			{
				NewTargetLocation = Unit.Location + (Vector(Unit.Rotation) * 1000.0f); // Aim straight forward
			}
		}	
	}
//************************************************************************************
	
//******** Ascertain whether the target/target location, or our location, has changed **********
	if( bFoundTarget )
	{
		`log("     A target was found, processing the target:", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		`log("          *(NewTargetLocation != TargetLocation):"@(NewTargetLocation != TargetLocation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');			
		TargetLocation = NewTargetLocation;

		`log("          *(NewTargetActor != TargetActor):"@(NewTargetActor != TargetActor), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		// Check to see if we were previously targeting a unit and no longer are
		TempUnit = XGUnit(TargetActor);
		if( TempUnit != none && 
		    !bActorFromTargetingMethod &&
			TempUnit.IdleStateMachine.LastAttacker != none &&
			TempUnit.IdleStateMachine.LastAttacker == Unit) //In order for us clearing the last attacker to be valid, we must have been the last attacker
		{	
			TempUnit.IdleStateMachine.LastAttacker = none;
		}

		TargetActor = NewTargetActor;			

		//If the new target actor is an XGUnit, mark us as an attacker
		TempUnit = XGUnit(TargetActor);
		if(TempUnit != none && bActorFromTargetingMethod && TempUnit.IdleStateMachine.LastAttacker != Unit)
		{   
			TempUnit.IdleStateMachine.LastAttacker = Unit;
			TempUnit.IdleStateMachine.CheckForStanceUpdate(); //Tell the target that we are targeting them now
		}
	}
//**********************************************************************************************

	UnitPawn.TargetLoc = TargetLocation;

	`log("     SetTargetUnit returning:"@bFoundTarget, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	`log("*** End Processing SetTargetUnit ***", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

	`assert(TargetActor != Unit);
	return bFoundTarget;
}

//Clears TargetActor and notifies the target
simulated function ClearTargetInformation()
{
	local XGUnit TempUnit;

	//If we had a previous TargetActor, and they were a unit, notify them that we are no longer targeting them
	TempUnit = XGUnit(TargetActor);
	if( TempUnit != none && 
		TempUnit.IdleStateMachine.LastAttacker != none && 
		TempUnit.IdleStateMachine.LastAttacker == Unit)//In order for us clearing the last attacker to be valid, we must have been the last attacker		
	{   
		TempUnit.IdleStateMachine.LastAttacker = none;
	}

	TargetActor = none;
}

function TestEnemyUnitsForPeekSides(int CoverIndex, out int HasEnemiesOnLeftPeek, out int HasEnemiesOnRightPeek)
{
	local array<StateObjectReference> EnemyUnitArray;
	local StateObjectReference EnemyReference;
	local XGUnit EnemyUnit;
	local int CanSeeFromDefault;
	local int RequiresLean;
	local int CoverIndexToEnemy;
	local UnitPeekSide PeekSide;
	local XComGameStateHistory History;
	local Vector TargetLoc;

	History = `XCOMHISTORY;

	HasEnemiesOnLeftPeek = 0;
	HasEnemiesOnRightPeek = 0;

	class'X2TacticalVisibilityHelpers'.static.GetAllVisibleEnemyUnitsForUnit(Unit.ObjectID, EnemyUnitArray);
	foreach EnemyUnitArray(EnemyReference)
	{
		EnemyUnit = XGUnit(History.GetVisualizer(EnemyReference.ObjectID));
		if( EnemyUnit.IsAlive() )
		{
			Unit.GetStepOutCoverInfo(EnemyUnit, TargetLoc, CoverIndexToEnemy, PeekSide, RequiresLean, CanSeeFromDefault);
			if( CoverIndex == CoverIndexToEnemy )
			{
				if( PeekSide == ePeekLeft )
				{
					HasEnemiesOnLeftPeek = 1;
				}
				else if( PeekSide == ePeekRight )
				{
					HasEnemiesOnRightPeek = 1;
				}
			}
		}
	}
}

function XGUnit GetClosestEnemyForPeekSide(int CoverIndex, UnitPeekSide PeekSide)
{
	local array<StateObjectReference> EnemyUnitArray;
	local StateObjectReference EnemyReference;
	local XGUnit EnemyUnit;
	local int CanSeeFromDefault;
	local int RequiresLean;
	local int CoverIndexToEnemy;
	local UnitPeekSide PeekSideToEnemy;
	local XComGameStateHistory History;
	local float BestDistanceSquared;
	local float DistanceSquared;
	local XGUnit TargetUnit;

	History = `XCOMHISTORY;

	BestDistanceSquared = -1.0f;
	TargetUnit = None;
	class'X2TacticalVisibilityHelpers'.static.GetAllVisibleEnemyUnitsForUnit(Unit.ObjectID, EnemyUnitArray);
	foreach EnemyUnitArray(EnemyReference)
	{
		EnemyUnit = XGUnit(History.GetVisualizer(EnemyReference.ObjectID));
		if( EnemyUnit.IsAlive() )
		{
			Unit.GetDirectionInfoForTarget(EnemyUnit, CoverIndexToEnemy, PeekSideToEnemy, CanSeeFromDefault, RequiresLean);
			if( CoverIndex == CoverIndexToEnemy && PeekSideToEnemy == PeekSide )
			{
				// Determine Distance
				DistanceSquared = VSizeSq(Unit.Location - EnemyUnit.Location);
				if( DistanceSquared < BestDistanceSquared || BestDistanceSquared == -1.0f )
				{
					BestDistanceSquared = DistanceSquared;
					TargetUnit = EnemyUnit;
				}
			}
		}
	}

	return TargetUnit;
}

//This function should only be called if there is a current target. Reference params return the desired cover index and peek side for the current target.
event GetDesiredCoverState(out int CoverIndex, out UnitPeekSide PeekSide)
{
	local XGUnit TargetUnit;
	local Vector ToTarget;  //Used for special case below
	local Vector ToRight;   //Used for special case below
	local float TempDot;    //Used for special case below
	local int bCanSeeFromDefault;
	local int bRequiresLean;
	local UnitPeekSide PreviousPeekSide;
	local int HasEnemiesOnLeftPeek;
	local int HasEnemiesOnRightPeek;

	PreviousPeekSide = PeekSide;

	if( Unit.CanUseCover() ) //CoverIndex and PeekSide are only relevant from cover
	{
		if( TargetActor != none && TargetActor.IsA('XGUnit') )
		{
			TargetUnit = XGUnit(TargetActor);
			Unit.GetDirectionInfoForTarget(TargetUnit, CoverIndex, PeekSide, bCanSeeFromDefault, bRequiresLean);
		}
		else
		{
			Unit.GetDirectionInfoForPosition(TargetLocation, CoverIndex, PeekSide, bCanSeeFromDefault, bRequiresLean);
		}

		CurrentCoverPeekData = UnitNative.GetCachedCoverAndPeekData(VisualizationMgr.LastStateHistoryVisualized);

		TestEnemyUnitsForPeekSides(CoverIndex, HasEnemiesOnLeftPeek, HasEnemiesOnRightPeek);

		if( (PreviousPeekSide == ePeekLeft && CurrentCoverPeekData.CoverDirectionInfo[DesiredCoverIndex].LeftPeek.bHasPeekaround == 1) ||
		   (PreviousPeekSide == ePeekRight && CurrentCoverPeekData.CoverDirectionInfo[DesiredCoverIndex].RightPeek.bHasPeekaround == 1) )
		{
			if( HasEnemiesOnLeftPeek == 0 && HasEnemiesOnRightPeek == 0 )
			{
				PeekSide = PreviousPeekSide;
			}
			else if( Unit.GetCoverType() == CT_MidLevel && bCanSeeFromDefault == 1 && PreviousPeekSide != eNoPeek && PreviousPeekSide != PeekSide )
			{
				// Keep your peek side if you can see the target from your default tile
				// Don't keep it if we have enemies on the new peek side and none on the previous peek side.
				if( ((PreviousPeekSide == ePeekLeft && PeekSide == ePeekRight) && (HasEnemiesOnLeftPeek == 0 && HasEnemiesOnRightPeek == 1) ||
					(PreviousPeekSide == ePeekRight && PeekSide == ePeekLeft) && (HasEnemiesOnLeftPeek == 1 && HasEnemiesOnRightPeek == 0))
					== false )
				{
					PeekSide = PreviousPeekSide;
				}
			}
		}		

		//If we have a cover direction but no peek then it means we can see the enemy unit from our default tile (ie. the unit doesn't need to use a peek tile to see the target)	
		//We don't have animations to represent this state though, so pick a peek side for the sake of the animation system
		if( CoverIndex > -1 && PeekSide == eNoPeek )
		{	
			if( VSizeSq(TargetLocation - CurrentCoverPeekData.DefaultVisibilityCheckLocation) > 0.0f )
			{			
				ToTarget = Normal(TargetLocation - CurrentCoverPeekData.DefaultVisibilityCheckLocation);

				//Determine whether the target is even in the direction of our cover direction, if not then we don't use cover
				TempDot = NoZDot(CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].CoverDirection, ToTarget);
				if( TempDot > 0.01f ) //0.01f so that there is a little bit of give in our determination. This is mainly to handle adjacent or very close enemies
				{
					//Pick the cover side that is closest to facing the target
					ToRight = TransformVectorByRotation(rot(0,16384,0), CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].CoverDirection);
					TempDot = NoZDot(ToRight, ToTarget);
					if(TempDot > 0.0f && CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].RightPeek.bHasPeekaround > 0)
					{
						PeekSide = ePeekRight;
					}
					else if(CurrentCoverPeekData.CoverDirectionInfo[CoverIndex].LeftPeek.bHasPeekaround > 0)
					{
						PeekSide = ePeekLeft;
					}
					else
					{
						//No peeks are possible, just keep our current peek side
						// Jwats: This should only happen when we don't actually have a target (we set a target loc in the direction of the cover)
						PeekSide = PreviousPeekSide;
					}
				}
				else
				{
					//The enemy unit is not in the direction of our cover, just turn to face them
					CoverIndex = -1;
					PeekSide = eNoPeek;
				}
			}
			else
			{
				//Fail through for cases where the target is on top of us. In this situation just face(?) the target, or spin like a helicopter TO VICTORY
				CoverIndex = -1;
				PeekSide = eNoPeek;
			}			
		}		
	}
	else
	{
		CoverIndex = -1;
		PeekSide = eNoPeek;
	}
}

function bool IsUnitPanicked()
{
	local bool ShouldPanick;
	UnitState = UnitNative.GetVisualizedGameState();

	ShouldPanick = UnitState.bPanicked;
	if( !bStartedPanick )
	{
		ShouldPanick = ShouldPanick && UnitState.ActionPoints.Length == 0;
	}

	return ShouldPanick;
}

function bool ShouldUseTargetingAnims()
{
	local bool ShouldHoldPeek;
	local int CoverDirection;
	local UnitPeekSide PeekSide;
	local int RequiresLean;
	local int SeeFromDefault;

	ShouldHoldPeek = Unit.GetStepOutCoverInfo(TargetActor, TargetLocation, CoverDirection, PeekSide, RequiresLean, SeeFromDefault);
	if( Unit.m_eCoverState == eCS_LowLeft || Unit.m_eCoverState == eCS_LowRight )
	{
		// LowCover holds peek even if it isn't going to step out
		ShouldHoldPeek = true;
	}

	return bTargeting && Unit.m_eCoverState != eCS_None && ShouldHoldPeek;
}

native latent function TurnTowardsPosition(const out vector Position, optional bool AwayFromCover = false);
native static function float GetNextIdleRate(); //This function reads from a table of anim rates to ensure that units do not randomly choose the same rate
//****************************************

auto state Dormant
{	
	event BeginState(name PreviousStateName)
	{
		if (Unit != none)
		{
			`log("*********************** BEGIN Dormant ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		}

		ReturnToState = PreviousStateName;

		if (UnitPawn != None) UnitPawn.EnableLeftHandIK(false);
	}

	event EndState(name NewStateName)
	{		
		if (Unit != none)
		{
			`log("*********************** END Dormant ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		}
	}
begin:
}

state Idle
{
	event BeginState(name PreviousStateName)
	{
		`log("*********************** BEGIN Idle ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{		
		`log("*********************** END Idle ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
	}

begin:
	bDeferredGotoIdle = false;

	if( !Unit.IsAlive() || UnitNative.GetVisualizedGameState().IsIncapacitated() || !Unit.GetIsAliveInVisualizer() )
	{
		//In the case where the unit is dead, make the idle state machine dormant
		GotoState('Dormant');
	}

	if( bWaitingForStanceEval )
	{		
		`log("*********************** GOTO EvaluateStance ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log(GetScriptTrace(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		GotoState('EvaluateStance');
	}

	if( bIsTurret && Unit.IsAlive() )
	{
		GotoState('TurretIdle');
	}
	else if( IsUnitPanicked() ) 
	{
		GotoState('Panicked');
	}
	else if( Unit.IsHunkeredDown() )
	{
		GotoState('HunkeredDown');
	}
	else if( ShouldUseTargetingAnims() )
	{
		GotoState('TargetingStart');
	}
	else if( PersistentEffectIdleName != '' )
	{
		GotoState('PersistentEffect');
	}
	else
	{
		if(FacePod(TempFaceLocation))
		{
			// in AI patrols, have the pod members turn to have each other while talking
			TurnTowardsPosition(TempFaceLocation);
		}

		PlayIdleAnim();
	}
	
	PeekTimeoutCurrent = Lerp(PeekTimeoutMin, PeekTimeoutMax, FRand());
	SetTimer(PeekTimeoutCurrent, false, nameof(PerformPeek), self);
}

state PeekStart
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN PeekStart ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END PeekStart ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
	}

begin:
	//Play and finish the intro anim
	AnimParams = default.AnimParams;

	switch( Unit.m_eCoverState )
	{
	case eCS_LowLeft:
		AnimParams.AnimName = 'LL_Peek_Start';
		break;
	case eCS_LowRight:
		AnimParams.AnimName = 'LR_Peek_Start';
		break;
	case eCS_HighLeft:
		AnimParams.AnimName = 'HL_Peek_Start';
		break;
	case eCS_HighRight:
		AnimParams.AnimName = 'HR_Peek_Start';
		break;
	}
	if( Unit.m_eCoverState != eCS_None )
	{
		FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	}

	//Play the loop anim once
	switch( Unit.m_eCoverState )
	{
	case eCS_LowLeft:
		AnimParams.AnimName = 'LL_Peek_Loop';
		break;
	case eCS_LowRight:
		AnimParams.AnimName = 'LR_Peek_Loop';
		break;
	case eCS_HighLeft:
		AnimParams.AnimName = 'HL_Peek_Loop';
		break;
	case eCS_HighRight:
		AnimParams.AnimName = 'HR_Peek_Loop';
		break;
	}

	if( Unit.m_eCoverState != eCS_None )
	{
		FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	}

	GotoState('PeekStop');
}

state PeekStop
{
	event BeginState(name PreviousStateName)
	{
		`log("*********************** BEGIN PeekStop ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{
		`log("*********************** END PeekStop ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
	}

begin:

	if(Unit.IsAlive())
	{
		//Play and finish the outro anim
		switch(Unit.m_eCoverState)
		{
			case eCS_LowLeft:
				AnimParams.AnimName = 'LL_Peek_Stop';
				break;
			case eCS_LowRight:
				AnimParams.AnimName = 'LR_Peek_Stop';
				break;
			case eCS_HighLeft:
				AnimParams.AnimName = 'HL_Peek_Stop';
				break;
			case eCS_HighRight:
				AnimParams.AnimName = 'HR_Peek_Stop';
				break;
		}
		if(Unit.m_eCoverState != eCS_None)
		{
			FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
		}
		GotoState('Idle');
	}
	else
	{
		GotoState('Dormant');
	}
}

state Flinch
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN Flinch ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		UnitPawn.EnableLeftHandIK(false);
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END Flinch ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
	}

begin:	
	AnimParams = default.AnimParams;
	switch (Unit.m_eCoverState)
	{
		case eCS_LowLeft:
		case eCS_HighLeft:
			AnimParams.AnimName = 'HL_Flinch';
			break;
		case eCS_LowRight:
		case eCS_HighRight:
			AnimParams.AnimName = 'HR_Flinch';
			break;
		case eCS_None:
			// Jwats: No cover randomizes between the 2 animations
			if (Rand(2) == 0)
			{
				AnimParams.AnimName = 'HL_Flinch';
			}
			else
			{
				AnimParams.AnimName = 'HR_Flinch';
			}
			break;
	}

	FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	
	GotoState('Idle');
}

state Crouch
{
	event BeginState(name PreviousStateName)
	{
		`log("*********************** BEGIN Crouch ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		bContinueCrouching = true;
	}

	event EndState(name NewStateName)
	{
		`log("*********************** END Crouch ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
	}

begin:
	if( UnitState.GetMyTemplate().CharacterGroupName != 'Sectopod' && Unit.m_eCoverState != eCS_LowLeft && Unit.m_eCoverState != eCS_LowRight )
	{
		AnimParams = default.AnimParams;
		AnimParams.AnimName = 'HL_Stand2Crouch';
		if( UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
		{
			FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
		}

		AnimParams = default.AnimParams;
		AnimParams.AnimName = 'LL_Idle';
		AnimParams.Looping = false;
		if( UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
		{
			UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
			while( bContinueCrouching )
			{
				Sleep(0.0f);
			}
		}

		AnimParams = default.AnimParams;
		AnimParams.AnimName = 'LL_Crouch2StandA';
		if( UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
		{
			FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
		}
	}

	GotoState('Idle');
}


state TargetingStart
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN TargetingStart ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END TargetingStart ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
		// Aim at your target?
	}

begin:
	//Play and finish the intro anim
	AnimParams = default.AnimParams;
	
	switch (Unit.m_eCoverState)
	{
		case eCS_LowLeft:
			AnimParams.AnimName = 'LL_Peek_Start';
			break;
		case eCS_LowRight:
			AnimParams.AnimName = 'LR_Peek_Start'; 
			break;
		case eCS_HighLeft:
			AnimParams.AnimName = 'HL_Peek_Start';
			break;
		case eCS_HighRight:
			AnimParams.AnimName = 'HR_Peek_Start';
			break;
		case eCS_None:
			break;
	}

	if (Unit.m_eCoverState != eCS_None)
	{
		FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	}

	GotoState('TargetingHold');
}

state TargetingHold
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN TargetingHold ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END TargetingHold ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
		// Aim at your target?
	}

begin:
	//Play and sustain the loop anim
	AnimParams = default.AnimParams;
	AnimParams.Looping = true;

	switch( Unit.m_eCoverState )
	{
	case eCS_LowLeft:
		AnimParams.AnimName = 'LL_Peek_Targeting_Loop';
		break;
	case eCS_LowRight:
		AnimParams.AnimName = 'LR_Peek_Targeting_Loop';
		break;
	case eCS_HighLeft:
		AnimParams.AnimName = 'HL_Peek_Targeting_Loop';
		break;
	case eCS_HighRight:
		AnimParams.AnimName = 'HR_Peek_Targeting_Loop';
		break;
	}

	if( Unit.m_eCoverState != eCS_None && UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
	{
		UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
	}
	else
	{
		switch( Unit.m_eCoverState )
		{
		case eCS_LowLeft:
			AnimParams.AnimName = 'LL_Peek_Loop';
			break;
		case eCS_LowRight:
			AnimParams.AnimName = 'LR_Peek_Loop';
			break;
		case eCS_HighLeft:
			AnimParams.AnimName = 'HL_Peek_Loop';
			break;
		case eCS_HighRight:
			AnimParams.AnimName = 'HR_Peek_Loop';
			break;
		}

		if( Unit.m_eCoverState != eCS_None )
		{
			UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
		}
	}

	// Hold your peek until you aren't targeting or need a stance eval
	while( ShouldUseTargetingAnims() )
	{
		sleep(0.0f);
	}

	GotoState('TargetingStop');
}

state TargetingStop
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN TargetingStop ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END TargetingStop ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
		// Aim at your target?
	}

begin:	

	if(Unit.IsAlive())
	{
		//Play and finish the outro anim
		AnimParams = default.AnimParams;
		switch(Unit.m_eCoverState)
		{
			case eCS_LowLeft:
				AnimParams.AnimName = 'LL_Peek_Stop';
				break;
			case eCS_LowRight:
				AnimParams.AnimName = 'LR_Peek_Stop';
				break;
			case eCS_HighLeft:
				AnimParams.AnimName = 'HL_Peek_Stop';
				break;
			case eCS_HighRight:
				AnimParams.AnimName = 'HR_Peek_Stop';
				break;
			case eCS_None:
				break;
		}

		if(Unit.m_eCoverState != eCS_None)
		{
			FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
		}

		GotoState('Idle');
	}
	else
	{
		GotoState('Dormant');
	}
}

state Signal
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN Signal ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		if( PreviousStateName == 'EvaluateStance' )
		{
			CheckForStanceUpdateOnIdle();
			// And keep whatever the return state is.  Go back to that.
		}
		else
		{
			ReturnToState = PreviousStateName;
		}
		
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END Signal ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

begin:
	TurnTowardsPosition(SignalFaceLocation);

	AnimParams = default.AnimParams;
	AnimParams.AnimName = SignalAnimationName;
	if( UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
	{
		FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	}

	GotoState(ReturnToState);
}

state Panicked
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN Panic ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END Panic ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{		
	}

begin:
	//Play and finish the intro anim
	if( !bStartedPanick )
	{
		AnimParams = default.AnimParams;
		AnimParams.AnimName = 'HL_Panic_Start';
		bStartedPanick = true;
		FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	}
	
	
	//Play and sustain the loop anim
	AnimParams.AnimName = 'HL_Panic_Loop';
	AnimParams.Looping = true;
	if( UnitPawnNative.GetAnimTreeController().CanPlayAnimation(AnimParams.AnimName) )
	{
		UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
	}
	else
	{
		// Do regular idle when there is no panic animation.  (Zombies)
		UnitPawn.EnableLeftHandIK(false);
		PlayIdleAnim();
	}

	while( IsUnitPanicked() )
	{
		sleep(0.0f);
	}
	
	//Play and finish the outro anim
	AnimParams.AnimName = 'HL_Panic_Stop';
	AnimParams.Looping = false;
	bStartedPanick = false;
	FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));

	GotoState('Idle');
}

state HunkeredDown
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN HunkeredDown ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END HunkeredDown ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{		
	}

begin:
	//Play and finish the intro anim
	AnimParams = default.AnimParams;
	switch (Unit.m_eCoverState)
	{
		case eCS_LowLeft:
		case eCS_LowRight:
			AnimParams.AnimName = 'LL_HunkerDwn_Start';
			break;
		case eCS_HighRight:
		case eCS_HighLeft:
		case eCS_None:
			AnimParams.AnimName = 'HL_HunkerDwn_Start';
			break;
	}
	FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	
	//Play and sustain the loop anim
	AnimParams = default.AnimParams;
	AnimParams.Looping = true;
	AnimParams.AnimName = 'HL_HunkerDwn_Loop';
	UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
	while(Unit.IsHunkeredDown())
	{
		sleep(0.0f);
	}
	
	//Play and finish the outro anim
	AnimParams = default.AnimParams;
	AnimParams.PlayRate = -1.0f;
	switch (Unit.m_eCoverState)
	{
		case eCS_LowLeft:
		case eCS_LowRight:
			AnimParams.AnimName = 'LL_HunkerDwn_Start';
			break;
		case eCS_HighRight:
		case eCS_HighLeft:
		case eCS_None:
			AnimParams.AnimName = 'HL_HunkerDwn_Start';
			break;
	}
	FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));

	GotoState('Idle');
}

//This state is entered whenever this unit determines that it needs to
//change facing. The facing could change as a result of losing cover, 
//enemies moving, or similar external events. This is the ONLY state in
//this object that can run while the state machine is 'Dormant'
state EvaluateStance
{
	//This function verifies that the unit is facing in the direction it thinks is. Root motion animations can potentially
	//leave a unit in a state where it is not facing a direction that matches its CoverDirectionIndex
	function bool UnitFacingMatchesDesiredDirection()
	{
		return !UnitNeedsToRotate(GetDesiredFaceLocation());
	}

	function bool UnitNeedsToRotate(vector FaceLocation)
	{
		local vector CurrentFacing;
		local vector DesiredFacing;
		local float Dot;
		local float DegreesOff;

		CurrentFacing = Vector(UnitPawn.Rotation);
		DesiredFacing = Normal(FaceLocation - Unit.Location);
		Dot = NoZDot(CurrentFacing, DesiredFacing);
		DegreesOff = Acos(Dot) * RadToDeg;
		
		return DegreesOff > 5.0f;
	}

	function vector GetDesiredFaceLocation()
	{
		local vector DesiredFaceLocation;

		if( DesiredCoverIndex > -1 && !bForceTurnTarget )
		{
			//Reverse facings since the animations are named after shoulders against cover and not facing
			if( DesiredPeekSide == ePeekLeft && CurrentCoverPeekData.CoverDirectionInfo[DesiredCoverIndex].LeftPeek.bHasPeekaround != 0 )
			{
				DesiredFaceLocation = Unit.Location + (CurrentCoverPeekData.CoverDirectionInfo[DesiredCoverIndex].LeftPeek.PeekaroundDirectionFromCoverPt * 1000.0f);
			}
			else if( DesiredPeekSide == ePeekRight && CurrentCoverPeekData.CoverDirectionInfo[DesiredCoverIndex].RightPeek.bHasPeekaround != 0 )
			{
				DesiredFaceLocation = Unit.Location + (CurrentCoverPeekData.CoverDirectionInfo[DesiredCoverIndex].RightPeek.PeekaroundDirectionFromCoverPt * 1000.0f);
			}
			else
			{
				DesiredFaceLocation = Unit.Location + (Vector(Unit.Rotation) * 1000.0f); // Keep your current facing
			}
		}
		else
		{
			DesiredFaceLocation = bForceTurnTarget ? TempFaceLocation : TargetLocation;
		}

		return DesiredFaceLocation;
	}

	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN EvaluateStance FROM"@ PreviousStateName @"************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		if (Unit.IsAlive() && UnitPawn.m_bInMatinee)
		{
			`Warn("Evaluating stance while in matinee may cause move-to-origin bugs!"$GetScriptTrace());
		}

		ReturnToState = PreviousStateName;
		StoredUpdateSkelWhenNotRendered = UnitPawn.GetUpdateSkelWhenNotRendered();
		UnitPawn.SetUpdateSkelWhenNotRendered(true);
	}

	event EndState( name NextStateName )
	{	
		`log("*********************** END EvaluateStance RETURN TO"@ NextStateName @"**********************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("     bDeferredGotoIdle: "@bDeferredGotoIdle, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("     bDeferredGotoDormant: "@bDeferredGotoDormant, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("     bForceDesiredCover: "@bForceDesiredCover, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("     bForceTurnTarget: "@bForceTurnTarget, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

		bDeferredGotoIdle = false;
		bDeferredGotoDormant = false;
		bForceDesiredCover = false;
		bIsExitFromCover = false;
		bForceTurnTarget = false;

		UnitPawn.SetUpdateSkelWhenNotRendered(StoredUpdateSkelWhenNotRendered);
	}

	simulated event Tick( float fDeltaT )
	{	
		if( bInLatentTurn )
		{
			UnitPawn.TargetLoc = UnitPawnNative.LatentTurnTarget;
		}

		ExitCoverAnimTimer += fDeltaT;
	}

begin:	
	if( bWaitingForStanceEval )
	{
		bWaitingForStanceEval = false;
	}

	`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	`log("Evaluating Unit States...", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

	if( bIsTurret )
	{
		if( RequiresTurretTransitionAnim(Unit.IdleTurretState) )
		{
			AnimParams.AnimName = GetTurretTransitionAnim(Unit.IdleTurretState);
			AnimParams.Looping = false;
			FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
		}
	}

	//If we are hunkered down, just go straight to back to idle
	`log("     Unit.IsHunkeredDown():"@Unit.IsHunkeredDown(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	if( Unit.IsHunkeredDown() )
	{
		GotoState('Idle');
	}

	//The unit has started suppressing, enter that state
	`log("     Unit.m_bSuppressing || Unit.m_bSuppressingArea :"@(Unit.m_bSuppressing || Unit.m_bSuppressingArea), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	if( Unit.m_bSuppressing || Unit.m_bSuppressingArea )
	{		
		GotoState('Fire');
	}

	`log("     Unit.IsDeadOrDying():"@(Unit.IsDeadOrDying()), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	if( Unit.IsDeadOrDying() || UnitNative.GetVisualizedGameState().IsIncapacitated() )
	{
		`log("     Cannot manipulate animation state while dead, exiting...", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		goto('Exit');
	}

	//Units temporarily exit their cover while shooting, stance evaluations that occur when this is happening are only allowed to turn the unit
	//and even then only via bForceDesiredCover or bForceTurnTarget
	if( Unit.bSteppingOutOfCover ) 
	{
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("The unit is stepping out of cover :"@Unit.bSteppingOutOfCover, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit.CanUseCover()                :"@Unit.CanUseCover(), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		if( bForceDesiredCover || bForceTurnTarget )
		{
			`log("Starting TurnTowardsPosition towards TargetLocation"@TargetLocation@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			TurnTowardsPosition(bForceTurnTarget ? TempFaceLocation : TargetLocation);//Latent turning function on XGUnit
			`log("Finished TurnTowardsPosition towards TargetLocation"@TargetLocation@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		}
	}
	else
	{
		//If we get here, then we just need to check whether our cover / enemies situation has changed
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Getting targeting information...", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		if( bForceDesiredCover || SetTargetUnit() )
		{	
			`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("Evaluating Cover States...", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("     bForceDesiredCover: "@bForceDesiredCover, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

			if( !bForceDesiredCover )
			{
				`log("     Calculating DesiredCoverState...", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				GetDesiredCoverState(DesiredCoverIndex, DesiredPeekSide);
			}

			`log("     DesiredCoverIndex: "@DesiredCoverIndex, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("     DesiredPeekSide: "@DesiredPeekSide, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("     CurrentCoverDirectionIndex: "@Unit.CurrentCoverDirectionIndex, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("     CurrentCoverState: "@Unit.m_eCoverState, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			
			if( (DesiredCoverIndex < 0 && Unit.m_eCoverState != eCS_None) || //The unit is getting out of cover
				(DesiredCoverIndex > -1 && Unit.m_eCoverState == eCS_None) || //The unit needs to get into cover from no cover
				(DesiredCoverIndex > -1 && DesiredCoverIndex != Unit.CurrentCoverDirectionIndex) || //The unit needs to change cover directions			
				(DesiredCoverIndex > -1 && !UnitFacingMatchesDesiredDirection()) ) //The unit thinks it is facing the right way, but really it isn't
			{	
				`log("Detected needs to change direction:", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				`log("     DesiredCoverIndex < 0                                                          :"@(DesiredCoverIndex < 0), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				`log("     DesiredCoverIndex > -1 && DesiredCoverIndex != Unit.CurrentCoverDirectionIndex :"@(DesiredCoverIndex > -1 && DesiredCoverIndex != Unit.CurrentCoverDirectionIndex), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				`log("     DesiredCoverIndex > -1 && Unit.m_eCoverState == eCS_None                       :"@(DesiredCoverIndex > -1 && Unit.m_eCoverState == eCS_None), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				`log("     DesiredCoverIndex > -1 && !UnitFacingMatchesDesiredDirection()                 :"@(DesiredCoverIndex > -1 && !UnitFacingMatchesDesiredDirection())@"  ("@vector(Unit.Rotation)@"vs."@Unit.GetCoverDirection(DesiredCoverIndex)@")", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

				//TempFaceLocation will be the target location for our turn node animation. This will also guide which exit cover animation is used
				TempFaceLocation = GetDesiredFaceLocation();
				
				if( DesiredCoverIndex > -1 && !bForceTurnTarget ) //The unit needs to: turn, enter cover in the new direction
				{
					if (UnitNeedsToRotate(TempFaceLocation))
					{
						if (ReturnToState == 'TargetingStart' || ReturnToState == 'TargetingHold')
						{
							bWaitingForStanceEval = true;
							GotoState('TargetingStop');
						}

						if( ReturnToState == 'PeekStart' )
						{
							bWaitingForStanceEval = true;
							GotoState('PeekStop');
						}

						`log("Starting TurnTowardsPosition towards TargetLocation"@(TempFaceLocation)@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
						TurnTowardsPosition(TempFaceLocation, true);//Latent turning function on XGUnit
						`log("Finished TurnTowardsPosition towards TargetLocation"@(TempFaceLocation)@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
					}
					Unit.SetCoverDirectionIndex(DesiredCoverIndex);
			
					//Enter the final cover state
					if( !UnitPawn.m_bInMatinee )
					{
						if( bDeferredGotoDormant || ReturnToState == 'Dormant' )
						{
							PlayIdleAnim();
						}

						if( Unit.GetCoverType() == CT_Standing )
						{
							if( DesiredPeekSide == ePeekLeft )
							{
								Unit.SetUnitCoverState(eCS_HighRight);
							}
							else if( DesiredPeekSide == ePeekRight)
							{
								Unit.SetUnitCoverState(eCS_HighLeft);
							}
						}
						else if( Unit.GetCoverType() == CT_MidLevel )
						{
							if( DesiredPeekSide == ePeekLeft )
							{
								Unit.SetUnitCoverState(eCS_LowRight);
							}
							else if( DesiredPeekSide == ePeekRight)
							{
								Unit.SetUnitCoverState(eCS_LowLeft);
							}
						}
					}
				}
				else //The unit has gone from in cover, to no cover and needs to turn to face a target. 
				{
					if( ReturnToState == 'TargetingStart' || ReturnToState == 'TargetingHold' )
					{
						bWaitingForStanceEval = true;
						GotoState('TargetingStop');
					}

					if( ReturnToState == 'PeekStart' )
					{
						bWaitingForStanceEval = true;
						GotoState('PeekStop');
					}

					`log("Starting TurnTowardsPosition towards TargetLocation"@TempFaceLocation@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
					TurnTowardsPosition(TempFaceLocation);//Latent turning function on XGUnit
					`log("Finished TurnTowardsPosition towards TargetLocation"@TempFaceLocation@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');

					if( !bIsExitFromCover )
					{
						Unit.SetUnitCoverState(eCS_None);
					}

					// Don't play flanked during your turn
					UnitState = UnitNative.GetVisualizedGameState(VisualizedHistoryIndex);
					if( UnitState.IsFlanked(,true, VisualizedHistoryIndex) && UnitState.ControllingPlayer.ObjectID != `TACTICALRULES.GetCachedUnitActionPlayerRef().ObjectID && ReturnToState != 'Dormant' )
					{
						Unit.UnitSpeak('SoldierFlanked');
						SignalAnimationName = 'HL_SignalNegative';
						SignalFaceLocation = TempFaceLocation;
						GotoState('Signal');
					}
				}				
			}
			else //If the unit did not need to change cover direction, check whether it needs to switch cover sides or turn
			{			
				if( Unit.m_eCoverState == eCS_None || bForceTurnTarget ) //The unit is not in cover, or is forcing a turn
				{
					`log("Starting TurnTowardsPosition towards TargetLocation"@(bForceTurnTarget ? TempFaceLocation : TargetLocation)@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
					TurnTowardsPosition(bForceTurnTarget ? TempFaceLocation : TargetLocation, true);//Latent turning function on XGUnit
					`log("Finished TurnTowardsPosition towards TargetLocation"@(bForceTurnTarget ? TempFaceLocation : TargetLocation)@" Rotator: "@Unit.Rotation@" Vector:"@vector(Unit.Rotation), `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
				}
			}
		}
		else
		{
			`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
			`log("No targets...", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		}
	}
	
Exit:
	if( bDeferredGotoIdle || bDeferredGotoDormant )
	{
		if( bDeferredGotoDormant )
		{
			bDeferredGotoDormant = false;
			GotoState('Dormant');
		}
		else if( bDeferredGotoIdle )
		{
			bDeferredGotoIdle = false;
			GotoState('Idle');
		}
	}
	else
	{
		// Returning to peek is awkward, if our peek was interupted just go to idle
		if( ReturnToState == 'PeekStart' )
		{
			GotoState('PeekStop');
		}

		GotoState(ReturnToState);
	}
}

simulated state Fire
{	
	simulated event BeginState(name PreviousStateName)
	{
		//`CONSTANTCOMBAT.iNumSuppressing += 1;
		//ResetNodes();
	}

	simulated event EndState(name NewStateName)
	{
		//`CONSTANTCOMBAT.iNumSuppressing -= 1;
		//ResetNodes();
		UnitPawn.StopSounds();
	}

	simulated event Tick(float DeltaTime)
	{
		
	}

	simulated function Name GetSuppressAnimName()
	{
		local XComWeapon Weapon;

		Weapon = XComWeapon(UnitPawn.Weapon);
		if( Weapon != None && UnitPawn.GetAnimTreeController().CanPlayAnimation(Weapon.WeaponSuppressionFireAnimSequenceName) )
		{
			return Weapon.WeaponSuppressionFireAnimSequenceName;
		}
		else if( UnitPawn.GetAnimTreeController().CanPlayAnimation(class'XComWeapon'.default.WeaponSuppressionFireAnimSequenceName) )
		{
			return class'XComWeapon'.default.WeaponSuppressionFireAnimSequenceName;
		}
	}

Begin:
	//Re-acquire the unit's target if it was lost ( for example, loading from a save )
	//if( TargetActor == none && Unit.GetNumberOfSuppressionTargets() > 0 )
	//{
	//	TargetActor = Unit.GetSuppressionTarget(0);
	//}
	XComWeapon(UnitPawn.Weapon).Spread[0] = 0.05f;

Firing:
	
	UnitPawn.TargetLoc = Unit.AdjustTargetForMiss(XGUnit(TargetActor), 3.0f);

	AnimParams = default.AnimParams;
	AnimParams.AnimName = GetSuppressAnimName();
	if( Unit.m_bSuppressing )
	{
		FinishAnim(UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams));
	}

	AnimParams.AnimName = 'FF_FireSuppressIdle';
	AnimParams.Looping = true;
	if( Unit.m_bSuppressing )
	{
		UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams); // idle until we shoot again
		Sleep(((Rand(100) / 100.0f)*2.0f) + 1.5f); // only shoot every 1.5-3.5 seconds when supressing (an addition 0.5 after target peeks)
		goto('Firing');
	}

	GotoState('Dormant'); // We'll be in a visualizer when we decide to stop suppressing
}

state PersistentEffect
{
	event BeginState(name PreviousStateName)
	{		
		`log("*********************** BEGIN PersistentEffect ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{	
		`log("*********************** END PersistentEffect ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	function Vector GetWrathCannonTargetLoc()
	{
		local XComGameStateHistory History;
		local Vector TargetLoc;
		local int ScanHistory;
		local XComGameStateContext_TacticalGameRule GameRuleContext;
		local XComGameStateContext_Ability AbilityContext;
		local XComGameState GameState;

		History = `XCOMHISTORY;

		for( ScanHistory = History.GetCurrentHistoryIndex(); ScanHistory >= 0; --ScanHistory )
		{
			GameState = History.GetGameStateFromHistory(ScanHistory);
			GameRuleContext = XComGameStateContext_TacticalGameRule(GameState.GetContext());
			if( GameRuleContext != None && GameRuleContext.GameRuleType == eGameRule_PlayerTurnBegin
			   && GameRuleContext.PlayerRef.ObjectID == UnitState.ControllingPlayer.ObjectID )
			{
				break;
			}

			AbilityContext = XComGameStateContext_Ability(GameState.GetContext());
			if( AbilityContext != None && AbilityContext.InputContext.AbilityTemplateName == class'X2Ability_Sectopod'.default.WrathCannonStage1AbilityName && AbilityContext.InputContext.SourceObject.ObjectID == UnitState.ObjectID )
			{
				TargetLoc = AbilityContext.InputContext.TargetLocations[0];
				break;
			}
		}

		// Update - always target the same Z level as the sectopod's head.
		TargetLoc.Z = UnitPawnNative.GetHeadLocation().Z;

		return TargetLoc;
	}

	function Rotator GetWrathCannonDesiredRotation(Vector WrathCannonTargetLoc)
	{
		local Rotator DesiredRotation;
		local Vector UnitLocation;

		UnitLocation = `XWORLD.GetPositionFromTileCoordinates(UnitState.TileLocation);
		DesiredRotation = Rotator(WrathCannonTargetLoc - UnitLocation);
		DesiredRotation.Pitch = 0;

		return DesiredRotation;
	}

	event Tick(float DeltaTime)
	{		
	}
Begin:
	AnimParams = default.AnimParams;
	AnimParams.AnimName = PersistentEffectIdleName;
	AnimParams.Looping = true;
	UnitPawnNative.GetAnimTreeController().SetAllowNewAnimations(true);
	UnitPawnNative.GetAnimTreeController().PlayFullBodyDynamicAnim(AnimParams);
	UnitPawnNative.GetAnimTreeController().SetAllowNewAnimations(false);

	if( UnitState.GetMyTemplate().CharacterGroupName == 'Sectopod' )
	{
		TargetLocation = GetWrathCannonTargetLoc();
		UnitPawn.SetRotation(GetWrathCannonDesiredRotation(TargetLocation));
	}

	while( PersistentEffectIdleName != '' )
	{
		UnitState = UnitNative.GetVisualizedGameState();
		if( -1 != UnitState.AppliedEffectNames.Find(class'X2Ability_Sectopod'.default.WrathCannonStage1EffectName) )
		{
			UnitPawnNative.TargetLoc = TargetLocation;
		}
		Sleep(0.0f);
	}
	
	UnitPawnNative.GetAnimTreeController().SetAllowNewAnimations(true);
	GotoState('Idle');
}

static function name ChooseAnimationForCover( XGUnit kUnit, name NoCover, name LowCover, name HighCover )
{
	if (kUnit.IsInCover( ))
	{
		if (kUnit.GetCoverType( ) == CT_MidLevel)
			return LowCover;
		return HighCover;
	}
	return NoCover;
}

state TurretIdle
{
	event BeginState(name PreviousStateName)
	{
		`log("*********************** BEGIN TurretIdle ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("Unit : "@Unit, `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
		`log("", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event EndState(name NewStateName)
	{
		`log("*********************** END TurretIdle ************************************************", `CHEATMGR.MatchesXComAnimUnitName(Unit.Name), 'XCom_Anim');
	}

	event Tick(float DeltaTime)
	{
	}
Begin:

	CurrentTurretState = Unit.IdleTurretState;
	PlayIdleAnim();

	while(CurrentTurretState == Unit.IdleTurretState)
	{
		Sleep(0.1f);
	}

	GotoState('Idle');
}

simulated event PostRenderFor(PlayerController kPC, Canvas kCanvas, vector vCameraPosition, vector vCameraDir)
{
	local string DebugString;
	local int UseCoverDirectionIndex;
	local UnitPeekSide UsePeekSide;  
	local int RequiresLean;			
	local int bCanSeeFromDefault;	
	local XGUnit ActiveUnit;	
	local bool bWillStepOut;

	ActiveUnit = XComTacticalController(GetALocalPlayerController()).GetActiveUnit();

	if(ActiveUnit == Unit && (`CHEATMGR != None && `CHEATMGR.bDebugIdleAnimationStateMachines))
	{
		DebugString =  "===================================\n";
		DebugString $= "=======    Step Outs Debugger  ========\n";
		DebugString $= "===================================\n\n";

		bWillStepOut = Unit.GetStepOutCoverInfo(TargetActor, TargetLocation, UseCoverDirectionIndex, UsePeekSide, RequiresLean, bCanSeeFromDefault);
		DebugString $= "Target:  "$TargetActor$"\n";
		DebugString $= "UseCoverDirectionIndex:  "$UseCoverDirectionIndex$"\n";
		DebugString $= "UsePeekSide:  "$UsePeekSide$"\n";
		DebugString $= "RequiresLean:  "$RequiresLean$"\n";
		DebugString $= "bCanSeeFromDefault:  "$bCanSeeFromDefault$"\n";
		DebugString $= "\n\nWILL STEP OUT:  "$ (bWillStepOut ? "Yes" : "No") $"\n";
		
		// draw a background box so the text is readable
		kCanvas.SetPos(10, 150);
		kCanvas.SetDrawColor(0, 0, 0, 100);
		kCanvas.DrawRect(400, 200);

		// draw the text
		kCanvas.SetPos(10, 150);
		kCanvas.SetDrawColor(0, 255, 0);
		kCanvas.DrawText(DebugString);
	}
}


defaultproperties
{
	PeekTimeoutCurrent = 0.0f;
	bWaitingForStanceEval = false;	
	bDeferredGotoIdle = false;	
	TurningSequence = none;
	bTargeting = false;	
}
