class SetQuestTargetWasSeen extends Event
{
	var wasSeen : Bool;
}

class SetAnyTargetIsLocked extends Event
{
	var wasSeen : Bool;
}

class ForceIgnoreTargets extends ActionBool
{
	var Repeat : Bool;
	var Attitude : EAIAttitude;

	public override function GetBaseCost() : Int32
	{
		if( m_isQuickHack )
		{
			return super.GetBaseCost();
		}
		return 0;
	}

	public function SetProperties()
	{
		actionName = 'ForceIgnoreTargets';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'ForceIgnoreTargets', true, 'LocKey#2241', 'LocKey#2241' );
	}

}

class SetDeviceTagKillMode extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'SetDeviceSupportMode';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( actionName, true, 'LocKey#363', 'LocKey#363' );
	}

	public override function GetTweakDBChoiceRecord() : String
	{
		if( TDBID.IsValid( m_objectActionID ) )
		{
			return super.GetTweakDBChoiceRecord();
		}
		else
		{
			return "SetDeviceSupportMode";
		}
	}

	public const override function GetInteractionIcon() : weak< ChoiceCaptionIconPart_Record >
	{
		return TweakDBInterface.GetChoiceCaptionIconPartRecord( T"ChoiceCaptionParts.Aim" );
	}

}

struct TargetingBehaviour
{
	var m_initialWakeState : ESensorDeviceWakeState;
	default m_initialWakeState = ESensorDeviceWakeState.CLOSED;
	instanceeditable var m_canRotate : Bool;
	default m_canRotate = true;
	instanceeditable var m_lostTargetLookAtTime : Float;
	default m_lostTargetLookAtTime = 2f;
	instanceeditable var m_lostTargetSearchTime : Float;
	default m_lostTargetSearchTime = 10f;
}

struct DetectionParameters
{
	instanceeditable var m_canDetectIntruders : Bool;
	default m_canDetectIntruders = true;
	instanceeditable var m_timeToActionAfterSpot : Float;
	default m_timeToActionAfterSpot = 2f;
	[ tooltip = "add rotation to upper body to override forward" ]
	instanceeditable var m_overrideRootRotation : Float;
	default m_overrideRootRotation = 0.0f;
	[ tooltip = " > 0 - rotate from to | =0 dont rotate | -1 rotate 360 left | -2 rotate 360 right" ]
	instanceeditable var m_maxRotationAngle : Float;
	default m_maxRotationAngle = 90.f;
	instanceeditable var m_pitchAngle : Float;
	default m_pitchAngle = -15.f;
	var m_rotationSpeed : Float;
	default m_rotationSpeed = 5.f;
}

class SensorDeviceController extends ExplosiveDeviceController
{

	public const override function GetPS() : SensorDeviceControllerPS
	{
		return ( ( SensorDeviceControllerPS )( GetBasePS() ) );
	}

	public function OnEditorAttach()
	{
		RegisterRenderDebug( "Components/SensorDeviceController/RenderDebug", 'OnSensorDeviceRenderDebug' );
	}

	public export function OnGameAttach()
	{
		RegisterRenderDebug( "Components/SensorDeviceController/RenderDebug", 'OnSensorDeviceRenderDebug' );
	}

	protected const function OnSensorDeviceRenderDebug( debugDrawer : DebugDrawer )
	{
		SensorDeviceRenderDebug( debugDrawer );
	}

	protected const function OnRenderSelection( debugDrawer : DebugDrawer )
	{
		if( DebugDrawer.TestDebugFilterMask( debugDrawer, "Components/SensorDeviceController/RenderSelection" ) )
		{
			SensorDeviceRenderDebug( debugDrawer );
		}
	}

	private const function SensorDeviceRenderDebug( debugDrawer : ref< DebugDrawer > )
	{
		var fragmentBuilder : FragmentBuilder;
		var maxRotationAngle : Vector4;
		var matrix : Matrix;
		FragmentBuilder.Construct( fragmentBuilder, debugDrawer );
		FragmentBuilder.PushLocalTransform( fragmentBuilder );
		matrix = EulerAngles.ToMatrix( EulerAngles( 0.0, ( ( SensorDeviceControllerPS )( GetPS() ) ).GetBehaviourOverrideRootRotation(), 0.0 ) );
		matrix *= WorldTransform.ToMatrix( GetEntity().GetWorldTransform() );
		FragmentBuilder.BindTransform( fragmentBuilder, matrix );
		FragmentBuilder.SetColor( fragmentBuilder, Color( 230, 38, 5, 255 ) );
		maxRotationAngle = Vector4.RotateAxis( Vector4( 0.0, 1.0, 0.0, 0.0 ), Vector4( 1.0, 0.0, 0.0, 0.0 ), Deg2Rad( ( ( SensorDeviceControllerPS )( GetPS() ) ).GetBehaviourPitchAngle() ) );
		FragmentBuilder.AddArrow( fragmentBuilder, Vector4( 0.0, 0.0, 0.0, 0.0 ), maxRotationAngle );
		FragmentBuilder.AddWireAngledRange( fragmentBuilder, Matrix.Identity(), 0.05, 1.0, 2.0 * ( ( SensorDeviceControllerPS )( GetPS() ) ).GetBehaviourMaxRotationAngle(), true );
		FragmentBuilder.PopLocalTransform( fragmentBuilder );
		FragmentBuilder.Done( fragmentBuilder );
	}

}

class SensorDeviceControllerPS extends ExplosiveDeviceControllerPS
{
	default m_deviceName = "Sensor Device";
	default m_canPlayerTakeOverControl = true;
	default m_shouldScannerShowAttitude = true;
	default m_shouldScannerShowHealth = true;
	default m_shouldScannerShowStatus = true;
	[ category = "Senses" ]
	private instanceeditable persistent var m_isRecognizableBySenses : Bool;
	default m_isRecognizableBySenses = true;
	protected instanceeditable persistent var m_targetingBehaviour : TargetingBehaviour;
	protected instanceeditable persistent var m_detectionParameters : DetectionParameters;
	[ customEditor = "TweakDBGroupInheritance;LookAtPreset" ]
	protected editable var m_lookAtPresetVert : TweakDBID;
	[ customEditor = "TweakDBGroupInheritance;LookAtPreset" ]
	protected editable var m_lookAtPresetHor : TweakDBID;
	[ category = "Game effect refs" ]
	protected editable var m_scanGameEffectRef : EffectRef;
	[ category = "Game effect refs" ]
	protected editable var m_visionConeEffectRef : EffectRef;
	[ category = "Game effect refs" ]
	protected editable var m_visionConeFriendlyEffectRef : EffectRef;
	[ category = "Game effect refs" ]
	protected editable var m_idleActiveRef : EffectRef;
	[ category = "Game effect refs" ]
	protected editable var m_idleFriendlyRef : EffectRef;
	protected persistent var m_canTagEnemies : Bool;
	protected var m_tagLockFromSystem : Bool;
	private var m_netrunnerID : EntityID;
	private var m_netrunnerProxyID : EntityID;
	private var m_netrunnerTargetID : EntityID;
	private var m_linkedStatusEffect : LinkedStatusEffect;
	private persistent var m_questForcedTargetID : EntityID;
	private persistent var m_isInFollowMode : Bool;
	private persistent var m_isAttitudeChanged : Bool;
	private persistent var m_isInTagKillMode : Bool;
	private persistent var m_isIdleForced : Bool;
	private persistent var m_questTargetToSpot : EntityID;
	private var m_questTargetSpotted : Bool;
	private var m_isAnyTargetIsLocked : Bool;
	protected editable var m_isPartOfPrevention : Bool;
	protected editable var m_ignoreTargetTrackerComponent : Bool;

	public const function GetBehaviourCanRotate() : Bool
	{
		return m_targetingBehaviour.m_canRotate;
	}

	public const function GetBehaviourLastTargetLookAtTime() : Float
	{
		return m_targetingBehaviour.m_lostTargetLookAtTime;
	}

	public const function GetBehaviourLostTargetSearchTime() : Float
	{
		return m_targetingBehaviour.m_lostTargetSearchTime;
	}

	public const function CanTagEnemies() : Bool
	{
		return m_canTagEnemies && !( m_tagLockFromSystem );
	}

	public const function GetInitialWakeState() : ESensorDeviceWakeState
	{
		return m_targetingBehaviour.m_initialWakeState;
	}

	public const function GetLookAtPresetVert() : TweakDBID
	{
		return m_lookAtPresetVert;
	}

	public const function GetLookAtPresetHor() : TweakDBID
	{
		return m_lookAtPresetHor;
	}

	public const function GetBehaviourCanDetectIntruders() : Bool
	{
		if( IsDistracting() || !( m_detectionParameters.m_canDetectIntruders ) )
		{
			return false;
		}
		return true;
	}

	public const function GetBehaviourOverrideRootRotation() : Float
	{
		return m_detectionParameters.m_overrideRootRotation;
	}

	public const function GetBehaviourMaxRotationAngle() : Float
	{
		return m_detectionParameters.m_maxRotationAngle;
	}

	public const function GetBehaviourPitchAngle() : Float
	{
		return m_detectionParameters.m_pitchAngle;
	}

	public const function GetBehaviourRotationSpeed() : Float
	{
		return m_detectionParameters.m_rotationSpeed;
	}

	public const function GetBehaviourtimeToTakeAction() : Float
	{
		if( IsPartOfPrevention() )
		{
			return 0.0;
		}
		return m_detectionParameters.m_timeToActionAfterSpot;
	}

	public const function GetForcedTargetID() : EntityID
	{
		return m_questForcedTargetID;
	}

	public const function IsInFollowMode() : Bool
	{
		return m_isInFollowMode;
	}

	public const function IsAttitudeChanged() : Bool
	{
		return m_isAttitudeChanged;
	}

	public const function IsInTagKillMode() : Bool
	{
		return m_isInTagKillMode;
	}

	public const function IsIdleForced() : Bool
	{
		return m_isIdleForced;
	}

	public const function IsPartOfPrevention() : Bool
	{
		return m_isPartOfPrevention;
	}

	public const function IgnoreTargetTrackerComponent() : Bool
	{
		return m_ignoreTargetTrackerComponent;
	}

	public quest const function IsQuestTargetSpotted() : Bool
	{
		return m_questTargetSpotted;
	}

	public quest const function IsAnyTargetLocked() : Bool
	{
		return m_isAnyTargetIsLocked;
	}

	public quest const function IsNoTargetLocked() : Bool
	{
		if( !( m_isAnyTargetIsLocked ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public const function GetQuestSpotTargetID() : EntityID
	{
		return m_questTargetToSpot;
	}

	public const function GetNetrunnerID() : EntityID
	{
		return m_netrunnerID;
	}

	public const function GetNetrunnerProxyID() : EntityID
	{
		return m_netrunnerProxyID;
	}

	public const function GetNetrunnerTargetID() : EntityID
	{
		return m_netrunnerTargetID;
	}

	public const function GetLinkedStatusEffect() : LinkedStatusEffect
	{
		return m_linkedStatusEffect;
	}

	public const function GetScanGameEffectRef() : EffectRef
	{
		return m_scanGameEffectRef;
	}

	public const function GetVisionConeEffectRef() : EffectRef
	{
		return m_visionConeEffectRef;
	}

	public const function GetVisionConeFriendlyEffectRef() : EffectRef
	{
		return m_visionConeFriendlyEffectRef;
	}

	public const function GetIdleActiveRef() : EffectRef
	{
		return m_idleActiveRef;
	}

	public const function GetIdleFriendlyRef() : EffectRef
	{
		return m_idleFriendlyRef;
	}

	public function SetIsAttitudeChanged( isChanged : Bool )
	{
		m_isAttitudeChanged = isChanged;
	}

	public function SetIsInTagKillMode( value : Bool )
	{
		m_isInTagKillMode = value;
	}

	public function SetIsIdleForced( value : Bool )
	{
		m_isIdleForced = value;
	}

	public function SetCanDetectIntruders( value : Bool )
	{
		m_detectionParameters.m_canDetectIntruders = value;
	}

	public function SetNetrunnerID( value : EntityID )
	{
		m_netrunnerID = value;
	}

	public function SetNetrunnerProxyID( value : EntityID )
	{
		m_netrunnerProxyID = value;
	}

	public function SetNetrunnerTargetID( value : EntityID )
	{
		m_netrunnerTargetID = value;
	}

	public function SetLinkedStatusEffect( const value : ref< LinkedStatusEffect > )
	{
		m_linkedStatusEffect = value;
	}

	public function SetTagLockFromSystem( value : Bool )
	{
		m_tagLockFromSystem = value;
	}

	public function SetQuestTargetSpotted( value : Bool )
	{
		var evt : SetQuestTargetWasSeen;
		evt = new SetQuestTargetWasSeen;
		evt.wasSeen = value;
		QueuePSEvent( this, evt );
	}

	public function OnSetQuestTargetWasSeen( evt : SetQuestTargetWasSeen ) : EntityNotificationType
	{
		m_questTargetSpotted = evt.wasSeen;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function SetTargetIsLocked( value : Bool )
	{
		var evt : SetAnyTargetIsLocked;
		evt = new SetAnyTargetIsLocked;
		evt.wasSeen = value;
		QueuePSEvent( this, evt );
	}

	public export function OnSetAnyTargetIsLocked( evt : SetAnyTargetIsLocked ) : EntityNotificationType
	{
		m_isAnyTargetIsLocked = evt.wasSeen;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public const virtual function IsDetectingDebug() : Bool
	{
		return false;
	}

	public const function GetCurrentTarget() : GameObject
	{
		return ( ( SensorDevice )( GetOwnerEntityWeak() ) ).GetCurrentlyFollowedTarget();
	}

	protected override function ActionQuickHackToggleON() : QuickHackToggleON
	{
		var action : QuickHackToggleON;
		action = super.ActionQuickHackToggleON();
		if( IsON() )
		{
			action.CreateInteraction( , , T"Interactions.Off" );
		}
		else
		{
			action.CreateInteraction( , , T"Interactions.On" );
		}
		return action;
	}

	protected function ActionQuestFollowTarget() : QuestFollowTarget
	{
		var action : QuestFollowTarget;
		action = new QuestFollowTarget;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestStopFollowingTarget() : QuestStopFollowingTarget
	{
		var action : QuestStopFollowingTarget;
		action = new QuestStopFollowingTarget;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestLookAtTarget() : QuestLookAtTarget
	{
		var action : QuestLookAtTarget;
		action = new QuestLookAtTarget;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestStopLookAtTarget() : QuestStopLookAtTarget
	{
		var action : QuestStopLookAtTarget;
		action = new QuestStopLookAtTarget;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestForceAttitude() : QuestForceAttitude
	{
		var action : QuestForceAttitude;
		action = new QuestForceAttitude;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties( 'attitudeNotProvied' );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestSetDetectionToTrue() : QuestSetDetectionToTrue
	{
		var action : QuestSetDetectionToTrue;
		action = new QuestSetDetectionToTrue;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestSetDetectionToFalse() : QuestSetDetectionToFalse
	{
		var action : QuestSetDetectionToFalse;
		action = new QuestSetDetectionToFalse;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected virtual function ActionForceIgnoreTargets() : ForceIgnoreTargets
	{
		var action : ForceIgnoreTargets;
		action = new ForceIgnoreTargets;
		action.Repeat = true;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTakeOverControl();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected virtual function ActionSetDeviceTagKillMode() : SetDeviceTagKillMode
	{
		var action : SetDeviceTagKillMode;
		action = new SetDeviceTagKillMode;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTakeOverControl();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected function ActionQuestSpotTargetReference() : QuestSpotTargetReference
	{
		var action : QuestSpotTargetReference;
		action = new QuestSpotTargetReference;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestForceScanEffect() : QuestForceScanEffect
	{
		var action : QuestForceScanEffect;
		action = new QuestForceScanEffect;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestForceScanEffectStop() : QuestForceScanEffectStop
	{
		var action : QuestForceScanEffectStop;
		action = new QuestForceScanEffectStop;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected override function PerformRestart()
	{
		super.PerformRestart();
		ExecutePSAction( ActionSetDeviceUnpowered() );
	}

	protected function OnSecuritySystemEnabled( evt : SecuritySystemEnabled ) : EntityNotificationType
	{
		SetIsAttitudeChanged( false );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnSecuritySystemDisabled( evt : SecuritySystemDisabled ) : EntityNotificationType
	{
		ExecutePSAction( ActionSetDeviceAttitude() );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export override function OnSecuritySystemOutput( evt : SecuritySystemOutput ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		super.OnSecuritySystemOutput( evt );
		if( evt.GetBreachOrigin() != EBreachOrigin.EXTERNAL )
		{
			notifier.SetInternalOnly();
			Notify( notifier, evt );
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export override function OnSecurityAreaCrossingPerimeter( evt : SecurityAreaCrossingPerimeter ) : EntityNotificationType
	{
		super.OnSecurityAreaCrossingPerimeter( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public constexpr function OnReprimandUpdate( evt : ReprimandUpdate ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	public export override function OnTargetAssessmentRequest( evt : TargetAssessmentRequest ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	public constexpr function OnQuestForceScanEffect( evt : QuestForceScanEffect ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	public constexpr function OnQuestForceScanEffectStop( evt : QuestForceScanEffectStop ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestSpotTargetReference( evt : QuestSpotTargetReference ) : EntityNotificationType
	{
		var followedTargetIds : array< EntityID >;
		if( EntityID.IsDefined( evt.m_ForcedTarget ) )
		{
			m_questTargetToSpot = evt.m_ForcedTarget;
		}
		else
		{
			GetFixedEntityIdsFromEntityReference( ( ( EntityReference )evt.prop.first ), GetGameInstance(), followedTargetIds );
			if( followedTargetIds.Size() )
			{
				m_questTargetToSpot = followedTargetIds[ 0 ];
			}
			else
			{
				m_questTargetToSpot = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerControlledGameObject().GetEntityID();
			}
		}
		if( !( EntityID.IsDefined( m_questTargetToSpot ) ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function OnQuestFollowTarget( evt : QuestFollowTarget ) : EntityNotificationType
	{
		var followedTargetIds : array< EntityID >;
		if( IsBroken() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( EntityID.IsDefined( evt.m_ForcedTarget ) )
		{
			m_questForcedTargetID = evt.m_ForcedTarget;
		}
		else
		{
			GetFixedEntityIdsFromEntityReference( ( ( EntityReference )evt.prop.first ), GetGameInstance(), followedTargetIds );
			if( followedTargetIds.Size() )
			{
				m_questForcedTargetID = followedTargetIds[ 0 ];
			}
			else
			{
				m_questForcedTargetID = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerControlledGameObject().GetEntityID();
			}
		}
		if( !( EntityID.IsDefined( m_questForcedTargetID ) ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		m_isInFollowMode = true;
		if( IsOFF() && !( IsBroken() ) )
		{
			ExecutePSAction( ActionToggleON() );
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestStopFollowingTarget( evt : QuestStopFollowingTarget ) : EntityNotificationType
	{
		evt.targetEntityID = m_questForcedTargetID;
		m_questForcedTargetID = EntityID();
		m_isInFollowMode = false;
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestLookAtTarget( evt : QuestLookAtTarget ) : EntityNotificationType
	{
		var followedTargetIds : array< EntityID >;
		if( IsOFF() )
		{
			ForceDeviceON();
		}
		GetFixedEntityIdsFromEntityReference( ( ( EntityReference )evt.prop.first ), GetGameInstance(), followedTargetIds );
		if( followedTargetIds.Size() )
		{
			m_questForcedTargetID = followedTargetIds[ 0 ];
		}
		else
		{
			m_questForcedTargetID = GetPlayerEntityID();
		}
		if( !( EntityID.IsDefined( m_questForcedTargetID ) ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		m_isInFollowMode = true;
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestStopLookAtTarget( evt : QuestStopLookAtTarget ) : EntityNotificationType
	{
		m_questForcedTargetID = EntityID();
		m_isInFollowMode = false;
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected export function OnQuestForceTakeControlOverCamera( evt : QuestForceTakeControlOverCamera ) : EntityNotificationType
	{
		SendQuestTakeOverControlRequest( false );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function OnQuestForceTakeControlOverCameraWithChain( evt : QuestForceTakeControlOverCameraWithChain ) : EntityNotificationType
	{
		SendQuestTakeOverControlRequest( true );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private function SendQuestTakeOverControlRequest( shouldCreateChain : Bool )
	{
		var inputLockRequest : RequestQuestTakeControlInputLock;
		inputLockRequest = new RequestQuestTakeControlInputLock;
		inputLockRequest.isLocked = true;
		inputLockRequest.isChainForced = shouldCreateChain;
		( ( TakeOverControlSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'TakeOverControlSystem' ) ) ).QueueRequest( inputLockRequest );
		ExecutePSAction( ActionToggleTakeOverControl() );
	}

	protected export function OnQuestForceStopTakeControlOverCamera( evt : QuestForceStopTakeControlOverCamera ) : EntityNotificationType
	{
		QuestReleaseCurrentObject();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function QuestReleaseCurrentObject()
	{
		var inputLockRequest : RequestQuestTakeControlInputLock;
		inputLockRequest = new RequestQuestTakeControlInputLock;
		inputLockRequest.isLocked = false;
		inputLockRequest.isChainForced = false;
		( ( TakeOverControlSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'TakeOverControlSystem' ) ) ).QueueRequest( inputLockRequest );
		TakeOverControlSystem.ReleaseControl( GetGameInstance() );
	}

	protected function OnQuestSetDetectionToTrue( evt : QuestSetDetectionToTrue ) : EntityNotificationType
	{
		SetCanDetectIntruders( true );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function OnQuestSetDetectionToFalse( evt : QuestSetDetectionToFalse ) : EntityNotificationType
	{
		SetCanDetectIntruders( false );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function OnSetDeviceAttitude( evt : SetDeviceAttitude ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetNone();
		Notify( notifier, evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected export function OnQuestForceAttitude( evt : QuestForceAttitude ) : EntityNotificationType
	{
		if( IsNameValid( ( ( CName )evt.prop.first ) ) )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function OnRevealEnemiesProgram( evt : RevealEnemiesProgram ) : EntityNotificationType
	{
		m_canTagEnemies = true;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function IsAttitudeFromContextHostile() : Bool
	{
		var playerAttitude : AttitudeAgent;
		var myAttitude : AttitudeAgent;
		playerAttitude = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject().GetAttitudeAgent();
		myAttitude = ( ( SensorDevice )( GameInstance.FindEntityByID( GetGameInstance(), GetMyEntityID() ) ) ).GetAttitudeAgent();
		return playerAttitude.GetAttitudeTowards( myAttitude ) != EAIAttitude.AIA_Friendly;
	}

	public function OnForceIgnoreTargets( evt : ForceIgnoreTargets ) : EntityNotificationType
	{
		var action : SetDeviceAttitude;
		action = ActionSetDeviceAttitude();
		action.Attitude = evt.Attitude;
		action.Repeat = evt.Repeat;
		action.SetDurationValue( evt.GetDurationTime() );
		ExecutePSAction( action );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public const function NotifyAboutSpottingPlayer( doSee : Bool )
	{
		var secSys : SecuritySystemControllerPS;
		var playerSpotted : PlayerSpotted;
		secSys = GetSecuritySystem();
		if( !( secSys ) )
		{
			return;
		}
		playerSpotted = PlayerSpotted.Construct( false, GetID(), doSee, GetSecurityAreas() );
		QueuePSEvent( secSys, playerSpotted );
	}

	protected constexpr export function OnSecuritySystemSupport( evt : SecuritySystemSupport ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected export function OnSecuritySystemSupport( evt : ReactoToPreventionSystem ) : EntityNotificationType
	{
		if( evt.wakeUp )
		{
			ExecutePSAction( ActionQuestForceON() );
		}
		else
		{
			ExecutePSAction( ActionQuestForceOFF() );
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected export function OnSetDeviceTagKillMode( evt : SetDeviceTagKillMode ) : EntityNotificationType
	{
		var action : SetDeviceAttitude;
		SetIsInTagKillMode( true );
		if( IsAttitudeFromContextHostile() )
		{
			action = ActionSetDeviceAttitude();
			action.Attitude = EAIAttitude.AIA_Friendly;
			action.IgnoreHostiles = true;
			ExecutePSAction( action );
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function ClearLinkedStatusEffect()
	{
		var emptyID : EntityID;
		m_linkedStatusEffect.netrunnerIDs.Clear();
		m_linkedStatusEffect.statusEffectList.Clear();
		m_linkedStatusEffect.targetID = emptyID;
	}

}

