abstract class ObjectScanningDescription extends IScriptable
{

	public const virtual function GetGameplayDesription() : TweakDBID
	{
		var id : TweakDBID;
		return id;
	}

	public const virtual function GetCustomDesriptions() : array< TweakDBID >
	{
		var ids : array< TweakDBID >;
		return ids;
	}

	public const function IsValid() : Bool
	{
		var customDescArr : array< TweakDBID >;
		var gmplDesc : TweakDBID;
		customDescArr = GetCustomDesriptions();
		gmplDesc = GetGameplayDesription();
		return TDBID.IsValid( gmplDesc ) || ( customDescArr.Size() > 0 );
	}

}

class DeviceScanningDescription extends ObjectScanningDescription
{
	[ customEditor = "TweakDBGroupInheritance;device_scanning_data" ]
	protected instanceeditable persistent var DeviceGameplayDescription : TweakDBID;
	[ customInnerTypeEditor = "TweakDBGroupInheritance;device_scanning_data" ]
	protected const instanceeditable persistent var DeviceCustomDescriptions : array< TweakDBID >;
	[ customEditor = "TweakDBGroupInheritance;device_gameplay_role" ]
	var DeviceGameplayRole : TweakDBID;
	[ customInnerTypeEditor = "TweakDBGroupInheritance;device_role_action_desctiption" ]
	var DeviceRoleActionsDescriptions : array< TweakDBID >;

	public const override function GetGameplayDesription() : TweakDBID
	{
		return DeviceGameplayDescription;
	}

	public const override function GetCustomDesriptions() : array< TweakDBID >
	{
		return DeviceCustomDescriptions;
	}

	public const function GetDeviceRoleActionsDescriptions() : array< TweakDBID >
	{
		return DeviceRoleActionsDescriptions;
	}

}

class NPCScanningDescription extends ObjectScanningDescription
{
	[ customEditor = "TweakDBGroupInheritance;npc_scanning_data" ]
	protected editable persistent var NPCGameplayDescription : TweakDBID;
	[ customInnerTypeEditor = "TweakDBGroupInheritance;npc_scanning_data" ]
	protected const instanceeditable persistent var NPCCustomDescriptions : array< TweakDBID >;

	public const override function GetGameplayDesription() : TweakDBID
	{
		return NPCGameplayDescription;
	}

	public const override function GetCustomDesriptions() : array< TweakDBID >
	{
		return NPCCustomDescriptions;
	}

}

class RefreshClueScanningDataEvent extends Event
{
}

class ToggleClueConclusionEvent extends Event
{
	instanceeditable var toggleConclusion : Bool;
	default toggleConclusion = false;
	instanceeditable var clueID : Int32;
	var updatePS : Bool;
	default updatePS = true;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Toggle Conclusion";
	}

}

class DisableScannerEvent extends Event
{
	instanceeditable var isDisabled : Bool;
	default isDisabled = true;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Disable Scanner";
	}

}

class DisableObjectDescriptionEvent extends Event
{
	instanceeditable var isDisabled : Bool;
	default isDisabled = true;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Disable Object Description";
	}

}

class SetCustomObjectDescriptionEvent extends Event
{
	private instanceeditable inlined var m_objectDescription : ObjectScanningDescription;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Set Custom Object Description";
	}

	public function GetObjectDescription() : ObjectScanningDescription
	{
		return m_objectDescription;
	}

}

class ClearCustomObjectDescriptionEvent extends Event
{

	public constexpr function GetFriendlyDescription() : String
	{
		return "Clear Custom Object Description";
	}

}

class ToggleFocusClueEvent extends Event
{
	instanceeditable var clueIndex : Int32;
	instanceeditable var isEnabled : Bool;
	instanceeditable var investigationState : EFocusClueInvestigationState;
	default investigationState = EFocusClueInvestigationState.NONE;
	var updatePS : Bool;
	default updatePS = true;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Toggle Focus Clue";
	}

}

enum EFocusClueInvestigationState
{
	NONE = 0,
	INSPECTED = 1,
	NOT_INSPECTED = 2,
}

class ClueScannedEvent extends Event
{
	var clueIndex : Int32;
	var requesterID : EntityID;
}

class InitializeFocusCluesEvent extends Event
{
	var requesterID : EntityID;
}

struct ClueRecordData
{
	[ customEditor = "TweakDBGroupInheritance;scanning.FocusClue;FocusClue" ]
	instanceeditable persistent var clueRecord : TweakDBID;
	[ rangeMin = "0.f" ][ rangeMax = "1.f" ]
	instanceeditable var percentage : Float;
	const instanceeditable var facts : array< SFactOperationData >;
	persistent var wasInspected : Bool;
}

enum EConclusionQuestState
{
	Undefined = 0,
	Active = 1,
	Inactive = 2,
	Shown = 3,
}

struct FocusClueDefinition
{
	[ category = "Extended Clues" ]
	const instanceeditable var extendedClueRecords : array< ClueRecordData >;
	[ category = "Final Conclusion" ][ customEditor = "TweakDBGroupInheritance;scanning.FocusClue;FocusClue" ]
	instanceeditable var clueRecord : TweakDBID;
	[ category = "Final Conclusion" ]
	instanceeditable var factToActivate : CName;
	[ category = "Final Conclusion" ]
	const instanceeditable var facts : array< SFactOperationData >;
	instanceeditable var useAutoInspect : Bool;
	instanceeditable var isEnabled : Bool;
	instanceeditable var isProgressing : Bool;
	default isProgressing = true;
	instanceeditable var clueGroupID : CName;
	var wasInspected : Bool;
	var qDbCallbackID : Uint32;
	var conclusionQuestState : EConclusionQuestState;
}

import struct ScanningTooltipElementDef
{
	import var recordID : TweakDBID;
	import var timePct : Float;
}

importonly final class OnScannableBraindanceClueEnabledEvent extends Event
{
}

importonly final class OnScannableBraindanceClueDisabledEvent extends Event
{
}

class CluePSData extends IScriptable
{
	private persistent var m_id : Int32;
	private persistent var m_isEnabled : Bool;
	private persistent var m_wasInspected : Bool;
	private persistent var m_isScanned : Bool;
	private persistent var m_conclusionQuestState : EConclusionQuestState;

	public function SetupData( id : Int32, isEnabled : Bool, wasInspected : Bool, isScanned : Bool, conclusionQuestState : EConclusionQuestState )
	{
		m_id = id;
		m_isEnabled = isEnabled;
		m_wasInspected = wasInspected;
		if( conclusionQuestState != EConclusionQuestState.Undefined )
		{
			m_conclusionQuestState = conclusionQuestState;
		}
	}

	public const function GetID() : Int32
	{
		return m_id;
	}

	public const function IsEnabled() : Bool
	{
		return m_isEnabled;
	}

	public const function IsScanned() : Bool
	{
		return m_isEnabled;
	}

	public const function WasInspected() : Bool
	{
		return m_wasInspected;
	}

	public const function GetConclusionState() : EConclusionQuestState
	{
		return m_conclusionQuestState;
	}

	public function SetConclusionState( state : EConclusionQuestState )
	{
		m_conclusionQuestState = state;
	}

}

import class gameScanningComponentPS extends GameComponentPS
{
	private persistent var m_storedClues : array< CluePSData >;
	private persistent var m_isScanningDisabled : Bool;
	private persistent var m_isDecriptionEnabled : Bool;
	default m_isDecriptionEnabled = true;
	private persistent var m_objectDescriptionOverride : ObjectScanningDescription;

	private const function GetOwnerEntityWeak() : weak< Entity >
	{
		return GameInstance.FindEntityByID( GetGameInstance(), GetMyEntityID() );
	}

	private const function GetMyEntityID() : EntityID
	{
		return PersistentID.ExtractEntityID( GetID() );
	}

	public const function IsScanningDisabled() : Bool
	{
		return m_isScanningDisabled;
	}

	public const function IsDescriptionEnabled() : Bool
	{
		return m_isDecriptionEnabled;
	}

	public const function HasAnyStoredClues() : Bool
	{
		return m_storedClues.Size() > 0;
	}

	public const function GetObjectDecriptionOverride() : ObjectScanningDescription
	{
		return m_objectDescriptionOverride;
	}

	public const function HasStoredClue( id : Int32 ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_storedClues.Size(); i += 1 )
		{
			if( id == m_storedClues[ i ].GetID() )
			{
				return true;
			}
		}
		return false;
	}

	public const function GetStoredClueData( id : Int32, out data : CluePSData ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_storedClues.Size(); i += 1 )
		{
			if( id == m_storedClues[ i ].GetID() )
			{
				data = m_storedClues[ i ];
				return true;
			}
		}
		return false;
	}

	public const function UpdateFocusClueData( id : Int32, clueData : ref< FocusClueDefinition > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_storedClues.Size(); i += 1 )
		{
			if( id == m_storedClues[ i ].GetID() )
			{
				clueData.isEnabled = m_storedClues[ i ].IsEnabled();
				clueData.wasInspected = m_storedClues[ i ].WasInspected();
				clueData.conclusionQuestState = m_storedClues[ i ].GetConclusionState();
				return true;
			}
		}
		return false;
	}

	public function StoreClueData( id : Int32, const clueData : ref< FocusClueDefinition >, isScanned : Bool )
	{
		var data : CluePSData;
		if( !( GetStoredClueData( id, data ) ) )
		{
			data = new CluePSData;
			m_storedClues.PushBack( data );
		}
		data.SetupData( id, clueData.isEnabled, clueData.wasInspected, isScanned, clueData.conclusionQuestState );
	}

	private function OnLinkedClueUpdateEvent( evt : linkedClueUpdateEvent ) : EntityNotificationType
	{
		var data : CluePSData;
		evt.updatePS = false;
		if( !( GetStoredClueData( evt.linkedCluekData.clueIndex, data ) ) )
		{
			data = new CluePSData;
			m_storedClues.PushBack( data );
		}
		data.SetupData( evt.linkedCluekData.clueIndex, evt.linkedCluekData.isEnabled, evt.linkedCluekData.wasInspected, evt.linkedCluekData.isScanned, EConclusionQuestState.Undefined );
		return EntityNotificationType.SendThisEventToEntity;
	}

	private export function OnToggleFocusClue( evt : ToggleFocusClueEvent ) : EntityNotificationType
	{
		var data : CluePSData;
		var isInspected : Bool;
		evt.updatePS = false;
		if( evt.investigationState == EFocusClueInvestigationState.INSPECTED )
		{
			isInspected = true;
		}
		else
		{
			isInspected = false;
		}
		if( !( GetStoredClueData( evt.clueIndex, data ) ) )
		{
			data = new CluePSData;
			m_storedClues.PushBack( data );
		}
		data.SetupData( evt.clueIndex, evt.isEnabled, isInspected, data.IsScanned(), EConclusionQuestState.Undefined );
		RequestFocusClueSystemUpdate( data );
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function OnClueStateChanged( evt : FocusClueStateChangeEvent ) : EntityNotificationType
	{
		var data : CluePSData;
		if( !( GetStoredClueData( evt.clueIndex, data ) ) )
		{
			data = new CluePSData;
			m_storedClues.PushBack( data );
		}
		data.SetupData( evt.clueIndex, evt.isEnabled, data.WasInspected(), data.IsScanned(), EConclusionQuestState.Undefined );
		RequestFocusClueSystemUpdate( data );
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function OnQuestToggleClueConclusion( evt : ToggleClueConclusionEvent ) : EntityNotificationType
	{
		var data : CluePSData;
		evt.updatePS = false;
		if( GetStoredClueData( evt.clueID, data ) )
		{
			if( evt.toggleConclusion )
			{
				data.SetConclusionState( EConclusionQuestState.Active );
			}
			else
			{
				data.SetConclusionState( EConclusionQuestState.Inactive );
			}
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function OnDisableScanner( evt : DisableScannerEvent ) : EntityNotificationType
	{
		m_isScanningDisabled = evt.isDisabled;
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function OnDisableObjectDescription( evt : DisableObjectDescriptionEvent ) : EntityNotificationType
	{
		m_isDecriptionEnabled = !( evt.isDisabled );
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function OnClearCustomObjectDescription( evt : ClearCustomObjectDescriptionEvent ) : EntityNotificationType
	{
		m_objectDescriptionOverride = NULL;
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function OnSetCustomObjectDescription( evt : SetCustomObjectDescriptionEvent ) : EntityNotificationType
	{
		m_objectDescriptionOverride = evt.GetObjectDescription();
		return EntityNotificationType.SendThisEventToEntity;
	}

	public const function GetFocusClueSystem() : FocusCluesSystem
	{
		return ( ( FocusCluesSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'FocusCluesSystem' ) ) );
	}

	private function RequestFocusClueSystemUpdate( clueData : CluePSData )
	{
		var clueRequest : UpdateLinkedClueskRequest;
		var linkedClueData : LinkedFocusClueData;
		var groupID : CName;
		if( GetOwnerEntityWeak() != NULL )
		{
			return;
		}
		if( GetFocusClueSystem().IsGroupped( GetMyEntityID(), groupID ) )
		{
			clueRequest = new UpdateLinkedClueskRequest;
			linkedClueData.ownerID = GetMyEntityID();
			linkedClueData.clueGroupID = groupID;
			linkedClueData.clueIndex = clueData.GetID();
			linkedClueData.isScanned = clueData.IsScanned();
			linkedClueData.wasInspected = clueData.WasInspected();
			linkedClueData.isEnabled = clueData.IsEnabled();
			clueRequest.linkedCluekData = linkedClueData;
			GetFocusClueSystem().QueueRequest( clueRequest );
		}
	}

}

import class ScanningComponent extends GameComponent
{
	private instanceeditable var m_isBraindanceClue : Bool;
	private instanceeditable var m_BraindanceLayer : braindanceVisionMode;
	private var m_isBraindanceBlocked : Bool;
	private var m_isBraindanceLayerUnlocked : Bool;
	private var m_isBraindanceTimelineUnlocked : Bool;
	private var m_isBraindanceActive : Bool;
	private var m_currentBraindanceLayer : Int32;
	private const instanceeditable var m_clues : array< FocusClueDefinition >;
	private instanceeditable inlined var m_objectDescription : ObjectScanningDescription;
	[ customEditor = "TweakDBGroupInheritance;device_descriptions.ScanningBarText" ]
	private instanceeditable var scanningBarText : TweakDBID;
	private var m_isFocusModeActive : Bool;
	private var m_currentHighlight : FocusForcedHighlightData;
	private var m_isHudManagerInitialized : Bool;
	private var m_isBeingScanned : Bool;
	private var m_isScanningCluesBlocked : Bool;
	private var m_isEntityVisible : Bool;
	default m_isEntityVisible = true;
	private var m_OnBraindanceVisionModeChangeCallback : CallbackHandle;
	private var m_OnBraindanceFppChangeCallback : CallbackHandle;

	public import function GetScanningProgress() : Float;
	public import function GetTimeNeeded() : Float;
	public import function GetBoundingSphere() : Sphere;
	public import const function IsScanned() : Bool;
	public import const function IsScanning() : Bool;
	public import function SetIsScanned_Event( val : Bool );
	public import function UpdateTooltipData();
	public import const function IsBlocked() : Bool;
	public import function SetBlocked( isBlocked : Bool );
	public import const function GetScanningState() : gameScanningState;
	public import function SetScannableThroughWalls( isScannableThroughWalls : Bool );

	private function ToggleScanningBlocked( isBlocked : Bool )
	{
		if( IsBlocked() != isBlocked )
		{
			SetBlocked( isBlocked );
		}
	}

	protected export function OnGameAttach()
	{
		var BraindanceBB : IBlackboard;
		InitializeQuestDBCallbacks();
		if( m_isBraindanceClue )
		{
			BraindanceBB = GameInstance.GetBlackboardSystem( GetOwner().GetGame() ).Get( GetAllBlackboardDefs().Braindance );
			m_OnBraindanceVisionModeChangeCallback = BraindanceBB.RegisterListenerInt( GetAllBlackboardDefs().Braindance.activeBraindanceVisionMode, this, 'OnBraindanceVisionModeChange' );
			m_OnBraindanceFppChangeCallback = BraindanceBB.RegisterListenerBool( GetAllBlackboardDefs().Braindance.IsFPP, this, 'OnBraindanceFppChange' );
			EvaluateBraindanceClueState();
		}
		RestoreClueState();
	}

	protected export function OnGameDetach()
	{
		UnInitializeQuestDBCallbacks();
	}

	private const function GetOwner() : GameObject
	{
		return ( ( GameObject )( GetEntity() ) );
	}

	private const function GetMyPS() : gameScanningComponentPS
	{
		return ( ( gameScanningComponentPS )( GetPS() ) );
	}

	private function RestoreClueState()
	{
		var i : Int32;
		var psData : FocusClueDefinition;
		var systemData : FocusClueDefinition;
		var hasSystemData : Bool;
		var hasPSData : Bool;
		var isGroupDisabled : Bool;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			psData = m_clues[ i ];
			systemData = m_clues[ i ];
			hasPSData = GetMyPS().UpdateFocusClueData( i, psData );
			if( IsClueLinked( i ) )
			{
				hasSystemData = GetFocusClueSystem().GetClueGroupData( systemData.clueGroupID, systemData );
				isGroupDisabled = GetFocusClueSystem().IsGroupDisabled( psData.clueGroupID );
				if( ( hasPSData && hasSystemData ) && !( GetFocusClueSystem().IsRegistered( GetOwner().GetEntityID(), psData.clueGroupID ) ) )
				{
					m_clues[ i ] = psData;
					RequestFocusClueSystemUpdate( i );
				}
				else if( hasPSData && isGroupDisabled )
				{
					m_clues[ i ] = psData;
				}
				else if( hasSystemData )
				{
					m_clues[ i ] = systemData;
				}
				else if( isGroupDisabled )
				{
					m_clues[ i ].wasInspected = true;
					m_clues[ i ].isEnabled = false;
				}
				else if( hasPSData )
				{
					RegisterGrouppedClue( i );
					if( m_clues[ i ].isEnabled != psData.isEnabled || m_clues[ i ].wasInspected != psData.wasInspected )
					{
						m_clues[ i ] = psData;
						RequestFocusClueSystemUpdate( i );
					}
					else
					{
						m_clues[ i ] = psData;
					}
				}
				RegisterGrouppedClue( i );
			}
			else if( GetMyPS().UpdateFocusClueData( i, psData ) )
			{
				m_clues[ i ] = psData;
			}
		}
	}

	private function ReEvaluateGrouppedCluesState() : Bool
	{
		var i : Int32;
		var systemData : FocusClueDefinition;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			systemData = m_clues[ i ];
			if( IsClueLinked( i ) )
			{
				if( GetFocusClueSystem().GetClueGroupData( systemData.clueGroupID, systemData ) )
				{
					if( systemData.isEnabled != m_clues[ i ].isEnabled || systemData.wasInspected != m_clues[ i ].wasInspected )
					{
						SetClueState( i, systemData.isEnabled, systemData.wasInspected, false, false );
						return true;
					}
				}
			}
		}
		return false;
	}

	private function RegisterGrouppedClue( clueIndex : Int32 ) : Bool
	{
		var clueRequest : RegisterLinkedCluekRequest;
		var linkedClueData : LinkedFocusClueData;
		if( !( m_clues[ clueIndex ].isEnabled ) && m_clues[ clueIndex ].wasInspected )
		{
			return false;
		}
		if( !( GetLinkedClueData( clueIndex, linkedClueData ) ) )
		{
			return false;
		}
		if( IsNameValid( linkedClueData.clueGroupID ) )
		{
			if( GetFocusClueSystem().IsRegistered( GetOwner().GetEntityID(), linkedClueData.clueGroupID ) )
			{
				return false;
			}
			clueRequest = new RegisterLinkedCluekRequest;
			clueRequest.linkedCluekData = linkedClueData;
			GetFocusClueSystem().QueueRequest( clueRequest );
		}
		return true;
	}

	public const function GetLinkedClueData( clueIndex : Int32, linkedClueData : ref< LinkedFocusClueData > ) : Bool
	{
		var clue : FocusClueDefinition;
		if( !( HasClueWithID( clueIndex ) ) )
		{
			return false;
		}
		clue = m_clues[ clueIndex ];
		if( IsNameValid( clue.clueGroupID ) )
		{
			linkedClueData.clueGroupID = clue.clueGroupID;
			linkedClueData.ownerID = GetOwner().GetEntityID();
			linkedClueData.clueIndex = clueIndex;
			linkedClueData.isEnabled = clue.isEnabled;
			linkedClueData.wasInspected = clue.wasInspected;
			linkedClueData.isScanned = IsScanned();
			linkedClueData.psData.id = GetPersistentID();
			linkedClueData.psData.className = 'gameScanningComponentPS';
			return true;
		}
		return false;
	}

	public const function IsBraindanceClue() : Bool
	{
		return m_isBraindanceClue;
	}

	public const function GetBraindanceLayer() : braindanceVisionMode
	{
		return m_BraindanceLayer;
	}

	public const function GetObjectDescription() : ObjectScanningDescription
	{
		var objectDescriptionOverride : ObjectScanningDescription;
		objectDescriptionOverride = GetMyPS().GetObjectDecriptionOverride();
		if( objectDescriptionOverride != NULL )
		{
			return objectDescriptionOverride;
		}
		else
		{
			return m_objectDescription;
		}
	}

	public const function IsObjectDescriptionEnabled() : Bool
	{
		return GetMyPS().IsDescriptionEnabled();
	}

	public const function HasValidObjectDescription() : Bool
	{
		var data : ObjectScanningDescription;
		data = GetObjectDescription();
		return ( data != NULL ) && data.IsValid();
	}

	public const function GetAllClues() : array< FocusClueDefinition >
	{
		return m_clues;
	}

	public const function GetScanningBarTextTweak() : TweakDBID
	{
		return scanningBarText;
	}

	public const function GetAvailableClueIndex() : Int32
	{
		var i : Int32;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled )
			{
				return i;
			}
		}
		return -1;
	}

	public function GetScannableObjects( out arr : array< ScanningTooltipElementDef > )
	{
		var i : Int32;
		var k : Int32;
		var objectData : ScanningTooltipElementDef;
		if( IsScanningCluesBlocked() )
		{
			return;
		}
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled )
			{
				for( k = 0; i < m_clues[ i ].extendedClueRecords.Size(); k += 1 )
				{
					objectData.recordID = m_clues[ i ].extendedClueRecords[ k ].clueRecord;
					objectData.timePct = m_clues[ i ].extendedClueRecords[ k ].percentage;
					if( TDBID.IsValid( objectData.recordID ) )
					{
						arr.PushBack( objectData );
					}
				}
				if( IsConclusionActive( i ) )
				{
					if( m_clues[ i ].extendedClueRecords.Size() > 0 )
					{
						objectData.timePct = 1.0;
					}
					else
					{
						objectData.timePct = 0.0;
					}
					objectData.recordID = m_clues[ i ].clueRecord;
					if( TDBID.IsValid( objectData.recordID ) )
					{
						arr.PushBack( objectData );
					}
				}
				return;
			}
		}
	}

	public function GetScannableDataForSingleClueByIndex( index : Int32, out conclusionData : ScanningTooltipElementDef ) : array< ScanningTooltipElementDef >
	{
		var i : Int32;
		var objectData : ScanningTooltipElementDef;
		var arr : array< ScanningTooltipElementDef >;
		if( IsScanningCluesBlocked() )
		{
			return arr;
		}
		for( i = 0; i < m_clues[ index ].extendedClueRecords.Size(); i += 1 )
		{
			objectData.recordID = m_clues[ index ].extendedClueRecords[ i ].clueRecord;
			objectData.timePct = m_clues[ index ].extendedClueRecords[ i ].percentage;
			if( TDBID.IsValid( objectData.recordID ) )
			{
				arr.PushBack( objectData );
			}
		}
		if( IsConclusionActive( index ) )
		{
			if( m_clues[ index ].extendedClueRecords.Size() > 0 )
			{
				objectData.timePct = 1.0;
			}
			else
			{
				objectData.timePct = 0.0;
			}
			objectData.recordID = m_clues[ index ].clueRecord;
			if( TDBID.IsValid( objectData.recordID ) )
			{
				arr.PushBack( objectData );
				conclusionData = objectData;
			}
		}
		return arr;
	}

	private function IsConclusionActive( clueIndex : Int32 ) : Bool
	{
		var isActive : Bool;
		var owner : GameObject;
		if( m_clues[ clueIndex ].conclusionQuestState != EConclusionQuestState.Undefined )
		{
			if( m_clues[ clueIndex ].conclusionQuestState == EConclusionQuestState.Active || m_clues[ clueIndex ].conclusionQuestState == EConclusionQuestState.Shown )
			{
				isActive = true;
			}
			else
			{
				isActive = false;
			}
		}
		else
		{
			if( !( IsNameValid( m_clues[ clueIndex ].factToActivate ) ) )
			{
				isActive = true;
			}
			else
			{
				owner = GetOwner();
				if( owner )
				{
					isActive = GameInstance.GetQuestsSystem( GetOwner().GetGame() ).GetFact( m_clues[ clueIndex ].factToActivate ) > 0;
				}
			}
		}
		return isActive;
	}

	public const function IsBraindanceBlocked() : Bool
	{
		if( GetOwner().GetHudManager().IsBraindanceActive() )
		{
			return !( m_isBraindanceClue ) || m_isBraindanceBlocked;
		}
		return m_isBraindanceClue;
	}

	public const function IsPhotoModeBlocked() : Bool
	{
		return GameInstance.GetPhotoModeSystem( GetOwner().GetGame() ).IsPhotoModeActive();
	}

	public const function IsClueLinked( index : Int32 ) : Bool
	{
		return IsNameValid( m_clues[ index ].clueGroupID );
	}

	public const function IsActiveClueUsingAutoInspect() : Bool
	{
		var id : Int32;
		id = GetAvailableClueIndex();
		if( id >= 0 )
		{
			return IsClueUsingAutoInspect( id );
		}
		else
		{
			return false;
		}
	}

	public const function IsClueUsingAutoInspect( index : Int32 ) : Bool
	{
		return m_clues[ index ].useAutoInspect;
	}

	public const function IsActiveClueLinked() : Bool
	{
		var id : Int32;
		id = GetAvailableClueIndex();
		if( id >= 0 )
		{
			return IsClueLinked( id );
		}
		else
		{
			return false;
		}
	}

	public const function GetClueGroupID( index : Int32 ) : CName
	{
		if( !( HasClueWithID( index ) ) )
		{
			return '';
		}
		else
		{
			return m_clues[ index ].clueGroupID;
		}
	}

	public const function GetClueByIndex( index : Int32 ) : FocusClueDefinition
	{
		var clue : FocusClueDefinition;
		if( !( HasClueWithID( index ) ) )
		{
			return clue;
		}
		else
		{
			clue = m_clues[ index ];
		}
		return clue;
	}

	public function GetExtendedClueRecords( clueIndex : Int32 ) : array< ClueRecordData >
	{
		var records : array< ClueRecordData >;
		if( clueIndex < m_clues.Size() )
		{
			records = m_clues[ clueIndex ].extendedClueRecords;
		}
		return records;
	}

	public function SetClueExtendedDescriptionAsInspected( clueIndex : Int32, descriptionIndex : Int32 )
	{
		if( clueIndex < m_clues.Size() )
		{
			if( descriptionIndex < m_clues[ clueIndex ].extendedClueRecords.Size() )
			{
				m_clues[ clueIndex ].extendedClueRecords[ descriptionIndex ].wasInspected = true;
			}
		}
	}

	public function SetClueState( clueIndex : Int32, isEnabled : Bool, isInspected : Bool, updateFocusClueSystem : Bool, ignorePS : Bool )
	{
		var i : Int32;
		var shouldNotifyChange : Bool;
		if( !( HasClueWithID( clueIndex ) ) )
		{
			return;
		}
		shouldNotifyChange = m_clues[ clueIndex ].wasInspected != isInspected || m_clues[ clueIndex ].isEnabled != isEnabled;
		m_clues[ clueIndex ].wasInspected = isInspected;
		if( m_clues[ clueIndex ].isEnabled != isEnabled )
		{
			if( !( isEnabled ) )
			{
				m_clues[ clueIndex ].isEnabled = false;
				if( isInspected )
				{
					SetIsScanned_Event( true );
				}
			}
			else
			{
				SetIsScanned_Event( false );
				for( i = 0; i < m_clues.Size(); i += 1 )
				{
					if( ( i == clueIndex ) || ( IsNameValid( m_clues[ clueIndex ].clueGroupID ) && m_clues[ i ].clueGroupID == m_clues[ clueIndex ].clueGroupID ) )
					{
						m_clues[ i ].isEnabled = true;
						RegisterGrouppedClue( i );
					}
					else
					{
						m_clues[ i ].isEnabled = false;
						GetMyPS().StoreClueData( i, m_clues[ i ], IsScanned() );
					}
				}
			}
		}
		if( shouldNotifyChange )
		{
			NotifyClueStateChanged( clueIndex, ignorePS, updateFocusClueSystem );
		}
	}

	public function SetClueState( clueIndex : Int32, isEnabled : Bool, updateFocusClueSystem : Bool, ignorePS : Bool )
	{
		var i : Int32;
		var shouldNotifyChange : Bool;
		if( !( HasClueWithID( clueIndex ) ) )
		{
			return;
		}
		shouldNotifyChange = isEnabled != m_clues[ clueIndex ].isEnabled;
		if( isEnabled != m_clues[ clueIndex ].isEnabled )
		{
			if( !( isEnabled ) )
			{
				m_clues[ clueIndex ].isEnabled = false;
			}
			else
			{
				SetIsScanned_Event( false );
				for( i = 0; i < m_clues.Size(); i += 1 )
				{
					if( ( i == clueIndex ) || ( IsNameValid( m_clues[ clueIndex ].clueGroupID ) && m_clues[ i ].clueGroupID == m_clues[ clueIndex ].clueGroupID ) )
					{
						m_clues[ i ].isEnabled = true;
						RegisterGrouppedClue( i );
					}
					else
					{
						m_clues[ i ].isEnabled = false;
						GetMyPS().StoreClueData( i, m_clues[ i ], IsScanned() );
					}
				}
			}
		}
		if( shouldNotifyChange )
		{
			NotifyClueStateChanged( clueIndex, ignorePS, updateFocusClueSystem );
		}
	}

	private function NotifyClueStateChanged( clueIndex : Int32, ignorePS, updateFocusClueSystem : Bool )
	{
		NotifyHudManager( IsAnyClueEnabled() );
		ForceReEvaluateGameplayRole();
		if( !( ignorePS ) )
		{
			GetMyPS().StoreClueData( clueIndex, m_clues[ clueIndex ], IsScanned() );
		}
		if( updateFocusClueSystem )
		{
			RequestFocusClueSystemUpdate( clueIndex );
		}
		if( m_isBeingScanned )
		{
			ResolveScannerAvailability();
		}
	}

	public const function IsScanningCluesBlocked() : Bool
	{
		var id : EntityID;
		id = GetOwner().GetHudManager().GetLockedClueID();
		if( EntityID.IsDefined( id ) && ( id != GetOwner().GetEntityID() ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public const function IsAnyClueEnabled() : Bool
	{
		var i : Int32;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled )
			{
				return true;
			}
		}
		return false;
	}

	public const function HasAnyStoredClues() : Bool
	{
		return GetMyPS().HasAnyStoredClues();
	}

	private const function IsAnyClueValid() : Bool
	{
		var i : Int32;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isProgressing )
			{
				return true;
			}
		}
		return false;
	}

	public const function IsClueInspected() : Bool
	{
		var i : Int32;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled && m_clues[ i ].wasInspected )
			{
				return true;
			}
		}
		return false;
	}

	public const function IsClueProgressing() : Bool
	{
		var i : Int32;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled && m_clues[ i ].isProgressing )
			{
				return true;
			}
		}
		return false;
	}

	public const function HasAnyClue() : Bool
	{
		return m_clues.Size() > 0;
	}

	public const function GetClueCount() : Int32
	{
		return m_clues.Size();
	}

	public const function HasClueWithID( clueID : Int32 ) : Bool
	{
		return ( clueID >= 0 ) && ( clueID < m_clues.Size() );
	}

	private function InitializeQuestDBCallbacks()
	{
		var i : Int32;
		var owner : GameObject;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled )
			{
				if( IsNameValid( m_clues[ i ].factToActivate ) )
				{
					owner = GetOwner();
					if( owner )
					{
						m_clues[ i ].qDbCallbackID = GameInstance.GetQuestsSystem( owner.GetGame() ).RegisterEntity( m_clues[ i ].factToActivate, owner.GetEntityID() );
					}
				}
			}
		}
	}

	private function UnInitializeQuestDBCallbacks()
	{
		var i : Int32;
		var owner : GameObject;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( m_clues[ i ].isEnabled )
			{
				if( IsNameValid( m_clues[ i ].factToActivate ) )
				{
					owner = GetOwner();
					if( owner )
					{
						GameInstance.GetQuestsSystem( owner.GetGame() ).UnregisterEntity( m_clues[ i ].factToActivate, m_clues[ i ].qDbCallbackID );
					}
				}
			}
		}
	}

	private function CancelForcedVisionAppearance( data : FocusForcedHighlightData, optional fast : Bool, optional ignoreStackEvaluation : Bool )
	{
		var evt : ForceVisionApperanceEvent;
		evt = new ForceVisionApperanceEvent;
		if( fast )
		{
			data.outTransitionTime = 0.2;
		}
		evt.forcedHighlight = data;
		evt.apply = false;
		evt.ignoreStackEvaluation = ignoreStackEvaluation;
		GetOwner().QueueEvent( evt );
	}

	private function ForceVisionAppearance( data : FocusForcedHighlightData )
	{
		var evt : ForceVisionApperanceEvent;
		evt = new ForceVisionApperanceEvent;
		evt.forcedHighlight = data;
		evt.apply = true;
		GetOwner().QueueEvent( evt );
	}

	private const function GetQuestHighlight( highlightInstructions : HighlightInstance ) : FocusForcedHighlightData
	{
		var data : FocusForcedHighlightData;
		data = new FocusForcedHighlightData;
		data.sourceID = GetOwner().GetEntityID();
		data.sourceName = GetClassName();
		data.highlightType = EFocusForcedHighlightType.QUEST;
		data.priority = EPriority.Medium;
		if( highlightInstructions.context == HighlightContext.FILL )
		{
			data.highlightType = EFocusForcedHighlightType.QUEST;
			data.outlineType = EFocusOutlineType.INVALID;
		}
		else if( highlightInstructions.context == HighlightContext.OUTLINE )
		{
			data.outlineType = EFocusOutlineType.QUEST;
			data.highlightType = EFocusForcedHighlightType.INVALID;
		}
		else if( highlightInstructions.context == HighlightContext.FULL )
		{
			data.highlightType = EFocusForcedHighlightType.QUEST;
			data.outlineType = EFocusOutlineType.QUEST;
		}
		return data;
	}

	private const function GetDefaultHighlight( highlightInstructions : HighlightInstance ) : FocusForcedHighlightData
	{
		var data : FocusForcedHighlightData;
		data = new FocusForcedHighlightData;
		data.sourceID = GetOwner().GetEntityID();
		data.sourceName = GetClassName();
		data.highlightType = EFocusForcedHighlightType.INTERACTION;
		data.priority = EPriority.Medium;
		if( highlightInstructions.context == HighlightContext.FILL )
		{
			data.highlightType = EFocusForcedHighlightType.INTERACTION;
			data.outlineType = EFocusOutlineType.INVALID;
		}
		else if( highlightInstructions.context == HighlightContext.OUTLINE )
		{
			data.outlineType = EFocusOutlineType.INTERACTION;
			data.highlightType = EFocusForcedHighlightType.INVALID;
		}
		else if( highlightInstructions.context == HighlightContext.FULL )
		{
			data.highlightType = EFocusForcedHighlightType.INTERACTION;
			data.outlineType = EFocusOutlineType.INTERACTION;
		}
		return data;
	}

	private const function GetClueHighlight( highlightInstructions : HighlightInstance ) : FocusForcedHighlightData
	{
		var data : FocusForcedHighlightData;
		data = new FocusForcedHighlightData;
		data.sourceID = GetOwner().GetEntityID();
		data.sourceName = GetClassName();
		data.highlightType = EFocusForcedHighlightType.CLUE;
		data.priority = EPriority.Medium;
		data.outTransitionTime = 8.0;
		if( highlightInstructions.context == HighlightContext.FILL )
		{
			data.highlightType = EFocusForcedHighlightType.CLUE;
			data.outlineType = EFocusOutlineType.INVALID;
		}
		else if( highlightInstructions.context == HighlightContext.OUTLINE )
		{
			data.outlineType = EFocusOutlineType.CLUE;
			data.highlightType = EFocusForcedHighlightType.INVALID;
		}
		else if( highlightInstructions.context == HighlightContext.FULL )
		{
			data.highlightType = EFocusForcedHighlightType.CLUE;
			data.outlineType = EFocusOutlineType.CLUE;
		}
		return data;
	}

	private function ToggleHighlight( toggle : Bool, highlightInstructions : HighlightInstance )
	{
		var newHighlight : FocusForcedHighlightData;
		newHighlight = GetClueHighlightData( highlightInstructions );
		if( toggle )
		{
			if( ( m_currentHighlight != NULL ) && ( m_currentHighlight.highlightType != newHighlight.highlightType || m_currentHighlight.outlineType != newHighlight.outlineType ) )
			{
				CancelForcedVisionAppearance( m_currentHighlight, true, newHighlight != NULL );
			}
			if( newHighlight != NULL )
			{
				ForceVisionAppearance( newHighlight );
			}
		}
		else
		{
			if( m_currentHighlight != NULL )
			{
				CancelForcedVisionAppearance( m_currentHighlight, false );
			}
		}
		m_currentHighlight = newHighlight;
	}

	public const function GetClueHighlightData( highlightInstructions : HighlightInstance ) : FocusForcedHighlightData
	{
		if( IsAnyClueEnabled() )
		{
			if( GameInstance.GetPhotoModeSystem( GetOwner().GetGame() ).IsPhotoModeActive() )
			{
				return NULL;
			}
			if( IsBraindanceBlocked() || IsPhotoModeBlocked() )
			{
				return NULL;
			}
			if( IsClueInspected() || !( IsClueProgressing() ) )
			{
				return GetDefaultHighlight( highlightInstructions );
			}
			else
			{
				return GetQuestHighlight( highlightInstructions );
			}
		}
		return NULL;
	}

	private function UpdateDefaultHighlight()
	{
		var updateHighlightEvt : ForceUpdateDefaultHighlightEvent;
		updateHighlightEvt = new ForceUpdateDefaultHighlightEvent;
		GetOwner().QueueEvent( updateHighlightEvt );
	}

	private function ResolveFocusClueOnScannCompleted()
	{
		var clueIndex : Int32;
		clueIndex = GetAvailableClueIndex();
		if( clueIndex < 0 )
		{
			return;
		}
		if( m_clues[ clueIndex ].useAutoInspect )
		{
			m_clues[ clueIndex ].wasInspected = true;
			GetMyPS().StoreClueData( clueIndex, m_clues[ clueIndex ], true );
		}
		if( m_isBeingScanned )
		{
			RequestFocusClueSystemUpdate( clueIndex );
		}
		StopBraindanceClueEffect();
		RequestHUDRefresh();
	}

	private export const function Script_IsScanningStateTransitionAllowed( currentState, newState : gameScanningState ) : Bool
	{
		if( currentState == gameScanningState.ShallowComplete && newState == gameScanningState.Started )
		{
			return !( HasAnyClue() ) || IsAnyClueEnabled();
		}
		return true;
	}

	private function HighLightWeakspots()
	{
		var weakspots : array< weak< WeakspotObject > >;
		var Puppet : NPCPuppet;
		var i : Int32;
		Puppet = ( ( NPCPuppet )( GetOwner() ) );
		Puppet.GetWeakspotComponent().GetWeakspots( weakspots );
		if( weakspots.Size() > 0 )
		{
			for( i = 0; i < weakspots.Size(); i += 1 )
			{
				SendHighlightEventToWeakspot( weakspots[ i ] );
			}
		}
	}

	private function SendHighlightEventToWeakspot( object : GameObject )
	{
		var evt : ScanningLookAtEvent;
		evt = new ScanningLookAtEvent;
		object.QueueEvent( evt );
	}

	protected event OnScanningLookedAt( evt : ScanningLookAtEvent )
	{
		m_isBeingScanned = evt.state;
		if( evt.state )
		{
			ResolveScannerAvailability();
		}
		HighLightWeakspots();
	}

	private function ResolveScannerAvailability()
	{
		if( ( ( ( !( m_isEntityVisible ) || IsBraindanceBlocked() ) || GetMyPS().IsScanningDisabled() ) || !( GetOwner().ShouldShowScanner() ) ) || IsPhotoModeBlocked() )
		{
			ToggleScanningBlocked( true );
		}
		else
		{
			ToggleScanningBlocked( false );
		}
	}

	protected event OnEnteventsSetVisibility( evt : enteventsSetVisibility )
	{
		if( evt.visible != m_isEntityVisible )
		{
			m_isEntityVisible = evt.visible;
			ResolveScannerAvailability();
		}
	}

	protected event OnScanningEvent( evt : ScanningEvent )
	{
		if( evt.state == gameScanningState.Complete )
		{
			ResolveFocusClueOnScannCompleted();
		}
	}

	protected event OnClueLockedByScene( evt : SetExclusiveFocusClueEntityEvent )
	{
		var request : ClueLockNotification;
		request = new ClueLockNotification;
		request.isLocked = evt.isSetExclusive;
		request.ownerID = GetOwner().GetEntityID();
		GetOwner().GetHudManager().QueueRequest( request );
	}

	protected event OnActivateConclusionFactChanged( evt : FactChangedEvent )
	{
		var i : Int32;
		var factName : CName;
		factName = evt.GetFactName();
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( !( m_clues[ i ].isEnabled ) )
			{
				continue;
			}
			if( factName == m_clues[ i ].factToActivate )
			{
				SetConclusionState( i, EConclusionQuestState.Active );
				UpdateTooltipData();
			}
		}
	}

	protected event OnHUDInstruction( evt : HUDInstruction )
	{
		if( !( m_isHudManagerInitialized ) )
		{
			NotifyHudManager( IsAnyClueEnabled() );
			m_isHudManagerInitialized = true;
			return false;
		}
		if( ReEvaluateGrouppedCluesState() )
		{
			return false;
		}
		if( evt.scannerInstructions.WasProcessed() )
		{
			ProcessScannerHudInstruction( evt.scannerInstructions );
		}
		if( evt.highlightInstructions.WasProcessed() )
		{
			ProcessHighlightHudInstruction( evt.highlightInstructions );
		}
		if( evt.braindanceInstructions.WasProcessed() )
		{
			ProcessBraindanceHudInstruction( evt.braindanceInstructions );
		}
	}

	protected function ProcessHighlightHudInstruction( instruction : HighlightInstance )
	{
		if( instruction.GetState() == InstanceState.ON )
		{
			m_isFocusModeActive = true;
			ToggleHighlight( true, instruction );
		}
		else
		{
			m_isFocusModeActive = false;
			ToggleHighlight( false, instruction );
		}
	}

	protected function ProcessScannerHudInstruction( instruction : ScanInstance )
	{
		var evaluate : Bool;
		if( instruction.GetState() == InstanceState.ON )
		{
			evaluate = instruction.isScanningCluesBlocked != m_isScanningCluesBlocked;
			m_isScanningCluesBlocked = instruction.isScanningCluesBlocked;
		}
		if( evaluate )
		{
			if( instruction.isLookedAt )
			{
				SetIsScanned_Event( false );
			}
			ResolveScannerAvailability();
		}
	}

	protected function ProcessBraindanceHudInstruction( instruction : BraindanceInstance )
	{
		var braindanceToggle : Bool;
		if( !( m_isBraindanceClue ) )
		{
			return;
		}
		braindanceToggle = instruction.GetState() == InstanceState.ON;
		if( m_isBraindanceActive != braindanceToggle )
		{
			m_isBraindanceActive = braindanceToggle;
			ToggleBraindance( m_isBraindanceActive );
		}
	}

	protected event OnDisableScanner( evt : DisableScannerEvent )
	{
		if( m_isBeingScanned )
		{
			ResolveScannerAvailability();
			UpdateTooltipData();
		}
	}

	protected event OnSetGameplayRole( evt : SetGameplayRoleEvent )
	{
		if( m_isFocusModeActive )
		{
			UpdateTooltipData();
		}
	}

	protected event OnDisableObjectDescription( evt : DisableObjectDescriptionEvent )
	{
		if( m_isFocusModeActive )
		{
			UpdateTooltipData();
		}
	}

	protected event OnSetCustomObjectDescription( evt : SetCustomObjectDescriptionEvent )
	{
		if( m_isFocusModeActive )
		{
			UpdateTooltipData();
		}
	}

	protected event OnClearCustomObjectDescription( evt : ClearCustomObjectDescriptionEvent )
	{
		if( m_isFocusModeActive )
		{
			UpdateTooltipData();
		}
	}

	protected event OnSetCurrentGameplayRole( evt : SetCurrentGameplayRoleEvent )
	{
		if( m_isFocusModeActive )
		{
			UpdateTooltipData();
		}
	}

	protected event OnQuestToggleClueConclusion( evt : ToggleClueConclusionEvent )
	{
		if( HasClueWithID( evt.clueID ) )
		{
			if( evt.toggleConclusion )
			{
				m_clues[ evt.clueID ].conclusionQuestState = EConclusionQuestState.Active;
			}
			else
			{
				m_clues[ evt.clueID ].conclusionQuestState = EConclusionQuestState.Inactive;
			}
			if( m_clues[ evt.clueID ].isEnabled )
			{
				UpdateTooltipData();
			}
			if( evt.updatePS )
			{
				GetMyPS().StoreClueData( evt.clueID, m_clues[ evt.clueID ], IsScanned() );
			}
		}
	}

	public function SetConclusionAsShown( clueID : Int32 )
	{
		if( !( HasClueWithID( clueID ) ) )
		{
			return;
		}
		SetConclusionState( clueID, EConclusionQuestState.Shown );
	}

	private function SetConclusionState( clueID : Int32, state : EConclusionQuestState )
	{
		if( !( HasClueWithID( clueID ) ) )
		{
			return;
		}
		m_clues[ clueID ].conclusionQuestState = state;
		GetMyPS().StoreClueData( clueID, m_clues[ clueID ], IsScanned() );
	}

	public const function WasConclusionShown( clueID : Int32 ) : Bool
	{
		if( !( HasClueWithID( clueID ) ) )
		{
			return false;
		}
		return m_clues[ clueID ].conclusionQuestState == EConclusionQuestState.Shown;
	}

	protected event OnToggleFocusClue( evt : ToggleFocusClueEvent )
	{
		var isInspected : Bool;
		if( evt.investigationState == EFocusClueInvestigationState.NONE )
		{
			SetClueState( evt.clueIndex, evt.isEnabled, true, !( evt.updatePS ) );
		}
		else
		{
			if( evt.investigationState == EFocusClueInvestigationState.INSPECTED )
			{
				isInspected = true;
			}
			else
			{
				isInspected = false;
			}
			SetClueState( evt.clueIndex, evt.isEnabled, isInspected, true, !( evt.updatePS ) );
		}
		if( ( m_isFocusModeActive && IsScanning() ) && evt.isEnabled )
		{
			SetIsScanned_Event( true );
		}
	}

	protected event OnClueStateChanged( evt : FocusClueStateChangeEvent )
	{
		SetClueState( evt.clueIndex, evt.isEnabled, true, true );
		if( ( m_isFocusModeActive && IsScanning() ) && evt.isEnabled )
		{
			SetIsScanned_Event( true );
		}
	}

	protected event OnLinkedClueUpdateEvent( evt : linkedClueUpdateEvent )
	{
		UpdateLinkedClues( evt.linkedCluekData, evt.updatePS );
	}

	private function UpdateLinkedClues( const linkedCluekData : ref< LinkedFocusClueData >, updatePS : Bool )
	{
		var i : Int32;
		var setAsScanned : Bool;
		for( i = 0; i < m_clues.Size(); i += 1 )
		{
			if( !( IsNameValid( m_clues[ i ].clueGroupID ) ) )
			{
				continue;
			}
			if( m_clues[ i ].clueGroupID == linkedCluekData.clueGroupID )
			{
				SetClueState( i, linkedCluekData.isEnabled, linkedCluekData.wasInspected, false, !( updatePS ) );
				if( ( linkedCluekData.isEnabled && !( IsScanned() ) ) && linkedCluekData.isScanned )
				{
					setAsScanned = true;
				}
			}
		}
		if( setAsScanned )
		{
			SetIsScanned_Event( true );
		}
	}

	protected event OnRevealStateChanged( evt : RevealStateChangedEvent )
	{
		if( evt.state == ERevealState.STARTED || evt.state == ERevealState.CONTINUE )
		{
			SetScannableThroughWallsIfPossible();
		}
		else if( evt.state == ERevealState.STOPPED )
		{
			SetScannableThroughWalls( false );
		}
	}

	public function SetScannableThroughWallsIfPossible()
	{
		var isNetRunnerNPC : Bool;
		var scriptedPuppet : ScriptedPuppet;
		scriptedPuppet = ( ( ScriptedPuppet )( GetOwner() ) );
		isNetRunnerNPC = scriptedPuppet && scriptedPuppet.IsNetrunnerPuppet();
		if( GetOwner().CanPlayerScanThroughWalls() )
		{
			if( ( GetOwner().IsDevice() || isNetRunnerNPC ) || ( !( isNetRunnerNPC ) && StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'Int60_Ping' ) ) )
			{
				SetScannableThroughWalls( true );
			}
			else
			{
				SetScannableThroughWalls( false );
			}
		}
	}

	protected event OnScannableBraindanceClueEnabledEvent( evt : OnScannableBraindanceClueEnabledEvent )
	{
		if( m_isBraindanceClue )
		{
			m_isBraindanceTimelineUnlocked = true;
			EvaluateBraindanceClueState();
		}
	}

	protected event OnScannableBraindanceClueDisabledEvent( evt : OnScannableBraindanceClueDisabledEvent )
	{
		if( m_isBraindanceClue )
		{
			m_isBraindanceTimelineUnlocked = false;
			EvaluateBraindanceClueState();
		}
	}

	protected event OnBraindanceVisionModeChange( value : Int32 )
	{
		m_currentBraindanceLayer = value;
		if( m_currentBraindanceLayer == ( ( Int32 )( m_BraindanceLayer ) ) )
		{
			m_isBraindanceLayerUnlocked = true;
		}
		else
		{
			m_isBraindanceLayerUnlocked = false;
		}
		EvaluateBraindanceClueState();
	}

	protected event OnBraindanceFppChange( fppToggle : Bool )
	{
		if( fppToggle )
		{
			m_isBraindanceLayerUnlocked = false;
		}
		else
		{
			if( m_currentBraindanceLayer == ( ( Int32 )( m_BraindanceLayer ) ) )
			{
				m_isBraindanceLayerUnlocked = true;
			}
			else
			{
				m_isBraindanceLayerUnlocked = false;
			}
		}
		EvaluateBraindanceClueState();
	}

	private function ToggleBraindanceScanning( value : Bool )
	{
		m_isBraindanceBlocked = !( value );
		if( !( IsScanned() ) )
		{
			SetIsScanned_Event( false );
		}
		ResolveScannerAvailability();
	}

	private function SignalScannablesBlackboard()
	{
		var Blackboard : IBlackboard;
		Blackboard = GameInstance.GetBlackboardSystem( GetOwner().GetGame() ).Get( GetAllBlackboardDefs().UI_Scanner );
		if( Blackboard )
		{
			Blackboard.SignalVariant( GetAllBlackboardDefs().UI_Scanner.Scannables );
			Blackboard.SignalEntityID( GetAllBlackboardDefs().UI_Scanner.ScannedObject );
		}
	}

	protected function ToggleBraindance( value : Bool )
	{
		if( value )
		{
			NotifyHudManager( false );
			if( 0 == ( ( Int32 )( m_BraindanceLayer ) ) )
			{
				m_isBraindanceLayerUnlocked = true;
			}
			else
			{
				m_isBraindanceLayerUnlocked = false;
			}
		}
		else
		{
			m_isBraindanceLayerUnlocked = false;
		}
		EvaluateBraindanceClueState();
	}

	private function StartBraindanceClueEffect()
	{
		var spawnEffectEvent : entSpawnEffectEvent;
		spawnEffectEvent = new entSpawnEffectEvent;
		spawnEffectEvent.effectName = 'braindanceClueEffect';
		GetOwner().QueueEvent( spawnEffectEvent );
	}

	private function StopBraindanceClueEffect()
	{
		var evt : entKillEffectEvent;
		evt = new entKillEffectEvent;
		evt.effectName = 'braindanceClueEffect';
		GetOwner().QueueEvent( evt );
	}

	private function EvaluateBraindanceClueState()
	{
		if( m_isBraindanceLayerUnlocked && m_isBraindanceTimelineUnlocked )
		{
			ToggleBraindanceScanning( true );
			if( !( IsScanned() ) )
			{
				StartBraindanceClueEffect();
			}
			SignalScannablesBlackboard();
			HideMappins( false );
		}
		else if( IsEnabled() )
		{
			ToggleBraindanceScanning( false );
			StopBraindanceClueEffect();
			HideMappins( true );
		}
		UpdateTooltipData();
		UpdateDefaultHighlight();
	}

	private function HideMappins( value : Bool )
	{
		var evt : ToggleGameplayMappinVisibilityEvent;
		evt = new ToggleGameplayMappinVisibilityEvent;
		evt.isHidden = value;
		GetOwner().QueueEvent( evt );
	}

	private function NotifyHudManager( isClue : Bool )
	{
		var request : ClueStatusNotification;
		request = new ClueStatusNotification;
		request.isClue = isClue;
		request.clueGroupID = GetClueGroupID( GetAvailableClueIndex() );
		request.ownerID = GetOwner().GetEntityID();
		GetOwner().GetHudManager().QueueRequest( request );
	}

	private function RequestHUDRefresh()
	{
		var request : RefreshActorRequest;
		request = new RefreshActorRequest;
		request.ownerID = GetOwner().GetEntityID();
		GetOwner().GetHudManager().QueueRequest( request );
	}

	public const function GetFocusClueSystem() : FocusCluesSystem
	{
		return ( ( FocusCluesSystem )( GameInstance.GetScriptableSystemsContainer( GetOwner().GetGame() ).Get( 'FocusCluesSystem' ) ) );
	}

	private function RequestFocusClueSystemUpdate( clueIndex : Int32 )
	{
		var clueRequest : UpdateLinkedClueskRequest;
		var linkedClueData : LinkedFocusClueData;
		var clue : FocusClueDefinition;
		if( !( HasClueWithID( clueIndex ) ) )
		{
			return;
		}
		clue = m_clues[ clueIndex ];
		if( IsNameValid( clue.clueGroupID ) )
		{
			clueRequest = new UpdateLinkedClueskRequest;
			linkedClueData.clueGroupID = clue.clueGroupID;
			linkedClueData.ownerID = GetOwner().GetEntityID();
			linkedClueData.clueIndex = clueIndex;
			linkedClueData.clueRecord = clue.clueRecord;
			linkedClueData.extendedClueRecords = clue.extendedClueRecords;
			linkedClueData.isScanned = IsScanned();
			linkedClueData.wasInspected = clue.wasInspected;
			linkedClueData.isEnabled = clue.isEnabled;
			clueRequest.linkedCluekData = linkedClueData;
			GetFocusClueSystem().QueueRequest( clueRequest );
		}
	}

	protected function ForceReEvaluateGameplayRole()
	{
		var evt : EvaluateGameplayRoleEvent;
		evt = new EvaluateGameplayRoleEvent;
		evt.force = true;
		GetOwner().QueueEvent( evt );
	}

}

