enum EPlaystyle
{
	NETRUNNER = 0,
	SOLO = 1,
	TECHIE = 2,
}

enum EVirtualSystem
{
	None = 0,
	SurveillanceSystem = 1,
	DoorSystem = 2,
	MediaSystem = 3,
	SecuritySystem = 4,
}

class ProcessRelevantDevicesForNetworkGridEvent extends ProcessDevicesEvent
{
	var ignoreRevealed : Bool;
	var finalizeRegistrationAsMaster : Bool;
	var breachedResource : FxResource;
	var defaultResource : FxResource;
	var isPing : Bool;
	var lifetime : Float;
	var revealSlave : Bool;
	var revealMaster : Bool;
}

class ForwardPingToSquadEvent extends Event
{
}

class PSRefreshEvent extends Event
{
}

class AddActiveContextEvent extends Event
{
	var context : gamedeviceRequestType;
}

class RemoveActiveContextEvent extends Event
{
	var context : gamedeviceRequestType;
}

class PingNetworkGridEvent extends Event
{
	var ownerEntityPosition : Vector4;
	var fxResource : FxResource;
	var lifetime : Float;
	default lifetime = -1.0f;
	var pingType : EPingType;
	var revealSlave : Bool;
	default revealSlave = false;
	var revealMaster : Bool;
	default revealMaster = true;
	var ignoreRevealed : Bool;
}

class RevealNetworkGridEvent extends Event
{
	var shouldDraw : Bool;
	var ownerEntityPosition : Vector4;
	var fxDefault : FxResource;
	var fxBreached : FxResource;
	var revealSlave : Bool;
	default revealSlave = true;
	var revealMaster : Bool;
	default revealMaster = true;
}

class RevealDevicesGridEvent extends Event
{
	var shouldDraw : Bool;
	var ownerEntityPosition : Vector4;
	var fxDefault : FxResource;
	var revealSlave : Bool;
	default revealSlave = true;
	var revealMaster : Bool;
	default revealMaster = true;
}

class SetCustomPersonalLinkReason extends ScriptableDeviceAction
{
	[ tooltip = "Default == Disable the feature. Other options enables: Disable Personal Link Auto Disconnect and Personal Link Forced flags" ][ customEditor = "TweakDBGroupInheritance;Interactions.None" ]
	editable var reason : TweakDBID;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Set Custom Personal Link Reason";
	}

}

class ResolveAllSkillchecksEvent extends Event
{

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Resolve All Skillchecks";
	}

}

class SetSkillcheckEvent extends Event
{
	[ tooltip = "You need to select same container type as target device, or it won't work" ]
	editable inlined var skillcheckContainer : BaseSkillCheckContainer;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Set Skillcheck";
	}

}

class ChangeLoopCurveEvent extends Event
{
	editable var loopTime : Float;
	editable var loopCurve : CName;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Change Loop Curve";
	}

}

class RevealDevicesGridOnEntityEvent extends Event
{
	var shouldDraw : Bool;
}

class WakeUpFromRestartEvent extends Event
{
}

class SetQuickHackEvent extends Event
{
	var wasQuickHacked : Bool;
	var quickHackName : CName;
}

class SetQuickHackAttemptEvent extends Event
{
	var wasQuickHackAttempt : Bool;
}

class DelayedDeviceActionEvent extends Event
{
	var action : ScriptableDeviceAction;
}

class SpiderbotOrderCompletedEvent extends Event
{
}

struct AuthorizationData
{
	instanceeditable persistent var m_isAuthorizationModuleOn : Bool;
	default m_isAuthorizationModuleOn = true;
	hint m_isAuthorizationModuleOn = "Device is secured and takes clearance into consideration";
	instanceeditable persistent var m_alwaysExposeActions : Bool;
	hint m_alwaysExposeActions = "If on then device can be controlled by master devices even if this its secured";
	instanceeditable persistent var m_authorizationDataEntry : SecurityAccessLevelEntryClient;

	public static function IsAuthorizationValid( self : AuthorizationData ) : Bool
	{
		if( self.m_isAuthorizationModuleOn )
		{
			return SecurityAccessLevelEntryClient.IsDataValid( self.m_authorizationDataEntry );
		}
		return false;
	}

}

struct IllegalActionTypes
{
	instanceeditable persistent var regularActions : Bool;
	instanceeditable persistent var quickHacks : Bool;
	instanceeditable persistent var skillChecks : Bool;
	default skillChecks = true;
}

struct DestructionData
{
	instanceeditable persistent var m_durabilityType : EDeviceDurabilityType;
	instanceeditable persistent var m_canBeFixed : Bool;
}

struct DisassembleOptions
{
	instanceeditable persistent var m_canBeDisassembled : Bool;
}

struct SpiderbotScavengeOptions
{
	instanceeditable persistent var m_scavengableBySpiderbot : Bool;
}

enum EActionContext
{
	None = -1,
	Direct = 0,
	QHack = 1,
	Master = 2,
	Spiderbot = 3,
}

struct SPerformedActions
{
	persistent var ID : CName;
	persistent var ActionContext : array< EActionContext >;

	public static function ContainsActionContext( const self : ref< SPerformedActions >, actionContext : EActionContext ) : Bool
	{
		var i : Int32;
		for( i = 0; i < self.ActionContext.Size(); i += 1 )
		{
			if( self.ActionContext[ i ] == actionContext )
			{
				return true;
			}
		}
		return false;
	}

	public static function GetContextFromAction( selfPSID : PersistentID, actionToResolve : ScriptableDeviceAction ) : EActionContext
	{
		if( actionToResolve.IsQuickHack() )
		{
			return EActionContext.QHack;
		}
		if( actionToResolve.IsSpiderbotAction() )
		{
			return EActionContext.Spiderbot;
		}
		if( PersistentID.ExtractEntityID( selfPSID ) == actionToResolve.GetRequesterID() )
		{
			return EActionContext.Direct;
		}
		else if( PersistentID.ExtractEntityID( selfPSID ) != actionToResolve.GetRequesterID() )
		{
			return EActionContext.Master;
		}
		return EActionContext.None;
	}

}

class ScriptableDeviceComponentPS extends SharedGameplayPS
{
	default m_deviceState = EDeviceStatus.ON;
	default m_revealDevicesGrid = true;
	protected persistent var m_isInitialized : Bool;
	[ category = "Initialization overrides" ]
	protected instanceeditable var m_forceResolveStateOnAttach : Bool;
	[ category = "Initialization overrides" ]
	protected instanceeditable var m_forceVisibilityInAnimSystemOnLogicReady : Bool;
	protected var m_masters : array< DeviceComponentPS >;
	protected var m_mastersCached : Bool;
	default m_mastersCached = false;
	protected instanceeditable var m_deviceName : String;
	protected persistent var m_activationState : EActivationState;
	[ category = "Devices Grid" ]
	protected instanceeditable persistent var m_drawGridLink : Bool;
	default m_drawGridLink = false;
	[ category = "Devices Grid" ]
	protected instanceeditable var m_isLinkDynamic : Bool;
	[ category = "Devices Grid" ]
	protected instanceeditable var m_fullDepth : Bool;
	default m_fullDepth = true;
	[ category = "Devices Grid" ][ customEditor = "TweakDBGroupInheritance;VirtualNetwork" ]
	instanceeditable var m_virtualNetworkShapeID : TweakDBID;
	[ customEditor = "TweakDBGroupInheritance;Device" ][ unsavable = "true" ]
	protected instanceeditable persistent var m_tweakDBRecord : TweakDBID;
	default m_tweakDBRecord = T"Devices.GenericDevice";
	protected var m_tweakDBDescriptionRecord : TweakDBID;
	[ customEditor = "TweakDBGroupInheritance;DeviceContentAssignment" ]
	protected instanceeditable var m_contentScale : TweakDBID;
	protected persistent var m_skillCheckContainer : BaseSkillCheckContainer;
	[ category = "UI Zoom / Fullscreen" ]
	protected instanceeditable editable var m_hasUICameraZoom : Bool;
	[ category = "UI Zoom / Fullscreen" ]
	protected instanceeditable editable var m_allowUICameraZoomDynamicSwitch : Bool;
	[ category = "UI Zoom / Fullscreen" ]
	protected editable var m_hasFullScreenUI : Bool;
	protected var m_hasAuthorizationModule : Bool;
	default m_hasAuthorizationModule = true;
	protected var m_hasPersonalLinkSlot : Bool;
	default m_hasPersonalLinkSlot = false;
	[ category = "Backdoor Properties" ]
	protected instanceeditable var m_backdoorBreachDifficulty : EGameplayChallengeLevel;
	default m_backdoorBreachDifficulty = EGameplayChallengeLevel.EASY;
	[ category = "Backdoor Properties" ]
	protected instanceeditable var m_shouldSkipNetrunnerMinigame : Bool;
	[ unsavable = "true" ][ category = "Backdoor Properties" ][ customEditor = "TweakDBGroupInheritance;Minigame_Def" ][ tooltip = "Specifies what kind of minigame will be forced on the player" ]
	protected instanceeditable persistent var m_minigameDefinition : TweakDBID;
	protected persistent var m_minigameAttempt : Int32;
	default m_minigameAttempt = 1;
	protected persistent var m_hackingMinigameState : HackingMinigameState;
	[ category = "Quest" ][ tooltip = "IMPORTANT!: If this is set to true. Player will not be disconnected from the personal link automatically. Make sure if QuestForcePersonalLinkDisconnect action is necessary or not " ]
	protected instanceeditable var m_disablePersonalLinkAutoDisconnect : Bool;
	protected var m_canHandleAdvancedInteraction : Bool;
	default m_canHandleAdvancedInteraction = false;
	protected var m_canBeTrapped : Bool;
	default m_canBeTrapped = false;
	protected instanceeditable persistent var m_disassembleProperties : DisassembleOptions;
	protected instanceeditable persistent var m_flatheadScavengeProperties : SpiderbotScavengeOptions;
	protected instanceeditable persistent var m_destructionProperties : DestructionData;
	protected var m_canPlayerTakeOverControl : Bool;
	default m_canPlayerTakeOverControl = false;
	protected var m_canBeInDeviceChain : Bool;
	default m_canBeInDeviceChain = false;
	[ category = "Quest" ][ tooltip = "IMPORTANT!: IF TRUE > Connect Personal Link interaction will show up regardless of whether it's viable or sensible from gameplay POV. Use cautiously." ]
	protected instanceeditable persistent var m_personalLinkForced : Bool;
	[ category = "Quest" ][ tooltip = "IMPORTANT!: IF SET UP > This device is treated as if Personal Link Forced was set to true as well." ][ customEditor = "TweakDBGroupInheritance;Interactions.None" ]
	protected instanceeditable persistent var m_personalLinkCustomInteraction : TweakDBID;
	protected var m_personalLinkStatus : EPersonalLinkConnectionStatus;
	default m_personalLinkStatus = EPersonalLinkConnectionStatus.NOT_CONNECTED;
	protected var m_isAdvancedInteractionModeOn : Bool;
	default m_isAdvancedInteractionModeOn = false;
	protected persistent var m_juryrigTrapState : EJuryrigTrapState;
	protected persistent var m_isControlledByThePlayer : Bool;
	private var m_isHighlightedInFocusMode : Bool;
	protected persistent var m_wasQuickHacked : Bool;
	protected var m_wasQuickHackAttempt : Bool;
	protected var m_lastPerformedQuickHack : CName;
	protected var m_isGlitching : Bool;
	protected var m_isTimedTurnOff : Bool;
	protected persistent var m_isRestarting : Bool;
	protected persistent var m_blockSecurityWakeUp : Bool;
	protected var m_isLockedViaSequencer : Bool;
	protected var m_distractExecuted : Bool;
	protected var m_distractionTimeCompleted : Bool;
	[ category = "NPC workspot" ]
	protected editable var m_hasNPCWorkspotKillInteraction : Bool;
	[ category = "NPC workspot" ]
	protected editable var m_shouldNPCWorkspotFinishLoop : Bool;
	protected persistent var m_durabilityState : EDeviceDurabilityState;
	protected persistent var m_hasBeenScavenged : Bool;
	protected persistent var m_currentlyAuthorizedUsers : array< SecuritySystemClearanceEntry >;
	protected persistent var m_performedActions : array< SPerformedActions >;
	protected persistent var m_isInitialStateOperationPerformed : Bool;
	protected instanceeditable persistent var m_illegalActions : IllegalActionTypes;
	[ category = "Quest" ]
	protected instanceeditable persistent var m_disableQuickHacks : Bool;
	private var m_availableQuickHacks : array< CName >;
	protected var m_isKeyloggerInstalled : Bool;
	private var m_actionsWithDisabledRPGChecks : array< TweakDBID >;
	private var m_availableSpiderbotActions : array< CName >;
	protected var m_currentSpiderbotActionPerformed : ScriptableDeviceAction;
	protected persistent var m_isSpiderbotInteractionOrdered : Bool;
	[ category = "Device Scanner Options" ]
	protected instanceeditable var m_shouldScannerShowStatus : Bool;
	default m_shouldScannerShowStatus = true;
	[ category = "Device Scanner Options" ]
	protected instanceeditable var m_shouldScannerShowNetwork : Bool;
	default m_shouldScannerShowNetwork = true;
	[ category = "Device Scanner Options" ]
	protected instanceeditable var m_shouldScannerShowAttitude : Bool;
	[ category = "Device Scanner Options" ]
	protected instanceeditable var m_shouldScannerShowRole : Bool;
	[ category = "Device Scanner Options" ]
	protected instanceeditable var m_shouldScannerShowHealth : Bool;
	[ category = "Device Debug Properties" ][ tooltip = "If this is on and someone used DebugDevices console command, only logs from this device will be shown" ]
	protected instanceeditable persistent var m_debugDevice : Bool;
	[ category = "Device Debug Properties" ]
	protected instanceeditable persistent var m_debugName : CName;
	[ category = "Device Debug Properties" ]
	protected var m_debugExposeQuickHacks : Bool;
	[ unsavable = "true" ]
	protected persistent var m_debugPath : CName;
	[ unsavable = "true" ]
	protected persistent var m_debugID : Uint32;
	private var m_isUnderEMPEffect : Bool;
	[ category = "Device Operations" ]
	protected instanceeditable inlined persistent var m_deviceOperationsSetup : DeviceOperationsContainer;
	[ category = "Connection Highlight Obejcts" ]
	protected const instanceeditable persistent var m_connectionHighlightObjects : array< NodeRef >;
	private var m_activeContexts : array< gamedeviceRequestType >;
	private var m_playstyles : array< EPlaystyle >;
	private var m_quickHackVulnerabilties : array< TweakDBID >;
	private var m_quickHackVulnerabiltiesInitialized : Bool;
	private mutable var m_willingInvestigators : array< EntityID >;
	private persistent var m_isInteractive : Bool;
	default m_isInteractive = true;

	protected event OnInstantiated()
	{
		if( !( IsFinal() ) )
		{
			m_debugPath = StringToName( StrAfterLast( EntityID.ToDebugString( PersistentID.ExtractEntityID( GetID() ) ), "/" ) );
			m_debugID = EntityID.GetHash( PersistentID.ExtractEntityID( GetID() ) );
		}
		if( !( IsInitialized() ) )
		{
			Initialize();
		}
	}

	public const override function GetParents( out outDevices : array< DeviceComponentPS > )
	{
		var i : Int32;
		if( m_mastersCached )
		{
			for( i = 0; i < m_masters.Size(); i += 1 )
			{
				outDevices.PushBack( m_masters[ i ] );
			}
			return;
		}
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetParents( GetMyEntityID(), outDevices );
	}

	public const override function GetImmediateParents() : array< DeviceComponentPS >
	{
		var masters : array< DeviceComponentPS >;
		if( m_mastersCached )
		{
			return m_masters;
		}
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetParents( GetMyEntityID(), masters );
		return masters;
	}

	protected virtual function Initialize()
	{
		m_isInitialized = true;
		if( !( IsFinal() ) )
		{
			m_debugPath = StringToName( StrAfterLast( EntityID.ToDebugString( PersistentID.ExtractEntityID( GetID() ) ), "/" ) );
			m_debugID = EntityID.GetHash( PersistentID.ExtractEntityID( GetID() ) );
		}
	}

	public export function OnGameAttached( evt : GameAttachedEvent ) : EntityNotificationType
	{
		var defaultTDBID : TweakDBID;
		CacheDevices();
		if( !( IsInitialized() ) )
		{
			Initialize();
		}
		if( TDBID.IsValid( m_personalLinkCustomInteraction ) )
		{
			defaultTDBID = T"Interactions.None";
			if( m_personalLinkCustomInteraction != defaultTDBID )
			{
				m_personalLinkForced = true;
				m_disablePersonalLinkAutoDisconnect = true;
			}
		}
		m_isAttachedToGame = true;
		if( IsStringValid( evt.displayName ) )
		{
			m_deviceName = evt.displayName;
		}
		if( evt.isGameplayRelevant )
		{
			if( TDBID.IsValid( evt.contentScale ) )
			{
				m_contentScale = evt.contentScale;
			}
			InitializeRPGParams();
			DetermineInitialPlaystyle();
		}
		GameAttached();
		return EntityNotificationType.SendThisEventToEntity;
	}

	public export function OnLogicReady( evt : SetLogicReadyEvent ) : EntityNotificationType
	{
		m_isLogicReady = evt.isReady;
		LogicReady();
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function GameAttached() {}

	protected virtual function LogicReady()
	{
		TryInitializeSkillChecks();
	}

	protected override function SetDeviceState( state : EDeviceStatus )
	{
		var removeFromChain : RemoveFromChainRequest;
		if( CanBeInDeviceChain() && ( ( ( Int32 )( state ) ) <= ( ( Int32 )( EDeviceStatus.UNPOWERED ) ) ) )
		{
			removeFromChain = new RemoveFromChainRequest;
			removeFromChain.requestSource = GetMyEntityID();
			GetTakeOverControlSystem().QueueRequest( removeFromChain );
		}
		super.SetDeviceState( state );
	}

	protected virtual function OnDeviceDynamicConnectionChange( evt : DeviceDynamicConnectionChange ) : EntityNotificationType
	{
		m_mastersCached = false;
		CacheDevices();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function CacheDevices()
	{
		if( !( m_mastersCached ) )
		{
			GameInstance.GetDeviceSystem( GetGameInstance() ).GetParents( GetMyEntityID(), m_masters );
			m_mastersCached = true;
		}
	}

	protected function InitializeRPGParams()
	{
		InitializeContentScale();
		InitializeStats();
		InitializeStatPools();
	}

	protected function InitializeContentScale()
	{
		var powerLevelMod : gameConstantStatModifierData;
		var statSystem : StatsSystem;
		statSystem = GameInstance.GetStatsSystem( GetGameInstance() );
		powerLevelMod = new gameConstantStatModifierData;
		powerLevelMod.modifierType = gameStatModifierType.Additive;
		powerLevelMod.statType = gamedataStatType.PowerLevel;
		powerLevelMod.value = ( ( Float )( GameInstance.GetLevelAssignmentSystem( GetGameInstance() ).GetLevelAssignment( m_contentScale ) ) );
		statSystem.AddModifier( GetMyEntityID(), powerLevelMod );
	}

	protected function InitializeStats()
	{
		var i : Int32;
		var record : Device_Record;
		var statList : array< weak< StatModifier_Record > >;
		var statModifiers : array< gameStatModifierData >;
		var statSystem : StatsSystem;
		statSystem = GameInstance.GetStatsSystem( GetGameInstance() );
		record = TweakDBInterface.GetDeviceRecord( GetTweakDBRecord() );
		if( record )
		{
			record.StatModifiers( statList );
			statModifiers.Resize( statList.Size() );
			for( i = 0; i < statList.Size(); i += 1 )
			{
				statModifiers[ i ] = RPGManager.StatRecordToModifier( statList[ i ] );
			}
			statSystem.AddModifiers( GetMyEntityID(), statModifiers );
		}
	}

	protected function InitializeStatPools()
	{
		var i : Int32;
		var record : Device_Record;
		var statPools : array< weak< StatPool_Record > >;
		var statPoolsSystem : StatPoolsSystem;
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGameInstance() );
		record = TweakDBInterface.GetDeviceRecord( GetTweakDBRecord() );
		if( record )
		{
			record.StatPools( statPools );
			for( i = 0; i < statPools.Size(); i += 1 )
			{
				statPoolsSystem.RequestAddingStatPool( GetMyEntityID(), statPools[ i ].GetID() );
			}
		}
	}

	protected virtual function GetSkillCheckContainerForSetup() : BaseSkillCheckContainer
	{
		return NULL;
	}

	protected function TryInitializeSkillChecks()
	{
		var container : BaseSkillCheckContainer;
		container = GetSkillCheckContainerForSetup();
		if( container )
		{
			InitializeSkillChecks( container );
		}
	}

	protected function InitializeSkillChecks( container : BaseSkillCheckContainer, optional isOverride : Bool )
	{
		if( !( m_skillCheckContainer ) )
		{
			m_skillCheckContainer = container;
		}
		if( isOverride || !( m_skillCheckContainer.IsInitialized() ) )
		{
			InitializeWrapper( container );
		}
	}

	protected function InitializeWrapper( container : BaseSkillCheckContainer )
	{
		m_skillCheckContainer.Initialize( container );
		if( m_skillCheckContainer.GetHackingSlot() )
		{
			m_skillCheckContainer.GetHackingSlot().SetDuration( 1.0 );
		}
		InitializeBackdoorSkillcheck();
	}

	public const function IsInitialized() : Bool
	{
		return m_isInitialized;
	}

	public const function ForceResolveGameplayStateOnAttach() : Bool
	{
		return m_forceResolveStateOnAttach;
	}

	public const function ForceVisibilityInAnimSystemOnLogicReady() : Bool
	{
		return m_forceVisibilityInAnimSystemOnLogicReady;
	}

	public const override function GetDeviceName() : String
	{
		return m_deviceName;
	}

	public const function GetTweakDBRecord() : TweakDBID
	{
		return m_tweakDBRecord;
	}

	public const function GetTweakDBDescriptionRecord() : TweakDBID
	{
		return m_tweakDBDescriptionRecord;
	}

	public const function GetContentAssignmentID() : TweakDBID
	{
		return m_contentScale;
	}

	public const virtual function IsConnectedToSystem() : Bool
	{
		var i : Int32;
		var ancestors : array< DeviceComponentPS >;
		GetAncestors( ancestors );
		for( i = 0; i < ancestors.Size(); i += 1 )
		{
			if( ( ( DeviceSystemBaseControllerPS )( ancestors[ i ] ) ) )
			{
				return true;
			}
		}
		return false;
	}

	public const override function IsPartOfSystem( systemType : ESystems ) : Bool
	{
		switch( systemType )
		{
			case ESystems.SecuritySystem:
				return IsConnectedToSecuritySystem();
			case ESystems.MaintenanceSystem:
				return IsConnectedToMaintenanceSystem();
			case ESystems.AccessPoints:
				return IsConnectedToBackdoorDevice();
			default:
				return false;
		}
	}

	public const function GetDurabilityState() : EDeviceDurabilityState
	{
		return m_durabilityState;
	}

	public const function GetActivationState() : EActivationState
	{
		return m_activationState;
	}

	public const function HasAdvancedInteractions() : Bool
	{
		return m_canHandleAdvancedInteraction;
	}

	public const function CanBeTrapped() : Bool
	{
		return m_canBeTrapped;
	}

	public quest const function CanBeDisassembled() : Bool
	{
		return m_disassembleProperties.m_canBeDisassembled;
	}

	public quest const function CanBeFixed() : Bool
	{
		return m_destructionProperties.m_canBeFixed;
	}

	public const function GetDurabilityType() : EDeviceDurabilityType
	{
		return m_destructionProperties.m_durabilityType;
	}

	public const function HasPersonalLinkSlot() : Bool
	{
		return m_hasPersonalLinkSlot;
	}

	public const function CanBeScavengedBySpiderbot() : Bool
	{
		return m_flatheadScavengeProperties.m_scavengableBySpiderbot;
	}

	public const function HasQuickHacksDisabled() : Bool
	{
		return m_disableQuickHacks;
	}

	public const virtual function GetMinigameDefinition() : TweakDBID
	{
		var ap : AccessPointControllerPS;
		if( TDBID.IsValid( m_minigameDefinition ) )
		{
			return m_minigameDefinition;
		}
		else
		{
			ap = GetBackdoorAccessPoint();
			if( ap )
			{
				return ap.GetMinigameDefinition();
			}
		}
		return m_minigameDefinition;
	}

	public const function OnQuestMinigameRequest()
	{
		var minigameID : TweakDBID;
		minigameID = GetMinigameDefinition();
		if( !( TDBID.IsValid( minigameID ) ) )
		{
			minigameID = T"minigame_v2.DefaultItemMinigame";
		}
		GetNetworkBlackboard().SetVariant( GetNetworkBlackboardDef().MinigameDef, minigameID );
	}

	protected const virtual function ShouldExposePersonalLinkAction() : Bool
	{
		if( IsPersonalLinkConnecting() )
		{
			return false;
		}
		if( TDBID.IsValid( m_personalLinkCustomInteraction ) && IsPersonalLinkConnected() )
		{
			return false;
		}
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTED || m_personalLinkForced )
		{
			return true;
		}
		if( IsHackingSkillCheckActive() )
		{
			return false;
		}
		if( IsGlitching() || IsDistracting() )
		{
			return false;
		}
		if( HasNetworkBackdoor() )
		{
			return !( WasHackingMinigameSucceeded() );
		}
		return false;
	}

	public function SetHasPersonalLinkSlot( isPersonalLinkSlotPresent : Bool )
	{
		m_hasPersonalLinkSlot = isPersonalLinkSlotPresent;
	}

	public function SetHasUICameraZoom( hasUICameraZoom : Bool )
	{
		m_hasUICameraZoom = hasUICameraZoom;
	}

	public function SetInteractionState( isActive : Bool )
	{
		if( m_isInteractive != isActive )
		{
			m_isInteractive = isActive;
		}
	}

	public const virtual function IsInteractive() : Bool
	{
		return m_isInteractive;
	}

	public quest const function IsAdvancedInteractionModeOn() : Bool
	{
		return m_isAdvancedInteractionModeOn;
	}

	public quest const function IsAdvancedInteractionModeOff() : Bool
	{
		return !( IsAdvancedInteractionModeOn() );
	}

	public const function GetPersonalLinkStatus() : EPersonalLinkConnectionStatus
	{
		return m_personalLinkStatus;
	}

	public quest const function IsPersonalLinkConnected() : Bool
	{
		return m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTED;
	}

	public quest const function IsPersonalLinkConnecting() : Bool
	{
		return m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTING;
	}

	public quest const function IsPersonalLinkDisconnecting() : Bool
	{
		return m_personalLinkStatus == EPersonalLinkConnectionStatus.DISCONNECTING;
	}

	public quest const function IsPersonalLinkDisconnected() : Bool
	{
		return ( m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED || m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTING ) || m_personalLinkStatus == EPersonalLinkConnectionStatus.DISCONNECTING;
	}

	public const function IsSecurityWakeUpBlocked() : Bool
	{
		return m_blockSecurityWakeUp;
	}

	public const function HasUICameraZoom() : Bool
	{
		return m_hasUICameraZoom;
	}

	public const function AllowsUICameraZoomDynamicSwitch() : Bool
	{
		return m_allowUICameraZoomDynamicSwitch;
	}

	public const function HasFullScreenUI() : Bool
	{
		return m_hasFullScreenUI;
	}

	public const function IsHighlightedInFocusMode() : Bool
	{
		return m_isHighlightedInFocusMode;
	}

	public const function IsControlledByPlayer() : Bool
	{
		return m_isControlledByThePlayer;
	}

	public const function CanPlayerTakeOverControl() : Bool
	{
		return m_canPlayerTakeOverControl;
	}

	public quest const function IsRestarting() : Bool
	{
		return m_isRestarting;
	}

	public const function IsGlitching() : Bool
	{
		return m_isGlitching;
	}

	public const function IsTimedTurnOff() : Bool
	{
		return m_isTimedTurnOff;
	}

	public quest const function IsDistracting() : Bool
	{
		return m_distractExecuted;
	}

	public const function IsActivated() : Bool
	{
		if( m_activationState == EActivationState.ACTIVATED )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public const function GetActiveContexts() : array< gamedeviceRequestType >
	{
		return m_activeContexts;
	}

	public const function GetMinigameAttempt() : Int32
	{
		return m_minigameAttempt;
	}

	public const function ShouldScannerShowStatus() : Bool
	{
		return m_shouldScannerShowStatus;
	}

	public const function ShouldScannerShowNetwork() : Bool
	{
		return m_shouldScannerShowNetwork;
	}

	public const function ShouldScannerShowAttitude() : Bool
	{
		return m_shouldScannerShowAttitude;
	}

	public const function ShouldScannerShowRole() : Bool
	{
		return m_shouldScannerShowRole;
	}

	public const function ShouldScannerShowHealth() : Bool
	{
		return m_shouldScannerShowHealth;
	}

	public const function CanBeInDeviceChain() : Bool
	{
		return ( m_canBeInDeviceChain && IsPowered() ) && GetDurabilityState() == EDeviceDurabilityState.NOMINAL;
	}

	public const virtual function ShouldShowExamineIntaraction() : Bool
	{
		return ( ( ( HasUICameraZoom() && !( m_isAdvancedInteractionModeOn ) ) && IsON() ) && !( IsPersonalLinkConnected() ) ) && !( IsPersonalLinkConnecting() );
	}

	public quest const function IsSpiderbotInteractionOrdered() : Bool
	{
		return m_isSpiderbotInteractionOrdered;
	}

	protected function OnSetIsSpiderbotInteractionOrderedEvent( evt : SetIsSpiderbotInteractionOrderedEvent ) : EntityNotificationType
	{
		m_isSpiderbotInteractionOrdered = evt.value;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public quest const function IsInDirectInteractionRange() : Bool
	{
		return HasActiveContext( gamedeviceRequestType.Direct );
	}

	public const function HasActiveContext( context : gamedeviceRequestType ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_activeContexts.Size(); i += 1 )
		{
			if( m_activeContexts[ i ] == context )
			{
				return true;
			}
		}
		return false;
	}

	public const function HasPlaystyle( playstyle : EPlaystyle ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_playstyles.Size(); i += 1 )
		{
			if( m_playstyles[ i ] == playstyle )
			{
				return true;
			}
		}
		return false;
	}

	public const function GetPlaystyles() : array< EPlaystyle >
	{
		return m_playstyles;
	}

	public const function HasAnyPlaystyle() : Bool
	{
		return m_playstyles.Size() > 0;
	}

	public function PassDeviceName( const deviceName : ref< String >, optional dbgDeviceName : CName )
	{
		if( !( IsStringValid( m_deviceName ) ) )
		{
			m_deviceName = deviceName;
		}
	}

	public function DisbaleRPGChecksForAction( actionID : TweakDBID )
	{
		if( !( IsActionRPGRequirementDisabled( actionID ) ) )
		{
			m_actionsWithDisabledRPGChecks.PushBack( actionID );
		}
	}

	public function EnableRPGChecksForAction( actionID : TweakDBID )
	{
		var i : Int32;
		for( i = m_actionsWithDisabledRPGChecks.Size() - 1; i >= 0; i -= 1 )
		{
			if( actionID == m_actionsWithDisabledRPGChecks[ i ] )
			{
				m_actionsWithDisabledRPGChecks.Erase( i );
			}
		}
	}

	public const function HasAnyActionsWithDisabledRPGChecks() : Bool
	{
		return m_actionsWithDisabledRPGChecks.Size() > 0;
	}

	private const function IsActionRPGRequirementDisabled( actionID : TweakDBID ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_actionsWithDisabledRPGChecks.Size(); i += 1 )
		{
			if( actionID == m_actionsWithDisabledRPGChecks[ i ] )
			{
				return true;
			}
		}
		return false;
	}

	public function SetGlitchingState( isGlitching : Bool )
	{
		m_isGlitching = isGlitching;
	}

	public function AddActiveContext( context : gamedeviceRequestType )
	{
		if( !( HasActiveContext( context ) ) )
		{
			m_activeContexts.PushBack( context );
			RefreshPS();
		}
	}

	public function OnAddActiveContext( evt : AddActiveContextEvent ) : EntityNotificationType
	{
		AddActiveContext( evt.context );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function RemoveActiveContext( context : gamedeviceRequestType )
	{
		var i : Int32;
		for( i = m_activeContexts.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_activeContexts[ i ] == context )
			{
				m_activeContexts.Erase( i );
				RefreshPS();
				break;
			}
		}
	}

	public function OnRemoveActiveContext( evt : RemoveActiveContextEvent ) : EntityNotificationType
	{
		RemoveActiveContext( evt.context );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function RefreshPS()
	{
		var evt : PSRefreshEvent;
		evt = new PSRefreshEvent;
		QueuePSEvent( this, evt );
	}

	public constexpr export function OnPSRefreshEvent( evt : PSRefreshEvent ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function AddPlaystyle( playstyle : EPlaystyle )
	{
		if( !( HasPlaystyle( playstyle ) ) )
		{
			m_playstyles.PushBack( playstyle );
		}
	}

	public function RemovePlaystyle( playstyle : EPlaystyle )
	{
		var i : Int32;
		for( i = m_playstyles.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_playstyles[ i ] == playstyle )
			{
				m_playstyles.Erase( i );
				break;
			}
		}
	}

	public function DetermineInitialPlaystyle()
	{
		if( ( !( m_disableQuickHacks ) && CanCreateAnyQuickHackActions() ) && ( ( IsQuickHacksExposed() || IsConnectedToBackdoorDevice() ) || HasNetworkBackdoor() ) )
		{
			AddPlaystyle( EPlaystyle.NETRUNNER );
			UpdateQuickHackableState( true );
		}
		else
		{
			RemovePlaystyle( EPlaystyle.NETRUNNER );
			UpdateQuickHackableState( false );
		}
		if( CanCreateAnySpiderbotActions() )
		{
			AddPlaystyle( EPlaystyle.TECHIE );
		}
		else
		{
			RemovePlaystyle( EPlaystyle.TECHIE );
		}
		m_quickHackVulnerabiltiesInitialized = false;
	}

	protected virtual function OnActionOverride( evt : ActionOverride ) : EntityNotificationType
	{
		QueuePSEvent( this, ActionSetDeviceOFF() );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function GetAllQuickHackVulnerabilities() : array< TweakDBID >
	{
		InitializeQuickHackVulnerabilities();
		return m_quickHackVulnerabilties;
	}

	public function GetActiveQuickHackVulnerabilities() : array< TweakDBID >
	{
		var i : Int32;
		var returnValue : array< TweakDBID >;
		InitializeQuickHackVulnerabilities();
		for( i = 0; i < m_quickHackVulnerabilties.Size(); i += 1 )
		{
			if( CanPlayerUseQuickHackVulnerability( m_quickHackVulnerabilties[ i ] ) )
			{
				returnValue.PushBack( m_quickHackVulnerabilties[ i ] );
			}
		}
		return returnValue;
	}

	public function HasAnyActiveQuickHackVulnerabilities() : Bool
	{
		var list : array< TweakDBID >;
		if( !( HasPlaystyle( EPlaystyle.NETRUNNER ) ) )
		{
			return false;
		}
		list = GetActiveQuickHackVulnerabilities();
		return list.Size() > 0;
	}

	public const function CanPlayerUseQuickHackVulnerability( data : TweakDBID ) : Bool
	{
		var i : Int32;
		var playerQhackList : array< TweakDBID >;
		var objectActionRecord : ObjectAction_Record;
		playerQhackList = RPGManager.GetPlayerQuickHackList( GetPlayer( GetGameInstance() ) );
		for( i = 0; i < playerQhackList.Size(); i += 1 )
		{
			objectActionRecord = TweakDBInterface.GetObjectActionRecord( playerQhackList[ i ] );
			if( ( objectActionRecord && objectActionRecord.GameplayCategory() ) && ( objectActionRecord.GameplayCategory().GetID() == data ) )
			{
				return true;
			}
		}
		return false;
	}

	private function InitializeQuickHackVulnerabilities()
	{
		var i : Int32;
		var actions : array< DeviceAction >;
		var context : GetActionsContext;
		var action : ScriptableDeviceAction;
		var categoryID : TweakDBID;
		if( m_quickHackVulnerabiltiesInitialized )
		{
			return;
		}
		m_quickHackVulnerabilties.Clear();
		context = GenerateContext( gamedeviceRequestType.Remote, GetClearance(), GetPlayerMainObject(), PersistentID.ExtractEntityID( GetID() ) );
		context.ignoresRPG = true;
		GetQuickHackActions( actions, context );
		for( i = 0; i < actions.Size(); i += 1 )
		{
			action = ( ( ScriptableDeviceAction )( actions[ i ] ) );
			if( action )
			{
				categoryID = action.GetGameplayCategoryID();
				if( TDBID.IsValid( categoryID ) )
				{
					AddQuickHackVulnerability( categoryID );
				}
			}
		}
		m_quickHackVulnerabiltiesInitialized = true;
	}

	public const function HasQuickHackVulnerability( data : TweakDBID ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_quickHackVulnerabilties.Size(); i += 1 )
		{
			if( m_quickHackVulnerabilties[ i ] == data )
			{
				return true;
			}
		}
		return false;
	}

	private const function HasHasQuickHackVulnerabilitiesInitialized() : Bool
	{
		return m_quickHackVulnerabiltiesInitialized;
	}

	public function AddQuickHackVulnerability( data : TweakDBID )
	{
		if( !( HasQuickHackVulnerability( data ) ) )
		{
			m_quickHackVulnerabilties.PushBack( data );
		}
	}

	public function RemoveQuickHackVoulnerability( data : TweakDBID )
	{
		var i : Int32;
		for( i = m_quickHackVulnerabilties.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_quickHackVulnerabilties[ i ] == data )
			{
				m_quickHackVulnerabilties.Erase( i );
				break;
			}
		}
	}

	private function UpdateQuickHackableState( isQuickHackable : Bool )
	{
		var quickHackableEvent : SetQuickHackableMask;
		quickHackableEvent = new SetQuickHackableMask;
		quickHackableEvent.isQuickHackable = isQuickHackable;
		GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), quickHackableEvent );
	}

	public function SetPlayerTakeOverControl( canBeControlled : Bool )
	{
		m_canPlayerTakeOverControl = canBeControlled;
	}

	public const function GetJuryrigTrapState() : EJuryrigTrapState
	{
		return m_juryrigTrapState;
	}

	public quest const function IsJuryrigTrapArmed() : Bool
	{
		return m_juryrigTrapState == EJuryrigTrapState.ARMED;
	}

	public quest const function IsJuryrigTrapUnarmed() : Bool
	{
		return m_juryrigTrapState == EJuryrigTrapState.UNARMED;
	}

	public quest const function IsJuryrigTrapTriggered() : Bool
	{
		return m_juryrigTrapState == EJuryrigTrapState.TRIGGERED;
	}

	public export quest const function IsON() : Bool
	{
		return m_deviceState == EDeviceStatus.ON;
	}

	protected function IsOFFTimed() : Bool
	{
		return !( !( IsON() ) && IsTimedTurnOff() );
	}

	public export quest const function IsOFF() : Bool
	{
		return m_deviceState == EDeviceStatus.OFF;
	}

	public quest const function IsEnabled() : Bool
	{
		return !( IsDisabled() );
	}

	public export quest const function IsDisabled() : Bool
	{
		return m_deviceState == EDeviceStatus.DISABLED;
	}

	public export quest const function IsPoweredAndEnabled() : Bool
	{
		return IsEnabled() && IsPowered();
	}

	public quest const function IsPowered() : Bool
	{
		if( m_deviceState == EDeviceStatus.UNPOWERED || m_deviceState == EDeviceStatus.DISABLED )
		{
			return false;
		}
		return true;
	}

	public export quest const function IsUnpowered() : Bool
	{
		if( !( IsInitialized() ) )
		{
			return false;
		}
		if( m_deviceState == EDeviceStatus.UNPOWERED )
		{
			return true;
		}
		return false;
	}

	public const override function GetDeviceStatus() : String
	{
		return GetDeviceStatusAction().GetCurrentDisplayString();
	}

	public const virtual function GetDeviceStatusAction() : BaseDeviceStatus
	{
		return ActionDeviceStatus();
	}

	public const function GetScannerStatusRecord() : TweakDBID
	{
		return GetDeviceStatusAction().GetScannerStatusRecord();
	}

	public const virtual function GetDeviceStatusTextData() : inkTextParams
	{
		var textData : inkTextParams;
		textData = new inkTextParams;
		textData.AddLocalizedString( "TEXT_PRIMARY", GetDeviceStatus() );
		return textData;
	}

	public function GetActionByName( actionName : CName, optional entityID : EntityID ) : DeviceAction
	{
		var actions : array< DeviceAction >;
		var foundAction : DeviceAction;
		var i : Int32;
		GetActions( actions, GetTotalClearance( entityID ) );
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( actions[ i ].actionName == actionName )
			{
				foundAction = actions[ i ];
				break;
			}
		}
		return foundAction;
	}

	public function GetActionByName( actionName : CName, const context : ref< GetActionsContext > ) : DeviceAction
	{
		var actions : array< DeviceAction >;
		var foundAction : DeviceAction;
		var i : Int32;
		if( context.requestType == gamedeviceRequestType.Remote )
		{
			if( IsPowered() )
			{
				GetQuickHackActions( actions, context );
			}
			GetSpiderbotActions( actions, context );
		}
		else
		{
			GetActions( actions, context );
		}
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( actions[ i ].actionName == actionName )
			{
				foundAction = actions[ i ];
				break;
			}
		}
		return foundAction;
	}

	public function GetMinigameActionByName( actionName : CName, const context : ref< GetActionsContext > ) : DeviceAction
	{
		var actions : array< DeviceAction >;
		var foundAction : DeviceAction;
		var i : Int32;
		GetMinigameActions( actions, context );
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( actions[ i ].actionName == actionName )
			{
				foundAction = actions[ i ];
				break;
			}
		}
		return foundAction;
	}

	protected function GetTotalClearance( optional entityID : EntityID ) : GetActionsContext
	{
		var emptyID : EntityID;
		var context : GetActionsContext;
		emptyID = EntityID();
		context.clearance = Clearance.CreateClearance( 0, 100 );
		if( EntityID.IsDefined( entityID ) )
		{
			context.requestorID = entityID;
		}
		else
		{
			context.requestorID = emptyID;
		}
		return context;
	}

	public function GetTotalClearanceValue() : Clearance
	{
		return Clearance.CreateClearance( 0, 100 );
	}

	protected function GetCustomClearance( min, max : Int32 ) : Clearance
	{
		var context : GetActionsContext;
		context.clearance = Clearance.CreateClearance( min, max );
		return context.clearance;
	}

	public const override function GetClearance() : Clearance
	{
		return Clearance.CreateClearance( 2, 5 );
	}

	public const function IsPlayerPerformingTakedown() : Bool
	{
		var blackboard : IBlackboard;
		var playerStateDefault : Bool;
		blackboard = GameInstance.GetBlackboardSystem( GetGameInstance() ).GetLocalInstanced( GetPlayer( GetGameInstance() ).GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		playerStateDefault = blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Takedown ) == ( ( Int32 )( gamePSMTakedown.Default ) );
		return !( playerStateDefault );
	}

	public const function IsDisruptivePlayerStatusEffectPresent() : Bool
	{
		var player : PlayerPuppet;
		player = GetPlayer( GetGameInstance() );
		return ( ( m_isUnderEMPEffect || StatusEffectSystem.ObjectHasStatusEffectOfType( player, gamedataStatusEffectType.Stunned ) ) || StatusEffectSystem.ObjectHasStatusEffectOfType( player, gamedataStatusEffectType.Knockdown ) ) || StatusEffectSystem.ObjectHasStatusEffectOfType( player, gamedataStatusEffectType.Electrocuted );
	}

	public override function GetActions( out outActions : array< DeviceAction >, context : GetActionsContext ) : Bool
	{
		var game : GameInstance;
		var isPerformingTakedown : Bool;
		isPerformingTakedown = IsPlayerPerformingTakedown();
		game = GetGameInstance();
		if( ToggleActivation.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionToggleActivation() );
		}
		if( IsDisabled() )
		{
			return false;
		}
		if( SetDeviceUnpowered.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionTogglePower() );
			outActions.PushBack( ActionSetDeviceUnpowered() );
			outActions.PushBack( ActionSetDevicePowered() );
		}
		if( IsPersonalLinkConnecting() )
		{
			if( TDBID.IsValid( m_personalLinkCustomInteraction ) && ( m_personalLinkCustomInteraction != T"Interactions.None" ) )
			{
				return false;
			}
			return false;
		}
		if( DisassembleDevice.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionDisassembleDevice() );
		}
		if( CanBeFixed() )
		{
			outActions.PushBack( ActionFixDevice() );
		}
		if( ToggleJuryrigTrap.IsDefaultConditionMet( this, context ) && GameInstance.GetTransactionSystem( game ).HasItem( GameInstance.GetPlayerSystem( game ).GetLocalPlayerMainGameObject(), ItemID.CreateQuery( T"Items.grenadeFrag" ) ) )
		{
			outActions.PushBack( ActionToggleJuryrigTrap() );
		}
		if( ActionScavenge.IsDefaultConditionMet( this ) )
		{
			outActions.PushBack( ActionScavenge( context ) );
		}
		if( SetAuthorizationModuleON.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionSetAuthorizationModuleON() );
		}
		if( SetAuthorizationModuleOFF.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionSetAuthorizationModuleOFF() );
		}
		if( ShouldShowExamineIntaraction() )
		{
			if( !( isPerformingTakedown ) )
			{
				outActions.PushBack( ActionToggleZoomInteraction() );
			}
		}
		if( ( HasFullScreenUI() && !( m_isAdvancedInteractionModeOn ) ) && IsPersonalLinkDisconnected() )
		{
			if( !( isPerformingTakedown ) )
			{
				outActions.PushBack( ActionOpenFullscreenUI() );
			}
		}
		if( HasPersonalLinkSlot() )
		{
			if( !( isPerformingTakedown ) )
			{
				if( ( context.requestType == gamedeviceRequestType.Direct && IsPowered() ) && ShouldExposePersonalLinkAction() )
				{
					if( m_disablePersonalLinkAutoDisconnect && m_personalLinkStatus != EPersonalLinkConnectionStatus.NOT_CONNECTED )
					{
					}
					else
					{
						if( m_skillCheckContainer.GetHackingSlot().IsActive() )
						{
						}
						else
						{
							outActions.PushBack( ActionTogglePersonalLink( context.processInitiatorObject ) );
						}
					}
				}
			}
		}
		if( SetDeviceON.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionSetDeviceON() );
			outActions.PushBack( ActionSetDeviceOFF() );
		}
		if( isPerformingTakedown )
		{
			return false;
		}
		if( PushReturnActions( outActions, context ) )
		{
			return false;
		}
		SetActionIllegality( outActions, m_illegalActions.regularActions );
		return true;
	}

	public const function IsPotentiallyQuickHackable() : Bool
	{
		return !( m_disableQuickHacks ) && CanCreateAnyQuickHackActions();
	}

	protected const virtual function CanCreateAnyQuickHackActions() : Bool
	{
		return false;
	}

	protected virtual function GetQuickHackActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		FinalizeGetQuickHackActions( outActions, context );
	}

	protected virtual function GetMinigameActions( outActions : ref< array< DeviceAction > >, const context : ref< GetActionsContext > ) {}

	protected function FinalizeGetQuickHackActions( outActions : ref< array< DeviceAction > >, const context : ref< GetActionsContext > )
	{
		var currentAction : ScriptableDeviceAction;
		if( GetDurabilityState() != EDeviceDurabilityState.NOMINAL )
		{
			return;
		}
		if( m_disableQuickHacks )
		{
			if( outActions.Size() > 0 )
			{
				outActions.Clear();
			}
			return;
		}
		if( IsConnectedToBackdoorDevice() )
		{
			currentAction = ActionRemoteBreach();
			currentAction.SetInactiveWithReason( !( IsBreached() ), "LocKey#27728" );
			outActions.PushBack( currentAction );
			currentAction = ActionPing();
			currentAction.SetInactiveWithReason( !( GetNetworkSystem().HasActivePing( GetMyEntityID() ) ), "LocKey#49279" );
			outActions.PushBack( currentAction );
		}
		else if( HasNetworkBackdoor() )
		{
			currentAction = ActionPing();
			currentAction.SetInactiveWithReason( !( GetNetworkSystem().HasActivePing( GetMyEntityID() ) ), "LocKey#49279" );
			outActions.PushBack( currentAction );
		}
		if( IsUnpowered() )
		{
			SetActionsInactiveAll( outActions, "LocKey#7013" );
		}
		if( !( context.ignoresRPG ) )
		{
			EvaluateActionsRPGAvailabilty( outActions, context );
			SetActionIllegality( outActions, m_illegalActions.quickHacks );
			MarkActionsAsQuickHacks( outActions );
			SetActionsQuickHacksExecutioner( outActions );
		}
	}

	protected function FinalizeGetActions( out outActions : array< DeviceAction > )
	{
		SetInactiveActionsWithExceptions( outActions );
	}

	public virtual function GetQuickHackActionsExternal( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > ) {}

	protected virtual function CanCreateAnySpiderbotActions() : Bool
	{
		return false;
	}

	protected virtual function GetSpiderbotActions( outActions : ref< array< DeviceAction > >, const context : ref< GetActionsContext > ) {}

	protected virtual function SetInactiveActionsWithExceptions( out outActions : array< DeviceAction > )
	{
		var i : Int32;
		var inactiveReason : String;
		var actionAllowedClassNames : array< String >;
		var actionDisallowedClassNames : array< String >;
		var sAction : ScriptableDeviceAction;
		if( GetActionsRestrictionData( actionAllowedClassNames, actionDisallowedClassNames, inactiveReason ) )
		{
			for( i = 0; i < outActions.Size(); i += 1 )
			{
				sAction = ( ( ScriptableDeviceAction )( outActions[ i ] ) );
				if( FindActionInTweakList( sAction.GetActionID(), actionAllowedClassNames ) )
				{
					continue;
				}
				else if( actionAllowedClassNames.Size() > 0 )
				{
					( ( ScriptableDeviceAction )( outActions[ i ] ) ).SetInactiveWithReason( false, inactiveReason );
				}
				else if( FindActionInTweakList( sAction.GetActionID(), actionDisallowedClassNames ) )
				{
					( ( ScriptableDeviceAction )( outActions[ i ] ) ).SetInactiveWithReason( false, inactiveReason );
				}
			}
		}
	}

	protected function GetActionsRestrictionData( allowedNames : ref< array< String > >, disallowedNames : ref< array< String > >, inactiveReason : ref< String > ) : Bool
	{
		var record : weak< ActionRestrictionGroup_Record >;
		var actionRestrictions : array< TweakDBID >;
		var i, i1, arrSize : Int32;
		var tmpVariant : Variant;
		var player : PlayerPuppet;
		var psmBB : IBlackboard;
		player = GetPlayer( GetGameInstance() );
		if( !( player ) )
		{
			return false;
		}
		psmBB = player.GetPlayerStateMachineBlackboard();
		if( !( psmBB ) )
		{
			return false;
		}
		tmpVariant = psmBB.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.ActionRestriction );
		if( tmpVariant.IsValid() )
		{
			actionRestrictions = ( ( array< TweakDBID > )tmpVariant );
		}
		for( i = 0; i < actionRestrictions.Size(); i += 1 )
		{
			if( TDBID.IsValid( actionRestrictions[ i ] ) )
			{
				record = TweakDBInterface.GetActionRestrictionGroupRecord( actionRestrictions[ i ] );
				if( !( record ) )
				{
					return false;
				}
				arrSize = record.GetAllowedActionNamesCount();
				if( arrSize > 0 )
				{
					for( i1 = 0; i1 < arrSize; i1 += 1 )
					{
						if( !( allowedNames.Contains( record.GetAllowedActionNamesItem( i1 ) ) ) )
						{
							allowedNames.PushBack( record.GetAllowedActionNamesItem( i1 ) );
						}
					}
					if( allowedNames.Size() > 0 )
					{
						inactiveReason = record.InactiveReason();
						return true;
					}
				}
				arrSize = record.GetDisallowedActionNamesCount();
				if( arrSize > 0 )
				{
					for( i1 = 0; i1 < arrSize; i1 += 1 )
					{
						if( IgnoreDisallowedAction( record.GetDisallowedActionNamesItem( i1 ) ) )
						{
							continue;
						}
						if( !( disallowedNames.Contains( record.GetDisallowedActionNamesItem( i1 ) ) ) )
						{
							disallowedNames.PushBack( record.GetDisallowedActionNamesItem( i1 ) );
						}
					}
					if( disallowedNames.Size() > 0 )
					{
						inactiveReason = record.InactiveReason();
						return true;
					}
				}
			}
		}
		return false;
	}

	protected virtual function IgnoreDisallowedAction( actionName : String ) : Bool
	{
		return false;
	}

	protected function FindActionInTweakList( actionName : CName, const allowedNames : ref< array< String > > ) : Bool
	{
		var i : Int32;
		if( allowedNames.Size() <= 0 )
		{
			return false;
		}
		for( i = 0; i < allowedNames.Size(); i += 1 )
		{
			if( NameToString( actionName ) == allowedNames[ i ] )
			{
				return true;
			}
		}
		return false;
	}

	protected const function GetLocalPassword() : CName
	{
		return m_authorizationProperties.m_authorizationDataEntry.m_password;
	}

	public const function HasAuthorizationModule() : Bool
	{
		return m_hasAuthorizationModule;
	}

	public quest const function IsAuthorizationModuleOn() : Bool
	{
		return HasAuthorizationModule() && m_authorizationProperties.m_isAuthorizationModuleOn;
	}

	public quest const function IsAuthorizationModuleOff() : Bool
	{
		return !( IsAuthorizationModuleOn() );
	}

	public function SetBlockSecurityWakeUp( value : Bool )
	{
		m_blockSecurityWakeUp = value;
		SendDeviceNotOperationalEvent();
	}

	public const function GetMySecurityAccessLevel() : ESecurityAccessLevel
	{
		var securityLevel : ESecurityAccessLevel;
		if( IsConnectedToSecuritySystem( securityLevel ) )
		{
			return securityLevel;
		}
		return m_authorizationProperties.m_authorizationDataEntry.m_level;
	}

	public const virtual function GetSecurityAlarm() : SecurityAlarmControllerPS
	{
		var ancestors : array< DeviceComponentPS >;
		var i : Int32;
		GetAncestors( ancestors );
		for( i = 0; i < ancestors.Size(); i += 1 )
		{
			if( ( ( SecurityAlarmControllerPS )( ancestors[ i ] ) ) )
			{
				return ( ( SecurityAlarmControllerPS )( ancestors[ i ] ) );
			}
		}
		return NULL;
	}

	public const function GetDropPointSystem() : DropPointSystem
	{
		var dps : DropPointSystem;
		dps = ( ( DropPointSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'DropPointSystem' ) ) );
		if( dps )
		{
			return dps;
		}
		return NULL;
	}

	public const function GetSecurityAreasWithUsersInside( uniqueUsers : ref< array< AreaEntry > > ) : array< SecurityAreaControllerPS >
	{
		var secAreas : array< SecurityAreaControllerPS >;
		var nonEmptyAreas : array< SecurityAreaControllerPS >;
		var areaUsers : array< AreaEntry >;
		var i, j : Int32;
		secAreas = GetSecurityAreas();
		for( i = 0; i < secAreas.Size(); i += 1 )
		{
			areaUsers = secAreas[ i ].GetUsersInPerimeter();
			if( areaUsers.Size() > 0 )
			{
				nonEmptyAreas.PushBack( secAreas[ i ] );
			}
			for( j = 0; j < areaUsers.Size(); j += 1 )
			{
				if( !( uniqueUsers.Contains( areaUsers[ j ] ) ) )
				{
					uniqueUsers.PushBack( areaUsers[ j ] );
				}
			}
		}
		return nonEmptyAreas;
	}

	public const function GetSecurityAreasWithUsersInside() : array< SecurityAreaControllerPS >
	{
		var trash : array< AreaEntry >;
		return GetSecurityAreasWithUsersInside( trash );
	}

	public const function GetSecurityAreasWithUserInside( whoToCheck : EntityID ) : array< SecurityAreaControllerPS >
	{
		var secAreas : array< SecurityAreaControllerPS >;
		var secAreasWithUser : array< SecurityAreaControllerPS >;
		var secSys : SecuritySystemControllerPS;
		var i : Int32;
		secAreas = GetSecurityAreas();
		if( secAreas.Size() > 0 )
		{
			for( i = 0; i < secAreas.Size(); i += 1 )
			{
				if( secAreas[ i ].IsUserInside( whoToCheck ) )
				{
					secAreasWithUser.PushBack( secAreas[ i ] );
				}
			}
			return secAreasWithUser;
		}
		secSys = GetSecuritySystem();
		if( secSys )
		{
			return secSys.GetSecurityAreasWithUserInside( whoToCheck );
		}
		return secAreas;
	}

	public const function GetSecurityAreasWithUserInside( whoToCheck : GameObject ) : array< SecurityAreaControllerPS >
	{
		var emptyArr : array< SecurityAreaControllerPS >;
		if( whoToCheck )
		{
			return GetSecurityAreasWithUserInside( whoToCheck.GetEntityID() );
		}
		return emptyArr;
	}

	protected function SetCurrentSpiderbotActionPerformed( action : ScriptableDeviceAction )
	{
		m_currentSpiderbotActionPerformed = action;
	}

	public const function GetCurrentlyQueuedSpiderbotAction() : ScriptableDeviceAction
	{
		return m_currentSpiderbotActionPerformed;
	}

	public quest const virtual function IsDeviceSecured() : Bool
	{
		var securityData : array< SecurityAccessLevelEntryClient >;
		var i : Int32;
		if( !( IsAuthorizationModuleOn() ) || m_authorizationProperties.m_alwaysExposeActions )
		{
			return false;
		}
		securityData = GetFullAuthorizationData();
		for( i = 0; i < securityData.Size(); i += 1 )
		{
			if( SecurityAccessLevelEntryClient.IsDataValid( securityData[ i ] ) )
			{
				return true;
			}
		}
		return false;
	}

	public const function IsDeviceSecuredWithPassword() : Bool
	{
		var passwords : array< CName >;
		passwords = GetPasswords();
		return passwords.Size() > 0;
	}

	public const function IsDeviceSecuredWithKeycard() : Bool
	{
		var keycards : array< TweakDBID >;
		keycards = GetKeycards();
		return keycards.Size() > 0;
	}

	public const function HasActiveStaticHackingSkillcheck() : Bool
	{
		var hackingSkillcheck : HackingSkillCheck;
		if( GetSkillCheckContainer() )
		{
			hackingSkillcheck = GetSkillCheckContainer().GetHackingSlot();
		}
		if( ( hackingSkillcheck && hackingSkillcheck.IsActive() ) && !( hackingSkillcheck.IsDynamic() ) )
		{
			return true;
		}
		return false;
	}

	public const virtual function IsPlayerAuthorized() : Bool
	{
		var player : GameObject;
		player = GetPlayerMainObject();
		return IsUserAuthorized( player.GetEntityID() );
	}

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

	public const virtual function IsUserAuthorized( user : EntityID ) : Bool
	{
		var i : Int32;
		var deviceLevel : ESecurityAccessLevel;
		if( !( IsDeviceSecured() ) )
		{
			return true;
		}
		if( IsConnectedToSecuritySystem( deviceLevel ) )
		{
			if( GetSecuritySystem().IsUserAuthorized( user, deviceLevel ) )
			{
				return true;
			}
		}
		for( i = 0; i < m_currentlyAuthorizedUsers.Size(); i += 1 )
		{
			if( m_currentlyAuthorizedUsers[ i ].user == user )
			{
				if( ( ( Int32 )( m_currentlyAuthorizedUsers[ i ].level ) ) >= ( ( Int32 )( GetMySecurityAccessLevel() ) ) )
				{
					return true;
				}
			}
		}
		if( UserAuthorizationAttempt( user ) )
		{
			return true;
		}
		return false;
	}

	public const virtual function GetUserAuthorizationLevel( user : EntityID ) : ESecurityAccessLevel
	{
		var secSys : SecuritySystemControllerPS;
		var secAuthLevel : ESecurityAccessLevel;
		var i : Int32;
		secSys = GetSecuritySystem();
		if( secSys )
		{
			secAuthLevel = secSys.GetUserAuthorizationLevel( user );
			if( secAuthLevel > ESecurityAccessLevel.ESL_NONE )
			{
				return secAuthLevel;
			}
		}
		for( i = 0; i < m_currentlyAuthorizedUsers.Size(); i += 1 )
		{
			if( m_currentlyAuthorizedUsers[ i ].user == user )
			{
				return m_currentlyAuthorizedUsers[ i ].level;
			}
		}
		return ESecurityAccessLevel.ESL_NONE;
	}

	public const function CurrentlyAuthorizedUsers() : array< SecuritySystemClearanceEntry >
	{
		return m_currentlyAuthorizedUsers;
	}

	public const function UserAuthorizationAttempt( userToAuthorize : EntityID, optional password : CName, optional userToAuthorizeHandle : GameObject ) : Bool
	{
		var keycards : array< TweakDBID >;
		var passwords : array< CName >;
		var i : Int32;
		GetFullAuthorizationDataSegregated( passwords, keycards );
		if( IsNameValid( password ) )
		{
			for( i = 0; i < passwords.Size(); i += 1 )
			{
				if( password == passwords[ i ] )
				{
					SetFactValue( GetGameInstance(), password, 1 );
					AddUser( userToAuthorize, GetMySecurityAccessLevel() );
					return true;
				}
			}
		}
		if( !( userToAuthorizeHandle ) )
		{
			userToAuthorizeHandle = ( ( GameObject )( GameInstance.FindEntityByID( GetGameInstance(), userToAuthorize ) ) );
		}
		for( i = 0; i < keycards.Size(); i += 1 )
		{
			if( GameInstance.GetTransactionSystem( GetGameInstance() ).HasItem( userToAuthorizeHandle, ItemID.CreateQuery( keycards[ i ] ) ) )
			{
				AddUser( userToAuthorize, GetMySecurityAccessLevel() );
				return true;
			}
		}
		return false;
	}

	public function MasterUserAuthorizationAttempt( userToAuthorize : EntityID, optional password : CName ) : Bool
	{
		AddUser( userToAuthorize, GetMySecurityAccessLevel() );
		return true;
	}

	protected virtual function ActionAuthorizeUser( optional isForced : Bool ) : AuthorizeUser
	{
		var action : AuthorizeUser;
		action = new AuthorizeUser;
		action.clearanceLevel = DefaultActionsParametersHolder.GetAuthorizeUserClearance();
		action.SetUp( this );
		action.SetProperties( GetPasswords(), isForced );
		action.AddDeviceName( GetDeviceName() );
		action.CreateActionWidgetPackage();
		return action;
	}

	protected virtual function ShouldForceAuthorizeUser( const context : ref< GetActionsContext > ) : Bool
	{
		return context.requestType == gamedeviceRequestType.External && context.allowsRemoteAuthorization;
	}

	public virtual function OnAuthorizeUser( evt : AuthorizeUser ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetNone();
		if( evt.GetRequesterID() != GetMyEntityID() )
		{
			if( MasterUserAuthorizationAttempt( evt.GetExecutor().GetEntityID(), evt.GetEnteredPassword() ) )
			{
				Notify( notifier, evt );
				return EntityNotificationType.SendThisEventToEntity;
			}
		}
		else
		{
			if( UserAuthorizationAttempt( evt.GetExecutor().GetEntityID(), evt.GetEnteredPassword() ) )
			{
				Notify( notifier, evt );
				return EntityNotificationType.SendThisEventToEntity;
			}
		}
		Notify( notifier, evt );
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	protected virtual function ActionSetAuthorizationModuleON() : SetAuthorizationModuleON
	{
		var action : SetAuthorizationModuleON;
		action = new SetAuthorizationModuleON;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( GetDeviceName() );
		return action;
	}

	public virtual function OnSetAuthorizationModuleON( evt : SetAuthorizationModuleON ) : EntityNotificationType
	{
		TurnAuthorizationModuleON();
		UseNotifier( evt );
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	protected virtual function ActionSetAuthorizationModuleOFF() : SetAuthorizationModuleOFF
	{
		var action : SetAuthorizationModuleOFF;
		action = new SetAuthorizationModuleOFF;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( GetDeviceName() );
		return action;
	}

	public virtual function OnSetAuthorizationModuleOFF( evt : SetAuthorizationModuleOFF ) : EntityNotificationType
	{
		TurnAuthorizationModuleOFF();
		UseNotifier( evt );
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	protected virtual function ActionPay( const context : ref< GetActionsContext > ) : Pay
	{
		var action : Pay;
		action = new Pay;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleLockClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public function TriggerSecuritySystemNotification( whoBreached : GameObject, lastKnownPosition : Vector4, type : ESecurityNotificationType, optional forceNotification : Bool )
	{
		var secSys : SecuritySystemControllerPS;
		if( ( IsON() || IsOFF() ) || forceNotification )
		{
			secSys = GetSecuritySystem();
			if( !( secSys ) )
			{
				return;
			}
			secSys.ReportPotentialSituation( ActionSecurityBreachNotification( lastKnownPosition, whoBreached, type ) );
			return;
		}
	}

	public const virtual function ActionSecurityBreachNotification( lastKnownPosition : Vector4, whoBreached : GameObject, type : ESecurityNotificationType ) : SecuritySystemInput
	{
		var action : SecuritySystemInput;
		action = new SecuritySystemInput;
		action.SetUp( this );
		action.SetProperties( lastKnownPosition, whoBreached, this, type, CanPerformReprimand(), false );
		action.AddDeviceName( GetDeviceName() );
		return action;
	}

	public export quest const function WasQuickHacked() : Bool
	{
		return m_wasQuickHacked;
	}

	public quest const function WasQuickHackAttempt() : Bool
	{
		return m_wasQuickHackAttempt;
	}

	public export quest const function WasQuickHackJustPerformed( quickHackName : CName ) : Bool
	{
		return m_lastPerformedQuickHack == quickHackName;
	}

	public export function OnSetWasQuickHacked( evt : SetQuickHackEvent ) : EntityNotificationType
	{
		m_wasQuickHacked = evt.wasQuickHacked;
		m_lastPerformedQuickHack = evt.quickHackName;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export function OnSetWasQuickHackedAtempt( evt : SetQuickHackAttemptEvent ) : EntityNotificationType
	{
		m_wasQuickHackAttempt = evt.wasQuickHackAttempt;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected const function IsConnectedToAccessPoint() : Bool
	{
		var i : Int32;
		var parents : array< DeviceComponentPS >;
		GetParents( parents );
		for( i = 0; i < parents.Size(); i += 1 )
		{
			if( ( ( AccessPointControllerPS )( parents[ i ] ) ) )
			{
				return true;
			}
		}
		return false;
	}

	public const virtual function GetNetworkSizeCount() : Int32
	{
		var aps : array< AccessPointControllerPS >;
		aps = GetAccessPoints();
		if( aps.Size() == 0 )
		{
			if( !( IsFinal() ) )
			{
			}
			return -1;
		}
		return aps[ 0 ].GetNetworkSizeCount() - 1;
	}

	public const function GetBackdoorDevices() : array< ScriptableDeviceComponentPS >
	{
		var backdoorDevices : array< ScriptableDeviceComponentPS >;
		var backdoor : ScriptableDeviceComponentPS;
		var parents : array< DeviceComponentPS >;
		var i : Int32;
		GetParents( parents );
		for( i = 0; i < parents.Size(); i += 1 )
		{
			if( ( ( ScriptableDeviceComponentPS )( parents[ i ] ) ).HasNetworkBackdoor() )
			{
				backdoor = ( ( ScriptableDeviceComponentPS )( parents[ i ] ) );
				backdoorDevices.PushBack( backdoor );
			}
		}
		return backdoorDevices;
	}

	public const virtual function IsMainframe() : Bool
	{
		var aps : array< AccessPointControllerPS >;
		if( !( HasNetworkBackdoor() ) )
		{
			return false;
		}
		aps = GetAccessPoints();
		if( aps.Size() == 1 )
		{
			return aps[ 0 ].IsMainframe();
		}
		if( !( IsFinal() ) )
		{
		}
		return false;
	}

	protected const virtual function GetNetworkArea() : weak< NetworkAreaControllerPS >
	{
		var networkArea : weak< NetworkAreaControllerPS >;
		var ancestors : array< DeviceComponentPS >;
		var i : Int32;
		GetAncestors( ancestors );
		for( i = 0; i < ancestors.Size(); i += 1 )
		{
			if( ( ( NetworkAreaControllerPS )( ancestors[ i ] ) ) )
			{
				networkArea = ( ( NetworkAreaControllerPS )( ancestors[ i ] ) );
				return networkArea;
			}
		}
		return NULL;
	}

	public export quest const function HackingPerformed() : Bool
	{
		if( !( m_skillCheckContainer ) || !( m_skillCheckContainer.GetHackingSlot() ) )
		{
			return false;
		}
		return m_skillCheckContainer.GetHackingSlot().WasPerformed();
	}

	public quest const function EngineeringPerformed() : Bool
	{
		if( !( m_skillCheckContainer ) || !( m_skillCheckContainer.GetEngineeringSlot() ) )
		{
			return false;
		}
		return m_skillCheckContainer.GetEngineeringSlot().WasPerformed();
	}

	public quest const function DemolitionPerformed() : Bool
	{
		if( !( m_skillCheckContainer ) || !( m_skillCheckContainer.GetDemolitionSlot() ) )
		{
			return false;
		}
		return m_skillCheckContainer.GetDemolitionSlot().WasPerformed();
	}

	protected virtual function ActionHacking( const context : ref< GetActionsContext > ) : ActionHacking
	{
		var action : ActionHacking;
		action = new ActionHacking;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( m_skillCheckContainer.GetHackingSlot() );
		action.AddDeviceName( m_deviceName );
		action.SetIllegal( m_illegalActions.skillChecks );
		action.RegisterAsRequester( context.requestorID );
		if( ( TDBID.IsValid( m_personalLinkCustomInteraction ) && ( m_personalLinkCustomInteraction != T"Interactions.None" ) ) && !( IsPersonalLinkConnecting() ) )
		{
			action.CreateInteraction( , , m_personalLinkCustomInteraction );
		}
		else
		{
			action.CreateInteraction( context.processInitiatorObject );
		}
		action.SetDurationValue( m_skillCheckContainer.GetHackingSlot().GetDuration() );
		return action;
	}

	public final function OnActionHacking( evt : ActionHacking ) : EntityNotificationType
	{
		var togglePersonalLink : TogglePersonalLink;
		if( evt.WasPassed() )
		{
			if( evt.IsCompleted() )
			{
				if( HasNetworkBackdoor() )
				{
				}
				else
				{
					DisconnectPersonalLink( evt );
				}
			}
			else
			{
				togglePersonalLink = ActionTogglePersonalLink( evt.GetExecutor() );
				ExecutePSAction( togglePersonalLink, evt.GetInteractionLayer() );
				ExecutePSActionWithDelay( evt, this, 4.19999981 );
			}
		}
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function ResolveActionHackingCompleted( evt : ActionHacking )
	{
		ResolveOtherSkillchecks();
	}

	protected final function InitializeBackdoorSkillcheck()
	{
		if( ( HasNetworkBackdoor() && !( ( ( AccessPointControllerPS )( this ) ) ) ) && HasPersonalLinkSlot() )
		{
			if( !( m_skillCheckContainer ) )
			{
				m_skillCheckContainer = new HackingContainer;
			}
			m_skillCheckContainer.InitializeBackdoor( m_backdoorBreachDifficulty );
			m_skillCheckContainer.GetHackingSlot().SetDynamic( true );
		}
	}

	protected virtual function ActionEngineering( const context : ref< GetActionsContext > ) : ActionEngineering
	{
		var action : ActionEngineering;
		action = new ActionEngineering;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( m_skillCheckContainer.GetEngineeringSlot() );
		action.AddDeviceName( m_deviceName );
		action.SetIllegal( m_illegalActions.skillChecks );
		action.RegisterAsRequester( context.requestorID );
		action.CreateInteraction( context.processInitiatorObject );
		action.SetDurationValue( m_skillCheckContainer.GetEngineeringSlot().GetDuration() );
		return action;
	}

	public virtual function OnActionEngineering( evt : ActionEngineering ) : EntityNotificationType
	{
		if( evt.WasPassed() )
		{
			m_skillCheckContainer.GetEngineeringSlot().SetIsActive( false );
			m_skillCheckContainer.GetEngineeringSlot().SetIsPassed( true );
			m_skillCheckContainer.GetEngineeringSlot().CheckPerformed();
			if( evt.IsCompleted() )
			{
				TurnAuthorizationModuleOFF();
			}
			else
			{
				ExecutePSActionWithDelay( evt, this );
			}
		}
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function ActionDemolition( const context : ref< GetActionsContext > ) : ActionDemolition
	{
		var action : ActionDemolition;
		action = new ActionDemolition;
		action.slotID.id = context.interactionLayerTag;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( m_skillCheckContainer.GetDemolitionSlot() );
		action.AddDeviceName( m_deviceName );
		action.SetIllegal( m_illegalActions.skillChecks );
		action.RegisterAsRequester( context.requestorID );
		action.CreateInteraction( context.processInitiatorObject );
		action.SetDurationValue( m_skillCheckContainer.GetDemolitionSlot().GetDuration() );
		return action;
	}

	public virtual function OnActionDemolition( evt : ActionDemolition ) : EntityNotificationType
	{
		if( evt.WasPassed() )
		{
			m_skillCheckContainer.GetDemolitionSlot().SetIsActive( false );
			m_skillCheckContainer.GetDemolitionSlot().SetIsPassed( true );
			m_skillCheckContainer.GetDemolitionSlot().CheckPerformed();
			if( !( evt.IsCompleted() ) )
			{
				ExecutePSActionWithDelay( evt, this );
			}
		}
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function OnResolveSkillchecksEvent( evt : ResolveSkillchecksEvent ) : EntityNotificationType
	{
		ResolveOtherSkillchecks();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function ResolveOtherSkillchecks() {}

	public const function GetSkillCheckContainer() : BaseSkillCheckContainer
	{
		return m_skillCheckContainer;
	}

	public const function HasAnySkillCheckActive() : Bool
	{
		return ( IsHackingSkillCheckActive() || IsDemolitionSkillCheckActive() ) || IsEngineeringSkillCheckActive();
	}

	public const function IsHackingSkillCheckActive() : Bool
	{
		if( ( m_skillCheckContainer == NULL ) || !( m_skillCheckContainer.GetHackingSlot() ) )
		{
			return false;
		}
		else
		{
			return m_skillCheckContainer.GetHackingSlot().IsActive();
		}
	}

	public const function IsDemolitionSkillCheckActive() : Bool
	{
		if( ( m_skillCheckContainer == NULL ) || !( m_skillCheckContainer.GetDemolitionSlot() ) )
		{
			return false;
		}
		else
		{
			return m_skillCheckContainer.GetDemolitionSlot().IsActive();
		}
	}

	public const function IsEngineeringSkillCheckActive() : Bool
	{
		if( ( m_skillCheckContainer == NULL ) || !( m_skillCheckContainer.GetEngineeringSlot() ) )
		{
			return false;
		}
		else
		{
			return m_skillCheckContainer.GetEngineeringSlot().IsActive();
		}
	}

	public const function CanPassEngineeringSkillCheck( requester : GameObject ) : Bool
	{
		if( ( ( m_skillCheckContainer == NULL ) || !( m_skillCheckContainer.GetEngineeringSlot() ) ) || !( m_skillCheckContainer.GetEngineeringSlot().IsActive() ) )
		{
			return false;
		}
		else
		{
			return m_skillCheckContainer.GetEngineeringSlot().Evaluate( requester );
		}
	}

	public const function CanPassDemolitionSkillCheck( requester : GameObject ) : Bool
	{
		if( ( ( m_skillCheckContainer == NULL ) || !( m_skillCheckContainer.GetDemolitionSlot() ) ) || !( m_skillCheckContainer.GetDemolitionSlot().IsActive() ) )
		{
			return false;
		}
		else
		{
			return m_skillCheckContainer.GetDemolitionSlot().Evaluate( requester );
		}
	}

	public const function CanPassHackingSkillCheck( requester : GameObject ) : Bool
	{
		if( ( ( m_skillCheckContainer == NULL ) || !( m_skillCheckContainer.GetHackingSlot() ) ) || !( m_skillCheckContainer.GetHackingSlot().IsActive() ) )
		{
			return false;
		}
		else
		{
			return m_skillCheckContainer.GetHackingSlot().Evaluate( requester );
		}
	}

	public const function CanPassAnySkillCheck( requester : GameObject ) : Bool
	{
		if( m_skillCheckContainer == NULL )
		{
			return false;
		}
		else
		{
			return ( CanPassEngineeringSkillCheck( requester ) || CanPassDemolitionSkillCheck( requester ) ) || CanPassHackingSkillCheck( requester );
		}
	}

	public const virtual function CanPassAnySkillCheckOnMaster( requester : GameObject ) : Bool
	{
		var parents : array< DeviceComponentPS >;
		var i : Int32;
		var parent : ScriptableDeviceComponentPS;
		GetParents( parents );
		for( i = 0; i < parents.Size(); i += 1 )
		{
			parent = ( ( ScriptableDeviceComponentPS )( parents[ i ] ) );
			if( parent == NULL )
			{
				continue;
			}
			if( parent.IsSkillCheckActive() && parent.CanPassAnySkillCheck( requester ) )
			{
				return true;
			}
		}
		return false;
	}

	public const function IsSkillCheckActive() : Bool
	{
		if( ( IsHackingSkillCheckActive() || IsDemolitionSkillCheckActive() ) || IsEngineeringSkillCheckActive() )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public const function WasHackingSkillCheckActive() : Bool
	{
		if( m_skillCheckContainer.GetHackingSlot() && ( m_skillCheckContainer.GetHackingSlot().IsActive() || m_skillCheckContainer.GetHackingSlot().IsPassed() ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public const function WasDemolitionSkillCheckActive() : Bool
	{
		if( m_skillCheckContainer.GetDemolitionSlot() && ( m_skillCheckContainer.GetDemolitionSlot().IsActive() || m_skillCheckContainer.GetDemolitionSlot().IsPassed() ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public const function WasEngineeringSkillCheckActive() : Bool
	{
		if( m_skillCheckContainer.GetEngineeringSlot() && ( m_skillCheckContainer.GetEngineeringSlot().IsActive() || m_skillCheckContainer.GetEngineeringSlot().IsPassed() ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	protected virtual function PushSkillCheckActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > ) : Bool
	{
		var skillCheckAdded : Bool;
		var skillCheckAction : ActionSkillCheck;
		if( m_skillCheckContainer == NULL )
		{
			return false;
		}
		if( ( ( ( m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED && m_skillCheckContainer.GetHackingSlot() ) && m_skillCheckContainer.GetHackingSlot().IsActive() ) && IsON() ) && HasPersonalLinkSlot() )
		{
			skillCheckAction = ActionHacking( context );
			skillCheckAction.RegisterAsRequester( context.requestorID );
			skillCheckAction.SetDurationValue( m_skillCheckContainer.GetHackingSlot().GetDuration() );
			outActions.PushBack( skillCheckAction );
			if( ShouldPersonalLinkBlockActions() )
			{
				skillCheckAdded = true;
			}
		}
		if( ( CanAddEngineeringSkillcheck() && m_skillCheckContainer.GetEngineeringSlot() ) && m_skillCheckContainer.GetEngineeringSlot().IsActive() )
		{
			skillCheckAction = ActionEngineering( context );
			if( ActionEngineering.IsDefaultConditionMet( this, context, skillCheckAction.AvailableOnUnpowered() ) )
			{
				skillCheckAction.RegisterAsRequester( context.requestorID );
				skillCheckAction.SetDurationValue( m_skillCheckContainer.GetEngineeringSlot().GetDuration() );
				outActions.PushBack( skillCheckAction );
				skillCheckAdded = true;
			}
		}
		if( m_skillCheckContainer.GetDemolitionSlot() && m_skillCheckContainer.GetDemolitionSlot().IsActive() )
		{
			skillCheckAction = ActionDemolition( context );
			if( ActionDemolition.IsDefaultConditionMet( this, context, skillCheckAction.AvailableOnUnpowered() ) )
			{
				skillCheckAction.RegisterAsRequester( context.requestorID );
				skillCheckAction.SetDurationValue( m_skillCheckContainer.GetDemolitionSlot().GetDuration() );
				outActions.PushBack( skillCheckAction );
				skillCheckAdded = true;
			}
		}
		return skillCheckAdded;
	}

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

	protected function HasCyberdeck() : Bool
	{
		return GameInstance.GetStatsSystem( GetGameInstance() ).GetStatBoolValue( GetPlayerEntityID(), gamedataStatType.HasCyberdeck );
	}

	protected virtual function ShouldPersonalLinkBlockActions() : Bool
	{
		return false;
	}

	public function CreateSkillcheckInfo( const context : ref< GetActionsContext > ) : array< UIInteractionSkillCheck >
	{
		var skillcheckDescription : UIInteractionSkillCheck;
		var blackboardDescription : array< UIInteractionSkillCheck >;
		if( m_skillCheckContainer.GetHackingSlot().IsActive() && IsON() )
		{
			skillcheckDescription = ActionHacking( context ).CreateSkillcheckInfo( context.processInitiatorObject );
			blackboardDescription.PushBack( skillcheckDescription );
		}
		if( m_skillCheckContainer.GetEngineeringSlot().IsActive() )
		{
			skillcheckDescription = ActionEngineering( context ).CreateSkillcheckInfo( context.processInitiatorObject );
			blackboardDescription.PushBack( skillcheckDescription );
		}
		if( m_skillCheckContainer.GetDemolitionSlot().IsActive() )
		{
			skillcheckDescription = ActionDemolition( context ).CreateSkillcheckInfo( context.processInitiatorObject );
			blackboardDescription.PushBack( skillcheckDescription );
		}
		return blackboardDescription;
	}

	protected function IsSpiderbotActionsConditionsFulfilled() : Bool
	{
		if( GameInstance.GetStatsSystem( GetGameInstance() ).GetStatBoolValue( GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject().GetEntityID(), gamedataStatType.HasSpiderBotControl ) )
		{
			if( AIActionHelper.CheckFlatheadStatPoolRequirements( GetGameInstance(), "DeviceAction" ) )
			{
				if( !( m_currentSpiderbotActionPerformed ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	protected virtual function ActionSpiderbotDistraction() : SpiderbotDistraction
	{
		var action : SpiderbotDistraction;
		action = new SpiderbotDistraction;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected virtual function ActionQuickHackDistraction() : QuickHackDistraction
	{
		var action : QuickHackDistraction;
		action = new QuickHackDistraction;
		action.SetUp( this );
		action.SetObjectActionID( T"DeviceAction.MalfunctionClassHack" );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected virtual function ActionQuickHackHighPitchNoise() : QuickHackHighPitchNoise
	{
		var action : QuickHackHighPitchNoise;
		action = new QuickHackHighPitchNoise;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		action.SetDurationValue( GetDistractionDuration( action ) );
		action.SetObjectActionID( T"DeviceAction.HighPitchNoiseQuickHack" );
		return action;
	}

	protected virtual function ActionQuickHackAoeDamage() : QuickHackAoeDamage
	{
		var action : QuickHackAoeDamage;
		action = new QuickHackAoeDamage;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		action.SetDurationValue( GetDistractionDuration( action ) );
		action.SetObjectActionID( T"DeviceAction.OverloadClassLvl4Hack" );
		return action;
	}

	public const function IsInvestigated() : Bool
	{
		var value : Bool;
		value = GetBlackboard().GetBool( GetBlackboardDef().IsInvestigated );
		return value;
	}

	public function FinishDistraction()
	{
		var distract : QuickHackDistraction;
		if( !( m_distractionTimeCompleted ) )
		{
			return;
		}
		distract = ActionQuickHackDistraction();
		distract.SetCompleted();
		QueuePSEvent( this, distract );
	}

	public export virtual function OnQuickHackDistraction( evt : QuickHackDistraction ) : EntityNotificationType
	{
		if( evt.IsStarted() )
		{
			m_distractExecuted = true;
			m_isGlitching = true;
			m_distractionTimeCompleted = false;
			evt.SetCanTriggerStim( true );
			evt.SetObjectActionID( T"DeviceAction.EndMalfunction" );
			ExecutePSActionWithDelay( evt, this, evt.GetDurationValue() );
		}
		else
		{
			m_distractionTimeCompleted = true;
			if( IsInvestigated() )
			{
				return EntityNotificationType.DoNotNotifyEntity;
			}
			m_distractExecuted = false;
			m_isGlitching = false;
			evt.SetCanTriggerStim( false );
		}
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuickHackHighPitchNoise( evt : QuickHackHighPitchNoise ) : EntityNotificationType
	{
		if( evt.IsStarted() )
		{
			ExecutePSActionWithDelay( evt, this, evt.GetDurationValue() );
		}
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuickHackAoeDamage( evt : QuickHackAoeDamage ) : EntityNotificationType
	{
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function OnQuickHackAuthorization( evt : QuickHackAuthorization ) : EntityNotificationType
	{
		if( IsConnectedToSecuritySystem() )
		{
			GetSecuritySystem().AuthorizeUser( evt.GetExecutor().GetEntityID(), ESecurityAccessLevel.ESL_4 );
		}
		else
		{
			AddUser( evt.GetExecutor().GetEntityID(), ESecurityAccessLevel.ESL_4 );
		}
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public const function GetConnectionHighlightObjects() : array< NodeRef >
	{
		return m_connectionHighlightObjects;
	}

	public const function ShouldDrawGridLink() : Bool
	{
		return m_drawGridLink;
	}

	public const function IsLinkDynamic() : Bool
	{
		return m_isLinkDynamic;
	}

	public const virtual function ShouldRevealDevicesGrid() : Bool
	{
		return m_revealDevicesGrid;
	}

	public const virtual function CanRevealDevicesGridWhenUnpowered() : Bool
	{
		return m_revealDevicesGridWhenUnpowered;
	}

	public const function GetVirtualNetworkShapeID() : TweakDBID
	{
		return m_virtualNetworkShapeID;
	}

	protected const function GetCityLightSystem() : CityLightSystem
	{
		return ( ( CityLightSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'CityLightSystem' ) ) );
	}

	protected const function GetEquipmentSystem() : EquipmentSystem
	{
		return ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'EquipmentSystem' ) ) );
	}

	public const function CheckIfMyBackdoorsWereRevealedInNetworkPing() : Bool
	{
		var backdoors : array< ScriptableDeviceComponentPS >;
		var i : Int32;
		backdoors = GetBackdoorDevices();
		for( i = 0; i < backdoors.Size(); i += 1 )
		{
			if( backdoors[ i ].WasRevealedInNetworkPing() )
			{
				continue;
			}
			else
			{
				return false;
			}
		}
		return true;
	}

	public const virtual function ShouldRevealNetworkGrid() : Bool
	{
		if( HasNetworkBackdoor() )
		{
			return true;
		}
		return false;
	}

	public function SetFocusModeData( isHighlighted : Bool )
	{
		m_isHighlightedInFocusMode = isHighlighted;
	}

	public constexpr function OnForceUpdateDefaultHighlightEvent( evt : ForceUpdateDefaultHighlightEvent ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected final function RevealNetworkGrid( shouldDraw : Bool, optional ownerEntityPosition : Vector4, optional fxDefault : FxResource, optional fxBreached : FxResource, optional isPing : Bool, optional lifetime : Float, optional revealSlave : Bool, optional revealMaster : Bool, optional ignoreRevealed : Bool )
	{
		var relevantDevices : array< DeviceComponentPS >;
		var backdoorAP : AccessPointControllerPS;
		var accessPoints : array< AccessPointControllerPS >;
		var nonVirtualBackdoorDevices : array< ScriptableDeviceComponentPS >;
		var nonVirtualBackdoorDevice : ScriptableDeviceComponentPS;
		var networkSystem : NetworkSystem;
		var unregisterLinkRequest : UnregisterNetworkLinksByIDRequest;
		var revDevicesEvt : ProcessRelevantDevicesForNetworkGridEvent;
		var finalizeRegistrationAsMaster : Bool;
		var i : Int32;
		var k : Int32;
		networkSystem = GetNetworkSystem();
		if( !( shouldDraw ) )
		{
			if( networkSystem.HasNetworkLink( PersistentID.ExtractEntityID( GetID() ) ) )
			{
				unregisterLinkRequest = new UnregisterNetworkLinksByIDRequest;
				unregisterLinkRequest.ID = PersistentID.ExtractEntityID( GetID() );
				networkSystem.QueueRequest( unregisterLinkRequest );
			}
			return;
		}
		if( ( IsUnpowered() && !( CanRevealDevicesGridWhenUnpowered() ) ) || IsDisabled() )
		{
			return;
		}
		if( HasNetworkBackdoor() )
		{
			backdoorAP = GetBackdoorAccessPoint();
			if( !( backdoorAP ) )
			{
				if( !( IsFinal() ) )
				{
				}
				return;
			}
			revDevicesEvt = new ProcessRelevantDevicesForNetworkGridEvent;
			revDevicesEvt.ignoreRevealed = ignoreRevealed;
			revDevicesEvt.finalizeRegistrationAsMaster = true;
			revDevicesEvt.breachedResource = fxBreached;
			revDevicesEvt.defaultResource = fxDefault;
			revDevicesEvt.isPing = isPing;
			revDevicesEvt.lifetime = lifetime;
			revDevicesEvt.revealSlave = revealSlave;
			revDevicesEvt.revealMaster = revealMaster;
			ProcessDevicesLazy( backdoorAP.GetLazySlaves(), revDevicesEvt );
			return;
		}
		else
		{
			accessPoints = GetAccessPoints();
			for( i = 0; i < accessPoints.Size(); i += 1 )
			{
				if( accessPoints[ i ].ShouldRevealNetworkGrid() )
				{
					if( ( ignoreRevealed && accessPoints[ i ].WasRevealedInNetworkPing() ) || ( accessPoints[ i ].IsUnpowered() && !( accessPoints[ i ].CanRevealDevicesGridWhenUnpowered() ) ) )
					{
					}
					else
					{
						relevantDevices.PushBack( accessPoints[ i ] );
					}
				}
				nonVirtualBackdoorDevices = accessPoints[ i ].GetDevicesThatPlayerCanBreach();
				for( k = 0; k < nonVirtualBackdoorDevices.Size(); k += 1 )
				{
					nonVirtualBackdoorDevice = nonVirtualBackdoorDevices[ k ];
					if( !( nonVirtualBackdoorDevice.ShouldRevealDevicesGrid() ) )
					{
						continue;
					}
					relevantDevices.PushBack( nonVirtualBackdoorDevice );
				}
			}
			finalizeRegistrationAsMaster = false;
		}
		FinalizeNetworkLinkRegistration( finalizeRegistrationAsMaster, relevantDevices, fxBreached, fxDefault, isPing, lifetime, revealSlave, revealMaster );
	}

	private export function OnProcessRelevantDevicesForNetworkGridEvent( evt : ProcessRelevantDevicesForNetworkGridEvent ) : EntityNotificationType
	{
		var i : Int32;
		var relevantDevice : ScriptableDeviceComponentPS;
		for( i = evt.devices.Size() - 1; i >= 0; i -= 1 )
		{
			relevantDevice = ( ( ScriptableDeviceComponentPS )( evt.devices[ i ] ) );
			if( relevantDevice == NULL )
			{
				evt.devices.Erase( i );
			}
			else if( evt.ignoreRevealed && relevantDevice.WasRevealedInNetworkPing() )
			{
				evt.devices.Erase( i );
			}
			else if( ( relevantDevice == this ) || !( relevantDevice.ShouldRevealDevicesGrid() ) )
			{
				evt.devices.Erase( i );
			}
			else if( relevantDevice.IsUnpowered() && !( relevantDevice.CanRevealDevicesGridWhenUnpowered() ) )
			{
				evt.devices.Erase( i );
			}
		}
		FinalizeNetworkLinkRegistration( evt.finalizeRegistrationAsMaster, evt.devices, evt.breachedResource, evt.defaultResource, evt.isPing, evt.lifetime, evt.revealSlave, evt.revealMaster );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private const function FinalizeNetworkLinkRegistration( registerAsMaster : Bool, const relevantDevices : ref< array< DeviceComponentPS > >, breachedResource : FxResource, defaultResource : FxResource, isPing : Bool, lifetime : Float, optional revealSlave : Bool, optional revealMaster : Bool )
	{
		var linkData : SNetworkLinkData;
		var links : array< SNetworkLinkData >;
		var myDevice : weak< Device >;
		var myPosition : Vector4;
		var otherDevice : weak< Device >;
		var otherDevicePosition : Vector4;
		var resourceToUse : FxResource;
		var registerLinkRequest : RegisterNetworkLinkRequest;
		var i : Int32;
		if( relevantDevices.Size() == 0 )
		{
			return;
		}
		if( IsAttachedToGame() )
		{
			myDevice = ( ( Device )( GetOwnerEntityWeak() ) );
			if( !( myDevice ) )
			{
				return;
			}
			else
			{
				myPosition = myDevice.GetNetworkBeamEndpoint();
			}
			if( Vector4.IsZero( myPosition ) )
			{
				return;
			}
		}
		else
		{
			return;
		}
		for( i = 0; i < relevantDevices.Size(); i += 1 )
		{
			if( relevantDevices[ i ].IsAttachedToGame() )
			{
				otherDevice = ( ( Device )( relevantDevices[ i ].GetOwnerEntityWeak() ) );
				if( !( otherDevice ) )
				{
					continue;
				}
				else
				{
					otherDevicePosition = otherDevice.GetNetworkBeamEndpoint();
				}
				if( Vector4.IsZero( otherDevicePosition ) )
				{
					continue;
				}
			}
			else
			{
				continue;
			}
			if( registerAsMaster )
			{
				linkData.masterID = PersistentID.ExtractEntityID( GetID() );
				linkData.masterPos = myPosition;
				linkData.slaveID = PersistentID.ExtractEntityID( relevantDevices[ i ].GetID() );
				linkData.slavePos = otherDevicePosition;
				if( IsBreached() )
				{
					resourceToUse = breachedResource;
				}
				else
				{
					resourceToUse = defaultResource;
				}
			}
			else
			{
				linkData.slaveID = PersistentID.ExtractEntityID( GetID() );
				linkData.slavePos = myPosition;
				linkData.masterID = PersistentID.ExtractEntityID( relevantDevices[ i ].GetID() );
				linkData.masterPos = otherDevicePosition;
				if( ( ( ScriptableDeviceComponentPS )( relevantDevices[ i ] ) ).IsBreached() )
				{
					resourceToUse = breachedResource;
				}
				else
				{
					resourceToUse = defaultResource;
				}
			}
			linkData.isDynamic = IsLinkDynamic() || ( ( ScriptableDeviceComponentPS )( relevantDevices[ i ] ) ).IsLinkDynamic();
			linkData.fxResource = resourceToUse;
			linkData.linkType = ELinkType.NETWORK;
			linkData.isPing = isPing;
			linkData.revealMaster = revealMaster;
			linkData.revealSlave = revealSlave;
			linkData.drawLink = true;
			linkData.lifetime = lifetime;
			if( isPing )
			{
				linkData.permanent = lifetime > 0.0;
			}
			links.PushBack( linkData );
		}
		if( links.Size() == 0 )
		{
			if( !( IsFinal() ) )
			{
			}
			return;
		}
		registerLinkRequest = new RegisterNetworkLinkRequest;
		registerLinkRequest.linksData = links;
		GetNetworkSystem().QueueRequest( registerLinkRequest );
	}

	public virtual function RevealDevicesGrid( shouldDraw : Bool, optional ownerEntityPosition : Vector4, optional fxDefault : FxResource, optional isPing : Bool, optional lifetime : Float, optional revealSlave : Bool, optional revealMaster : Bool, optional ignoreRevealed : Bool )
	{
		var ancestors : array< DeviceComponentPS >;
		var ancestor : ScriptableDeviceComponentPS;
		var parentEntity : Device;
		var parentPosition : Vector4;
		var networkSystem : NetworkSystem;
		var linkData : SNetworkLinkData;
		var linksData : array< SNetworkLinkData >;
		var registerLinkRequest : RegisterNetworkLinkRequest;
		var unregisterLinkRequest : UnregisterNetworkLinksByIDRequest;
		var i : Int32;
		if( !( ShouldRevealDevicesGrid() ) )
		{
			return;
		}
		networkSystem = GetNetworkSystem();
		if( !( shouldDraw ) )
		{
			if( networkSystem.HasNetworkLink( PersistentID.ExtractEntityID( GetID() ) ) )
			{
				unregisterLinkRequest = new UnregisterNetworkLinksByIDRequest;
				unregisterLinkRequest.ID = PersistentID.ExtractEntityID( GetID() );
				networkSystem.QueueRequest( unregisterLinkRequest );
			}
			return;
		}
		if( Vector4.IsZero( ownerEntityPosition ) )
		{
			return;
		}
		if( ( ( IsUnpowered() && !( CanRevealDevicesGridWhenUnpowered() ) ) || IsDisabled() ) || ( ignoreRevealed && WasRevealedInNetworkPing() ) )
		{
			return;
		}
		if( m_fullDepth )
		{
			GetAncestors( ancestors );
		}
		else
		{
			GetParents( ancestors );
		}
		if( ancestors.Size() == 0 )
		{
			return;
		}
		linkData.slaveID = PersistentID.ExtractEntityID( GetID() );
		linkData.slavePos = ownerEntityPosition;
		linkData.linkType = ELinkType.GRID;
		linkData.isPing = isPing;
		linkData.lifetime = lifetime;
		linkData.revealMaster = revealMaster;
		linkData.revealSlave = revealMaster;
		if( isPing )
		{
			linkData.permanent = lifetime > 0.0;
		}
		linkData.fxResource = fxDefault;
		for( i = 0; i < ancestors.Size(); i += 1 )
		{
			ancestor = ( ( ScriptableDeviceComponentPS )( ancestors[ i ] ) );
			if( ( ( ( ancestor == NULL ) || !( ancestor.ShouldRevealDevicesGrid() ) ) || ( ignoreRevealed && ancestor.WasRevealedInNetworkPing() ) ) || ( ancestor.IsUnpowered() && !( ancestor.CanRevealDevicesGridWhenUnpowered() ) ) )
			{
				continue;
			}
			parentEntity = ( ( Device )( ancestors[ i ].GetOwnerEntityWeak() ) );
			if( !( parentEntity ) )
			{
				GameInstance.GetDeviceSystem( GetGameInstance() ).GetNodePosition( PersistentID.ExtractEntityID( ancestors[ i ].GetID() ), parentPosition );
			}
			else
			{
				parentPosition = parentEntity.GetNetworkBeamEndpoint();
			}
			if( Vector4.IsZero( parentPosition ) )
			{
				continue;
			}
			linkData.masterID = PersistentID.ExtractEntityID( ancestors[ i ].GetID() );
			linkData.masterPos = parentPosition;
			linkData.drawLink = m_drawGridLink && ancestor.ShouldDrawGridLink();
			linkData.isDynamic = IsLinkDynamic() || ancestor.IsLinkDynamic();
			if( isPing && !( linkData.drawLink ) )
			{
				continue;
			}
			linksData.PushBack( linkData );
		}
		if( linksData.Size() > 0 )
		{
			registerLinkRequest = new RegisterNetworkLinkRequest;
			registerLinkRequest.linksData = linksData;
			networkSystem.QueueRequest( registerLinkRequest );
		}
	}

	protected function RevealDevicesGridOnEntity_Event( shouldDraw : Bool, target : EntityID )
	{
		var evt : RevealDevicesGridOnEntityEvent;
		evt = new RevealDevicesGridOnEntityEvent;
		evt.shouldDraw = shouldDraw;
		GetPersistencySystem().QueueEntityEvent( target, evt );
	}

	protected export virtual function OnRevealNetworkGridEvent( evt : RevealNetworkGridEvent ) : EntityNotificationType
	{
		RevealNetworkGrid( evt.shouldDraw, evt.ownerEntityPosition, evt.fxDefault, evt.fxBreached, false, 0.0, evt.revealSlave, evt.revealMaster );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected export virtual function OnRevealDevicesGridEvent( evt : RevealDevicesGridEvent ) : EntityNotificationType
	{
		RevealDevicesGrid( evt.shouldDraw, evt.ownerEntityPosition, evt.fxDefault, false, 0.0, evt.revealSlave, evt.revealMaster );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function OnPingNetworkGridEvent( evt : PingNetworkGridEvent ) : EntityNotificationType
	{
		RevealNetworkGrid( true, evt.ownerEntityPosition, evt.fxResource, evt.fxResource, true, evt.lifetime, evt.revealSlave, evt.revealMaster, evt.ignoreRevealed );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public const function ShouldDebug() : Bool
	{
		return m_debugDevice;
	}

	public const function GetDebugName() : String
	{
		return NameToString( m_debugName );
	}

	public const function GetDebugPath() : CName
	{
		return m_debugPath;
	}

	public const virtual function GetDebugTags() : String
	{
		var tags : String;
		tags = "PL";
		if( HasPersonalLinkSlot() )
		{
			tags += "+";
		}
		else
		{
			tags += "-";
		}
		return tags;
	}

	public override function GetWidgetTypeName() : CName
	{
		return 'GenericDeviceWidget';
	}

	public override function GetDeviceIconPath() : String
	{
		return "";
	}

	public override function GetDeviceIconID() : CName
	{
		return 'device';
	}

	public virtual function OnRequestActionWidgetsUpdate( evt : RequestActionWidgetsUpdateEvent )
	{
		RequestActionWidgetsUpdate( GetBlackboard() );
	}

	public virtual function OnRequestUIRefresh( evt : RequestUIRefreshEvent )
	{
		RefreshUI( GetBlackboard() );
	}

	public override function ResloveUIOnAction( action : ScriptableDeviceAction )
	{
		var deviceWidgetEvent : RequestDeviceWidgetUpdateEvent;
		var entityID : EntityID;
		if( action )
		{
			if( !( action.HasUI() ) )
			{
				return;
			}
			entityID = PersistentID.ExtractEntityID( GetID() );
			RequestActionWidgetsUpdate( GetBlackboard() );
			if( action.GetRequesterID() != entityID )
			{
				deviceWidgetEvent = new RequestDeviceWidgetUpdateEvent;
				deviceWidgetEvent.requester = GetID();
				GetPersistencySystem().QueueEntityEvent( action.GetRequesterID(), deviceWidgetEvent );
			}
		}
	}

	public const override function GetVirtualSystem( out vs : VirtualSystemPS ) : Bool
	{
		var masters : array< DeviceComponentPS >;
		var i : Int32;
		GetParents( masters );
		for( i = 0; i < masters.Size(); i += 1 )
		{
			if( ( ( TerminalControllerPS )( masters[ i ] ) ) )
			{
				if( masters[ i ].GetVirtualSystem( this, vs ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	protected override function GetInkWidgetTweakDBID( const context : ref< GetActionsContext > ) : TweakDBID
	{
		if( !( IsUserAuthorized( context.processInitiatorObject.GetEntityID() ) ) && !( context.ignoresAuthorization ) )
		{
			if( IsDeviceSecuredWithPassword() )
			{
				return GetKeypadWidgetStyle();
			}
			else
			{
				return GetKeypadWidgetStyle();
			}
		}
		return super.GetInkWidgetTweakDBID( context );
	}

	protected const virtual function GetKeypadWidgetStyle() : TweakDBID
	{
		return T"DevicesUIDefinitions.GenericKeypadWidget";
	}

	public virtual function OnThumbnailUI( evt : ThumbnailUI ) : EntityNotificationType
	{
		var sAction : ScriptableDeviceAction;
		var deviceWidgetEvent : RequestDeviceWidgetUpdateEvent;
		sAction = ( ( ScriptableDeviceAction )( evt ) );
		if( !( IsFinal() ) )
		{
		}
		deviceWidgetEvent = new RequestDeviceWidgetUpdateEvent;
		deviceWidgetEvent.requester = GetID();
		GetPersistencySystem().QueueEntityEvent( sAction.GetRequesterID(), deviceWidgetEvent );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public override function RefreshUI( blackboard : IBlackboard )
	{
		var update : Bool;
		if( blackboard )
		{
			update = blackboard.GetBool( GetBlackboardDef().UIupdate );
			blackboard.SetBool( GetBlackboardDef().UIupdate, !( update ) );
			blackboard.FireCallbacks();
		}
	}

	public override function RequestBreadCrumbUpdate( blackboard : IBlackboard, data : SBreadCrumbUpdateData )
	{
		if( blackboard )
		{
			blackboard.SetVariant( GetBlackboardDef().BreadCrumbElement, data, true );
			blackboard.FireCallbacks();
		}
	}

	private function ResolveDeviceWidgetTweakDBData( data : SDeviceWidgetPackage ) : SDeviceWidgetPackage
	{
		var record : WidgetDefinition_Record;
		if( TDBID.IsValid( data.widgetTweakDBID ) )
		{
			record = TweakDBInterface.GetWidgetDefinitionRecord( data.widgetTweakDBID );
			if( record != NULL )
			{
				data.libraryPath = record.LibraryPath();
				data.libraryID = StringToName( record.LibraryID() );
			}
		}
		return data;
	}

	protected virtual function GetWidgetVisualState() : EWidgetState
	{
		var widgetState : EWidgetState;
		if( IsOFF() || IsUnpowered() )
		{
			widgetState = EWidgetState.LOCKED;
		}
		else
		{
			widgetState = EWidgetState.ALLOWED;
		}
		return widgetState;
	}

	public override function GetDeviceWidget( const context : ref< GetActionsContext > ) : SDeviceWidgetPackage
	{
		var widgetData : SDeviceWidgetPackage;
		var libraryPath : ResRef;
		var libraryID : CName;
		if( IsDisabled() )
		{
			widgetData.isValid = false;
			return widgetData;
		}
		widgetData.widgetTweakDBID = GetInkWidgetTweakDBID( context );
		if( SWidgetPackageBase.ResolveWidgetTweakDBData( widgetData.widgetTweakDBID, libraryID, libraryPath ) )
		{
			widgetData.libraryID = libraryID;
			widgetData.libraryPath = libraryPath;
		}
		else
		{
			widgetData.libraryPath = GetInkWidgetLibraryPath();
			widgetData.libraryID = GetInkWidgetLibraryID( context );
		}
		widgetData.isValid = ResRef.IsValid( widgetData.libraryPath ) || IsNameValid( widgetData.libraryID );
		if( !( widgetData.isValid ) )
		{
			return widgetData;
		}
		widgetData.widgetState = GetWidgetVisualState();
		widgetData.ownerID = GetID();
		widgetData.widgetName = GetDeviceName();
		widgetData.displayName = GetDeviceName();
		widgetData.actionWidgets = GetActionWidgets( context );
		widgetData.deviceStatus = "LocKey#42210";
		widgetData.bckgroundTextureID = GetBackgroundTextureTweakDBID();
		widgetData.deviceState = GetDeviceState();
		widgetData.textData = GetDeviceStatusTextData();
		return widgetData;
	}

	public override function GetThumbnailWidget() : SThumbnailWidgetPackage
	{
		var action : ThumbnailUI;
		var widgetData : SThumbnailWidgetPackage;
		if( IsDisabled() )
		{
			widgetData.isValid = false;
			return widgetData;
		}
		action = GetThumbnailAction();
		widgetData = action.GetThumbnailWidgetPackage();
		if( !( widgetData.isValid ) )
		{
			return widgetData;
		}
		widgetData.ownerID = GetID();
		widgetData.iconID = GetDeviceIconID();
		widgetData.iconTextureID = GetDeviceIconTweakDBID();
		widgetData.textData = GetDeviceStatusTextData();
		widgetData.widgetState = GetWidgetVisualState();
		return widgetData;
	}

	protected override function GetActionWidgets( const context : ref< GetActionsContext > ) : array< SActionWidgetPackage >
	{
		var i : Int32;
		var actions : array< DeviceAction >;
		var action : ScriptableDeviceAction;
		var widgetsData : array< SActionWidgetPackage >;
		var widgetData : SActionWidgetPackage;
		GetActions( actions, context );
		FinalizeGetActions( actions );
		for( i = 0; i < actions.Size(); i += 1 )
		{
			action = ( ( ScriptableDeviceAction )( actions[ i ] ) );
			if( action.HasUI() )
			{
				widgetData = action.GetActionWidgetPackage();
				widgetData.ownerID = GetID();
				if( widgetData.isValid )
				{
					widgetsData.PushBack( widgetData );
				}
			}
		}
		return widgetsData;
	}

	public override function RequestActionWidgetsUpdate( blackboard : IBlackboard )
	{
		var widgetsData : array< SActionWidgetPackage >;
		widgetsData = GetActionWidgets( GenerateContext( gamedeviceRequestType.Internal, GetClearance() ) );
		if( blackboard )
		{
			blackboard.SetVariant( GetBlackboardDef().ActionWidgetsData, widgetsData );
			blackboard.SignalVariant( GetBlackboardDef().ActionWidgetsData );
			blackboard.FireCallbacks();
		}
	}

	public virtual function RequestDeviceWidgetsUpdate( blackboard : IBlackboard )
	{
		var widgetsData : array< SDeviceWidgetPackage >;
		widgetsData.PushBack( GetDeviceWidget( GenerateContext( gamedeviceRequestType.Internal, GetClearance() ) ) );
		RequestDeviceWidgetsUpdate( blackboard, widgetsData );
	}

	protected function RequestDeviceWidgetsUpdate( blackboard : IBlackboard, widgetsData : array< SDeviceWidgetPackage > )
	{
		if( blackboard && ( widgetsData.Size() > 0 ) )
		{
			blackboard.SetVariant( GetBlackboardDef().DeviceWidgetsData, widgetsData );
			blackboard.SignalVariant( GetBlackboardDef().DeviceWidgetsData );
			blackboard.FireCallbacks();
		}
	}

	protected virtual function ResolveBaseActionOperation( action : ScriptableDeviceAction )
	{
		var evt : PerformedAction;
		evt = new PerformedAction;
		evt.m_action = action;
		StorePerformedActionID( action );
		GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), evt );
		if( action.GetRequesterID() != PersistentID.ExtractEntityID( GetID() ) )
		{
			evt = new PerformedAction;
			evt.m_action = action;
			GetPersistencySystem().QueueEntityEvent( action.GetRequesterID(), evt );
		}
	}

	public const function GetDeviceOperationsContainer() : DeviceOperationsContainer
	{
		return m_deviceOperationsSetup;
	}

	public function StorePerformedActionID( oryginalAction : ScriptableDeviceAction )
	{
		var index : Int32;
		var newStructure : SPerformedActions;
		var context : EActionContext;
		var actionID : CName;
		actionID = oryginalAction.GetActionID();
		index = WasActionPerformed( actionID );
		if( index > -1 )
		{
			newStructure = m_performedActions[ index ];
			m_performedActions.Erase( index );
		}
		else
		{
			newStructure.ID = actionID;
		}
		context = SPerformedActions.GetContextFromAction( GetID(), oryginalAction );
		if( !( SPerformedActions.ContainsActionContext( newStructure, context ) ) )
		{
			newStructure.ActionContext.PushBack( context );
		}
		m_performedActions.PushBack( newStructure );
	}

	public function ResetPerformedActionsStorage()
	{
		m_performedActions.Clear();
	}

	public const function GetPerformedActionsIDs() : array< CName >
	{
		var i : Int32;
		var IDs : array< CName >;
		for( i = 0; i < m_performedActions.Size(); i += 1 )
		{
			IDs.PushBack( m_performedActions[ i ].ID );
		}
		return IDs;
	}

	public const function GetPerformedActions() : array< SPerformedActions >
	{
		return m_performedActions;
	}

	public const function WasActionPerformed( actionID : CName ) : Int32
	{
		var i : Int32;
		for( i = 0; i < m_performedActions.Size(); i += 1 )
		{
			if( m_performedActions[ i ].ID == actionID )
			{
				return i;
			}
		}
		return -1;
	}

	public quest const function WasDeviceActionPerformed( actionID : CName ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_performedActions.Size(); i += 1 )
		{
			if( m_performedActions[ i ].ID == actionID )
			{
				return true;
			}
		}
		return false;
	}

	public const function WasActionPerformed( actionID : CName, context : EActionContext ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_performedActions.Size(); i += 1 )
		{
			if( m_performedActions[ i ].ID == actionID )
			{
				if( SPerformedActions.ContainsActionContext( m_performedActions[ i ], context ) )
				{
					return true;
				}
				else
				{
					return false;
				}
			}
		}
		return false;
	}

	public const function IsIniatialStateOperationPerformed() : Bool
	{
		return m_isInitialStateOperationPerformed;
	}

	public function SetInitialStateOperataionPerformed( value : Bool )
	{
		m_isInitialStateOperationPerformed = value;
	}

	private function SceneInteractionsBlocked() : Bool
	{
		var system : InteractionManager;
		system = GameInstance.GetInteractionManager( GetGameInstance() );
		if( system )
		{
			return system.AreSceneInteractionsBlocked();
		}
		return false;
	}

	public override function DetermineInteractionState( interactionComponent : InteractionComponent, const context : ref< GetActionsContext > )
	{
		var actions : array< DeviceAction >;
		var allChoices : array< InteractionChoice >;
		var activeChoices : array< InteractionChoice >;
		if( m_isLockedViaSequencer )
		{
			return;
		}
		if( ( IsMarkedAsQuest() || IsAnyMasterFlaggedAsQuest() ) || IsNameValid( GetFactToDisableQuestMarkName() ) )
		{
			if( SceneInteractionsBlocked() )
			{
				return;
			}
		}
		if( !( context.requestType == gamedeviceRequestType.Direct || context.requestType == gamedeviceRequestType.Remote ) )
		{
			return;
		}
		if( m_isInteractive && !( GetHudManager().IsQuickHackPanelOpened() ) )
		{
			if( HasActiveContext( gamedeviceRequestType.Remote ) )
			{
				if( !( m_disableQuickHacks ) && ( IsQuickHacksExposed() || m_debugExposeQuickHacks ) )
				{
					if( IsPowered() )
					{
						GetQuickHackActions( actions, context );
						UpdateAvailAbleQuickHacks( actions );
					}
				}
				if( IsSpiderbotActionsConditionsFulfilled() )
				{
					GetSpiderbotActions( actions, context );
					UpdateAvailableSpiderbotActions( actions );
				}
			}
			if( HasActiveContext( gamedeviceRequestType.Direct ) )
			{
				if( !( GetTakeOverControlSystem().IsDeviceControlled() ) )
				{
					GetActions( actions, context );
					FinalizeGetActions( actions );
				}
			}
			BasicInteractionInterpreter.Evaluate( IsDeviceSecured(), actions, allChoices, activeChoices );
			if( ( activeChoices.Size() == 0 ) && context.requestType != gamedeviceRequestType.Remote )
			{
				PushInactiveInteractionChoice( context, allChoices );
			}
		}
		PushChoicesToInteractionComponent( interactionComponent, context, allChoices );
	}

	public static function SetActionsInactiveAll( actions : ref< array< DeviceAction > >, optional reason : String, optional exludedAction : CName )
	{
		var sAction : ScriptableDeviceAction;
		var i : Int32;
		for( i = 0; i < actions.Size(); i += 1 )
		{
			sAction = ( ( ScriptableDeviceAction )( actions[ i ] ) );
			if( IsNameValid( exludedAction ) && sAction.GetClassName() == exludedAction )
			{
				continue;
			}
			sAction.SetInactive();
			sAction.SetInactiveReason( reason );
		}
	}

	protected virtual function PushInactiveInteractionChoice( const context : ref< GetActionsContext >, choices : ref< array< InteractionChoice > > )
	{
		var baseAction : DisassembleDevice;
		var inactiveChoice : InteractionChoice;
		baseAction = ActionDisassembleDevice();
		inactiveChoice.choiceMetaData.tweakDBName = baseAction.GetTweakDBChoiceRecord();
		inactiveChoice.caption = "DEBUG: Reason Unhandled";
		ChoiceTypeWrapper.SetType( inactiveChoice.choiceMetaData.type, gameinteractionsChoiceType.Inactive );
	}

	protected function DetermineAreaHintIndicatorState( context : GetActionsContext )
	{
		var actions : array< DeviceAction >;
		var allChoices : array< InteractionChoice >;
		var activeChoices : array< InteractionChoice >;
		context.requestType = gamedeviceRequestType.Direct;
		GetActions( actions, context );
		if( IsQuickHacksExposed() && m_isScanned )
		{
			context.requestType = gamedeviceRequestType.Remote;
			GetActions( actions, context );
		}
		BasicInteractionInterpreter.Evaluate( IsDeviceSecured(), actions, allChoices, activeChoices );
	}

	private function PushChoicesToInteractionComponent( interactionComponent : InteractionComponent, const context : ref< GetActionsContext >, choices : ref< array< InteractionChoice > > )
	{
		var layerName : CName;
		var shouldPushChoices : Bool;
		var requestType : gamedeviceRequestType;
		if( HasActiveContext( gamedeviceRequestType.Direct ) )
		{
			requestType = gamedeviceRequestType.Direct;
		}
		else
		{
			requestType = gamedeviceRequestType.Remote;
		}
		switch( requestType )
		{
			case gamedeviceRequestType.Direct:
				layerName = 'direct';
			shouldPushChoices = true;
			break;
		}
		if( interactionComponent )
		{
			interactionComponent.ResetChoices( layerName );
		}
		if( choices.Size() == 0 )
		{
			return;
		}
		if( shouldPushChoices )
		{
			if( IsUserAuthorized( context.processInitiatorObject.GetEntityID() ) )
			{
				TutorialProcessSkillcheck( choices );
			}
			if( interactionComponent )
			{
				interactionComponent.SetChoices( choices, layerName );
			}
		}
	}

	private function TutorialProcessSkillcheck( const choices : ref< array< InteractionChoice > > )
	{
		var i : Int32;
		var questSystem : QuestsSystem;
		questSystem = GameInstance.GetQuestsSystem( GetGameInstance() );
		if( ( questSystem.GetFact( 'skillcheck_tutorial' ) > 0 ) || ( questSystem.GetFact( 'disable_tutorials' ) != 0 ) )
		{
			return;
		}
		for( i = 0; i < choices.Size(); i += 1 )
		{
			if( ChoiceTypeWrapper.IsType( choices[ i ].choiceMetaData.type, gameinteractionsChoiceType.CheckFailed ) )
			{
				questSystem.SetFact( 'skillcheck_tutorial', 1 );
				return;
			}
		}
	}

	public const virtual function GenerateContext( requestType : gamedeviceRequestType, providedClearance : Clearance, optional providedProcessInitiator : GameObject, optional providedRequestor : EntityID ) : GetActionsContext
	{
		var generatedContext : GetActionsContext;
		var device : Device;
		generatedContext.clearance = providedClearance;
		if( EntityID.IsDefined( providedRequestor ) )
		{
			generatedContext.requestorID = providedRequestor;
		}
		else
		{
			generatedContext.requestorID = GetMyEntityID();
		}
		generatedContext.requestType = requestType;
		if( requestType == gamedeviceRequestType.Remote )
		{
			generatedContext.interactionLayerTag = 'remote';
		}
		else if( requestType == gamedeviceRequestType.Direct )
		{
			generatedContext.interactionLayerTag = 'direct';
		}
		else if( requestType == gamedeviceRequestType.Internal )
		{
			generatedContext.interactionLayerTag = 'any';
		}
		else if( requestType == gamedeviceRequestType.External )
		{
			generatedContext.interactionLayerTag = 'any';
		}
		if( providedProcessInitiator )
		{
			generatedContext.processInitiatorObject = providedProcessInitiator;
		}
		else
		{
			device = ( ( Device )( GetOwnerEntityWeak() ) );
			if( device && device.IsPlayerAround() )
			{
				generatedContext.processInitiatorObject = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject();
			}
			else
			{
			}
		}
		return generatedContext;
	}

	protected virtual function DetermineGameplayViability( const context : ref< GetActionsContext >, hasActiveActions : Bool ) : Bool
	{
		var decision : EViabilityDecision;
		decision = BasicViabilityInterpreter.Evaluate( this, hasActiveActions );
		if( decision == EViabilityDecision.NONVIABLE )
		{
			return false;
		}
		return true;
	}

	protected virtual function PushReturnActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > ) : Bool
	{
		var shouldReturn : Bool;
		if( IsUnpowered() )
		{
			shouldReturn = true;
			if( context.requestType == gamedeviceRequestType.Direct )
			{
				PushSkillCheckActions( outActions, context );
			}
		}
		else
		{
			if( context.requestType == gamedeviceRequestType.Direct )
			{
				if( PushSkillCheckActions( outActions, context ) )
				{
					shouldReturn = true;
				}
			}
			if( context.processInitiatorObject )
			{
				if( ( !( IsUserAuthorized( context.processInitiatorObject.GetEntityID() ) ) && !( context.ignoresAuthorization ) ) && !( CanPayToAuthorize() ) )
				{
					shouldReturn = true;
					outActions.PushBack( ActionAuthorizeUser( ShouldForceAuthorizeUser( context ) ) );
				}
			}
		}
		return shouldReturn;
	}

	protected const function SetActionIllegality( outActions : ref< array< DeviceAction > >, isIllegal : Bool )
	{
		var i : Int32;
		for( i = 0; i < outActions.Size(); i += 1 )
		{
			if( ( ( ScriptableDeviceAction )( outActions[ i ] ) ) )
			{
				( ( ScriptableDeviceAction )( outActions[ i ] ) ).SetIllegal( isIllegal );
			}
		}
	}

	public function GetRemoteActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		if( m_disableQuickHacks || IsDisabled() )
		{
			return;
		}
		GetQuickHackActions( outActions, context );
		if( !( IsQuickHacksExposed() ) || IsLockedViaSequencer() )
		{
			SetActionsInactiveAll( outActions, "LocKey#7021", 'RemoteBreach' );
		}
	}

	protected const function EvaluateActionsRPGAvailabilty( outActions : ref< array< DeviceAction > >, const context : ref< GetActionsContext > )
	{
		var i : Int32;
		var action : ScriptableDeviceAction;
		var reason : String;
		if( context.ignoresRPG )
		{
			return;
		}
		for( i = outActions.Size() - 1; i >= 0; i -= 1 )
		{
			action = ( ( ScriptableDeviceAction )( outActions[ i ] ) );
			if( IsActionRPGRequirementDisabled( action.GetObjectActionID() ) )
			{
				action.SetIsActionRPGCheckDissabled( true );
			}
			if( !( IsActionRPGRequirementDisabled( action.GetObjectActionID() ) ) && !( action.IsVisible( context ) ) )
			{
				reason = action.GetInactiveReason();
				if( ( ( RemoteBreach )( action ) ) && IsStringValid( reason ) )
				{
					action.SetInactiveWithReason( false, reason );
				}
				else
				{
					action.SetInactiveWithReason( false, "LocKey#53826" );
				}
			}
		}
	}

	protected function SetActionsQuickHacksExecutioner( outActions : ref< array< DeviceAction > > )
	{
		var i : Int32;
		for( i = 0; i < outActions.Size(); i += 1 )
		{
			if( ( ( ScriptableDeviceAction )( outActions[ i ] ) ) )
			{
				( ( ScriptableDeviceAction )( outActions[ i ] ) ).SetExecutor( GetLocalPlayerControlledGameObject() );
			}
		}
	}

	protected function MarkActionsAsQuickHacks( actionsToMark : ref< array< DeviceAction > > )
	{
		var i : Int32;
		var acion : ScriptableDeviceAction;
		for( i = 0; i < actionsToMark.Size(); i += 1 )
		{
			acion = ( ( ScriptableDeviceAction )( actionsToMark[ i ] ) );
			if( acion )
			{
				acion.SetAsQuickHack( WasActionPerformed( acion.GetActionID(), EActionContext.QHack ) );
			}
		}
	}

	protected function MarkActionsAsSpiderbotActions( actionsToMark : ref< array< DeviceAction > > )
	{
		var i : Int32;
		for( i = 0; i < actionsToMark.Size(); i += 1 )
		{
			if( ( ( ScriptableDeviceAction )( actionsToMark[ i ] ) ) )
			{
				( ( ScriptableDeviceAction )( actionsToMark[ i ] ) ).SetAsSpiderbotAction();
			}
		}
	}

	protected function ExtractActions( const actionNames : ref< array< CName > > ) : array< DeviceAction >
	{
		var extractedActions : array< DeviceAction >;
		var i : Int32;
		for( i = 0; i < actionNames.Size(); i += 1 )
		{
			extractedActions.PushBack( GetActionByName( actionNames[ i ] ) );
			( ( ScriptableDeviceAction )( extractedActions[ i ] ) ).RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
		}
		return extractedActions;
	}

	public override function GetQuestActionByName( actionName : CName ) : DeviceAction
	{
		var action : DeviceAction;
		switch( actionName )
		{
			case 'ForceEnabled':
				action = ActionQuestForceEnabled();
			break;
			case 'ForceDisabled':
				action = ActionQuestForceDisabled();
			break;
			case 'ForcePower':
				action = ActionQuestForcePower();
			break;
			case 'ForceUnpower':
				action = ActionQuestForceUnpower();
			break;
			case 'ForceON':
				action = ActionQuestForceON();
			break;
			case 'ForceOFF':
				action = ActionQuestForceOFF();
			break;
			case 'QuestResetDeviceToInitialState':
				action = ActionQuestResetDeviceToInitialState();
			break;
			case 'ForceDestructible':
				action = ActionQuestForceDestructible();
			break;
			case 'QuestForceIndestructible':
				action = ActionQuestForceIndestructible();
			break;
			case 'QuestForceInvulnerable':
				action = ActionQuestForceInvulnerable();
			break;
			case 'AuthorizationEnable':
				action = ActionQuestForceAuthorizationEnabled();
			break;
			case 'AuthorizationDisable':
				action = ActionQuestForceAuthorizationDisabled();
			break;
			case 'EnableFixing':
				action = ActionQuestEnableFixing();
			break;
			case 'DisableFixing':
				action = ActionQuestDisableFixing();
			break;
			case 'QuestRemoveQuickHacks':
				action = ActionQuestRemoveQuickHacks();
			break;
			case 'QuestRestoreQuickHacks':
				action = ActionQuestRestoreQuickHacks();
			break;
			case 'QuestForceDisconnectPersonalLink':
				action = ActionQuestForceDisconnectPersonalLink();
			break;
			case 'QuestForcePersonalLinkUnderStrictQuestControl':
				action = ActionQuestForcePersonalLinkUnderStrictQuestControl();
			break;
			case 'QuestStartGlitch':
				action = ActionQuestStartGlitch();
			break;
			case 'QuestStopGlitch':
				action = ActionQuestStopGlitch();
			break;
			case 'QuestResetPerformedActionsStorage':
				action = ActionQuestResetPerfomedActionsStorage();
			break;
			case 'JuryrigTrapArmed':
				action = ActionQuestForceJuryrigTrapArmed();
			break;
			case 'JuryrigTrapDeactivate':
				action = ActionQuestForceJuryrigTrapDeactivated();
			break;
			case 'QuestForceEnableCameraZoom':
				action = ActionQuestForceCameraZoom( true );
			break;
			case 'QuestForceDisableCameraZoom':
				action = ActionQuestForceCameraZoom( false );
			break;
		}
		return action;
	}

	public override function GetQuestActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		if( Clearance.IsInRange( context.clearance, DefaultActionsParametersHolder.GetQuestClearance() ) )
		{
			outActions.PushBack( ActionQuestForceEnabled() );
			outActions.PushBack( ActionQuestForceDisabled() );
			outActions.PushBack( ActionQuestForcePower() );
			outActions.PushBack( ActionQuestForceUnpower() );
			outActions.PushBack( ActionQuestForceON() );
			outActions.PushBack( ActionQuestForceOFF() );
			outActions.PushBack( ActionQuestResetDeviceToInitialState() );
			outActions.PushBack( ActionQuestForceDestructible() );
			outActions.PushBack( ActionQuestForceIndestructible() );
			outActions.PushBack( ActionQuestForceInvulnerable() );
			outActions.PushBack( ActionQuestForceAuthorizationEnabled() );
			outActions.PushBack( ActionQuestForceAuthorizationDisabled() );
			outActions.PushBack( ActionQuestEnableFixing() );
			outActions.PushBack( ActionQuestDisableFixing() );
			outActions.PushBack( ActionQuestRemoveQuickHacks() );
			outActions.PushBack( ActionQuestRestoreQuickHacks() );
			outActions.PushBack( ActionQuestForceDisconnectPersonalLink() );
			outActions.PushBack( ActionQuestForcePersonalLinkUnderStrictQuestControl() );
			outActions.PushBack( ActionQuestStartGlitch() );
			outActions.PushBack( ActionQuestStopGlitch() );
			outActions.PushBack( ActionQuestResetPerfomedActionsStorage() );
			if( m_canBeTrapped )
			{
				outActions.PushBack( ActionQuestForceJuryrigTrapArmed() );
				outActions.PushBack( ActionQuestForceJuryrigTrapDeactivated() );
			}
			outActions.PushBack( ActionQuestForceCameraZoom( true ) );
			outActions.PushBack( ActionQuestForceCameraZoom( false ) );
		}
		return;
	}

	protected const virtual function ActionDeviceStatus() : BaseDeviceStatus
	{
		var action : BaseDeviceStatus;
		action = new BaseDeviceStatus;
		action.clearanceLevel = DefaultActionsParametersHolder.GetStatusClearance();
		action.SetUp( this );
		action.SetProperties( this );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionToggleActivation() : ToggleActivation
	{
		var action : ToggleActivation;
		action = new ToggleActivation;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleActivationClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public virtual function OnToggleActivation( evt : ToggleActivation ) : EntityNotificationType
	{
		var value : Bool;
		value = ( ( Bool )evt.prop.first );
		if( !( value ) )
		{
			DisableDevice();
		}
		else
		{
			EnableDevice();
		}
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function ActionToggleActivate() : ToggleActivate
	{
		var action : ToggleActivate;
		action = new ToggleActivate;
		action.clearanceLevel = 2;
		action.SetUp( this );
		action.SetProperties( m_activationState );
		action.AddDeviceName( m_deviceName );
		action.CreateActionWidgetPackage();
		return action;
	}

	public virtual function OnToggleActivate( evt : ToggleActivate ) : EntityNotificationType
	{
		if( m_activationState == EActivationState.DEACTIVATED )
		{
			ExecutePSAction( ActionActivateDevice() );
		}
		else
		{
			ExecutePSAction( ActionDeactivateDevice() );
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionActivateDevice() : ActivateDevice
	{
		var action : ActivateDevice;
		action = new ActivateDevice;
		action.clearanceLevel = 2;
		action.SetUp( this );
		action.SetProperties( 'LocKey#233' );
		action.AddDeviceName( m_deviceName );
		action.CreateActionWidgetPackage();
		return action;
	}

	protected virtual function OnActivateDevice( evt : ActivateDevice ) : EntityNotificationType
	{
		m_activationState = EActivationState.ACTIVATED;
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionDeactivateDevice() : DeactivateDevice
	{
		var action : DeactivateDevice;
		action = new DeactivateDevice;
		action.clearanceLevel = 2;
		action.SetUp( this );
		action.SetProperties( 'Deactivate' );
		action.AddDeviceName( m_deviceName );
		action.CreateActionWidgetPackage();
		return action;
	}

	protected export virtual function OnDeactivateDevice( evt : DeactivateDevice ) : EntityNotificationType
	{
		m_activationState = EActivationState.DEACTIVATED;
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function ActionTogglePower() : TogglePower
	{
		var action : TogglePower;
		action = new TogglePower;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState );
		action.AddDeviceName( m_deviceName );
		action.CreateActionWidgetPackage();
		return action;
	}

	public virtual function OnTogglePower( evt : TogglePower ) : EntityNotificationType
	{
		var value : Bool;
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetNone();
		value = ( ( Bool )evt.prop.first );
		if( !( value ) )
		{
			UnpowerDevice();
		}
		else
		{
			PowerDevice();
		}
		if( !( IsFinal() ) )
		{
		}
		Notify( notifier, evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionToggleON() : ToggleON
	{
		var action : ToggleON;
		action = new ToggleON;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleOnClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		action.SetWidgetStyle( m_deviceUIStyle );
		action.CreateActionWidgetPackage();
		return action;
	}

	public export virtual function OnToggleON( evt : ToggleON ) : EntityNotificationType
	{
		var value : Bool;
		var notifier : ActionNotifier;
		var player : GameObject;
		notifier = new ActionNotifier;
		notifier.SetNone();
		value = ( ( Bool )evt.prop.first );
		if( !( value ) )
		{
			SetDeviceState( EDeviceStatus.ON );
		}
		else
		{
			SetDeviceState( EDeviceStatus.OFF );
			player = GetLocalPlayer();
			if( player == evt.GetExecutor() )
			{
				SetBlockSecurityWakeUp( true );
			}
		}
		if( !( IsFinal() ) )
		{
		}
		Notify( notifier, evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionSetDeviceON() : SetDeviceON
	{
		var action : SetDeviceON;
		action = new SetDeviceON;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected export virtual function OnSetDeviceON( evt : SetDeviceON ) : EntityNotificationType
	{
		if( IsUnpowered() || IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Unpowered or Disabled" );
		}
		SetDeviceState( EDeviceStatus.ON );
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionSetDeviceOFF() : SetDeviceOFF
	{
		var action : SetDeviceOFF;
		action = new SetDeviceOFF;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected export virtual function OnSetDeviceOFF( evt : SetDeviceOFF ) : EntityNotificationType
	{
		if( IsUnpowered() || IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Unpowered or Disabled" );
		}
		SetDeviceState( EDeviceStatus.OFF );
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionSetDevicePowered() : SetDevicePowered
	{
		var action : SetDevicePowered;
		action = new SetDevicePowered;
		action.clearanceLevel = DefaultActionsParametersHolder.GetSetOnSetOffActions();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected export virtual function OnSetDevicePowered( evt : SetDevicePowered ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		if( IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Disabled" );
		}
		PowerDevice();
		Notify( notifier, evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function ActionSetDeviceUnpowered() : SetDeviceUnpowered
	{
		var action : SetDeviceUnpowered;
		action = new SetDeviceUnpowered;
		action.clearanceLevel = DefaultActionsParametersHolder.GetSetOnSetOffActions();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected virtual function OnSetDeviceUnpowered( evt : SetDeviceUnpowered ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetNone();
		if( IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Disabled" );
		}
		UnpowerDevice();
		if( !( IsFinal() ) )
		{
		}
		Notify( notifier, evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected final function ActionTogglePersonalLink( executor : weak< GameObject >, optional questForcesDisconnection : Bool, optional skipMinigame : Bool ) : TogglePersonalLink
	{
		var action : TogglePersonalLink;
		action = new TogglePersonalLink;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		if( questForcesDisconnection && m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED )
		{
			SetPersonalLinkStatus( EPersonalLinkConnectionStatus.CONNECTED );
		}
		action.SetProperties( m_personalLinkStatus, skipMinigame );
		action.AddDeviceName( m_deviceName );
		action.SetExecutor( executor );
		if( ( TDBID.IsValid( m_personalLinkCustomInteraction ) && ( m_personalLinkCustomInteraction != T"Interactions.None" ) ) && !( IsPersonalLinkConnected() ) )
		{
			action.CreateInteraction( , , m_personalLinkCustomInteraction );
		}
		else
		{
			action.CreateInteraction();
		}
		if( IsPersonalLinkActionIllegal() )
		{
			action.SetIllegal( true );
		}
		if( !( CanConnectToPersonalLink() ) )
		{
			action.SetInactive();
		}
		return action;
	}

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

	protected function CanConnectToPersonalLink() : Bool
	{
		return ( m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED && !( IsPlayerPerformingTakedown() ) ) && !( IsDisruptivePlayerStatusEffectPresent() );
	}

	public final function OnTogglePersonalLink( evt : TogglePersonalLink ) : EntityNotificationType
	{
		if( ( IsPlayerPerformingTakedown() || IsDisruptivePlayerStatusEffectPresent() ) && m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED && ( evt.GetDurationValue() == 0.0 ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.DISCONNECTING && evt.IsCompleted() )
		{
			SetPersonalLinkStatus( EPersonalLinkConnectionStatus.NOT_CONNECTED );
			UseNotifier( evt );
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTED )
		{
			SetPersonalLinkStatus( EPersonalLinkConnectionStatus.DISCONNECTING );
			ResolvePersonalLinkConnection( evt, true );
			ExecutePSActionWithDelay( evt, this );
			UseNotifier( evt );
			return EntityNotificationType.SendThisEventToEntity;
		}
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTING )
		{
			if( !( evt.IsCompleted() ) )
			{
				SetPersonalLinkStatus( EPersonalLinkConnectionStatus.DISCONNECTING );
				ResolvePersonalLinkConnection( evt, true );
				UseNotifier( evt );
				ExecutePSActionWithDelay( evt, this );
				return EntityNotificationType.SendThisEventToEntity;
			}
		}
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED )
		{
			m_personalLinkStatus = EPersonalLinkConnectionStatus.CONNECTING;
		}
		if( evt.IsCompleted() && m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTING )
		{
			SetPersonalLinkStatus( EPersonalLinkConnectionStatus.CONNECTED );
			if( IsUnpowered() || IsDisabled() )
			{
				return SendActionFailedEvent( evt, evt.GetRequesterID(), "Unpowered, Disabled" );
			}
			ResolvePersonalLinkConnection( evt, false );
		}
		else
		{
			if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTING )
			{
				ExecutePSActionWithDelay( evt, this );
			}
		}
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function SetPersonalLinkStatus( status : EPersonalLinkConnectionStatus )
	{
		m_personalLinkStatus = status;
		GetNetworkBlackboard().SetInt( GetNetworkBlackboardDef().PersonalLinkStatus, ( ( Int32 )( m_personalLinkStatus ) ) );
	}

	protected virtual function ResolvePersonalLinkConnection( evt : TogglePersonalLink, abortOperations : Bool )
	{
		var toggleNetrunnerDive : ToggleNetrunnerDive;
		var hasNetworkBackdoor : Bool;
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTING )
		{
			if( !( IsFinal() ) )
			{
			}
			return;
		}
		hasNetworkBackdoor = HasNetworkBackdoor();
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTED )
		{
			if( m_shouldSkipNetrunnerMinigame || !( hasNetworkBackdoor ) )
			{
				SetMinigameState( HackingMinigameState.Succeeded );
			}
			if( hasNetworkBackdoor && !( WasHackingMinigameSucceeded() ) )
			{
				toggleNetrunnerDive = ActionToggleNetrunnerDive( abortOperations, evt.m_shouldSkipMiniGame );
				toggleNetrunnerDive.SetExecutor( evt.GetExecutor() );
				ExecutePSAction( toggleNetrunnerDive, evt.GetInteractionLayer() );
			}
			else
			{
				ResolveOtherSkillchecks();
			}
		}
		else
		{
			if( HasNetworkBackdoor() )
			{
				toggleNetrunnerDive = ActionToggleNetrunnerDive( true );
				toggleNetrunnerDive.SetExecutor( evt.GetExecutor() );
				ExecutePSAction( toggleNetrunnerDive, evt.GetInteractionLayer() );
			}
		}
	}

	public function DisconnectPersonalLink( executor : GameObject, layer : CName, optional isForcedByQuest : Bool )
	{
		var togglePersonalLink : TogglePersonalLink;
		var emptyTweak : TweakDBID;
		if( m_personalLinkStatus == EPersonalLinkConnectionStatus.NOT_CONNECTED )
		{
			return;
		}
		if( m_disablePersonalLinkAutoDisconnect && !( isForcedByQuest ) )
		{
			return;
		}
		m_personalLinkForced = false;
		if( m_disablePersonalLinkAutoDisconnect )
		{
			m_disablePersonalLinkAutoDisconnect = false;
			if( isForcedByQuest )
			{
				togglePersonalLink = ActionTogglePersonalLink( GetPlayerMainObject(), isForcedByQuest );
				ExecutePSAction( togglePersonalLink, layer );
				m_personalLinkCustomInteraction = emptyTweak;
			}
			return;
		}
		togglePersonalLink = ActionTogglePersonalLink( executor );
		ExecutePSAction( togglePersonalLink, layer );
	}

	public final function DisconnectPersonalLink( evt : ScriptableDeviceAction, optional isForcedByQuest : Bool )
	{
		DisconnectPersonalLink( evt.GetExecutor(), evt.GetInteractionLayer(), isForcedByQuest );
	}

	private const function ActionRemoteBreach() : RemoteBreach
	{
		var action : RemoteBreach;
		action = new RemoteBreach;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties();
		action.SetObjectActionID( T"DeviceAction.RemoteBreach" );
		return action;
	}

	private function OnActionRemoteBreach( evt : RemoteBreach ) : EntityNotificationType
	{
		GetNetworkBlackboard().SetBool( GetNetworkBlackboardDef().RemoteBreach, true );
		ExecutePSAction( ActionToggleNetrunnerDive( false, false, true ), this );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private const function ActionPing() : PingDevice
	{
		var action : PingDevice;
		action = new PingDevice;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties();
		action.SetObjectActionID( T"DeviceAction.PingDevice" );
		return action;
	}

	private function OnActionPing( evt : PingDevice ) : EntityNotificationType
	{
		var slave : DeviceComponentPS;
		var ap : AccessPointControllerPS;
		if( m_hasNetworkBackdoor )
		{
			ap = GetBackdoorAccessPoint();
		}
		if( ap )
		{
			slave = GetAttachedSlaveForPing( ap );
		}
		else if( !( IsAttachedToGame() ) )
		{
			slave = GetAttachedSlaveForPing();
		}
		if( slave )
		{
			QueuePSEvent( slave, evt );
			return EntityNotificationType.DoNotNotifyEntity;
		}
		else
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
	}

	private const function GetNetworkBlackboardDef() : NetworkBlackboardDef
	{
		return GetAllBlackboardDefs().NetworkBlackboard;
	}

	private const function GetNetworkBlackboard() : IBlackboard
	{
		return GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetNetworkBlackboardDef() );
	}

	private function OnPingSquadEvent( evt : ForwardPingToSquadEvent ) : EntityNotificationType
	{
		PingSquad();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private virtual function PingSquad()
	{
		var i : Int32;
		var aps : array< AccessPointControllerPS >;
		var evt : ForwardPingToSquadEvent;
		aps = GetAccessPoints();
		for( i = 0; i < aps.Size(); i += 1 )
		{
			evt = new ForwardPingToSquadEvent;
			QueuePSEvent( aps[ i ], evt );
		}
	}

	protected const final function ActionToggleNetrunnerDive( abortDive : Bool, optional skipMinigame : Bool, optional isRemote : Bool ) : ToggleNetrunnerDive
	{
		var action : ToggleNetrunnerDive;
		action = new ToggleNetrunnerDive;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleAuthorizationClearance();
		action.SetUp( this );
		action.SetProperties( abortDive, skipMinigame, m_minigameAttempt, isRemote );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public final function OnToggleNetrunnerDive( evt : ToggleNetrunnerDive ) : EntityNotificationType
	{
		if( evt.ShouldTerminate() )
		{
			DisconnectPersonalLink( evt );
			return EntityNotificationType.SendThisEventToEntity;
		}
		else
		{
			if( m_personalLinkStatus == EPersonalLinkConnectionStatus.CONNECTED || evt.m_isRemote )
			{
				if( m_shouldSkipNetrunnerMinigame || evt.m_skipMinigame )
				{
					ResolveDive( false );
					DisconnectPersonalLink( evt );
				}
				else
				{
					return EntityNotificationType.SendThisEventToEntity;
				}
			}
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected const virtual function ResolveDive( isBackdoor : Bool )
	{
		var ap : AccessPointControllerPS;
		var aps : array< AccessPointControllerPS >;
		var exposeQuickHacks : SetExposeQuickHacks;
		var bumpMinigameLevel : BumpNetrunnerMinigameLevel;
		var i : Int32;
		var isRemoteBreach : Bool;
		isRemoteBreach = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().NetworkBlackboard ).GetBool( GetAllBlackboardDefs().NetworkBlackboard.RemoteBreach );
		exposeQuickHacks = new SetExposeQuickHacks;
		exposeQuickHacks.isRemote = isRemoteBreach;
		bumpMinigameLevel = new BumpNetrunnerMinigameLevel;
		bumpMinigameLevel.Set( GetPlayerMainObject(), m_skillCheckContainer.GetHackingSlot().GetBaseSkill().GetRequiredLevel( GetGameInstance() ) );
		( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'PlayerDevelopmentSystem' ) ) ).QueueRequest( bumpMinigameLevel );
		if( isBackdoor && isRemoteBreach )
		{
			aps = GetAccessPoints();
			for( i = 0; i < aps.Size(); i += 1 )
			{
				ExecutePSAction( exposeQuickHacks, aps[ i ] );
			}
			return;
		}
		if( !( HasNetworkBackdoor() ) )
		{
			if( !( IsFinal() ) )
			{
			}
			return;
		}
		ap = GetBackdoorAccessPoint();
		if( ap )
		{
			ExecutePSAction( exposeQuickHacks, ap );
			return;
		}
		if( !( IsFinal() ) )
		{
		}
		return;
	}

	public function HackingMinigameEnded( state : HackingMinigameState )
	{
		SetMinigameState( state );
		FinalizeNetrunnerDive( state );
	}

	protected function SetMinigameState( state : HackingMinigameState )
	{
		m_hackingMinigameState = state;
		if( IsPersonalLinkConnected() && state == HackingMinigameState.Succeeded )
		{
			TurnAuthorizationModuleOFF();
			m_skillCheckContainer.GetHackingSlot().SetIsActive( false );
			m_skillCheckContainer.GetHackingSlot().SetIsPassed( true );
			m_skillCheckContainer.GetHackingSlot().CheckPerformed();
			ResolveOtherSkillchecks();
		}
	}

	public quest const function WasHackingMinigameSucceeded() : Bool
	{
		return m_hackingMinigameState == HackingMinigameState.Succeeded;
	}

	public quest const function WashackingMinigameFailed() : Bool
	{
		return m_hackingMinigameState == HackingMinigameState.Failed;
	}

	public virtual function FinalizeNetrunnerDive( state : HackingMinigameState )
	{
		var toggleNetrunnerDive : ToggleNetrunnerDive;
		var player : GameObject;
		if( state == HackingMinigameState.Succeeded )
		{
			ResolveDive( !( HasNetworkBackdoor() ) );
		}
		else if( state == HackingMinigameState.Failed )
		{
			m_minigameAttempt += 1;
		}
		player = GetPlayerMainObject();
		toggleNetrunnerDive = ActionToggleNetrunnerDive( true );
		toggleNetrunnerDive.SetExecutor( player );
		ExecutePSAction( toggleNetrunnerDive );
	}

	public function ActionToggleZoomInteraction() : ToggleZoomInteraction
	{
		var action : ToggleZoomInteraction;
		action = new ToggleZoomInteraction;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( m_isAdvancedInteractionModeOn );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	public function ActionQuestForceCameraZoom( enable : Bool, optional instant : Bool ) : QuestForceCameraZoom
	{
		var action : QuestForceCameraZoom;
		action = new QuestForceCameraZoom;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties( enable, instant );
		action.AddDeviceName( m_deviceName );
		action.SetUseWorkspot( true );
		return action;
	}

	protected function ActionQuestForceCameraZoomNoWorkspot( value : Bool ) : QuestForceCameraZoom
	{
		var action : QuestForceCameraZoom;
		action = new QuestForceCameraZoom;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties( value );
		action.AddDeviceName( m_deviceName );
		action.SetUseWorkspot( false );
		return action;
	}

	protected function ActionOpenFullscreenUI() : OpenFullscreenUI
	{
		var action : OpenFullscreenUI;
		action = new OpenFullscreenUI;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( m_isAdvancedInteractionModeOn );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	public virtual function OnToggleZoomInteraction( evt : ToggleZoomInteraction ) : EntityNotificationType
	{
		UseNotifier( evt );
		if( IsUnpowered() || IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Unpowered or Disabled (or both)" );
		}
		m_isAdvancedInteractionModeOn = !( m_isAdvancedInteractionModeOn );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function OnQuestForceCameraZoom( evt : QuestForceCameraZoom ) : EntityNotificationType
	{
		UseNotifier( evt );
		m_isAdvancedInteractionModeOn = ( ( Bool )evt.prop.first );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected export function OnSetCustomPersonalLinkReason( evt : SetCustomPersonalLinkReason ) : EntityNotificationType
	{
		if( evt.reason == T"Interactions.None" )
		{
			m_personalLinkForced = false;
			m_disablePersonalLinkAutoDisconnect = false;
		}
		else
		{
			m_personalLinkForced = true;
			m_disablePersonalLinkAutoDisconnect = true;
		}
		m_personalLinkCustomInteraction = evt.reason;
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function OnOpenFullscreenUI( evt : OpenFullscreenUI ) : EntityNotificationType
	{
		UseNotifier( evt );
		if( IsUnpowered() || IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Unpowered or Disabled (or both)" );
		}
		m_isAdvancedInteractionModeOn = !( m_isAdvancedInteractionModeOn );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function ActionDisassembleDevice() : DisassembleDevice
	{
		var action : DisassembleDevice;
		action = new DisassembleDevice;
		action.clearanceLevel = DefaultActionsParametersHolder.GetDisassembleClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected virtual function ActionFixDevice() : FixDevice
	{
		var action : FixDevice;
		action = new FixDevice;
		action.clearanceLevel = DefaultActionsParametersHolder.GetFixingClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	public virtual function OnFixDevice( evt : FixDevice ) : EntityNotificationType
	{
		m_destructionProperties.m_canBeFixed = false;
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function OnDisassembleDevice( evt : DisassembleDevice ) : EntityNotificationType
	{
		TurnAuthorizationModuleOFF();
		m_disassembleProperties.m_canBeDisassembled = false;
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function ActionToggleJuryrigTrap() : ToggleJuryrigTrap
	{
		var action : ToggleJuryrigTrap;
		action = new ToggleJuryrigTrap;
		action.SetUp( this );
		action.SetProperties( m_juryrigTrapState );
		action.AddDeviceName( m_deviceName );
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.CreateInteraction();
		return action;
	}

	public function OnToggleJuryrigTrap( evt : ToggleJuryrigTrap ) : EntityNotificationType
	{
		if( IsJuryrigTrapTriggered() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Trap Triggered" );
		}
		( ( IsJuryrigTrapArmed() ) ? ( SetJuryrigTrapArmedState( EJuryrigTrapState.UNARMED ) ) : ( SetJuryrigTrapArmedState( EJuryrigTrapState.ARMED ) ) );
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function ActionScavenge( const context : ref< GetActionsContext > ) : ActionScavenge
	{
		var action : ActionScavenge;
		action = new ActionScavenge;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( 10 );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	public function OnActionScavenge( evt : ActionScavenge ) : EntityNotificationType
	{
		m_hasBeenScavenged = true;
		GameInstance.GetActivityLogSystem( GetGameInstance() ).AddLog( "Scraps looted: " + IntToString( ( ( Int32 )evt.prop.first ) ) );
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public export virtual function OnSetExposeQuickHacks( evt : SetExposeQuickHacks ) : EntityNotificationType
	{
		var validate : Validate;
		var backdoors : array< ScriptableDeviceComponentPS >;
		var i : Int32;
		if( IsQuickHacksExposed() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		ExposeQuickHacks( true );
		UseNotifier( evt );
		backdoors = GetBackdoorDevices();
		for( i = 0; i < backdoors.Size(); i += 1 )
		{
			if( !( backdoors[ i ].IsBreached() ) )
			{
				validate = new Validate;
				QueuePSEvent( backdoors[ i ].GetBackdoorAccessPoint(), validate );
			}
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected event OnRevokeQuickHackAccess( evt : RevokeQuickHackAccess )
	{
		ExposeQuickHacks( false );
	}

	protected virtual function ActionQuickHackToggleON() : QuickHackToggleON
	{
		var action : QuickHackToggleON;
		action = new QuickHackToggleON;
		action.Repeat = true;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState );
		action.AddDeviceName( m_deviceName );
		action.SetObjectActionID( T"DeviceAction.ToggleStateClassHack" );
		action.CreateInteraction();
		return action;
	}

	public virtual function OnQuickHackToggleOn( evt : QuickHackToggleON ) : EntityNotificationType
	{
		var action : ToggleON;
		SetBlockSecurityWakeUp( ( ( Bool )evt.prop.first ) );
		evt.SetDurationValue( evt.GetDurationTime() );
		action = ActionToggleON();
		ExecutePSAction( action, 'remote' );
		UseNotifier( evt );
		if( !( IsON() ) && evt.Repeat )
		{
			evt.Repeat = false;
		}
		if( ( IsON() && evt.IsStarted() ) && evt.Repeat )
		{
			m_isTimedTurnOff = true;
		}
		else
		{
			m_isTimedTurnOff = false;
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function ActionGlitchScreen( actionID : TweakDBID, programID : TweakDBID, optional timeout : Float ) : GlitchScreen
	{
		var action : GlitchScreen;
		action = new GlitchScreen;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties( m_isGlitching, actionID, programID );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		action.SetDurationValue( timeout );
		return action;
	}

	public function OnGlitchScreen( evt : GlitchScreen ) : EntityNotificationType
	{
		if( !( IsON() ) )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Disabled or Unpowered" );
		}
		if( evt.IsStarted() )
		{
			m_isGlitching = true;
			evt.SetCanTriggerStim( true );
			ExecutePSActionWithDelay( evt, this, evt.GetDurationValue() );
		}
		else
		{
			m_isGlitching = false;
			evt.SetCanTriggerStim( false );
		}
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public export virtual function OnQuestForceEnabled( evt : QuestForceEnabled ) : EntityNotificationType
	{
		if( !( IsDisabled() ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( m_wasStateCached && m_cachedDeviceState != EDeviceStatus.DISABLED )
		{
			SetDeviceState( m_cachedDeviceState );
		}
		else
		{
			SetDeviceState( EDeviceStatus.OFF );
		}
		if( !( IsFinal() ) )
		{
		}
		InitializeBackdoorSkillcheck();
		DetermineInitialPlaystyle();
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public export function OnQuestForceDisabled( evt : QuestForceDisabled ) : EntityNotificationType
	{
		DisableDevice();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	protected export virtual function OnQuestForcePower( evt : QuestForcePower ) : EntityNotificationType
	{
		if( IsPowered() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( GetDeviceState() == EDeviceStatus.DISABLED )
		{
			evt.SetShouldActivateDevice( true );
		}
		PowerDevice();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	protected export virtual function OnQuestForceUnpower( evt : QuestForceUnpower ) : EntityNotificationType
	{
		if( GetDeviceState() == EDeviceStatus.DISABLED )
		{
			evt.SetShouldActivateDevice( true );
		}
		UnpowerDevice();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function ActionQuestForceON() : QuestForceON
	{
		var action : QuestForceON;
		action = new QuestForceON;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected export virtual function OnQuestForceON( evt : QuestForceON ) : EntityNotificationType
	{
		if( IsON() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( GetDeviceState() == EDeviceStatus.DISABLED )
		{
			evt.SetShouldActivateDevice( true );
		}
		SetDeviceState( EDeviceStatus.ON );
		if( !( IsFinal() ) )
		{
		}
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function ActionQuestForceOFF() : QuestForceOFF
	{
		var action : QuestForceOFF;
		action = new QuestForceOFF;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected export virtual function OnQuestForceOFF( evt : QuestForceOFF ) : EntityNotificationType
	{
		if( IsOFF() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		if( GetDeviceState() == EDeviceStatus.DISABLED )
		{
			evt.SetShouldActivateDevice( true );
		}
		SetDeviceState( EDeviceStatus.OFF );
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public function OnQuestForceDestructible( evt : QuestForceDestructible ) : EntityNotificationType
	{
		SetDurabilityType( EDeviceDurabilityType.DESTRUCTIBLE );
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	public function OnQuestForceIndestructible( evt : QuestForceIndestructible ) : EntityNotificationType
	{
		SetDurabilityType( EDeviceDurabilityType.INDESTRUCTIBLE );
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	public function OnQuestForceInvulnerable( evt : QuestForceInvulnerable ) : EntityNotificationType
	{
		SetDurabilityType( EDeviceDurabilityType.INVULNERABLE );
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	public function OnQuestForceAuthorizationEnabled( evt : QuestForceAuthorizationEnabled ) : EntityNotificationType
	{
		if( IsAuthorizationModuleOn() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		m_hasAuthorizationModule = true;
		TurnAuthorizationModuleON();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

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

	public virtual function OnQuestEnableFixing( evt : QuestEnableFixing ) : EntityNotificationType
	{
		m_destructionProperties.m_canBeFixed = true;
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function OnQuestDisableFixing( evt : QuestDisableFixing ) : EntityNotificationType
	{
		m_destructionProperties.m_canBeFixed = false;
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected final function ActionQuestRemoveQuickHacks() : QuestRemoveQuickHacks
	{
		var action : QuestRemoveQuickHacks;
		action = new QuestRemoveQuickHacks;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected final function ActionQuestRestoreQuickHacks() : QuestRestoreQuickHacks
	{
		var action : QuestRestoreQuickHacks;
		action = new QuestRestoreQuickHacks;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public export virtual function OnQuestRemoveQuickHacks( evt : QuestRemoveQuickHacks ) : EntityNotificationType
	{
		ExposeQuickHacks( false );
		m_disableQuickHacks = true;
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export virtual function OnQuestRestoreQuickHacks( evt : QuestRestoreQuickHacks ) : EntityNotificationType
	{
		ExposeQuickHacks( true );
		m_disableQuickHacks = false;
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected final function ActionQuestResetPerfomedActionsStorage() : QuestResetPerformedActionsStorage
	{
		var action : QuestResetPerformedActionsStorage;
		action = new QuestResetPerformedActionsStorage;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public virtual function OnQuestResetPerfomedActionsStorage( evt : QuestResetPerformedActionsStorage ) : EntityNotificationType
	{
		ResetPerformedActionsStorage();
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected final function ActionQuestBreachAccessPoint() : QuestBreachAccessPoint
	{
		var action : QuestBreachAccessPoint;
		action = new QuestBreachAccessPoint;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public virtual function OnQuestBreachAccessPoint( evt : QuestBreachAccessPoint ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	public export function OnQuestForceAuthorizationDisabled( evt : QuestForceAuthorizationDisabled ) : EntityNotificationType
	{
		if( !( IsAuthorizationModuleOn() ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		TurnAuthorizationModuleOFF();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	private function OnQuestForceDisconnectPersonalLink( evt : QuestForceDisconnectPersonalLink ) : EntityNotificationType
	{
		evt.SetExecutor( GetPlayerMainObject() );
		DisconnectPersonalLink( evt, true );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	private function OnQuestForcePersonalLinkUnderStrictQuestControl( evt : QuestForcePersonalLinkUnderStrictQuestControl ) : EntityNotificationType
	{
		m_personalLinkForced = true;
		m_disablePersonalLinkAutoDisconnect = true;
		UseNotifier( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	public function OnQuestForceJuryrigTrapArmed( evt : QuestForceJuryrigTrapArmed ) : EntityNotificationType
	{
		if( GetJuryrigTrapState() == EJuryrigTrapState.ARMED )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		else
		{
			SetJuryrigTrapArmedState( EJuryrigTrapState.ARMED );
			UseNotifier( evt );
			if( !( IsFinal() ) )
			{
			}
			return EntityNotificationType.SendThisEventToEntity;
		}
	}

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

	public function OnQuestForceJuryrigTrapDeactivated( evt : QuestForceJuryrigTrapDeactivated ) : EntityNotificationType
	{
		if( GetJuryrigTrapState() == EJuryrigTrapState.UNARMED )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		else
		{
			SetJuryrigTrapArmedState( EJuryrigTrapState.UNARMED );
			UseNotifier( evt );
			if( !( IsFinal() ) )
			{
			}
			return EntityNotificationType.SendThisEventToEntity;
		}
	}

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

	public export virtual function OnQuestResetDeviceToInitialState( evt : QuestResetDeviceToInitialState ) : EntityNotificationType
	{
		ExecutePSAction( ActionQuestForceEnabled() );
		SetMinigameState( HackingMinigameState.Unknown );
		TurnAuthorizationModuleON();
		m_skillCheckContainer.GetHackingSlot().SetIsActive( true );
		m_skillCheckContainer.GetHackingSlot().SetIsPassed( false );
		GameInstance.GetStatPoolsSystem( GetGameInstance() ).RequestSettingStatPoolMaxValue( GetMyEntityID(), gamedataStatPoolType.Health, NULL );
		SetBlockSecurityWakeUp( false );
		if( IsBroken() )
		{
			SetDurabilityState( EDeviceDurabilityState.NOMINAL );
		}
		if( !( IsON() ) )
		{
			ExecutePSAction( ActionToggleON() );
		}
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	protected function OnQuestStartGlitch( evt : QuestStartGlitch ) : EntityNotificationType
	{
		SetGlitchingState( true );
		UseNotifier( evt );
		m_distractExecuted = true;
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	protected function OnQuestStopGlitch( evt : QuestStopGlitch ) : EntityNotificationType
	{
		SetGlitchingState( false );
		m_distractExecuted = false;
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public constexpr function OnSetAsQuestImportantEvent( evt : SetAsQuestImportantEvent ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public virtual function OnQuestForceSecuritySystemSafe( evt : QuestForceSecuritySystemSafe ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public virtual function OnQuestForceSecuritySystemAlarmed( evt : QuestForceSecuritySystemAlarmed ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

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

	public virtual function OnQuestForceSecuritySystemArmed( evt : QuestForceSecuritySystemArmed ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

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

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

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

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

	public virtual function OnQuestForceActivate( evt : QuestForceActivate ) : EntityNotificationType
	{
		QueuePSEvent( GetID(), GetClassName(), ActionActivateDevice() );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	public virtual function OnQuestForceDeactivate( evt : QuestForceDeactivate ) : EntityNotificationType
	{
		QueuePSEvent( GetID(), GetClassName(), ActionDeactivateDevice() );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected const function ActionTakeOverSecuritySystem( executor : GameObject ) : TakeOverSecuritySystem
	{
		var action : TakeOverSecuritySystem;
		action = new TakeOverSecuritySystem;
		action.clearanceLevel = DefaultActionsParametersHolder.GetSystemCompatibleClearance();
		action.SetProperties();
		action.SetUp( this );
		action.AddDeviceName( GetDeviceName() );
		action.SetWidgetStyle( m_deviceUIStyle );
		action.CreateActionWidgetPackage();
		action.SetExecutor( executor );
		return action;
	}

	public export virtual function OnSecuritySystemOutput( evt : SecuritySystemOutput ) : EntityNotificationType
	{
		if( evt.GetCachedSecurityState() == ESecuritySystemState.COMBAT )
		{
			WakeUpDevice();
		}
		if( evt.GetCachedSecurityState() == ESecuritySystemState.ALERTED )
		{
			if( evt.GetBreachOrigin() == EBreachOrigin.LOCAL )
			{
				WakeUpDevice();
			}
		}
		if( evt.GetCachedSecurityState() == ESecuritySystemState.SAFE )
		{
			NotifyParents_Event();
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export virtual function OnSecuritySystemForceAttitudeChange( evt : SecuritySystemForceAttitudeChange ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	public export virtual function OnSecurityAlarmBreachResponse( evt : SecurityAlarmBreachResponse ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export virtual function OnSecurityAreaCrossingPerimeter( evt : SecurityAreaCrossingPerimeter ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public export virtual function OnTargetAssessmentRequest( evt : TargetAssessmentRequest ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public virtual function OnActionForceResetDevice( evt : ActionForceResetDevice ) : EntityNotificationType
	{
		PerformRestart();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public virtual function OnFullSystemRestart( evt : FullSystemRestart ) : EntityNotificationType
	{
		m_isRestarting = true;
		PerformRestart();
		if( !( IsFinal() ) )
		{
		}
		TriggerWakeUpDelayedEvent( evt.m_restartDuration );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected virtual function PerformRestart() {}

	protected function TriggerWakeUpDelayedEvent( duration : Int32 )
	{
		var wakeUpEvent : WakeUpFromRestartEvent;
		wakeUpEvent = new WakeUpFromRestartEvent;
		GameInstance.GetDelaySystem( GetGameInstance() ).DelayPSEvent( GetID(), GetClassName(), wakeUpEvent, ( ( Float )( duration ) ) );
	}

	public function OnWakeUpEvent( evt : WakeUpFromRestartEvent ) : EntityNotificationType
	{
		m_isRestarting = false;
		WakeUpDevice();
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected const virtual function CanPerformReprimand() : Bool
	{
		return false;
	}

	public virtual function ActionSetDeviceAttitude() : SetDeviceAttitude
	{
		var action : SetDeviceAttitude;
		action = new SetDeviceAttitude;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTakeOverControl();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected virtual function Notify( notifier : ActionNotifier, action : ScriptableDeviceAction )
	{
		var deviceChangedEvent : PSDeviceChangedEvent;
		deviceChangedEvent = new PSDeviceChangedEvent;
		deviceChangedEvent.persistentID = GetID();
		deviceChangedEvent.className = GetClassName();
		if( notifier )
		{
			if( notifier.IsFailed() )
			{
				SendActionFailedEvent( action, action.GetRequesterID(), "Failed Toggle" );
				return;
			}
			else if( notifier.IsInternalOnly() )
			{
				GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), action );
				GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), deviceChangedEvent );
			}
			else if( notifier.IsExternalOnly() )
			{
				GetPersistencySystem().QueueEntityEvent( action.GetRequesterID(), deviceChangedEvent );
			}
			else if( notifier.IsAll() )
			{
				GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), action );
				GetPersistencySystem().QueueEntityEvent( action.GetRequesterID(), deviceChangedEvent );
				GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), deviceChangedEvent );
			}
		}
		ResloveUIOnAction( action );
		ResolveBaseActionOperation( action );
	}

	protected function SendPSChangedEvent()
	{
		var evt : PSChangedEvent;
		evt = new PSChangedEvent;
		GetPersistencySystem().QueuePSEvent( GetID(), GetClassName(), evt );
	}

	public const function NotifyParents()
	{
		var i : Int32;
		var devices : array< DeviceComponentPS >;
		var deviceChangedEvent : PSDeviceChangedEvent;
		deviceChangedEvent = new PSDeviceChangedEvent;
		deviceChangedEvent.persistentID = GetID();
		deviceChangedEvent.className = GetClassName();
		GetParents( devices );
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( GetID() == devices[ i ].GetID() )
			{
				continue;
			}
			GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( devices[ i ].GetID() ), deviceChangedEvent );
		}
	}

	protected function NotifyParents_Event()
	{
		var evt : NotifyParentsEvent;
		evt = new NotifyParentsEvent;
		GetPersistencySystem().QueuePSEvent( GetID(), GetClassName(), evt );
	}

	private function OnNotifyParents( evt : NotifyParentsEvent ) : EntityNotificationType
	{
		NotifyParents();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function UseNotifier( action : ScriptableDeviceAction )
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetNone();
		Notify( notifier, action );
	}

	protected virtual function SendActionFailedEvent( failedAction : ScriptableDeviceAction, whereToSend : EntityID, optional context : String ) : EntityNotificationType
	{
		var failedActionEvent : FailedActionEvent;
		failedActionEvent = new FailedActionEvent;
		failedActionEvent.action = failedAction;
		failedActionEvent.whoFailed = GetID();
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueueEntityEvent( whereToSend, failedActionEvent );
		context += " FAILED ACTION EVENT";
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function WakeUpDevice() : Bool
	{
		if( ( IsOFF() && !( IsSecurityWakeUpBlocked() ) ) && !( IsBroken() ) )
		{
			ExecutePSAction( ActionQuestForceON() );
			return true;
		}
		return false;
	}

	protected virtual function PowerDevice()
	{
		if( m_wasStateCached )
		{
			if( m_cachedDeviceState == EDeviceStatus.UNPOWERED )
			{
				SetDeviceState( EDeviceStatus.OFF );
			}
			else
			{
				SetDeviceState( m_cachedDeviceState );
			}
		}
		else
		{
			SetDeviceState( EDeviceStatus.ON );
		}
	}

	public virtual function UnpowerDevice()
	{
		if( !( IsUnpowered() ) && !( IsDisabled() ) )
		{
			CacheDeviceState( m_deviceState );
		}
		SetDeviceState( EDeviceStatus.UNPOWERED );
		if( !( IsAttachedToGame() ) )
		{
			RevealNetworkGrid( false );
			RevealDevicesGrid( false );
		}
	}

	protected virtual function DisableDevice()
	{
		if( !( IsUnpowered() ) && !( IsDisabled() ) )
		{
			CacheDeviceState( m_deviceState );
		}
		DetermineInitialPlaystyle();
		SetDeviceState( EDeviceStatus.DISABLED );
		if( !( IsAttachedToGame() ) )
		{
			RevealNetworkGrid( false );
			RevealDevicesGrid( false );
		}
	}

	public function ForceDisableDevice()
	{
		var action : QuestForceDisabled;
		action = ActionQuestForceDisabled();
		GetPersistencySystem().QueuePSDeviceEvent( action );
	}

	public function ForceEnableDevice()
	{
		var action : QuestForceEnabled;
		action = ActionQuestForceEnabled();
		GetPersistencySystem().QueuePSDeviceEvent( action );
	}

	public function ForceDeviceON()
	{
		var action : QuestForceON;
		action = ActionQuestForceON();
		GetPersistencySystem().QueuePSDeviceEvent( action );
	}

	public virtual function BreakDevice()
	{
		DisableDevice();
	}

	protected function EnableDevice()
	{
		if( m_cachedDeviceState != EDeviceStatus.DISABLED || m_cachedDeviceState != EDeviceStatus.UNPOWERED )
		{
			CacheDeviceState( m_deviceState );
		}
		else
		{
			SetDeviceState( EDeviceStatus.DISABLED );
		}
		DetermineInitialPlaystyle();
	}

	protected const function GetFullAuthorizationData() : array< SecurityAccessLevelEntryClient >
	{
		var data : array< SecurityAccessLevelEntryClient >;
		var externalDataEntry : SecurityAccessLevelEntryClient;
		var externalData : array< SecurityAccessLevelEntry >;
		var securityLevel : ESecurityAccessLevel;
		var i : Int32;
		if( AuthorizationData.IsAuthorizationValid( m_authorizationProperties ) )
		{
			data.PushBack( m_authorizationProperties.m_authorizationDataEntry );
		}
		if( IsConnectedToSecuritySystem( securityLevel ) )
		{
			externalData = GetSecuritySystem().GetSecurityAccessData( securityLevel );
			for( i = 0; i < externalData.Size(); i += 1 )
			{
				externalDataEntry.m_keycard = externalData[ i ].m_keycard;
				externalDataEntry.m_password = externalData[ i ].m_password;
				externalDataEntry.m_level = securityLevel;
				data.PushBack( externalDataEntry );
			}
		}
		return data;
	}

	public const function IsAuthorizationValid() : Bool
	{
		if( AuthorizationData.IsAuthorizationValid( m_authorizationProperties ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	private const function GetFullAuthorizationDataSegregated( passwords : ref< array< CName > >, keycards : ref< array< TweakDBID > > )
	{
		passwords = GetPasswords();
		keycards = GetKeycards();
	}

	public const virtual function GetPasswords() : array< CName >
	{
		var passwords : array< CName >;
		var securityData : array< SecurityAccessLevelEntryClient >;
		securityData = GetFullAuthorizationData();
		passwords = ExtractPasswordsFromAuthorizationData( securityData );
		return passwords;
	}

	public const function GetKeycards() : array< TweakDBID >
	{
		var keycards : array< TweakDBID >;
		var securityData : array< SecurityAccessLevelEntryClient >;
		securityData = GetFullAuthorizationData();
		keycards = ExtractKeycardsFromAuthorizationData( securityData );
		return keycards;
	}

	protected const function ExtractKeycardsFromAuthorizationData( const data : ref< array< SecurityAccessLevelEntryClient > > ) : array< TweakDBID >
	{
		var i : Int32;
		var keycards : array< TweakDBID >;
		for( i = 0; i < data.Size(); i += 1 )
		{
			if( SecurityAccessLevelEntryClient.IsKeycardValid( data[ i ] ) )
			{
				keycards.PushBack( data[ i ].m_keycard );
			}
		}
		return keycards;
	}

	protected const function ExtractPasswordsFromAuthorizationData( const data : ref< array< SecurityAccessLevelEntryClient > > ) : array< CName >
	{
		var i : Int32;
		var passwords : array< CName >;
		for( i = 0; i < data.Size(); i += 1 )
		{
			if( SecurityAccessLevelEntryClient.IsPasswordValid( data[ i ] ) )
			{
				passwords.PushBack( data[ i ].m_password );
			}
		}
		return passwords;
	}

	protected const function ExtractKeycardsFromAuthorizationData( const data : ref< array< SecurityAccessLevelEntry > > ) : array< TweakDBID >
	{
		var i : Int32;
		var keycards : array< TweakDBID >;
		for( i = 0; i < data.Size(); i += 1 )
		{
			if( SecurityAccessLevelEntry.IsKeycardValid( data[ i ] ) )
			{
				keycards.PushBack( data[ i ].m_keycard );
			}
		}
		return keycards;
	}

	protected const function ExtractPasswordsFromAuthorizationData( const data : ref< array< SecurityAccessLevelEntry > > ) : array< CName >
	{
		var i : Int32;
		var passwords : array< CName >;
		for( i = 0; i < data.Size(); i += 1 )
		{
			if( SecurityAccessLevelEntry.IsPasswordValid( data[ i ] ) )
			{
				passwords.PushBack( data[ i ].m_password );
			}
		}
		return passwords;
	}

	protected const final function FindCurrentAuthorizationLevelForUser( user : EntityID ) : ESecurityAccessLevel
	{
		var i : Int32;
		for( i = 0; i < m_currentlyAuthorizedUsers.Size(); i += 1 )
		{
			if( m_currentlyAuthorizedUsers[ i ].user == user )
			{
				return m_currentlyAuthorizedUsers[ i ].level;
			}
		}
		return ESecurityAccessLevel.ESL_NONE;
	}

	protected const function AddUser( user : EntityID, level : ESecurityAccessLevel ) : Bool
	{
		var newUserEntry : SecuritySystemClearanceEntry;
		var addUserEvent : AddUserEvent;
		var index : Int32;
		if( level == ESecurityAccessLevel.ESL_NONE )
		{
			level = ESecurityAccessLevel.ESL_0;
		}
		index = IsUserAlreadyOnTheList( user );
		if( ( index >= 0 ) && ( ( ( Int32 )( m_currentlyAuthorizedUsers[ index ].level ) ) >= ( ( Int32 )( level ) ) ) )
		{
			return false;
		}
		newUserEntry.user = user;
		newUserEntry.level = level;
		addUserEvent = new AddUserEvent;
		addUserEvent.userEntry = newUserEntry;
		GetPersistencySystem().QueuePSEvent( GetID(), GetClassName(), addUserEvent );
		return true;
	}

	public export virtual function OnAddUserEvent( evt : AddUserEvent ) : EntityNotificationType
	{
		var index : Int32;
		DeviceHelper.DebugLog( GetGameInstance(), "SecSys: User authorized" );
		DeviceHelper.DebugLog( GetGameInstance(), "SecSys: User Authorized on: " + GetDeviceName() );
		DeviceHelper.DebugLog( GetGameInstance(), "SecSys: User authorized on level: " + EnumValueToString( "ESecurityAccessLevel", ( ( Int64 )( evt.userEntry.level ) ) ) );
		if( !( IsFinal() ) )
		{
		}
		index = IsUserAlreadyOnTheList( evt.userEntry.user );
		if( index >= 0 )
		{
			m_currentlyAuthorizedUsers.Erase( index );
		}
		m_currentlyAuthorizedUsers.PushBack( evt.userEntry );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected const function IsUserAlreadyOnTheList( entityID : EntityID ) : Int32
	{
		var i : Int32;
		for( i = 0; i < m_currentlyAuthorizedUsers.Size(); i += 1 )
		{
			if( m_currentlyAuthorizedUsers[ i ].user == entityID )
			{
				return i;
			}
		}
		return -1;
	}

	protected virtual function RemoveUser( user : EntityID ) : Bool
	{
		var index : Int32;
		index = IsUserAlreadyOnTheList( user );
		if( index >= 0 )
		{
			if( !( IsFinal() ) )
			{
			}
			m_currentlyAuthorizedUsers.Erase( index );
			return true;
		}
		return false;
	}

	protected const function GetKeycardRecord( record : TweakDBID ) : Item_Record
	{
		return TweakDBInterface.GetItemRecord( record );
	}

	protected const function GetKeycardLocalizedString( record : TweakDBID ) : String
	{
		return NameToString( GetKeycardRecord( record ).DisplayName() );
	}

	public function TurnAuthorizationModuleON() : Bool
	{
		if( HasAuthorizationModule() )
		{
			m_authorizationProperties.m_isAuthorizationModuleOn = true;
			return true;
		}
		return false;
	}

	public virtual function TurnAuthorizationModuleOFF()
	{
		m_authorizationProperties.m_isAuthorizationModuleOn = false;
	}

	public const function ExecutePSAction( action : ScriptableDeviceAction, optional layerTag : CName )
	{
		if( !( EntityID.IsDefined( action.GetRequesterID() ) ) )
		{
			action.RegisterAsRequester( GetMyEntityID() );
		}
		if( layerTag != '' )
		{
			action.SetInteractionLayer( layerTag );
		}
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueuePSDeviceEvent( action );
	}

	public const function ExecutePSAction( action : ScriptableDeviceAction, const persistentState : PersistentState )
	{
		if( !( EntityID.IsDefined( action.GetRequesterID() ) ) )
		{
			action.RegisterAsRequester( GetMyEntityID() );
		}
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueuePSEvent( persistentState.GetID(), persistentState.GetClassName(), action );
	}

	protected const function ExecutePSActionWithDelay( action : ScriptableDeviceAction, persistentState : PersistentState, optional forcedDelay : Float )
	{
		var delay : Float;
		var evt : DelayedDeviceActionEvent;
		if( !( persistentState ) )
		{
			return;
		}
		evt = new DelayedDeviceActionEvent;
		if( forcedDelay == 0.0 )
		{
			delay = action.GetDurationValue();
		}
		else
		{
			delay = forcedDelay;
		}
		evt.action = action;
		QueuePSEventWithDelay( persistentState.GetID(), persistentState.GetClassName(), evt, delay );
	}

	public export function OnDelayedActionEvent( evt : DelayedDeviceActionEvent ) : EntityNotificationType
	{
		var action : ScriptableDeviceAction;
		action = evt.action;
		if( action != NULL )
		{
			action.SetDurationValue( 0.0 );
			ExecutePSAction( action );
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public virtual function ExecuteCurrentSpiderbotActionPerformed()
	{
		ExecutePSAction( m_currentSpiderbotActionPerformed );
		m_currentSpiderbotActionPerformed = NULL;
	}

	public const function ExtractIDs( const persistentStates : ref< array< PersistentState > >, persistentIDs : ref< array< PersistentID > > )
	{
		var i : Int32;
		for( i = 0; i < persistentStates.Size(); i += 1 )
		{
			persistentIDs.PushBack( persistentStates[ i ].GetID() );
		}
	}

	protected const function ExtractEntityID( evt : TriggerEvent ) : EntityID
	{
		var entityID : EntityID;
		entityID = EntityGameInterface.GetEntity( evt.activator ).GetEntityID();
		return entityID;
	}

	public const function GetAvailableQuickHacks() : array< CName >
	{
		return m_availableQuickHacks;
	}

	public const function HasAnyAvailableQuickHack() : Bool
	{
		return m_availableQuickHacks.Size() > 0;
	}

	public function HasAnyQuickHack() : Bool
	{
		var actions : array< DeviceAction >;
		var context : GetActionsContext;
		context = GenerateContext( gamedeviceRequestType.Remote, GetClearance(), GetPlayerMainObject(), PersistentID.ExtractEntityID( GetID() ) );
		context.ignoresRPG = true;
		GetQuickHackActions( actions, context );
		return actions.Size() > 0;
	}

	protected function UpdateAvailAbleQuickHacks( const actions : ref< array< DeviceAction > > )
	{
		var i : Int32;
		var action : ScriptableDeviceAction;
		ClearAvailableQuickHacks();
		for( i = 0; i < actions.Size(); i += 1 )
		{
			action = ( ( ScriptableDeviceAction )( actions[ i ] ) );
			if( action )
			{
				AddAvailableQuickHack( action.GetActionID() );
			}
		}
	}

	public function AddAvailableQuickHack( quickHackName : CName )
	{
		if( !( m_availableQuickHacks.Contains( quickHackName ) ) )
		{
			m_availableQuickHacks.PushBack( quickHackName );
		}
	}

	protected function RemoveAvailableQuickHack( quickHackName : CName )
	{
		if( m_availableQuickHacks.Contains( quickHackName ) )
		{
			m_availableQuickHacks.Remove( quickHackName );
		}
	}

	public function ClearAvailableQuickHacks()
	{
		m_availableQuickHacks.Clear();
	}

	public const function GetAvailableSpiderbotActions() : array< CName >
	{
		return m_availableSpiderbotActions;
	}

	public const function HasAnyAvailableSpiderbotActions() : Bool
	{
		return m_availableSpiderbotActions.Size() > 0;
	}

	public function HasAnySpiderbotAction() : Bool
	{
		var actions : array< DeviceAction >;
		var context : GetActionsContext;
		context = GenerateContext( gamedeviceRequestType.Remote, GetClearance(), GetPlayerMainObject(), PersistentID.ExtractEntityID( GetID() ) );
		context.ignoresRPG = true;
		GetSpiderbotActions( actions, context );
		return actions.Size() > 0;
	}

	protected function UpdateAvailableSpiderbotActions( const actions : ref< array< DeviceAction > > )
	{
		var i : Int32;
		ClearAvailableSpiderbotActions();
		for( i = 0; i < actions.Size(); i += 1 )
		{
			AddAvailableSpiderbotActions( actions[ i ].actionName );
		}
	}

	protected function AddAvailableSpiderbotActions( SpiderbotActionName : CName )
	{
		if( !( m_availableSpiderbotActions.Contains( SpiderbotActionName ) ) )
		{
			m_availableSpiderbotActions.PushBack( SpiderbotActionName );
		}
	}

	protected function RemoveAvailableSpiderbotActions( SpiderbotActionName : CName )
	{
		if( m_availableSpiderbotActions.Contains( SpiderbotActionName ) )
		{
			m_availableSpiderbotActions.Remove( SpiderbotActionName );
		}
	}

	protected function ClearAvailableSpiderbotActions()
	{
		m_availableSpiderbotActions.Clear();
	}

	public const function GetPlayerEntityID() : EntityID
	{
		return GetLocalPlayerControlledGameObject().GetEntityID();
	}

	protected const function GetPlayerMainObject() : GameObject
	{
		return GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject();
	}

	protected const function GetLocalPlayerControlledGameObject() : GameObject
	{
		return GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerControlledGameObject();
	}

	protected const function GetLocalPlayer() : GameObject
	{
		return GetLocalPlayerControlledGameObject();
	}

	private function RegisterDebugEnableQuickHacksListener()
	{
		GameInstance.GetQuestsSystem( GetGameInstance() ).RegisterListener( 'DebugEnableQuickHacks', this, 'EnableDebugQuickHacks' );
	}

	public function EnableDebugQuickHacks( val : Int32 )
	{
		m_debugExposeQuickHacks = ( ( Bool )( val ) );
		if( val > 0 )
		{
			m_isScanned = true;
		}
	}

	public const function GetMasterDevicesTweaks() : array< TweakDBID >
	{
		var parents : array< DeviceComponentPS >;
		var parent : ScriptableDeviceComponentPS;
		var MastersTweaks : array< TweakDBID >;
		var i : Int32;
		GetParents( parents );
		for( i = 0; i < parents.Size(); i += 1 )
		{
			parent = ( ( ScriptableDeviceComponentPS )( parents[ i ] ) );
			if( !( parent ) )
			{
				continue;
			}
			if( !( parent.ShouldRevealDevicesGrid() ) )
			{
				continue;
			}
			MastersTweaks.PushBack( parent.GetTweakDBRecord() );
		}
		return MastersTweaks;
	}

	protected function IsLogInExclusiveMode() : Bool
	{
		var isOverride : Int32;
		isOverride = GetFact( GetGameInstance(), 'dbgDevices' );
		return isOverride;
	}

	public virtual function OnNotifyHighlightedDevice( evt : NotifyHighlightedDevice ) : EntityNotificationType
	{
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function SetDurabilityType( durabilityType : EDeviceDurabilityType )
	{
		m_destructionProperties.m_durabilityType = durabilityType;
	}

	public function SetJuryrigTrapActiveState( newState : Bool )
	{
		m_canBeTrapped = newState;
	}

	public function SetJuryrigTrapArmedState( newState : EJuryrigTrapState )
	{
		m_juryrigTrapState = newState;
	}

	public function SetDurabilityState( newState : EDeviceDurabilityState )
	{
		var removeFromChain : RemoveFromChainRequest;
		if( ( ( ( Int32 )( newState ) ) >= ( ( Int32 )( EDeviceDurabilityState.BROKEN ) ) ) && CanBeInDeviceChain() )
		{
			removeFromChain = new RemoveFromChainRequest;
			removeFromChain.requestSource = GetMyEntityID();
			GetTakeOverControlSystem().QueueRequest( removeFromChain );
		}
		m_durabilityState = newState;
	}

	protected const function GetDistractionDuration( effectName : CName ) : Float
	{
		var device : weak< Device >;
		var duration : Float;
		device = ( ( Device )( GetOwnerEntityWeak() ) );
		if( device )
		{
			duration = device.GetAreaEffectLifetimeByName( effectName );
		}
		return duration;
	}

	public const function GetUniqueConnectionTypes() : array< DeviceConnectionScannerData >
	{
		var connection : DeviceConnectionScannerData;
		var uniqueConnections : array< DeviceConnectionScannerData >;
		var parents : array< DeviceComponentPS >;
		var parent : ScriptableDeviceComponentPS;
		var parentRecord : TweakDBID;
		var deviceRole : ScannableData_Record;
		var categoryName : String;
		var isRepetition : Bool;
		var i : Int32;
		var k : Int32;
		GetParents( parents );
		for( i = 0; i < parents.Size(); i += 1 )
		{
			parent = ( ( ScriptableDeviceComponentPS )( parents[ i ] ) );
			if( !( parent ) )
			{
				continue;
			}
			isRepetition = false;
			if( !( parent.ShouldRevealDevicesGrid() ) && !( parent.ShouldRevealNetworkGrid() ) )
			{
				continue;
			}
			if( ( ( ComputerControllerPS )( parent ) ) )
			{
				parentRecord = ( ( TerminalControllerPS )( parent ) ).GetTweakDBRecord();
			}
			else
			{
				parentRecord = parent.GetTweakDBRecord();
			}
			TDBID.Append( parentRecord, T".deviceType" );
			categoryName = TDB.GetString( parentRecord, "Default" );
			deviceRole = TweakDBInterface.GetScannableDataRecord( TDBID.Create( "device_role_actions." + categoryName ) );
			if( !( deviceRole ) )
			{
				continue;
			}
			connection.connectionType = LocKeyToString( deviceRole.LocalizedDescription() );
			connection.icon = deviceRole.IconName();
			connection.amount = 1;
			for( k = 0; k < uniqueConnections.Size(); k += 1 )
			{
				if( uniqueConnections[ k ].connectionType == connection.connectionType )
				{
					uniqueConnections[ k ].amount += 1;
					isRepetition = true;
				}
			}
			if( isRepetition )
			{
				continue;
			}
			uniqueConnections.PushBack( connection );
		}
		return uniqueConnections;
	}

	public const function GetDistractionDuration( action : ScriptableDeviceAction ) : Float
	{
		var device : weak< Device >;
		var result : Float;
		result = 0.0;
		if( TryGetDevice( device ) )
		{
			result = MaxF( device.GetAreaEffectLifetimeByAction( action ), action.GetDurationTime() );
		}
		return result;
	}

	public const function GetAreaEffectStimRangeByAction( action : ScriptableDeviceAction ) : Float
	{
		var device : weak< Device >;
		return ( ( TryGetDevice( device ) ) ? ( device.GetAreaEffectStimRangeByAction( action ) ) : ( 0.0 ) );
	}

	protected const function TryGetDevice( out device : weak< Device > ) : Bool
	{
		device = ( ( Device )( GetOwnerEntityWeak() ) );
		return device;
	}

	public const function IsConnectedToActionsSequencer() : Bool
	{
		var sequencer : ActionsSequencerControllerPS;
		sequencer = GetActionsSequencer();
		if( sequencer )
		{
			return true;
		}
		return false;
	}

	public const function GetActionsSequencer() : ActionsSequencerControllerPS
	{
		var masters : array< DeviceComponentPS >;
		var i : Int32;
		GetParents( masters );
		for( i = 0; i < masters.Size(); i += 1 )
		{
			if( ( ( ActionsSequencerControllerPS )( masters[ i ] ) ) )
			{
				return ( ( ActionsSequencerControllerPS )( masters[ i ] ) );
			}
		}
		return NULL;
	}

	protected function OnSequencerLock( evt : SequencerLock ) : EntityNotificationType
	{
		m_isLockedViaSequencer = evt.shouldLock;
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	public const function IsLockedViaSequencer() : Bool
	{
		return m_isLockedViaSequencer;
	}

	public quest const function IsControlledByThePlayer() : Bool
	{
		return m_isControlledByThePlayer;
	}

	public const function IsBroken() : Bool
	{
		if( GetDurabilityState() == EDeviceDurabilityState.BROKEN )
		{
			return true;
		}
		return false;
	}

	public function SetAdvancedInteractionModeOn( value : Bool )
	{
		m_isAdvancedInteractionModeOn = value;
		ForcePersistentStateChanged();
	}

	public const function CanBeScavenged() : Bool
	{
		if( ( m_durabilityState == EDeviceDurabilityState.DESTROYED && !( m_hasBeenScavenged ) ) && !( CanBeDisassembled() ) )
		{
			return true;
		}
		return false;
	}

	public constexpr static function IsConnectedToMaintenanceSystem() : Bool
	{
		return false;
	}

	public function PushPersistentData( const data : ref< BaseDeviceData > )
	{
		var defaultState : EDeviceStatus;
		if( !( m_wasStateSet ) )
		{
			SetDeviceState( ( ( data.m_deviceState != defaultState ) ? ( data.m_deviceState ) : ( defaultState ) ) );
			CacheDeviceState( ( ( m_deviceState != defaultState ) ? ( m_deviceState ) : ( defaultState ) ) );
		}
		m_destructionProperties.m_durabilityType = data.m_durabilityType;
	}

	public export function PushResaveData( data : BaseResaveData )
	{
		LogResaveInfo();
	}

	protected virtual function LogResaveInfo()
	{
	}

	protected function SendSpiderbotToPerformAction( action : ScriptableDeviceAction, oryginalExecutor : weak< GameObject > )
	{
		var evt : SendSpiderbotToPerformActionEvent;
		SetCurrentSpiderbotActionPerformed( action );
		evt = new SendSpiderbotToPerformActionEvent;
		evt.executor = oryginalExecutor;
		QueueEntityEvent( GetMyEntityID(), evt );
	}

	public function QuestResolveSkillchecks()
	{
		if( m_skillCheckContainer.GetDemolitionSlot().IsActive() )
		{
			m_skillCheckContainer.GetDemolitionSlot().SetIsActive( false );
			m_skillCheckContainer.GetDemolitionSlot().SetIsPassed( true );
		}
		if( m_skillCheckContainer.GetEngineeringSlot().IsActive() )
		{
			m_skillCheckContainer.GetEngineeringSlot().SetIsActive( false );
			m_skillCheckContainer.GetEngineeringSlot().SetIsPassed( true );
		}
		if( m_skillCheckContainer.GetHackingSlot().IsActive() )
		{
			m_skillCheckContainer.GetHackingSlot().SetIsActive( false );
			m_skillCheckContainer.GetHackingSlot().SetIsPassed( true );
		}
		NotifyParents();
	}

	protected function OnQuestResolveSkillchecks( evt : ResolveAllSkillchecksEvent ) : EntityNotificationType
	{
		QuestResolveSkillchecks();
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestSetSkillchecks( evt : SetSkillcheckEvent ) : EntityNotificationType
	{
		QuestSetSkillchecks( evt.skillcheckContainer );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function QuestSetSkillchecks( container : BaseSkillCheckContainer )
	{
		ErasePassedSkillchecks();
		InitializeSkillChecks( container, true );
		NotifyParents();
	}

	protected function ErasePassedSkillchecks()
	{
		if( m_skillCheckContainer.GetDemolitionSlot().IsPassed() )
		{
			m_skillCheckContainer.GetDemolitionSlot().SetIsPassed( false );
		}
		if( m_skillCheckContainer.GetEngineeringSlot().IsPassed() )
		{
			m_skillCheckContainer.GetEngineeringSlot().SetIsPassed( false );
		}
		if( m_skillCheckContainer.GetHackingSlot().IsPassed() )
		{
			m_skillCheckContainer.GetHackingSlot().SetIsPassed( false );
		}
	}

	public function OnActionCooldownEvent( evt : ActionCooldownEvent ) : EntityNotificationType
	{
		var player : PlayerPuppet;
		player = ( ( PlayerPuppet )( GetPlayerMainObject() ) );
		if( player )
		{
			player.GetCooldownStorage().ResolveCooldownEvent( evt );
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected const function GetHudManager() : HUDManager
	{
		return ( ( HUDManager )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'HUDManager' ) ) );
	}

	protected const function GetTakeOverControlSystem() : TakeOverControlSystem
	{
		return ( ( TakeOverControlSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'TakeOverControlSystem' ) ) );
	}

	public const virtual function IsConnectedToCLS() : Bool
	{
		var parents : array< DeviceComponentPS >;
		var i : Int32;
		var parent : ScriptableDeviceComponentPS;
		parents = GetImmediateParents();
		for( i = 0; i < parents.Size(); i += 1 )
		{
			parent = ( ( ScriptableDeviceComponentPS )( parents[ i ] ) );
			if( parent && parent.IsConnectedToCLS() )
			{
				return true;
			}
		}
		return false;
	}

	protected export virtual function OnToggleTakeOverControl( evt : ToggleTakeOverControl ) : EntityNotificationType
	{
		UseNotifier( evt );
		if( CanPlayerTakeOverControl() )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function ActionToggleTakeOverControl() : ToggleTakeOverControl
	{
		var action : ToggleTakeOverControl;
		action = new ToggleTakeOverControl;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTakeOverControl();
		action.SetUp( this );
		action.SetProperties( m_isControlledByThePlayer );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		action.SetWidgetStyle( m_deviceUIStyle );
		action.CreateActionWidgetPackage();
		return action;
	}

	protected export virtual function OnVehicleOverrideForceBrakes( evt : VehicleOverrideForceBrakes ) : EntityNotificationType
	{
		UseNotifier( evt );
		if( CanPlayerTakeOverControl() )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	protected export virtual function OnVehicleOverrideExplode( evt : VehicleOverrideExplode ) : EntityNotificationType
	{
		UseNotifier( evt );
		if( CanPlayerTakeOverControl() )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	protected export virtual function OnVehicleOverrideAccelerate( evt : VehicleOverrideAccelerate ) : EntityNotificationType
	{
		UseNotifier( evt );
		if( CanPlayerTakeOverControl() )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

	protected export function OnTCSTakeOverControlActivate( evt : TCSTakeOverControlActivate ) : EntityNotificationType
	{
		var toggleOn : SetDeviceON;
		var chainBlackBoard : IBlackboard;
		if( IsControlledByPlayer() )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		chainBlackBoard = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().DeviceTakeControl );
		chainBlackBoard.SetBool( GetAllBlackboardDefs().DeviceTakeControl.IsDeviceWorking, true, true );
		m_isControlledByThePlayer = true;
		if( !( IsDisabled() ) )
		{
			CacheDeviceState( GetDeviceState() );
			if( !( IsON() ) )
			{
				toggleOn = ActionSetDeviceON();
				toggleOn.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
				GetPersistencySystem().QueuePSDeviceEvent( toggleOn );
			}
		}
		else
		{
			chainBlackBoard.SetBool( GetAllBlackboardDefs().DeviceTakeControl.IsDeviceWorking, false, true );
		}
		SendDeviceNotOperationalEvent();
		SetPSMPostpondedParameterBool( m_isControlledByThePlayer );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public virtual function SendDeviceNotOperationalEvent() {}

	protected export virtual function OnTCSTakeOverControlDeactivate( evt : TCSTakeOverControlDeactivate ) : EntityNotificationType
	{
		m_isControlledByThePlayer = false;
		SetPSMPostpondedParameterBool( m_isControlledByThePlayer );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function SetPSMPostpondedParameterBool( value : Bool )
	{
		var player : GameObject;
		var stateChangeEvent : PSMPostponedParameterBool;
		player = GetPlayerMainObject();
		stateChangeEvent = new PSMPostponedParameterBool;
		if( value )
		{
			stateChangeEvent.id = 'DeviceControlStart';
		}
		else
		{
			stateChangeEvent.id = 'DeviceControlStop';
		}
		stateChangeEvent.value = value;
		GetOwnerEntityWeak().QueueEventForEntityID( player.GetEntityID(), stateChangeEvent );
	}

	public virtual function ActionProgramSetDeviceOff() : ProgramSetDeviceOff
	{
		var action : ProgramSetDeviceOff;
		var multiplier : Float;
		action = new ProgramSetDeviceOff;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		multiplier = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( GetPlayer( GetGameInstance() ).GetEntityID(), gamedataStatType.CameraShutdownExtension );
		action.SetDurationValue( action.GetDurationFromTDBRecord( T"MinigameAction.NetworkCameraShutdown" ) * multiplier );
		return action;
	}

	protected virtual function OnProgramSetDeviceOff( evt : ProgramSetDeviceOff ) : EntityNotificationType
	{
		if( evt.IsStarted() )
		{
			QueuePSEvent( this, ActionSetDeviceOFF() );
			ExecutePSActionWithDelay( evt, this, evt.GetDurationValue() );
		}
		else
		{
			QueuePSEvent( this, ActionSetDeviceON() );
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public virtual function ActionProgramSetDeviceAttitude() : ProgramSetDeviceAttitude
	{
		var action : ProgramSetDeviceAttitude;
		action = new ProgramSetDeviceAttitude;
		action.clearanceLevel = DefaultActionsParametersHolder.GetTakeOverControl();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.SetDurationValue( action.GetDurationFromTDBRecord( T"MinigameAction.NetworkCameraFriendly" ) );
		return action;
	}

	protected virtual function OnProgramSetDeviceAttitude( evt : ProgramSetDeviceAttitude ) : EntityNotificationType
	{
		if( evt.IsStarted() )
		{
			QueuePSEvent( this, ActionSetDeviceAttitude() );
			ExecutePSActionWithDelay( evt, this, evt.GetDurationValue() );
		}
		else
		{
			QueuePSEvent( this, ActionSetDeviceON() );
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function AddWillingInvestigator( id : EntityID )
	{
		if( !( HasWillingInvestigator( id ) ) )
		{
			m_willingInvestigators.PushBack( id );
		}
	}

	public const function HasWillingInvestigator( id : EntityID ) : Bool
	{
		return m_willingInvestigators.Contains( id );
	}

	public const function GetWillingInvestigators() : array< EntityID >
	{
		return m_willingInvestigators;
	}

	public function ClearWillingInvestigators()
	{
		m_willingInvestigators.Clear();
	}

	public function IsSomeoneUsingNPCWorkspot() : Bool
	{
		var npc : GameObject;
		npc = GameInstance.GetWorkspotSystem( GetGameInstance() ).GetDeviceUser( PersistentID.ExtractEntityID( GetID() ) );
		if( npc && !( npc.IsDead() ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	protected virtual function ActionOverloadDevice() : OverloadDevice
	{
		var action : OverloadDevice;
		action = new OverloadDevice;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected virtual function OnOverloadDevice( evt : OverloadDevice ) : EntityNotificationType
	{
		m_wasQuickHacked = true;
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public const function ShouldNPCWorkspotFinishLoop() : Bool
	{
		return m_shouldNPCWorkspotFinishLoop;
	}

	public const function HasNPCWorkspotKillInteraction() : Bool
	{
		return m_hasNPCWorkspotKillInteraction;
	}

	public const function DrawBetweenEntities( shouldDraw : Bool, focusModeOnly : Bool, fxResource : FxResource, masterID : EntityID, slaveID : EntityID, revealMaster : Bool, revealSlave : Bool, optional onlyRemoveWeakLink : Bool, optional isEyeContact : Bool, optional isPermanent : Bool )
	{
		var registerLinkRequest : RegisterNetworkLinkRequest;
		var unregisterLinkRequest : UnregisterNetworkLinkBetweenTwoEntitiesRequest;
		var unregisterLinkRequestByID : UnregisterNetworkLinksByIDRequest;
		var newLink : SNetworkLinkData;
		var masterPuppet : ScriptedPuppet;
		var slavePuppet : ScriptedPuppet;
		var currentID : EntityID;
		newLink.slaveID = slaveID;
		newLink.masterID = masterID;
		newLink.permanent = isPermanent;
		if( shouldDraw )
		{
			masterPuppet = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( GetGameInstance(), masterID ) ) );
			slavePuppet = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( GetGameInstance(), slaveID ) ) );
			newLink.weakLink = isEyeContact;
			if( masterPuppet != NULL )
			{
				newLink.masterPos = masterPuppet.GetWorldPosition();
			}
			if( slavePuppet != NULL )
			{
				newLink.slavePos = slavePuppet.GetWorldPosition();
			}
			newLink.linkType = ELinkType.NETWORK;
			newLink.isDynamic = true;
			newLink.fxResource = fxResource;
			newLink.revealMaster = revealMaster;
			newLink.revealSlave = revealSlave;
			newLink.drawLink = true;
			if( focusModeOnly )
			{
				newLink.isNetrunner = true;
			}
			else
			{
				newLink.isPing = true;
			}
			registerLinkRequest = new RegisterNetworkLinkRequest;
			registerLinkRequest.linksData.PushBack( newLink );
			GetNetworkSystem().QueueRequest( registerLinkRequest );
		}
		else
		{
			if( EntityID.IsDefined( masterID ) && EntityID.IsDefined( slaveID ) )
			{
				unregisterLinkRequest = new UnregisterNetworkLinkBetweenTwoEntitiesRequest;
				unregisterLinkRequest.firstID = slaveID;
				unregisterLinkRequest.secondID = masterID;
				unregisterLinkRequest.onlyRemoveWeakLink = onlyRemoveWeakLink;
				GetNetworkSystem().QueueRequest( unregisterLinkRequest );
			}
			else
			{
				if( EntityID.IsDefined( masterID ) )
				{
					currentID = masterID;
				}
				else if( EntityID.IsDefined( slaveID ) )
				{
					currentID = slaveID;
				}
				unregisterLinkRequestByID = new UnregisterNetworkLinksByIDRequest;
				unregisterLinkRequestByID.ID = currentID;
				GetNetworkSystem().QueueRequest( unregisterLinkRequestByID );
			}
		}
	}

	public function SetEMPEffectActiveState( isActive : Bool )
	{
		m_isUnderEMPEffect = isActive;
	}

}

operator^( a : Bool, b : Bool ) : Bool
{
	return a != b;
}

