abstract class AILookatTask extends AIbehaviortaskScript
{
}

abstract class AIGenericLookatTask extends AILookatTask
{

	protected virtual function GetSoftLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.None;
	}

	protected virtual function GetHardLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.None;
	}

	protected virtual function GetHardLimitDistanceType() : animLookAtLimitDistanceType
	{
		return animLookAtLimitDistanceType.None;
	}

	protected virtual function GetBackLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.Normal;
	}

	protected virtual function GetLookatStyle() : animLookAtStyle
	{
		return animLookAtStyle.Normal;
	}

	protected virtual function GetHasOutTransition() : Bool
	{
		return true;
	}

	protected virtual function GetOutTransitionStyle() : animLookAtStyle
	{
		return animLookAtStyle.Slow;
	}

	protected virtual function GetLookAtSlotName() : CName
	{
		return 'Chest';
	}

	protected virtual function GetLookActivationDelay() : Float
	{
		return -1.0;
	}

	protected virtual function GetLookAtDeactivationDelay() : Float
	{
		return -1.0;
	}

	protected virtual function ActivateLookat( context : ScriptExecutionContext ) {}

	protected virtual function DeactivateLookat( context : ScriptExecutionContext, optional instant : Bool ) {}

	protected virtual function ShouldLookatBeActive( context : ScriptExecutionContext ) : Bool
	{
		return true;
	}

	protected function UpdateLookat( context : ScriptExecutionContext )
	{
		if( ShouldLookatBeActive( context ) )
		{
			ActivateLookat( context );
		}
		else
		{
			DeactivateLookat( context );
		}
	}

	private override function Update( context : ScriptExecutionContext ) : AIbehaviorUpdateOutcome
	{
		UpdateLookat( context );
		return AIbehaviorUpdateOutcome.IN_PROGRESS;
	}

	private override function Deactivate( context : ScriptExecutionContext )
	{
		super.Deactivate( context );
		DeactivateLookat( context );
	}

	protected function DeactivateLookatInternal( context : ScriptExecutionContext, optional instant : Bool )
	{
		var lookAtDeactivationDelay : Float;
		if( !( GetLookAtEvent() ) )
		{
			return;
		}
		lookAtDeactivationDelay = GetLookAtDeactivationDelay();
		if( ( lookAtDeactivationDelay <= 0.0 ) || instant )
		{
			LookAtRemoveEvent.QueueRemoveLookatEvent( GetPuppet( context ), GetLookAtEvent() );
			SetLookAtEvent( NULL );
		}
		else
		{
			LookAtRemoveEvent.QueueDelayedRemoveLookatEvent( context, GetLookAtEvent(), lookAtDeactivationDelay );
		}
	}

	protected virtual function GetLookAtEvent() : LookAtAddEvent
	{
		return NULL;
	}

	protected virtual function SetLookAtEvent( lookAtEvent : LookAtAddEvent ) {}
}

abstract class AIGenericEntityLookatTask extends AIGenericLookatTask
{
	private var m_lookAtEvent : LookAtAddEvent;
	private var m_activationTimeStamp : Float;
	private var m_lookatTarget : weak< Entity >;

	protected override function GetLookAtEvent() : LookAtAddEvent
	{
		return m_lookAtEvent;
	}

	protected override function SetLookAtEvent( lookAtEvent : LookAtAddEvent )
	{
		m_lookAtEvent = lookAtEvent;
	}

	protected virtual function GetAimingLookatTarget( context : ScriptExecutionContext ) : GameObject
	{
		return NULL;
	}

	private override function ActivateLookat( context : ScriptExecutionContext )
	{
		if( m_lookAtEvent )
		{
			return;
		}
		m_lookAtEvent = new LookAtAddEvent;
		m_lookAtEvent.SetEntityTarget( m_lookatTarget, GetLookAtSlotName(), Vector4.EmptyVector() );
		m_lookAtEvent.SetLimits( GetSoftLimitDegreesType(), GetHardLimitDegreesType(), GetHardLimitDistanceType(), GetBackLimitDegreesType() );
		m_lookAtEvent.SetStyle( GetLookatStyle() );
		m_lookAtEvent.request.hasOutTransition = GetHasOutTransition();
		m_lookAtEvent.SetOutTransitionStyle( GetOutTransitionStyle() );
		if( !( IsFinal() ) )
		{
			m_lookAtEvent.SetDebugInfo( "ScriptAIGenericEntityLookatTask" );
		}
		GetPuppet( context ).QueueEvent( m_lookAtEvent );
	}

	private override function DeactivateLookat( context : ScriptExecutionContext, optional instant : Bool )
	{
		DeactivateLookatInternal( context, instant );
	}

	protected override function ShouldLookatBeActive( context : ScriptExecutionContext ) : Bool
	{
		if( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_disabled", false ) )
		{
			return false;
		}
		m_lookatTarget = GetAimingLookatTarget( context );
		if( !( m_lookatTarget ) )
		{
			return false;
		}
		if( EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) ) < ( m_activationTimeStamp + GetLookActivationDelay() ) )
		{
			return false;
		}
		return true;
	}

	private override function Activate( context : ScriptExecutionContext )
	{
		super.Activate( context );
		m_activationTimeStamp = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
		if( m_lookAtEvent )
		{
			DeactivateLookat( context, true );
		}
		UpdateLookat( context );
	}

}

abstract class AIGenericAdvancedLookatTask extends AIGenericLookatTask
{
	private var m_lookAtEvent : LookAtAddEvent;
	private var m_activationTimeStamp : Float;
	private var m_lookatTarget : weak< Entity >;

	protected virtual function GetAimingLookatTarget( context : ScriptExecutionContext ) : GameObject
	{
		return NULL;
	}

	private override function ActivateLookat( context : ScriptExecutionContext )
	{
		if( m_lookAtEvent )
		{
			return;
		}
		m_lookAtEvent = new LookAtAddEvent;
		m_lookAtEvent.SetEntityTarget( m_lookatTarget, GetLookAtSlotName(), Vector4.EmptyVector() );
		m_lookAtEvent.SetLimits( GetSoftLimitDegreesType(), GetHardLimitDegreesType(), GetHardLimitDistanceType(), GetBackLimitDegreesType() );
		m_lookAtEvent.SetStyle( GetLookatStyle() );
		if( !( IsFinal() ) )
		{
			m_lookAtEvent.SetDebugInfo( "ScriptAIGenericAdvancedLookatTask" );
		}
		GetPuppet( context ).QueueEvent( m_lookAtEvent );
	}

	private override function DeactivateLookat( context : ScriptExecutionContext, optional instant : Bool )
	{
		DeactivateLookatInternal( context, instant );
	}

	protected override function ShouldLookatBeActive( context : ScriptExecutionContext ) : Bool
	{
		if( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_disabled", false ) )
		{
			return false;
		}
		m_lookatTarget = GetAimingLookatTarget( context );
		if( !( m_lookatTarget ) )
		{
			return false;
		}
		if( EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) ) < ( m_activationTimeStamp + GetLookActivationDelay() ) )
		{
			return false;
		}
		return true;
	}

	private override function Activate( context : ScriptExecutionContext )
	{
		super.Activate( context );
		m_activationTimeStamp = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
		if( m_lookAtEvent )
		{
			DeactivateLookat( context, true );
		}
		UpdateLookat( context );
	}

}

abstract class AIGenericStaticLookatTask extends AIGenericLookatTask
{
	private var m_lookAtEvent : LookAtAddEvent;
	private var m_activationTimeStamp : Float;
	private var m_lookatTarget : Vector4;
	private var m_currentLookatTarget : Vector4;

	protected virtual function GetAimingLookatTarget( context : ScriptExecutionContext ) : Vector4
	{
		return Vector4( 0.0, 0.0, 0.0, 0.0 );
	}

	private override function ActivateLookat( context : ScriptExecutionContext )
	{
		if( m_lookAtEvent )
		{
			if( m_currentLookatTarget == m_lookatTarget )
			{
				return;
			}
			else
			{
				DeactivateLookat( context, true );
			}
		}
		m_lookAtEvent = new LookAtAddEvent;
		m_lookAtEvent.SetStaticTarget( m_lookatTarget );
		m_currentLookatTarget = m_lookatTarget;
		m_lookAtEvent.SetLimits( GetSoftLimitDegreesType(), GetHardLimitDegreesType(), GetHardLimitDistanceType(), GetBackLimitDegreesType() );
		m_lookAtEvent.SetStyle( GetLookatStyle() );
		if( !( IsFinal() ) )
		{
			m_lookAtEvent.SetDebugInfo( "ScriptAIGenericStaticLookatTask" );
		}
		GetPuppet( context ).QueueEvent( m_lookAtEvent );
	}

	private override function DeactivateLookat( context : ScriptExecutionContext, optional instant : Bool )
	{
		DeactivateLookatInternal( context, instant );
	}

	protected override function ShouldLookatBeActive( context : ScriptExecutionContext ) : Bool
	{
		if( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_disabled", false ) )
		{
			return false;
		}
		m_lookatTarget = GetAimingLookatTarget( context );
		if( Vector4.IsZero( m_lookatTarget ) )
		{
			return false;
		}
		if( EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) ) < ( m_activationTimeStamp + GetLookActivationDelay() ) )
		{
			return false;
		}
		return true;
	}

	private override function Activate( context : ScriptExecutionContext )
	{
		super.Activate( context );
		m_activationTimeStamp = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
		if( m_lookAtEvent )
		{
			DeactivateLookat( context, true );
		}
		UpdateLookat( context );
	}

}

abstract class AISearchingLookat extends AIGenericStaticLookatTask
{
	editable inlined var m_minAngleDifferenceMapping : AIArgumentMapping;
	protected var m_minAngleDifference : Float;
	editable inlined var m_maxLookAroundAngleMapping : AIArgumentMapping;
	protected var m_maxLookAroundAngle : Float;
	private var m_currentTarget : Vector4;
	private var m_lastTarget : Vector4;
	private var m_targetSwitchTimeStamp : Float;
	private var m_targetSwitchCooldown : Float;
	private var m_sideHorizontal : Int32;
	default m_sideHorizontal = 1;
	private var m_sideVertical : Int32;
	default m_sideVertical = 1;

	private virtual function InitializeMemberVariables( context : ScriptExecutionContext ) : Bool
	{
		return false;
	}

	private virtual function GetLookatTargetPosition( context : ScriptExecutionContext ) : Vector4
	{
		return Vector4( 0.0, 0.0, 0.0, 0.0 );
	}

	private override function GetHardLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.None;
	}

	private override function GetHardLimitDistanceType() : animLookAtLimitDistanceType
	{
		return animLookAtLimitDistanceType.None;
	}

	private override function GetSoftLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.None;
	}

	private override function GetBackLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.Normal;
	}

	private override function GetLookatStyle() : animLookAtStyle
	{
		return animLookAtStyle.Fast;
	}

	private override function GetAimingLookatTarget( context : ScriptExecutionContext ) : Vector4
	{
		if( ( EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) ) < ( m_targetSwitchTimeStamp + m_targetSwitchCooldown ) ) && ( GetAbsAngleToTarget( context ) < ( GetSoftLookatLimitDegrees() * 0.5 ) ) )
		{
			return m_currentTarget;
		}
		else
		{
			m_targetSwitchCooldown = RandRangeF( 1.5, 3.0 );
			m_targetSwitchTimeStamp = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
			return SelectNewAimingLookatTarget( context );
		}
	}

	private final function SelectNewAimingLookatTarget( context : ScriptExecutionContext ) : Vector4
	{
		var distanceToTarget : Float;
		var leftOffsetAngleCap : Float;
		var rightOffsetAngleCap : Float;
		var sideOffsetAngle : Float;
		var sideOffset : Float;
		var offsetVector : Vector4;
		if( !( InitializeMemberVariables( context ) ) )
		{
			return Vector4( 0.0, 0.0, 0.0, 0.0 );
		}
		distanceToTarget = Vector4.Distance( GetPuppet( context ).GetWorldPosition(), GetLookatTargetPosition( context ) );
		if( RandRange( 0, 2 ) > 0 )
		{
			m_sideHorizontal *= -1;
		}
		if( RandRange( 0, 2 ) > 0 )
		{
			m_sideVertical *= -1;
		}
		LookatOffsetAngleLimit( context, m_lastTarget, leftOffsetAngleCap, rightOffsetAngleCap );
		if( m_sideHorizontal > 0 )
		{
			sideOffsetAngle = ClampF( m_maxLookAroundAngle, 0.0, rightOffsetAngleCap );
			if( sideOffsetAngle < m_minAngleDifference )
			{
				sideOffsetAngle = ClampF( m_maxLookAroundAngle, 0.0, leftOffsetAngleCap );
				m_sideHorizontal *= -1;
			}
		}
		else
		{
			sideOffsetAngle = ClampF( m_maxLookAroundAngle, 0.0, leftOffsetAngleCap );
		}
		sideOffsetAngle = RandRangeF( 0.0, sideOffsetAngle );
		sideOffset = distanceToTarget * TanF( Deg2Rad( sideOffsetAngle ) );
		offsetVector = Vector4.FromHeading( Vector4.Heading( GetPuppet( context ).GetWorldPosition() - GetLookatTargetPosition( context ) ) + ( 90.0 * ( ( Float )( m_sideHorizontal ) ) ) );
		offsetVector.X *= sideOffset;
		offsetVector.Y *= sideOffset;
		if( m_sideVertical > 0 )
		{
			offsetVector.Z *= ( sideOffset * 0.5 );
		}
		else
		{
			offsetVector.Z *= ( sideOffset * -0.25 );
		}
		m_currentTarget = GetLookatTargetPosition( context ) + offsetVector;
		m_currentTarget.W = 1.0;
		m_lastTarget = m_currentTarget;
		return m_currentTarget;
	}

	protected final function LookatOffsetAngleLimit( context : ScriptExecutionContext, lastTargetPosition : Vector4, out leftAngleCap : Float, out rightAngleCap : Float )
	{
		var lastDirection : Vector4;
		var originalDirection : Vector4;
		var angleToTarget : Float;
		var angleToCheck : Float;
		if( Vector4.IsZero( lastTargetPosition ) )
		{
			lastTargetPosition = GetLookatTargetPosition( context );
		}
		lastDirection = lastTargetPosition - GetPuppet( context ).GetWorldPosition();
		originalDirection = GetLookatTargetPosition( context ) - GetPuppet( context ).GetWorldPosition();
		angleToCheck = GetLookAtLimitDegreesValue( GetSoftLimitDegreesType() ) * 0.5;
		angleToTarget = Vector4.GetAngleBetween( lastDirection, originalDirection );
		leftAngleCap = AbsF( -( angleToCheck ) - angleToTarget );
		rightAngleCap = angleToCheck - angleToTarget;
	}

	protected final function GetSoftLookatLimitDegrees() : Float
	{
		return GetLookAtLimitDegreesValue( GetSoftLimitDegreesType() );
	}

	protected final function GetAbsAngleToTarget( context : ScriptExecutionContext ) : Float
	{
		var vecToTarget : Vector4;
		var absAngleToTarget : Float;
		vecToTarget = GetLookatTargetPosition( context ) - ScriptExecutionContext.GetOwner( context ).GetWorldPosition();
		absAngleToTarget = AbsF( Vector4.GetAngleDegAroundAxis( vecToTarget, ScriptExecutionContext.GetOwner( context ).GetWorldForward(), ScriptExecutionContext.GetOwner( context ).GetWorldUp() ) );
		return absAngleToTarget;
	}

}

class LookatCompanion extends AIGenericAdvancedLookatTask
{

	private override function GetAimingLookatTarget( context : ScriptExecutionContext ) : GameObject
	{
		return GetCompanion( context );
	}

	private override function GetHardLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.None;
	}

	private override function GetHardLimitDistanceType() : animLookAtLimitDistanceType
	{
		return animLookAtLimitDistanceType.None;
	}

	private override function GetSoftLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.None;
	}

	private override function GetBackLimitDegreesType() : animLookAtLimitDegreesType
	{
		return animLookAtLimitDegreesType.Normal;
	}

	private override function GetLookatStyle() : animLookAtStyle
	{
		return animLookAtStyle.Normal;
	}

	private override function GetLookActivationDelay() : Float
	{
		return 0.5;
	}

	private override function GetLookAtDeactivationDelay() : Float
	{
		return 1.0;
	}

}

class LookatCombatTarget extends AIGenericEntityLookatTask
{

	private override function GetAimingLookatTarget( context : ScriptExecutionContext ) : GameObject
	{
		return GetCombatTarget( context );
	}

	private override function GetLookatStyle() : animLookAtStyle
	{
		return animLookAtStyle.Fast;
	}

}

class HeadLookatCombatTarget extends LookatCombatTarget
{

	private override function GetLookAtSlotName() : CName
	{
		return 'Head';
	}

}

class LookatCombatTarget_WithoutArms extends AIGenericEntityLookatTask
{

	private override function GetAimingLookatTarget( context : ScriptExecutionContext ) : GameObject
	{
		return GetCombatTarget( context );
	}

}

class WoundedLookatController extends AIGenericEntityLookatTask
{

	private override function Activate( context : ScriptExecutionContext )
	{
		var m_aimingLookatEvent : LookAtAddEvent;
		m_aimingLookatEvent = new LookAtAddEvent;
		m_aimingLookatEvent.SetEntityTarget( GetCombatTarget( context ), 'Head', Vector4.EmptyVector() );
		m_aimingLookatEvent.SetLimits( animLookAtLimitDegreesType.None, animLookAtLimitDegreesType.None, animLookAtLimitDistanceType.None, animLookAtLimitDegreesType.None );
		if( !( IsFinal() ) )
		{
			m_aimingLookatEvent.SetDebugInfo( "ScriptWoundedLookatController" );
		}
		GetPuppet( context ).QueueEvent( m_aimingLookatEvent );
	}

	private override function Update( context : ScriptExecutionContext ) : AIbehaviorUpdateOutcome
	{
		return AIbehaviorUpdateOutcome.IN_PROGRESS;
	}

	private override function Deactivate( context : ScriptExecutionContext ) {}
}

class SearchPatternMappingLookat extends AISearchingLookat
{
	editable inlined var m_targetObjectMapping : AIArgumentMapping;
	protected var m_lookatTargetObject : weak< GameObject >;

	private override function GetLookatTargetPosition( context : ScriptExecutionContext ) : Vector4
	{
		return m_lookatTargetObject.GetWorldPosition();
	}

	private override function InitializeMemberVariables( context : ScriptExecutionContext ) : Bool
	{
		if( m_targetObjectMapping && !( m_lookatTargetObject ) )
		{
			m_lookatTargetObject = ( ( weak< weak< GameObject > > )( ScriptExecutionContext.GetMappingValue( context, m_targetObjectMapping ) ) );
		}
		if( !( m_lookatTargetObject ) )
		{
			return false;
		}
		if( m_minAngleDifferenceMapping && ( m_minAngleDifference == 0.0 ) )
		{
			m_minAngleDifference = ( ( Float )( ScriptExecutionContext.GetMappingValue( context, m_minAngleDifferenceMapping ) ) );
		}
		if( m_maxLookAroundAngleMapping && ( m_maxLookAroundAngle == 0.0 ) )
		{
			m_maxLookAroundAngle = ( ( Float )( ScriptExecutionContext.GetMappingValue( context, m_maxLookAroundAngleMapping ) ) );
		}
		return true;
	}

}

class SearchInFrontPatternLookat extends AISearchingLookat
{

	private override function GetLookatTargetPosition( context : ScriptExecutionContext ) : Vector4
	{
		return GetPuppet( context ).GetWorldPosition() + ( 10.0 * Vector4.Normalize( GetPuppet( context ).GetWorldForward() ) );
	}

	private override function InitializeMemberVariables( context : ScriptExecutionContext ) : Bool
	{
		if( m_minAngleDifferenceMapping && ( m_minAngleDifference == 0.0 ) )
		{
			m_minAngleDifference = ( ( Float )( ScriptExecutionContext.GetMappingValue( context, m_minAngleDifferenceMapping ) ) );
		}
		if( m_maxLookAroundAngleMapping && ( m_maxLookAroundAngle == 0.0 ) )
		{
			m_maxLookAroundAngle = ( ( Float )( ScriptExecutionContext.GetMappingValue( context, m_maxLookAroundAngleMapping ) ) );
		}
		return true;
	}

}

class CentaurShieldLookatController extends AILookatTask
{
	private var m_mainShieldLookat : LookAtAddEvent;
	private var m_mainShieldlookatActive : Bool;
	private var m_currentLookatTarget : weak< GameObject >;
	private var m_shieldTarget : weak< GameObject >;
	private var m_centaurBlackboard : weak< IBlackboard >;
	private var m_shieldTargetTimeStamp : Float;
	default m_shieldTargetTimeStamp = -1.f;

	protected override function Activate( context : ScriptExecutionContext )
	{
		super.Activate( context );
		if( !( m_centaurBlackboard ) )
		{
			m_centaurBlackboard = GetPuppet( context ).GetCustomBlackboard();
		}
	}

	protected override function Update( context : ScriptExecutionContext ) : AIbehaviorUpdateOutcome
	{
		switch( GetShieldState( context ) )
		{
			case ECentaurShieldState.Active:
				UpdateActiveShield( context );
			break;
			case ECentaurShieldState.Destroyed:
				DeactivateMainShieldLookat( context );
			break;
			default:
				break;
		}
		return AIbehaviorUpdateOutcome.IN_PROGRESS;
	}

	protected override function Deactivate( context : ScriptExecutionContext )
	{
		super.Deactivate( context );
		if( ( GetShieldState( context ) == ECentaurShieldState.Active && GetCombatTarget( context ) ) && !( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_shield_disabled", false ) ) )
		{
			ActivateMainShieldLookat( context, GetCombatTarget( context ) );
		}
		else
		{
			DeactivateMainShieldLookat( context );
		}
	}

	private function GetShieldState( context : ScriptExecutionContext ) : ECentaurShieldState
	{
		if( GetPuppet( context ).GetCustomBlackboard() )
		{
			return ( ( ECentaurShieldState )( GetPuppet( context ).GetCustomBlackboard().GetInt( GetAllBlackboardDefs().CustomCentaurBlackboard.ShieldState ) ) );
		}
		return ECentaurShieldState.Inactive;
	}

	private final function UpdateActiveShield( context : ScriptExecutionContext )
	{
		ReevaluateDesiredLookatTarget( context );
		if( ShouldLookatAtShieldTarget( context ) )
		{
			ActivateMainShieldLookat( context, m_shieldTarget );
		}
		else if( ShouldLookatAtCombatTarget( context ) )
		{
			ActivateMainShieldLookat( context, GetCombatTarget( context ) );
		}
		else
		{
			DeactivateMainShieldLookat( context );
		}
	}

	private function ShouldLookatAtShieldTarget( context : ScriptExecutionContext ) : Bool
	{
		var upperBodyState : gamedataNPCUpperBodyState;
		if( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_shield_disabled", false ) )
		{
			return false;
		}
		upperBodyState = GetUpperBodyState( context );
		if( upperBodyState == gamedataNPCUpperBodyState.Defend )
		{
			return false;
		}
		if( m_shieldTarget )
		{
			return true;
		}
		return false;
	}

	private function ShouldLookatAtCombatTarget( context : ScriptExecutionContext ) : Bool
	{
		var upperBodyState : gamedataNPCUpperBodyState;
		if( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_shield_disabled", false ) )
		{
			return false;
		}
		if( !( GetCombatTarget( context ) ) )
		{
			return false;
		}
		upperBodyState = GetUpperBodyState( context );
		if( upperBodyState == gamedataNPCUpperBodyState.Attack )
		{
			return false;
		}
		if( upperBodyState == gamedataNPCUpperBodyState.Defend )
		{
			return false;
		}
		if( GetPuppet( context ).GetBoolFromCharacterTweak( "lookat_shield_enableOnCumulatedDamage", false ) && ( GetHitReactionComponent( context ).GetCumulatedDamage() <= 0.0 ) )
		{
			return false;
		}
		return true;
	}

	private function ActivateMainShieldLookat( context : ScriptExecutionContext, lookatTarget : weak< GameObject > )
	{
		if( lookatTarget != m_currentLookatTarget )
		{
			DeactivateMainShieldLookat( context );
		}
		if( m_mainShieldlookatActive )
		{
			return;
		}
		m_currentLookatTarget = lookatTarget;
		m_mainShieldLookat = new LookAtAddEvent;
		m_mainShieldLookat.SetEntityTarget( m_currentLookatTarget, 'Head', Vector4.EmptyVector() );
		m_mainShieldLookat.bodyPart = 'LeftHand';
		m_mainShieldLookat.SetLimits( animLookAtLimitDegreesType.None, animLookAtLimitDegreesType.None, animLookAtLimitDistanceType.None, animLookAtLimitDegreesType.None );
		if( !( IsFinal() ) )
		{
			m_mainShieldLookat.SetDebugInfo( "ScriptActivateMainShieldLookat" );
		}
		GetPuppet( context ).QueueEvent( m_mainShieldLookat );
		AnimationControllerComponent.SetInputBoolToReplicate( ScriptExecutionContext.GetOwner( context ), 'shield_lookat_active', true );
		m_mainShieldlookatActive = true;
	}

	protected function DeactivateMainShieldLookat( context : ScriptExecutionContext )
	{
		if( !( m_mainShieldlookatActive ) )
		{
			return;
		}
		LookAtRemoveEvent.QueueRemoveLookatEvent( GetPuppet( context ), m_mainShieldLookat );
		AnimationControllerComponent.SetInputBoolToReplicate( ScriptExecutionContext.GetOwner( context ), 'shield_lookat_active', false );
		m_mainShieldlookatActive = false;
	}

	private function ReevaluateDesiredLookatTarget( context : ScriptExecutionContext )
	{
		var shieldTargetID : EntityID;
		var emptyID : EntityID;
		shieldTargetID = m_centaurBlackboard.GetEntityID( GetAllBlackboardDefs().CustomCentaurBlackboard.ShieldTarget );
		if( EntityID.IsDefined( shieldTargetID ) )
		{
			m_shieldTarget = ( ( GameObject )( GameInstance.FindEntityByID( GetGame( context ), shieldTargetID ) ) );
		}
		if( m_shieldTarget != NULL )
		{
			if( m_shieldTargetTimeStamp < 0.0 )
			{
				m_shieldTargetTimeStamp = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
			}
			if( IsShieldTargetValid( context ) )
			{
				return;
			}
		}
		m_shieldTarget = NULL;
		m_centaurBlackboard.SetEntityID( GetAllBlackboardDefs().CustomCentaurBlackboard.ShieldTarget, emptyID );
		m_shieldTargetTimeStamp = -1.0;
	}

	private function IsShieldTargetValid( context : ScriptExecutionContext ) : Bool
	{
		var shieldTargetTimeout : Float;
		var maxDistance : Float;
		shieldTargetTimeout = GetPuppet( context ).GetFloatFromCharacterTweak( "lookat_shield_targetTimeout", -1.0 );
		maxDistance = GetPuppet( context ).GetFloatFromCharacterTweak( "lookat_shield_targetMaxDistance", -1.0 );
		if( ( maxDistance > 0.0 ) && ( GetDistanceToShieldTarget( context ) > maxDistance ) )
		{
			return false;
		}
		if( ( shieldTargetTimeout > 0.0 ) && ( EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) ) >= ( m_shieldTargetTimeStamp + shieldTargetTimeout ) ) )
		{
			return false;
		}
		return true;
	}

	private function GetDistanceToShieldTarget( context : ScriptExecutionContext ) : Float
	{
		if( !( m_shieldTarget ) )
		{
			return -1.0;
		}
		return Vector4.Distance( ScriptExecutionContext.GetOwner( context ).GetWorldPosition(), m_shieldTarget.GetWorldPosition() );
	}

}

