import enum gameEnemyStealthAwarenessState
{
	Relaxed,
	Aware,
	Alerted,
	Combat,
}

import enum gameReprimandMappinAnimationState
{
	None,
	Normal,
	Fast,
}

class StealthMappinGameController extends inkGameController
{
	private var m_controller : weak< StealthMappinController >;
	private var m_ownerNPC : weak< NPCPuppet >;

	protected event OnInitialize()
	{
		var ownerObject : weak< GameObject >;
		var gameInstance : GameInstance;
		m_controller = ( ( StealthMappinController )( GetRootWidget().GetController() ) );
		ownerObject = ( ( GameObject )( GetOwnerEntity() ) );
		gameInstance = ownerObject.GetGame();
		m_ownerNPC = ( ( NPCPuppet )( GameInstance.FindEntityByID( gameInstance, m_controller.GetMappin().GetEntityID() ) ) );
		m_controller.SetGameInstance( gameInstance );
	}

}

import class StealthMappinController extends BaseInteractionMappinController
{
	private editable var m_arrow : inkImageRef;
	private editable var m_fill : inkWidgetRef;
	private editable var m_eyeFillWhite : inkWidgetRef;
	private editable var m_eyeFillBlack : inkWidgetRef;
	private editable var m_markExclamation : inkTextRef;
	private editable var m_distance : inkTextRef;
	private editable var m_mainArt : inkWidgetRef;
	private editable var m_frame : inkImageRef;
	private editable var m_eliteFrameName : CName;
	private editable var m_eliteFrameSize : Vector2;
	private editable var m_objectMarker : inkWidgetRef;
	private editable var m_levelIcon : inkImageRef;
	private editable var m_taggedContainer : inkWidgetRef;
	private editable var m_background : inkCompoundRef;
	private editable var m_contagiousQuickhackArrows : inkCompoundRef;
	[ category = "Status Effect" ]
	private editable var m_statusEffectMain : inkWidgetRef;
	[ category = "Status Effect" ]
	private editable var m_statusEffectIcon : inkImageRef;
	[ category = "Status Effect" ]
	private editable var m_statusEffectFill : inkWidgetRef;
	[ category = "Status Effect" ]
	private editable var m_statusEffectBackground : inkWidgetRef;
	private var m_ownerObject : weak< GameObject >;
	private var m_ownerNPC : weak< NPCPuppet >;
	private var m_ownerDevice : weak< Device >;
	private var m_mappin : weak< StealthMappin >;
	private var m_root : weak< inkWidget >;
	private var m_canvas : weak< inkWidget >;
	private var m_nameplateController : weak< NpcNameplateGameController >;
	private var m_isFriendly : Bool;
	private var m_isFriendlyFromHack : Bool;
	private var m_isHostile : Bool;
	private var m_isAggressive : Bool;
	private var m_isNCPD : Bool;
	private var m_isDevice : Bool;
	private var m_isDrone : Bool;
	private var m_isMech : Bool;
	private var m_isCamera : Bool;
	private var m_isTurret : Bool;
	private var m_isHiddenByQuest : Bool;
	private var m_hideUIElements : Bool;
	private var m_puppetStateBlackboard : weak< IBlackboard >;
	private var m_highLevelState : gamedataNPCHighLevelState;
	private var m_numberOfCombatants : Int32;
	private var m_waitingToExitCombat : Bool;
	private var m_reaction : gamedataOutput;
	private var m_lastState : gamedataNPCHighLevelState;
	private var m_lastReaction : gamedataOutput;
	private var m_lastPercent : Float;
	private var m_canSeePlayer : Bool;
	private var m_squadInCombat : Bool;
	private var m_archetypeTextureName : CName;
	private var m_isTagged : Bool;
	private var m_canHaveObjectMarker : Bool;
	private var m_canShowObjectMarker : Bool;
	private var m_objectMarkerVisible : Bool;
	private var m_nameplateVisible : Bool;
	private var m_detectionVisible : Bool;
	private var m_inNameplateMode : Bool;
	private var m_objectMarkerFirstShowing : Bool;
	default m_objectMarkerFirstShowing = true;
	private var m_statusEffectShowing : Bool;
	private var m_statusEffectCurrentPriority : Float;
	private var m_isInCombatWithPlayer : Bool;
	private var m_animationIsPlaying : Bool;
	private var m_animationProxy : inkAnimProxy;
	private var m_nameplateAnimationProxy : inkAnimProxy;
	private var m_nameplateAnimationIsPlaying : Bool;
	private var m_reprimandAnimationProxy : inkAnimProxy;
	private var m_reprimandAnimationIsPlaying : Bool;
	private var m_reprimandAnimationState : gameReprimandMappinAnimationState;
	default m_reprimandAnimationState = gameReprimandMappinAnimationState.None;
	private var m_monowireHackAnimationProxy : inkAnimProxy;
	private var m_currentAnimState : gameEnemyStealthAwarenessState;
	default m_currentAnimState = gameEnemyStealthAwarenessState.Relaxed;
	private const var c_emptyThreshold : Float;
	default c_emptyThreshold = 0.0f;
	private const var c_awareToCombatThreshold : Float;
	default c_awareToCombatThreshold = 0.0f;
	private const var c_combatToAwareThreshold : Float;
	default c_combatToAwareThreshold = 99.9f;
	private const var c_deviceCombatToAwareThreshold : Float;
	default c_deviceCombatToAwareThreshold = 35.0f;
	private const var c_objectMarkerMaxDistance : Float;
	default c_objectMarkerMaxDistance = 50.0f;
	private const var c_objectMarkerMaxCameraDistance : Float;
	default c_objectMarkerMaxCameraDistance = 30.0f;
	private var m_statusEffectStartTime : Float;
	private var m_statusEffectTextureName : String;

	protected event OnInitialize()
	{
		m_root = GetRootWidget();
		m_canvas = GetWidget( 'Canvas' );
		inkTextRef.SetOpacity( m_markExclamation, 0.0 );
		m_canShowObjectMarker = false;
	}

	protected event OnUninitialize()
	{
		m_puppetStateBlackboard = NULL;
	}

	protected event OnIntro()
	{
		m_mappin = ( ( StealthMappin )( GetMappin() ) );
		OnUpdate();
	}

	public function SetGameInstance( gameInstance : GameInstance )
	{
		var blackboard : IBlackboard;
		var npcType : gamedataNPCType;
		m_mappin = ( ( StealthMappin )( GetMappin() ) );
		m_mappin.EnableVisibilityThroughWalls( true );
		OverrideClamp( true );
		if( GameInstance.IsValid( gameInstance ) )
		{
			m_ownerObject = ( ( GameObject )( GameInstance.FindEntityByID( gameInstance, m_mappin.GetEntityID() ) ) );
			m_ownerNPC = ( ( NPCPuppet )( m_ownerObject ) );
			if( m_ownerNPC )
			{
				npcType = m_ownerNPC.GetNPCType();
				if( npcType == gamedataNPCType.Drone )
				{
					m_isDrone = true;
				}
				else if( npcType == gamedataNPCType.Mech )
				{
					m_isMech = true;
				}
			}
			else
			{
				m_ownerDevice = ( ( Device )( m_ownerObject ) );
			}
			m_isDevice = m_ownerObject.IsDevice();
			m_isCamera = ( m_isDevice && m_ownerObject.IsSensor() ) && !( m_ownerObject.IsTurret() );
			m_isTurret = m_ownerObject.IsTurret();
			m_mappin.UpdateObjectMarkerThreshold( ( ( m_isCamera ) ? ( c_objectMarkerMaxCameraDistance ) : ( c_objectMarkerMaxDistance ) ) );
			m_mappin.UpdateCombatToAwareThreshold( ( ( m_isDevice ) ? ( c_deviceCombatToAwareThreshold ) : ( c_combatToAwareThreshold ) ) );
			if( m_ownerNPC )
			{
				blackboard = GameInstance.GetBlackboardSystem( m_ownerNPC.GetGame() ).Get( GetAllBlackboardDefs().UI_Stealth );
				m_puppetStateBlackboard = m_ownerNPC.GetPuppetStateBlackboard();
				m_highLevelState = ( ( gamedataNPCHighLevelState )( m_puppetStateBlackboard.GetInt( GetAllBlackboardDefs().PuppetState.HighLevel ) ) );
				m_reaction = ( ( gamedataOutput )( m_puppetStateBlackboard.GetInt( GetAllBlackboardDefs().PuppetState.ReactionBehavior ) ) );
				m_numberOfCombatants = ( ( Int32 )( blackboard.GetUint( GetAllBlackboardDefs().UI_Stealth.numberOfCombatants ) ) );
				m_hideUIElements = m_ownerNPC.GetBoolFromCharacterTweak( "hideUIElements", false );
				OnUpdate();
			}
		}
	}

	protected event OnUpdate()
	{
		var percent : Float;
		var distance : Float;
		var shouldShow : Bool;
		var attitude : EAIAttitude;
		var ownerPuppet : ScriptedPuppet;
		var playerPuppet : PlayerPuppet;
		var npcRarity : gamedataNPCRarity;
		var showEliteIndicator : Bool;
		attitude = m_mappin.GetAttitudeTowardsPlayer();
		if( ( !( m_isHostile ) && attitude == EAIAttitude.AIA_Hostile ) && !( m_objectMarkerVisible ) )
		{
			RequestForcedUpdate();
		}
		m_isFriendly = attitude == EAIAttitude.AIA_Friendly;
		m_isFriendlyFromHack = m_mappin.IsFriendlyFromHack();
		m_isHostile = attitude == EAIAttitude.AIA_Hostile;
		m_isAggressive = m_mappin.IsAggressive();
		m_isHiddenByQuest = m_mappin.IsHiddenByQuestIn3D();
		m_isNCPD = ScriptedPuppet.IsCharacterPolice( m_ownerObject );
		if( ShouldDisableMappin() )
		{
			inkWidgetRef.SetVisible( m_mainArt, false );
			inkImageRef.SetVisible( m_arrow, false );
			if( m_isFriendlyFromHack && ScriptedPuppet.IsActive( m_ownerObject ) )
			{
				UpdateObjectMarkerAndTagging();
				m_root.SetState( 'Friendly' );
				m_mappin.UpdateCombatantState( false );
				m_mappin.SetVisibleIn3D( m_objectMarkerVisible );
				SetRootVisible( m_objectMarkerVisible );
			}
			else
			{
				UpdateObjectMarkerVisibility( false, false );
				m_mappin.SetVisibleIn3D( false );
				SetRootVisible( false );
			}
			return true;
		}
		ownerPuppet = ( ( ScriptedPuppet )( m_ownerObject ) );
		playerPuppet = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( m_ownerObject.GetGame() ).GetLocalPlayerControlledGameObject() ) );
		percent = m_mappin.GetDetectionProgress();
		m_canSeePlayer = m_mappin.CanSeePlayer();
		m_squadInCombat = m_mappin.IsSquadInCombat();
		m_numberOfCombatants = ( ( Int32 )( m_mappin.GetNumberOfCombatants() ) );
		m_isInCombatWithPlayer = ownerPuppet && NPCPuppet.IsInCombatWithTarget( ownerPuppet, playerPuppet );
		if( ( m_mappin.HideUIDetection() || !( m_mappin.GetNameplateEnabled() ) ) || ( ( !( m_canSeePlayer ) && m_currentAnimState != gameEnemyStealthAwarenessState.Combat ) && ( m_numberOfCombatants >= 1 ) ) )
		{
			m_detectionVisible = false;
		}
		else
		{
			m_detectionVisible = ( m_currentAnimState != gameEnemyStealthAwarenessState.Relaxed && m_currentAnimState != gameEnemyStealthAwarenessState.Combat ) || m_animationIsPlaying;
		}
		OverrideClamp( m_detectionVisible );
		UpdateStatusEffectIcon();
		UpdateCanvasOpacity();
		npcRarity = ownerPuppet.GetNPCRarity();
		showEliteIndicator = ( !( m_statusEffectShowing ) && !( m_detectionVisible ) ) && ( npcRarity == gamedataNPCRarity.Elite || npcRarity == gamedataNPCRarity.MaxTac );
		inkImageRef.SetVisible( m_levelIcon, showEliteIndicator );
		shouldShow = ( ( ( m_detectionVisible || m_statusEffectShowing ) || m_inNameplateMode ) || m_nameplateAnimationIsPlaying ) && !( m_isHiddenByQuest );
		inkWidgetRef.SetVisible( m_mainArt, shouldShow );
		if( m_ownerNPC )
		{
			UpdateNPCDetection( percent );
		}
		else
		{
			UpdateDeviceDetection( percent );
		}
		UpdateObjectMarkerAndTagging();
		UpdateDetectionMeter( percent );
		inkImageRef.SetVisible( m_arrow, isCurrentlyClamped && shouldShow );
		if( ShouldShowDistance() )
		{
			distance = GetDistanceToPlayer();
			inkTextRef.SetText( m_distance, UnitsLocalizationHelper.LocalizeDistance( distance ) );
			inkTextRef.SetVisible( m_distance, distance >= 10.0 );
		}
		m_mappin.SetStealthAwarenessState( m_currentAnimState );
		m_mappin.SetVisibleIn3D( shouldShow || m_objectMarkerVisible );
		SetRootVisible( shouldShow || m_objectMarkerVisible );
		SetIgnorePriority( !( m_detectionVisible ) );
		m_lastPercent = percent;
	}

	private function ResetMainArtWidgetProperties()
	{
		inkWidgetRef.SetOpacity( m_mainArt, 1.0 );
		inkWidgetRef.SetScale( m_mainArt, Vector2( 1.0, 1.0 ) );
	}

	private function NotifyDroneAboutStartingDetection()
	{
		var startingToDetectEvent : NPCStartingDetectionEvent;
		startingToDetectEvent = new NPCStartingDetectionEvent;
		m_ownerNPC.QueueEvent( startingToDetectEvent );
	}

	private function NotifyDroneAboutStoppingDetection()
	{
		var startingToDetectEvent : NPCStoppingDetectionEvent;
		startingToDetectEvent = new NPCStoppingDetectionEvent;
		m_ownerNPC.QueueEvent( startingToDetectEvent );
	}

	private function ShouldDisableMappin() : Bool
	{
		if( !( m_ownerObject ) )
		{
			return true;
		}
		if( m_isFriendly )
		{
			return true;
		}
		if( !( m_ownerNPC ) )
		{
			return false;
		}
		if( ( ( ScriptedPuppet.IsDefeated( m_ownerNPC ) || m_ownerNPC.IsAboutToBeDefeated() ) || m_ownerNPC.IsDead() ) || m_ownerNPC.IsAboutToDie() )
		{
			return true;
		}
		if( m_ownerNPC.IsMaxTac() && m_ownerNPC.IsPrevention() )
		{
			if( !( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.MaxtacFightStartHelperStatus" ) ) )
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		return false;
	}

	private function UpdateStatusEffectIcon()
	{
		var statusEffectTime : Float;
		var textureName : String;
		var fillPrecentage : Float;
		var statusEffectChanged : Bool;
		m_statusEffectCurrentPriority = m_mappin.GetStatusEffectCurrentPriority();
		if( ( m_statusEffectCurrentPriority == 0.0 ) || !( m_mappin.GetNameplateEnabled() ) )
		{
			ShowStatusEffect( false );
			return;
		}
		statusEffectTime = m_mappin.GetStatusEffectTimeRemaining();
		if( statusEffectTime == 0.0 )
		{
			inkCompoundRef.SetState( m_background, 'Default' );
			inkWidgetRef.SetState( m_statusEffectBackground, 'Default' );
			ShowStatusEffect( false );
			return;
		}
		textureName = m_mappin.GetStatusEffectIconPath();
		if( m_statusEffectTextureName != textureName )
		{
			inkCompoundRef.SetState( m_background, 'Default' );
			inkWidgetRef.SetState( m_statusEffectBackground, 'Default' );
			m_statusEffectTextureName = textureName;
			statusEffectChanged = true;
		}
		if( textureName == "" )
		{
			ShowStatusEffect( false );
			return;
		}
		if( ( !( m_statusEffectShowing ) || statusEffectChanged ) || ( statusEffectTime > m_statusEffectStartTime ) )
		{
			m_statusEffectStartTime = statusEffectTime;
		}
		inkImageRef.SetTexturePart( m_statusEffectIcon, StringToName( textureName ) );
		if( statusEffectTime > 0.0 )
		{
			fillPrecentage = statusEffectTime / m_statusEffectStartTime;
			inkWidgetRef.SetOpacity( m_statusEffectFill, 0.30000001 );
			inkWidgetRef.SetScale( m_statusEffectFill, Vector2( 1.0, fillPrecentage ) );
		}
		ShowStatusEffect( true );
	}

	private function ShowStatusEffect( show : Bool )
	{
		var hasContagiousQuickhackStatusEffect : Bool;
		hasContagiousQuickhackStatusEffect = StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.MonoWireQuickhackContagiousHittableTarget" );
		if( show )
		{
			if( hasContagiousQuickhackStatusEffect )
			{
				inkCompoundRef.SetState( m_background, 'Relic' );
				inkWidgetRef.SetState( m_statusEffectBackground, 'Relic' );
				TriggerStatusEffectAnimation( true );
				m_detectionVisible = false;
			}
			if( ( m_statusEffectShowing && m_detectionVisible ) && ( m_statusEffectCurrentPriority < 0.0 ) )
			{
				inkWidgetRef.SetVisible( m_statusEffectMain, false );
				m_statusEffectShowing = false;
				inkWidgetRef.SetOpacity( m_fill, 0.5 );
			}
			else if( !( m_statusEffectShowing ) && ( ( m_statusEffectCurrentPriority > 0.0 ) || !( m_detectionVisible ) ) )
			{
				inkWidgetRef.SetVisible( m_statusEffectMain, true );
				m_statusEffectShowing = true;
				inkWidgetRef.SetOpacity( m_statusEffectFill, 0.30000001 );
			}
		}
		else if( !( show ) && m_statusEffectShowing )
		{
			inkWidgetRef.SetVisible( m_statusEffectMain, false );
			m_statusEffectShowing = false;
			inkWidgetRef.SetOpacity( m_fill, 0.5 );
		}
		if( !( hasContagiousQuickhackStatusEffect ) )
		{
			TriggerStatusEffectAnimation( false );
		}
	}

	private function TriggerStatusEffectAnimation( show : Bool )
	{
		if( show )
		{
			inkCompoundRef.SetVisible( m_contagiousQuickhackArrows, true );
			if( !( m_monowireHackAnimationProxy ) || !( m_monowireHackAnimationProxy.IsPlaying() ) )
			{
				m_monowireHackAnimationProxy = PlayLibraryAnimation( 'monowire', GetAnimOptionsInfiniteLoop( inkanimLoopType.Cycle ) );
			}
		}
		else
		{
			inkCompoundRef.SetState( m_background, 'Default' );
			inkWidgetRef.SetState( m_statusEffectBackground, 'Default' );
			m_monowireHackAnimationProxy.GotoEndAndStop();
			inkCompoundRef.SetVisible( m_contagiousQuickhackArrows, false );
		}
	}

	private function UpdateObjectMarkerAndTagging()
	{
		var distance : Float;
		var canShowObjectMarker, canHaveObjectMarker, playerAwareOfObject, objectMarkerVisible, isTagged, objectMarkersEnabled : Bool;
		distance = GetDistanceToPlayer();
		objectMarkersEnabled = m_mappin.GetObjectMarkersEnabled();
		canHaveObjectMarker = ( ( ( ( m_isHostile || m_isAggressive ) || m_isFriendlyFromHack ) || m_isDevice ) && !( m_isHiddenByQuest ) ) && !( m_hideUIElements );
		playerAwareOfObject = ( ( m_mappin.HasBeenSeen() || m_currentAnimState != gameEnemyStealthAwarenessState.Relaxed ) || m_nameplateVisible ) || !( m_objectMarkerFirstShowing );
		if( m_isCamera )
		{
			canShowObjectMarker = ( ( canHaveObjectMarker && playerAwareOfObject ) && ( distance <= c_objectMarkerMaxCameraDistance ) ) && ( m_numberOfCombatants == 0 );
		}
		else
		{
			canShowObjectMarker = ( canHaveObjectMarker && playerAwareOfObject ) && ( distance <= c_objectMarkerMaxDistance );
		}
		if( ( canShowObjectMarker && m_isDevice ) && m_ownerDevice )
		{
			canShowObjectMarker = m_ownerDevice.GetDevicePS().IsON();
		}
		isTagged = m_mappin.IsTagged();
		if( m_canShowObjectMarker != canShowObjectMarker || m_isTagged != isTagged )
		{
			m_canShowObjectMarker = canShowObjectMarker;
			m_isTagged = isTagged;
			m_mappin.EnableVisibilityThroughWalls( !( m_canShowObjectMarker ) || m_isTagged );
		}
		objectMarkerVisible = !( isCurrentlyClamped ) && ( ( canHaveObjectMarker && isTagged ) || ( canShowObjectMarker && ( ( m_mappin.IsVisible() && objectMarkersEnabled ) || m_nameplateVisible ) ) );
		UpdateObjectMarkerVisibility( canHaveObjectMarker, objectMarkerVisible );
		if( ( m_objectMarkerFirstShowing && m_objectMarkerVisible ) && objectMarkersEnabled )
		{
			PlayLibraryAnimation( 'intro' );
			m_objectMarkerFirstShowing = false;
		}
		inkWidgetRef.SetVisible( m_taggedContainer, isTagged );
	}

	private function UpdateObjectMarkerVisibility( canHaveObjectMarker : Bool, objectMarkerVisible : Bool )
	{
		var objectMarkerVisibilityUpdateNeeded : Bool;
		objectMarkerVisibilityUpdateNeeded = false;
		if( m_canHaveObjectMarker != canHaveObjectMarker )
		{
			m_canHaveObjectMarker = canHaveObjectMarker;
			objectMarkerVisibilityUpdateNeeded = true;
		}
		if( m_objectMarkerVisible != objectMarkerVisible )
		{
			m_objectMarkerVisible = objectMarkerVisible;
			inkWidgetRef.SetVisible( m_objectMarker, m_objectMarkerVisible );
			objectMarkerVisibilityUpdateNeeded = true;
		}
		if( objectMarkerVisibilityUpdateNeeded )
		{
			m_mappin.UpdateObjectMarkerVisibility( m_canHaveObjectMarker, m_objectMarkerVisible );
		}
	}

	private function UpdateDetectionMeter( percent : Float )
	{
		if( inkWidgetRef.IsVisible( m_statusEffectMain ) || !( m_mappin.GetNameplateEnabled() ) )
		{
			return;
		}
		if( m_inNameplateMode || m_nameplateAnimationIsPlaying )
		{
			inkWidgetRef.SetScale( m_fill, Vector2( 1.0, 0.0 ) );
			inkWidgetRef.SetScale( m_eyeFillBlack, Vector2( 1.0, 0.0 ) );
			inkWidgetRef.SetScale( m_eyeFillWhite, Vector2( 1.0, 1.0 ) );
		}
		else if( m_currentAnimState == gameEnemyStealthAwarenessState.Combat || m_currentAnimState == gameEnemyStealthAwarenessState.Alerted )
		{
			inkWidgetRef.SetScale( m_fill, Vector2( 1.0, 1.0 ) );
			inkWidgetRef.SetScale( m_eyeFillBlack, Vector2( 1.0, 1.0 ) );
			inkWidgetRef.SetScale( m_eyeFillWhite, Vector2( 1.0, 0.0 ) );
		}
		else
		{
			inkWidgetRef.SetScale( m_fill, Vector2( 1.0, percent * 0.0095 ) );
			inkWidgetRef.SetScale( m_eyeFillBlack, Vector2( 1.0, percent * 0.0095 ) );
			inkWidgetRef.SetScale( m_eyeFillWhite, Vector2( 1.0, 1.0 - ( percent * 0.0095 ) ) );
			if( ( m_detectionVisible && !( m_isHiddenByQuest ) ) && !( m_animationIsPlaying ) )
			{
				ResetMainArtWidgetProperties();
			}
		}
	}

	private function UpdateDeviceDetection( percent : Float )
	{
		if( !( m_animationIsPlaying ) )
		{
			if( m_isCamera && ( m_numberOfCombatants >= 1 ) )
			{
				if( m_currentAnimState != gameEnemyStealthAwarenessState.Relaxed )
				{
					if( m_currentAnimState == gameEnemyStealthAwarenessState.Aware )
					{
						if( m_nameplateVisible )
						{
							PlayAnim( 'aware_to_nameplate', 'OnBasicAnimFinished' );
							m_inNameplateMode = true;
						}
						else
						{
							PlayAnim( 'aware_to_relaxed', 'OnBasicAnimFinished' );
						}
					}
					m_currentAnimState = gameEnemyStealthAwarenessState.Relaxed;
				}
			}
			else if( ( percent == 100.0 ) && m_currentAnimState != gameEnemyStealthAwarenessState.Combat )
			{
				if( m_inNameplateMode )
				{
					PlayAnim( 'nameplate_to_alert', 'OnPotentialCombatAnimFinished' );
					m_inNameplateMode = false;
				}
				else if( m_currentAnimState == gameEnemyStealthAwarenessState.Relaxed )
				{
					PlayAnim( 'relaxed_to_alert', 'OnPotentialCombatAnimFinished' );
				}
				else
				{
					PlayAnim( 'aware_to_alert', 'OnPotentialCombatAnimFinished' );
				}
				m_currentAnimState = gameEnemyStealthAwarenessState.Combat;
				if( m_isTurret )
				{
					m_mappin.UpdateCombatantState( true );
				}
			}
			else if( ( percent < c_deviceCombatToAwareThreshold ) && m_currentAnimState == gameEnemyStealthAwarenessState.Combat )
			{
				PlayAnim( 'alert_to_aware', 'OnBasicAnimFinished' );
				m_currentAnimState = gameEnemyStealthAwarenessState.Aware;
				if( m_isTurret )
				{
					m_mappin.UpdateCombatantState( false );
				}
			}
			else if( ( percent > c_emptyThreshold ) && m_currentAnimState == gameEnemyStealthAwarenessState.Relaxed )
			{
				if( m_inNameplateMode )
				{
					PlayAnim( 'nameplate_to_aware', 'OnBasicAnimFinished' );
					m_inNameplateMode = false;
				}
				else
				{
					PlayAnim( 'relaxed_to_aware', 'OnBasicAnimFinished' );
				}
				m_currentAnimState = gameEnemyStealthAwarenessState.Aware;
			}
			else if( ( percent <= c_emptyThreshold ) && m_currentAnimState != gameEnemyStealthAwarenessState.Relaxed )
			{
				if( m_nameplateVisible )
				{
					PlayAnim( 'aware_to_nameplate', 'OnBasicAnimFinished' );
					m_inNameplateMode = true;
				}
				else
				{
					PlayAnim( 'aware_to_relaxed', 'OnBasicAnimFinished' );
				}
				m_currentAnimState = gameEnemyStealthAwarenessState.Relaxed;
			}
		}
		if( m_currentAnimState == gameEnemyStealthAwarenessState.Combat )
		{
			m_root.SetState( 'Combat' );
		}
		else
		{
			if( ( percent > 0.0 ) || m_animationIsPlaying )
			{
				m_root.SetState( 'Increasing' );
			}
			else
			{
				m_root.SetState( '' );
			}
		}
		if( m_isTurret )
		{
			UpdateNameplateColor( m_currentAnimState == gameEnemyStealthAwarenessState.Combat );
		}
	}

	private function UpdateNPCDetection( percent : Float )
	{
		if( !( m_animationIsPlaying ) && !( m_nameplateAnimationIsPlaying ) )
		{
			m_highLevelState = m_mappin.GetHighLevelState();
			if( ( ( m_highLevelState == gamedataNPCHighLevelState.Combat && m_currentAnimState != gameEnemyStealthAwarenessState.Combat ) && m_isInCombatWithPlayer ) && ( percent >= c_awareToCombatThreshold ) )
			{
				if( m_inNameplateMode )
				{
					PlayAnim( 'nameplate_to_alert', 'OnPotentialCombatAnimFinished' );
					m_inNameplateMode = false;
				}
				else if( m_currentAnimState == gameEnemyStealthAwarenessState.Relaxed )
				{
					if( !( m_squadInCombat ) || ( isCurrentlyClamped && IsObjectOffScreen() ) )
					{
						PlayAnim( 'relaxed_to_alert', 'OnPotentialCombatAnimFinished' );
					}
				}
				else if( m_currentAnimState == gameEnemyStealthAwarenessState.Aware )
				{
					PlayAnim( 'aware_to_alert', 'OnPotentialCombatAnimFinished' );
				}
				else
				{
					PlayAnim( 'enter_combat', 'OnBasicAnimFinished' );
				}
				m_currentAnimState = gameEnemyStealthAwarenessState.Combat;
				m_mappin.UpdateCombatantState( true );
			}
			else if( ( percent > c_emptyThreshold ) && m_currentAnimState == gameEnemyStealthAwarenessState.Relaxed )
			{
				if( !( m_squadInCombat ) )
				{
					if( m_inNameplateMode )
					{
						PlayAnim( 'nameplate_to_aware', 'OnBasicAnimFinished' );
						m_inNameplateMode = false;
					}
					else
					{
						PlayAnim( 'relaxed_to_aware', 'OnBasicAnimFinished' );
					}
					m_currentAnimState = gameEnemyStealthAwarenessState.Aware;
				}
				if( m_isDrone )
				{
					NotifyDroneAboutStartingDetection();
				}
			}
			else if( ( percent >= 100.0 ) && m_currentAnimState == gameEnemyStealthAwarenessState.Aware )
			{
				PlayAnim( 'aware_to_alert', 'OnBasicAnimFinished' );
				m_currentAnimState = gameEnemyStealthAwarenessState.Alerted;
			}
			else if( ( percent < c_combatToAwareThreshold ) && m_currentAnimState == gameEnemyStealthAwarenessState.Combat )
			{
				if( !( m_squadInCombat ) && ( m_numberOfCombatants <= 0 ) )
				{
					PlayAnim( 'alert_to_aware', 'OnBasicAnimFinished' );
					m_currentAnimState = gameEnemyStealthAwarenessState.Aware;
				}
				if( m_highLevelState == gamedataNPCHighLevelState.Relaxed || m_highLevelState == gamedataNPCHighLevelState.Alerted )
				{
					m_mappin.UpdateCombatantState( false );
				}
			}
			else if( ( ( percent > c_emptyThreshold ) && ( percent < 100.0 ) ) && m_currentAnimState == gameEnemyStealthAwarenessState.Alerted )
			{
				if( m_squadInCombat )
				{
					PlayAnim( 'alert_to_aware_nointro', 'OnPotentialRelaxedAnimFinished' );
					m_currentAnimState = gameEnemyStealthAwarenessState.Relaxed;
				}
				else
				{
					PlayAnim( 'alert_to_aware_nointro', 'OnBasicAnimFinished' );
					m_currentAnimState = gameEnemyStealthAwarenessState.Aware;
				}
				if( m_isDrone )
				{
					NotifyDroneAboutStoppingDetection();
				}
			}
			else if( ( percent <= c_emptyThreshold ) && m_currentAnimState != gameEnemyStealthAwarenessState.Relaxed )
			{
				if( m_currentAnimState == gameEnemyStealthAwarenessState.Alerted )
				{
					PlayAnim( 'alert_to_aware_nointro', 'OnPotentialRelaxedAnimFinished' );
				}
				else
				{
					if( m_nameplateVisible )
					{
						PlayAnim( 'aware_to_nameplate', 'OnBasicAnimFinished' );
						m_inNameplateMode = true;
					}
					else
					{
						PlayAnim( 'aware_to_relaxed', 'OnBasicAnimFinished' );
					}
				}
				m_currentAnimState = gameEnemyStealthAwarenessState.Relaxed;
				if( m_isDrone )
				{
					NotifyDroneAboutStoppingDetection();
				}
			}
			else if( ( m_currentAnimState == gameEnemyStealthAwarenessState.Aware && m_squadInCombat ) && ( m_numberOfCombatants >= 1 ) )
			{
				if( m_nameplateVisible )
				{
					PlayAnim( 'aware_to_nameplate', 'OnBasicAnimFinished' );
					m_inNameplateMode = true;
				}
				else
				{
					PlayAnim( 'aware_to_relaxed', 'OnBasicAnimFinished' );
				}
				m_currentAnimState = gameEnemyStealthAwarenessState.Relaxed;
			}
		}
		if( m_currentAnimState == gameEnemyStealthAwarenessState.Combat )
		{
			UpdateReprimandAnimation( 0.0, true );
			UpdateNameplateColor( true );
			m_root.SetState( 'Combat' );
		}
		else
		{
			if( ( percent > 0.0 ) || m_animationIsPlaying )
			{
				if( m_mappin.WillReprimand() )
				{
					UpdateReprimandAnimation( percent, false );
					UpdateNameplateColor( false );
					( ( m_isNCPD ) ? ( m_root.SetState( 'NCPD' ) ) : ( m_root.SetState( 'Increasing_Reprimand' ) ) );
				}
				else
				{
					UpdateReprimandAnimation( 0.0, true );
					UpdateNameplateColor( true );
					( ( m_isNCPD ) ? ( m_root.SetState( 'NCPD' ) ) : ( m_root.SetState( 'Increasing' ) ) );
				}
			}
			else
			{
				UpdateReprimandAnimation( 0.0, false );
				UpdateNameplateColor( false );
				( ( m_isNCPD ) ? ( m_root.SetState( 'NCPD' ) ) : ( m_root.SetState( '' ) ) );
			}
		}
		m_lastState = m_highLevelState;
		m_lastReaction = m_reaction;
	}

	private function UpdateCanvasOpacity()
	{
		if( ( ( m_inNameplateMode || m_currentAnimState == gameEnemyStealthAwarenessState.Combat ) || m_canSeePlayer ) || m_statusEffectShowing )
		{
			m_canvas.SetOpacity( 1.0 );
		}
		else
		{
			m_canvas.SetOpacity( 0.30000001 );
		}
	}

	private function UpdateReprimandAnimation( percent : Float, forceStop : Bool )
	{
		var animState : gameReprimandMappinAnimationState;
		var animOptions : inkAnimOptions;
		var playing : Bool;
		playing = ( percent >= 100.0 ) && !( forceStop );
		if( playing )
		{
			animState = m_mappin.GetReprimandAnimationState();
		}
		else
		{
			animState = m_reprimandAnimationState;
		}
		if( m_reprimandAnimationIsPlaying != playing || m_reprimandAnimationState != animState )
		{
			if( m_reprimandAnimationProxy && m_reprimandAnimationProxy.IsPlaying() )
			{
				m_reprimandAnimationProxy.Stop();
			}
			if( playing )
			{
				animOptions.loopType = inkanimLoopType.Cycle;
				animOptions.loopInfinite = true;
				if( animState == gameReprimandMappinAnimationState.None )
				{
					m_reprimandAnimationProxy = ( ( m_isNCPD ) ? ( PlayLibraryAnimation( 'reprimand_blinking_ncpd' ) ) : ( PlayLibraryAnimation( 'reprimand_blinking' ) ) );
				}
				else if( animState == gameReprimandMappinAnimationState.Normal )
				{
					m_reprimandAnimationProxy = ( ( m_isNCPD ) ? ( PlayLibraryAnimation( 'reprimand_blinking_ncpd' ) ) : ( PlayLibraryAnimation( 'reprimand_blinking', animOptions ) ) );
				}
				else
				{
					m_reprimandAnimationProxy = ( ( m_isNCPD ) ? ( PlayLibraryAnimation( 'reprimand_blinking2_ncpd' ) ) : ( PlayLibraryAnimation( 'reprimand_blinking2', animOptions ) ) );
				}
			}
			else if( ( !( forceStop ) && m_reprimandAnimationIsPlaying ) && m_reprimandAnimationState != gameReprimandMappinAnimationState.None )
			{
				m_reprimandAnimationProxy = ( ( m_isNCPD ) ? ( PlayLibraryAnimation( 'reprimand_blinking_ncpd' ) ) : ( PlayLibraryAnimation( 'reprimand_blinking' ) ) );
			}
			m_reprimandAnimationIsPlaying = playing;
			m_reprimandAnimationState = animState;
		}
	}

	private function UpdateNameplateColor( isHostile : Bool )
	{
		if( m_nameplateVisible )
		{
			if( m_nameplateController )
			{
				m_nameplateController.UpdateHealthbarColor( isHostile );
			}
		}
	}

	private function IsObjectOffScreen() : Bool
	{
		var player : weak< PlayerPuppet >;
		var enemyDirection : Vector4;
		var dot : Float;
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( m_ownerObject.GetGame() ).GetLocalPlayerControlledGameObject() ) );
		if( player )
		{
			enemyDirection = m_ownerObject.GetWorldPosition() - player.GetWorldPosition();
			dot = Vector4.Dot( Vector4.Normalize( enemyDirection ), Matrix.GetDirectionVector( player.GetFPPCameraComponent().GetLocalToWorld() ) );
			if( dot <= 0.69999999 )
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		return true;
	}

	private function PlayAnim( animName : CName, callBack : CName )
	{
		if( m_animationIsPlaying )
		{
			m_animationProxy.GotoEndAndStop();
		}
		m_animationProxy = PlayLibraryAnimation( animName );
		m_animationProxy.RegisterToCallback( inkanimEventType.OnFinish, this, callBack );
		m_animationIsPlaying = true;
	}

	private function PlayNameplateAnim( animName : CName, callBack : CName )
	{
		if( m_nameplateAnimationIsPlaying )
		{
			m_nameplateAnimationProxy.Stop();
		}
		m_nameplateAnimationProxy = PlayLibraryAnimation( animName );
		m_nameplateAnimationProxy.RegisterToCallback( inkanimEventType.OnFinish, this, callBack );
		m_nameplateAnimationIsPlaying = true;
	}

	protected event OnBasicAnimFinished( proxy : inkAnimProxy )
	{
		m_animationProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnBasicAnimFinished' );
		m_animationIsPlaying = false;
		OnUpdate();
	}

	protected event OnPotentialRelaxedAnimFinished( proxy : inkAnimProxy )
	{
		m_animationProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnPotentialRelaxedAnimFinished' );
		m_animationIsPlaying = false;
		if( m_currentAnimState == gameEnemyStealthAwarenessState.Relaxed )
		{
			if( m_nameplateVisible )
			{
				PlayAnim( 'aware_to_nameplate', 'OnBasicAnimFinished' );
				m_inNameplateMode = true;
			}
			else
			{
				PlayAnim( 'aware_to_relaxed', 'OnBasicAnimFinished' );
			}
		}
		else
		{
			OnUpdate();
		}
	}

	protected event OnPotentialCombatAnimFinished( proxy : inkAnimProxy )
	{
		m_animationProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnPotentialCombatAnimFinished' );
		m_animationIsPlaying = false;
		if( m_currentAnimState == gameEnemyStealthAwarenessState.Combat )
		{
			PlayAnim( 'enter_combat', 'OnBasicAnimFinished' );
		}
		else
		{
			OnUpdate();
		}
	}

	protected event OnNameplateAnimFinished( proxy : inkAnimProxy )
	{
		m_nameplateAnimationProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnNameplateAnimFinished' );
		m_nameplateAnimationIsPlaying = false;
		OnUpdate();
	}

	public override function GetWidgetForNameplateSlot() : weak< inkWidget >
	{
		return GetRootWidget();
	}

	protected event OnNameplate( isNameplateVisible : Bool, nameplateController : weak< NpcNameplateGameController > )
	{
		if( ( m_isAggressive || m_isHostile ) || m_isTurret )
		{
			m_nameplateVisible = isNameplateVisible;
			if( isNameplateVisible )
			{
				OverrideScaleByDistance( false );
				SetProjectToScreenSpace( false );
				m_nameplateController = nameplateController;
			}
			else
			{
				OverrideScaleByDistance( true );
				SetProjectToScreenSpace( true );
			}
			OnUpdate();
		}
	}

}

class NPCStartingDetectionEvent extends Event
{
}

class NPCStoppingDetectionEvent extends Event
{
}

