class Validate extends Event
{
}

class QuestResetPerfomedActionsStorage extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'QuestResetPerfomedActionsStorage';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'QuestResetPerfomedActionsStorage', true, 'QuestResetPerfomedActionsStorage', 'QuestResetPerfomedActionsStorage' );
	}

}

class QuestRemoveQuickHacks extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'QuestRemoveQuickHacks';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'QuestRemoveQuickHacks', true, 'QuestRemoveQuickHacks', 'QuestRemoveQuickHacks' );
	}

}

class QuestRestoreQuickHacks extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'QuestRestoreQuickHacks';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'QuestRestoreQuickHacks', true, 'QuestRestoreQuickHacks', 'QuestRestoreQuickHacks' );
	}

}

class QuestBreachAccessPoint extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'QuestBreachAccessPoint';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'QuestBreachAccessPoint', true, 'QuestBreachAccessPoint', 'QuestBreachAccessPoint' );
	}

}

class SpiderbotEnableAccessPoint extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'SpiderbotEnableAccessPoint';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( actionName, true, 'SpiderbotEnableAccessPoint', 'SpiderbotEnableAccessPoint' );
	}

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

	public constexpr static function IsAvailable( device : ScriptableDeviceComponentPS ) : Bool
	{
		return true;
	}

	public static function IsClearanceValid( clearance : Clearance ) : Bool
	{
		if( Clearance.IsInRange( clearance, DefaultActionsParametersHolder.GetInteractiveClearance() ) )
		{
			return true;
		}
		return false;
	}

	public static function IsContextValid( const context : ref< GetActionsContext > ) : Bool
	{
		if( context.requestType == gamedeviceRequestType.Remote )
		{
			return true;
		}
		return false;
	}

}

abstract class ProgramAction extends ActionBool
{
}

class RevealEnemiesProgram extends ProgramAction
{

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

}

class ResetNetworkBreachState extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'ResetNetworkBreachState';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( actionName, true, actionName, actionName );
	}

}

class ToggleNetrunnerDive extends ActionBool
{
	var m_skipMinigame : Bool;
	var m_attempt : Int32;
	var m_isRemote : Bool;

	public function SetProperties( terminateDive : Bool, skipMinigame : Bool, attempt : Int32, isRemote : Bool )
	{
		actionName = 'ToggleNetrunnerDive';
		m_skipMinigame = skipMinigame;
		m_attempt = attempt;
		m_isRemote = isRemote;
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( actionName, terminateDive, 'LocKey#17841', 'LocKey#17841' );
	}

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

	public const function ShouldTerminate() : Bool
	{
		return ( ( Bool )prop.first );
	}

	public static function IsAvailable( device : ScriptableDeviceComponentPS ) : Bool
	{
		return device.IsPowered();
	}

	public static function IsClearanceValid( clearance : Clearance ) : Bool
	{
		if( Clearance.IsInRange( clearance, DefaultActionsParametersHolder.GetInteractiveClearance() ) )
		{
			return true;
		}
		return false;
	}

	public static function IsContextValid( const context : ref< GetActionsContext > ) : Bool
	{
		if( context.requestType == gamedeviceRequestType.Direct )
		{
			return true;
		}
		return false;
	}

}

class DepleteCharges extends Event
{
}

class AccessPointController extends MasterController
{

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

}

class NetworkMoneySiphoned extends Event
{
}

class AccessPointControllerPS extends MasterControllerPS
{
	default m_deviceName = "LocKey#138";
	default m_tweakDBRecord = T"Devices.AccessPoint";
	default m_tweakDBDescriptionRecord = T"device_descriptions.AccessPoint";
	default m_hasPersonalLinkSlot = true;
	default m_revealDevicesGrid = false;
	default m_drawGridLink = false;
	default m_hasNetworkBackdoor = true;
	default m_shouldScannerShowNetwork = false;
	private var m_rewardNotificationIcons : array< String >;
	private var m_rewardNotificationString : String;
	private instanceeditable inlined var m_accessPointSkillChecks : HackingContainer;
	private persistent var m_isBreached : Bool;
	private persistent var m_moneyAwarded : Bool;
	private editable var m_isVirtual : Bool;

	protected event OnInstantiated()
	{
		super.OnInstantiated();
		if( !( IsStringValid( m_deviceName ) ) )
		{
			m_deviceName = "LocKey#138";
		}
	}

	protected override function GetSkillCheckContainerForSetup() : BaseSkillCheckContainer
	{
		return m_accessPointSkillChecks;
	}

	protected override function GameAttached() {}

	public const function IsVirtual() : Bool
	{
		return m_isVirtual;
	}

	public const override final function HasNetworkBackdoor() : Bool
	{
		if( ( ( Int32 )( GetDeviceState() ) ) <= ( ( Int32 )( EDeviceStatus.UNPOWERED ) ) )
		{
			return false;
		}
		return true;
	}

	public const override function GetMinigameDefinition() : TweakDBID
	{
		return m_minigameDefinition;
	}

	public const override function GetBackdoorAccessPoint() : AccessPointControllerPS
	{
		var masterAP : AccessPointControllerPS;
		masterAP = super.GetBackdoorAccessPoint();
		if( masterAP )
		{
			return masterAP;
		}
		return ( ( AccessPointControllerPS )( this ) );
	}

	public const function GetDevicesThatPlayerCanBreach() : array< ScriptableDeviceComponentPS >
	{
		var children : array< DeviceComponentPS >;
		var breachableDevices : array< ScriptableDeviceComponentPS >;
		var currentDevice : ScriptableDeviceComponentPS;
		var i : Int32;
		GetChildren( children );
		for( i = 0; i < children.Size(); i += 1 )
		{
			if( ( ( ScriptableDeviceComponentPS )( children[ i ] ) ) )
			{
				currentDevice = ( ( ScriptableDeviceComponentPS )( children[ i ] ) );
				if( currentDevice.ShouldRevealNetworkGrid() )
				{
					breachableDevices.PushBack( currentDevice );
				}
			}
		}
		return breachableDevices;
	}

	public const function IsAccessPointOf( slaveToCheck : PersistentID ) : Bool
	{
		var children : array< DeviceComponentPS >;
		var slaveAPs : array< DeviceComponentPS >;
		var singleSlaveChildren : array< DeviceComponentPS >;
		var i : Int32;
		var k : Int32;
		GetChildren( children );
		for( i = 0; i < children.Size(); i += 1 )
		{
			if( children[ i ].GetID() == slaveToCheck )
			{
				return true;
			}
			if( ( ( AccessPointControllerPS )( children[ i ] ) ) )
			{
				slaveAPs.PushBack( children[ i ] );
			}
		}
		for( i = 0; i < slaveAPs.Size(); i += 1 )
		{
			slaveAPs[ i ].GetChildren( singleSlaveChildren );
			for( k = 0; k < singleSlaveChildren.Size(); k += 1 )
			{
				if( singleSlaveChildren[ k ].GetID() == slaveToCheck )
				{
					return true;
				}
			}
		}
		return false;
	}

	public const override function IsConnectedToBackdoorDevice() : Bool
	{
		return true;
	}

	public const override function ShouldRevealNetworkGrid() : Bool
	{
		if( m_isVirtual )
		{
			return false;
		}
		else
		{
			return HasNetworkBackdoor();
		}
	}

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

	protected const override function GetClearance() : Clearance
	{
		return Clearance.CreateClearance( 1, 9 );
	}

	private function SetIsBreached( isBreached : Bool )
	{
		m_isBreached = isBreached;
		ExposeQuickHacks( isBreached );
	}

	public const override function GetNetworkName() : String
	{
		var networkName : String;
		networkName = GetDeviceName();
		if( IsStringValid( networkName ) )
		{
			return networkName;
		}
		else
		{
			return "LOCAL NETWORK";
		}
	}

	public const override function GetNetworkSizeCount() : Int32
	{
		var slaves : array< DeviceComponentPS >;
		GetChildren( slaves );
		return slaves.Size();
	}

	public quest const function IsNetworkBreached() : Bool
	{
		return m_isBreached;
	}

	public export quest const override function IsBreached() : Bool
	{
		return m_isBreached || WasHackingMinigameSucceeded();
	}

	public function BreachConnectedDevices()
	{
		RefreshSlaves_Event();
	}

	protected function ActionSpiderbotEnableAccessPoint() : SpiderbotEnableAccessPoint
	{
		var action : SpiderbotEnableAccessPoint;
		action = new SpiderbotEnableAccessPoint;
		action.clearanceLevel = DefaultActionsParametersHolder.GetSpiderbotClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	protected function ActionRevealEnemiesProgram() : RevealEnemiesProgram
	{
		var action : RevealEnemiesProgram;
		action = new RevealEnemiesProgram;
		action.clearanceLevel = DefaultActionsParametersHolder.GetInteractiveClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public function OnRevealEnemiesProgram( evt : RevealEnemiesProgram ) : EntityNotificationType
	{
		SendActionToAllSlaves( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private function RefreshSlaves( const devices : ref< array< DeviceComponentPS > > )
	{
		var i : Int32;
		var minigamePrograms : array< TweakDBID >;
		var minigameBB : IBlackboard;
		var TS : TransactionSystem;
		var lootQ003 : TweakDBID;
		var lootAllID, lootAllAdvancedID, lootAllMasterID : TweakDBID;
		var markForErase : Bool;
		var baseShardDropChance : Float;
		var baseMoney : Float;
		var craftingMaterial : Bool;
		var shouldLoot : Bool;
		TS = GameInstance.GetTransactionSystem( GetGameInstance() );
		minigameBB = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().HackingMinigame );
		minigamePrograms = ( ( array< TweakDBID > )( minigameBB.GetVariant( GetAllBlackboardDefs().HackingMinigame.ActivePrograms ) ) );
		CheckMasterRunnerAchievement( minigamePrograms.Size() );
		FilterRedundantPrograms( minigamePrograms );
		lootQ003 = T"MinigameAction.NetworkLootQ003";
		lootAllID = T"MinigameAction.NetworkDataMineLootAll";
		lootAllAdvancedID = T"MinigameAction.NetworkDataMineLootAllAdvanced";
		lootAllMasterID = T"MinigameAction.NetworkDataMineLootAllMaster";
		baseMoney = 0.0;
		craftingMaterial = false;
		baseShardDropChance = 0.0;
		for( i = minigamePrograms.Size() - 1; i >= 0; i -= 1 )
		{
			if( minigamePrograms[ i ] == T"minigame_v2.FindAnna" )
			{
				AddFact( GetPlayerMainObject().GetGame(), 'Kab08Minigame_program_uploaded' );
			}
			else if( minigamePrograms[ i ] == lootQ003 )
			{
				TS.GiveItemByItemQuery( GetPlayerMainObject(), T"Query.Q003CyberdeckProgram" );
			}
			else if( ( ( minigamePrograms[ i ] == lootAllID ) || ( minigamePrograms[ i ] == lootAllAdvancedID ) ) || ( minigamePrograms[ i ] == lootAllMasterID ) )
			{
				if( minigamePrograms[ i ] == lootAllID )
				{
					baseMoney += 1.0;
				}
				else if( minigamePrograms[ i ] == lootAllAdvancedID )
				{
					baseMoney += 1.0;
					craftingMaterial = true;
				}
				else if( minigamePrograms[ i ] == lootAllMasterID )
				{
					baseShardDropChance += 1.0;
				}
				shouldLoot = true;
				markForErase = true;
			}
		}
		if( markForErase )
		{
			minigamePrograms.Erase( i );
			minigameBB.SetVariant( GetAllBlackboardDefs().HackingMinigame.ActivePrograms, minigamePrograms );
		}
		if( shouldLoot )
		{
			ProcessLoot( baseMoney, craftingMaterial, baseShardDropChance, TS );
		}
		ProcessMinigameNetworkActions( this );
		for( i = 0; i < devices.Size(); i += 1 )
		{
			QueuePSEvent( devices[ i ], ActionSetExposeQuickHacks() );
			ProcessMinigameNetworkActions( devices[ i ] );
		}
		if( ( baseMoney >= 1.0 ) && ShouldRewardMoney() )
		{
			RewardMoney( baseMoney );
		}
		RPGManager.GiveReward( GetGameInstance(), T"RPGActionRewards.Hacking", GetMyEntityID() );
	}

	private function FilterRedundantPrograms( programs : ref< array< TweakDBID > > )
	{
		if( programs.Contains( T"MinigameAction.NetworkTurretShutdown" ) && programs.Contains( T"MinigameAction.NetworkTurretFriendly" ) )
		{
			programs.Remove( T"MinigameAction.NetworkTurretShutdown" );
		}
	}

	private function ProcessLoot( baseMoney : Float, craftingMaterial : Bool, baseShardDropChance : Float, TS : TransactionSystem )
	{
		var playerLevel : Float;
		CleanRewardNotification();
		playerLevel = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( GetPlayerMainObject().GetEntityID(), gamedataStatType.Level );
		if( baseShardDropChance > 0.0 )
		{
			GetQuickhackReward( playerLevel, TS );
		}
		if( craftingMaterial )
		{
			GenerateMaterialDrops( playerLevel, TS );
		}
		ShowRewardNotification();
	}

	private function GetQuickhackReward( playerLevel : Float, TS : TransactionSystem )
	{
		var recipes : array< TweakDBID >;
		var hacksPool : array< TweakDBID >;
		var hacksToGet : array< TweakDBID >;
		var playerHacksTweak : array< TweakDBID >;
		var hacksPlayerMissing : array< TweakDBID >;
		var recipesPlayerMissing : array< TweakDBID >;
		var craftingSystem : CraftingSystem;
		var playerCraftBook : CraftBook;
		var i : Int32;
		var tweakID : TweakDBID;
		var recipeResult : TweakDBID;
		var getBonusReward : Float;
		var randomHack : Int32;
		var difficulty : Float;
		var failedattempbonus : Float;
		var quality : gamedataQuality;
		var debugForceBonus : Bool;
		var recipeItem : ItemRecipe_Record;
		var dataTrackingSystem : DataTrackingSystem;
		var dataTrackingEvent : UpdateShardFailedDropsRequest;
		var legendaryPlusPlusAwarded : FirstPlusPlusLegendaryAwardedRequest;
		craftingSystem = CraftingSystem.GetInstance( GetGameInstance() );
		playerCraftBook = craftingSystem.GetPlayerCraftBook();
		debugForceBonus = false;
		getBonusReward = RandRangeF( 0.0, 1.0 );
		difficulty = ( ( Float )( ( ( Int32 )( m_accessPointSkillChecks.GetHackingSlot().GetDifficulty() ) ) ) );
		getBonusReward = getBonusReward + ( difficulty * 0.05 );
		if( debugForceBonus )
		{
			getBonusReward = 1.0;
		}
		if( playerLevel < 3.0 )
		{
			getBonusReward = 0.80000001;
		}
		if( ( playerLevel >= 3.0 ) && ( playerLevel < 14.0 ) )
		{
			recipes = TDB.GetForeignKeyArray( T"Items.AllUncommonProgramsRecipes.recipes" );
		}
		else if( ( playerLevel >= 14.0 ) && ( playerLevel < 22.0 ) )
		{
			recipes = TDB.GetForeignKeyArray( T"Items.AllRareProgramsRecipes.recipes" );
		}
		else if( ( playerLevel >= 22.0 ) && ( playerLevel < 30.0 ) )
		{
			recipes = TDB.GetForeignKeyArray( T"Items.AllEpicProgramsRecipes.recipes" );
		}
		else if( playerLevel >= 30.0 )
		{
			recipes = TDB.GetForeignKeyArray( T"Items.AllLegendaryProgramsRecipes.recipes" );
		}
		if( playerLevel < 8.0 )
		{
			hacksPool = TDB.GetForeignKeyArray( T"Items.AllCommonPrograms.programs" );
			quality = gamedataQuality.Common;
		}
		if( ( playerLevel >= 8.0 ) && ( playerLevel < 16.0 ) )
		{
			hacksPool = TDB.GetForeignKeyArray( T"Items.AllUncommonPrograms.programs" );
			quality = gamedataQuality.Uncommon;
		}
		else if( ( playerLevel >= 16.0 ) && ( playerLevel < 24.0 ) )
		{
			hacksPool = TDB.GetForeignKeyArray( T"Items.AllRarePrograms.programs" );
			quality = gamedataQuality.Rare;
		}
		else if( ( playerLevel >= 24.0 ) && ( playerLevel < 32.0 ) )
		{
			hacksPool = TDB.GetForeignKeyArray( T"Items.AllEpicPrograms.programs" );
			quality = gamedataQuality.Epic;
		}
		else if( playerLevel >= 32.0 )
		{
			hacksPool = TDB.GetForeignKeyArray( T"Items.AllLegendaryPrograms.programs" );
			quality = gamedataQuality.Legendary;
		}
		if( playerLevel >= 51.0 )
		{
			dataTrackingSystem = ( ( DataTrackingSystem )( GameInstance.GetScriptableSystemsContainer( GetPlayerMainObject().GetGame() ).Get( 'DataTrackingSystem' ) ) );
			if( !( dataTrackingSystem.IsFirstPlusPlusLegendaryAwarded() ) )
			{
				legendaryPlusPlusAwarded = new FirstPlusPlusLegendaryAwardedRequest;
				GameInstance.GetScriptableSystemsContainer( GetPlayerMainObject().GetGame() ).Get( 'DataTrackingSystem' ).QueueRequest( legendaryPlusPlusAwarded );
				getBonusReward = 1.0;
			}
			else
			{
				failedattempbonus = dataTrackingSystem.GetFailedShardDrops() * 0.1;
				getBonusReward += failedattempbonus;
			}
			if( getBonusReward >= 0.94999999 )
			{
				recipes = TDB.GetForeignKeyArray( T"Items.AllLegendaryPlusPlusProgramsRecipes.recipes" );
				if( getBonusReward >= 0.99000001 )
				{
					quality = gamedataQuality.LegendaryPlusPlus;
					hacksPool = TDB.GetForeignKeyArray( T"Items.AllLegendaryPlusPlusPrograms.programs" );
					getBonusReward = 0.80000001;
				}
				else
				{
					getBonusReward = 0.0;
				}
				dataTrackingEvent.resetCounter = true;
			}
			else
			{
				dataTrackingEvent = new UpdateShardFailedDropsRequest;
				dataTrackingEvent.newFailedAttempts = 1.0;
			}
			GameInstance.GetScriptableSystemsContainer( GetPlayerMainObject().GetGame() ).Get( 'DataTrackingSystem' ).QueueRequest( dataTrackingEvent );
		}
		hacksPlayerMissing = GetPlayersUniqueHacks( TS, hacksPool, quality, playerHacksTweak );
		for( i = 0; i < recipes.Size(); i += 1 )
		{
			if( !( craftingSystem.IsRecipeKnown( recipes[ i ], playerCraftBook ) ) )
			{
				recipesPlayerMissing.PushBack( recipes[ i ] );
			}
		}
		if( getBonusReward <= 0.89999998 )
		{
			if( recipesPlayerMissing.Size() > 0 )
			{
				if( ( recipesPlayerMissing.Size() - 1 ) > 0 )
				{
					for( i = 0; i < recipesPlayerMissing.Size(); i += 1 )
					{
						recipeItem = TweakDBInterface.GetItemRecipeRecord( recipesPlayerMissing[ i ] );
						recipeResult = recipeItem.CraftingResult().ItemHandle().GetID();
						if( !( playerHacksTweak.Contains( recipeResult ) ) )
						{
							tweakID = recipesPlayerMissing[ i ];
							break;
						}
					}
					if( !( TDBID.IsValid( tweakID ) ) )
					{
						tweakID = recipesPlayerMissing[ RandRange( 0, recipesPlayerMissing.Size() - 1 ) ];
					}
				}
				else
				{
					tweakID = recipesPlayerMissing[ 0 ];
				}
				AddHackReward( TS, tweakID, 1 );
				getBonusReward = 0.0;
				return;
			}
		}
		if( ( getBonusReward >= 0.94999999 ) && ( recipesPlayerMissing.Size() == 0 ) )
		{
			if( hacksPlayerMissing.Size() == 0 )
			{
				for( i = 0; i < 2; i += 1 )
				{
					randomHack = RandRange( 0, hacksPool.Size() - 1 );
					tweakID = hacksPool[ i ];
					hacksToGet.PushBack( tweakID );
				}
			}
			else if( hacksPlayerMissing.Size() == 1 )
			{
				tweakID = hacksPlayerMissing[ i ];
				hacksToGet.PushBack( tweakID );
				randomHack = RandRange( 0, hacksPool.Size() - 1 );
				tweakID = hacksPool[ randomHack ];
				hacksToGet.PushBack( tweakID );
			}
			else if( hacksPlayerMissing.Size() >= 2 )
			{
				randomHack = RandRange( 0, hacksPlayerMissing.Size() - 1 );
				tweakID = hacksPlayerMissing[ randomHack ];
				hacksToGet.PushBack( tweakID );
				hacksPlayerMissing.Remove( tweakID );
				if( hacksPlayerMissing.Size() == 1 )
				{
					tweakID = hacksPlayerMissing[ 0 ];
				}
				else
				{
					randomHack = RandRange( 0, hacksPlayerMissing.Size() - 1 );
					tweakID = hacksPlayerMissing[ randomHack ];
				}
				hacksToGet.PushBack( tweakID );
			}
		}
		else
		{
			if( hacksPlayerMissing.Size() == 0 )
			{
				randomHack = RandRange( 0, hacksPool.Size() - 1 );
				tweakID = hacksPool[ randomHack ];
			}
			else
			{
				if( hacksPlayerMissing.Size() == 1 )
				{
					tweakID = hacksPlayerMissing[ 0 ];
				}
				else
				{
					randomHack = RandRange( 0, hacksPlayerMissing.Size() - 1 );
					tweakID = hacksPlayerMissing[ randomHack ];
				}
			}
			hacksToGet.PushBack( tweakID );
		}
		for( i = 0; i < hacksToGet.Size(); i += 1 )
		{
			AddHackReward( TS, hacksToGet[ i ], 1 );
		}
	}

	private const function GetPlayersUniqueHacks( TS : TransactionSystem, hacksPool : array< TweakDBID >, quality : gamedataQuality, out playerHacksTweak : array< TweakDBID > ) : array< TweakDBID >
	{
		var hacksPlayerMissing : array< TweakDBID >;
		var playersHacks : array< weak< gameItemData > >;
		var player : PlayerPuppet;
		var hackID : ItemID;
		var tweakID : TweakDBID;
		var hackQuality : gamedataQuality;
		var i : Int32;
		player = ( ( PlayerPuppet )( GetPlayerMainObject() ) );
		playerHacksTweak = PlayerPuppet.GetPlayerQuickHackInCyberDeckTweakDBID( player, quality );
		TS.GetItemListByTag( player, 'SoftwareShard', playersHacks );
		for( i = 0; i < playersHacks.Size(); i += 1 )
		{
			hackID = playersHacks[ i ].GetID();
			tweakID = ItemID.GetTDBID( hackID );
			hackQuality = TweakDBInterface.GetItemRecord( tweakID ).Quality().Type();
			if( quality == gamedataQuality.LegendaryPlusPlus )
			{
				if( hackQuality == gamedataQuality.Legendary && RPGManager.IsItemIconic( RPGManager.GetItemData( player.GetGame(), player, hackID ) ) )
				{
					playerHacksTweak.PushBack( tweakID );
				}
			}
			else
			{
				if( hackQuality == quality )
				{
					playerHacksTweak.PushBack( tweakID );
				}
			}
		}
		for( i = 0; i < playerHacksTweak.Size(); i += 1 )
		{
			if( playerHacksTweak.Count( playerHacksTweak[ i ] ) > 1 )
			{
				playerHacksTweak.Remove( playerHacksTweak[ i ] );
			}
		}
		if( playerHacksTweak.Size() < hacksPool.Size() )
		{
			for( i = 0; i < hacksPool.Size(); i += 1 )
			{
				if( !( playerHacksTweak.Contains( hacksPool[ i ] ) ) )
				{
					hacksPlayerMissing.PushBack( hacksPool[ i ] );
				}
			}
		}
		return hacksPlayerMissing;
	}

	private const function WasMoneyAwarded() : Bool
	{
		return m_moneyAwarded;
	}

	private const function ShouldRewardMoney() : Bool
	{
		var shouldRewardMoney : Bool;
		var master : AccessPointControllerPS;
		if( IsMainframe() )
		{
			shouldRewardMoney = !( WasMoneyAwarded() );
		}
		else
		{
			master = GetBackdoorAccessPoint();
			if( master )
			{
				shouldRewardMoney = !( master.WasMoneyAwarded() );
			}
		}
		return shouldRewardMoney;
	}

	private function RewardMoney( baseMoney : Float )
	{
		var moneyModifier : Float;
		var relevantAP : AccessPointControllerPS;
		var systemReplacementID : ItemID;
		var player : PlayerPuppet;
		var deckQuality : gamedataQuality;
		var moneyReward : Float;
		player = ( ( PlayerPuppet )( GetPlayerMainObject() ) );
		systemReplacementID = EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		if( EquipmentSystem.IsCyberdeckEquipped( player ) )
		{
			deckQuality = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( systemReplacementID ) ).Quality().Type();
		}
		moneyModifier = ( ( ( Float )( ( ( Int32 )( m_accessPointSkillChecks.GetHackingSlot().GetDifficulty() ) ) ) ) * 0.025 ) + baseMoney;
		switch( deckQuality )
		{
			case gamedataQuality.Common:
				moneyReward = 100.0;
			break;
			case gamedataQuality.CommonPlus:
				moneyReward = 150.0;
			break;
			case gamedataQuality.Uncommon:
				moneyReward = 250.0;
			break;
			case gamedataQuality.UncommonPlus:
				moneyReward = 350.0;
			break;
			case gamedataQuality.Rare:
				moneyReward = 500.0;
			break;
			case gamedataQuality.RarePlus:
				moneyReward = 800.0;
			break;
			case gamedataQuality.Epic:
				moneyReward = 1200.0;
			break;
			case gamedataQuality.EpicPlus:
				moneyReward = 1500.0;
			break;
			case gamedataQuality.Legendary:
				moneyReward = 2000.0;
			break;
			case gamedataQuality.LegendaryPlusPlus:
				moneyReward = 2500.0;
			break;
			default:
				moneyReward = 100.0;
			break;
		}
		if( RPGManager.IsItemIconic( RPGManager.GetItemData( player.GetGame(), player, systemReplacementID ) ) )
		{
			moneyReward *= 1.04999995;
		}
		GameInstance.GetTransactionSystem( player.GetGame() ).GiveMoney( GetPlayerMainObject(), ( ( Int32 )( moneyReward * moneyModifier ) ), 'money' );
		relevantAP = GetBackdoorAccessPoint();
		QueuePSEvent( relevantAP, new NetworkMoneySiphoned );
	}

	private function OnNetworkMoneySiphoned( evt : NetworkMoneySiphoned ) : EntityNotificationType
	{
		m_moneyAwarded = true;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private function GenerateMaterialDrops( playerLevel : Float, TS : TransactionSystem )
	{
		var materialsMultiplier : Int32;
		var materialsAmmountUnc, materialsAmmountRare, materialsAmmountEpic, materialsAmmountLeg : Int32;
		materialsAmmountRare = materialsAmmountEpic = materialsAmmountLeg = 0;
		materialsMultiplier = ( ( Int32 )( m_accessPointSkillChecks.GetHackingSlot().GetDifficulty() ) );
		if( materialsMultiplier == 0 )
		{
			materialsMultiplier = 1;
		}
		if( playerLevel < 14.0 )
		{
			materialsAmmountUnc = 15;
		}
		else if( ( playerLevel >= 14.0 ) && ( playerLevel < 22.0 ) )
		{
			materialsAmmountUnc = 20;
			materialsAmmountRare = 5;
		}
		else if( ( playerLevel >= 22.0 ) && ( playerLevel < 32.0 ) )
		{
			materialsAmmountUnc = 25;
			materialsAmmountRare = 10;
			materialsAmmountEpic = 4;
		}
		else if( ( playerLevel >= 32.0 ) && ( playerLevel < 37.0 ) )
		{
			materialsAmmountUnc = 25;
			materialsAmmountRare = 15;
			materialsAmmountEpic = 8;
			materialsAmmountLeg = 1;
		}
		else if( playerLevel >= 37.0 )
		{
			materialsAmmountUnc = 30;
			materialsAmmountRare = 20;
			materialsAmmountEpic = 10;
			materialsAmmountLeg = 2;
		}
		materialsAmmountUnc = RandRange( materialsAmmountUnc, ( materialsAmmountUnc + materialsMultiplier ) + 2 );
		AddReward( TS, T"Query.QuickHackUncommonMaterial", ( ( Uint32 )( materialsAmmountUnc ) ) );
		if( materialsAmmountRare > 0 )
		{
			materialsAmmountRare = RandRange( materialsAmmountRare, materialsAmmountRare + materialsMultiplier );
			AddReward( TS, T"Query.QuickHackRareMaterial", ( ( Uint32 )( materialsAmmountRare ) ) );
		}
		if( materialsAmmountEpic > 0 )
		{
			materialsAmmountEpic = RandRange( materialsAmmountEpic, materialsAmmountEpic + materialsMultiplier );
			AddReward( TS, T"Query.QuickHackEpicMaterial", ( ( Uint32 )( materialsAmmountEpic ) ) );
		}
		if( materialsAmmountLeg > 0 )
		{
			materialsAmmountLeg = RandRange( materialsAmmountLeg, materialsAmmountLeg );
			AddReward( TS, T"Query.QuickHackLegendaryMaterial", ( ( Uint32 )( materialsAmmountLeg ) ) );
		}
	}

	private function AddReward( TS : TransactionSystem, itemQueryTDBID : TweakDBID, optional amount : Uint32 )
	{
		var itemTypeRecordName : CName;
		var itemRecordID : TweakDBID;
		var itemRecord : Item_Record;
		var iconName : String;
		var iconsNameResolver : IconsNameResolver;
		if( amount > ( ( Uint32 )( 0 ) ) )
		{
			itemTypeRecordName = TDB.GetItemQueryRecord( itemQueryTDBID ).RecordType();
			itemRecordID = TDBID.Create( NameToString( itemTypeRecordName ) );
			itemRecord = TDB.GetItemRecord( itemRecordID );
			iconsNameResolver = IconsNameResolver.GetIconsNameResolver();
			iconName = itemRecord.IconPath();
			if( !( IsStringValid( iconName ) ) )
			{
				iconName = NameToString( iconsNameResolver.TranslateItemToIconName( itemRecordID, true ) );
			}
			if( iconName != "None" && iconName != "" )
			{
				m_rewardNotificationIcons.PushBack( iconName );
			}
			m_rewardNotificationString += ( ( ( ( Int32 )( amount ) ) + " " ) + GetLocalizedTextByKey( itemRecord.DisplayName() ) );
			if( StrLen( m_rewardNotificationString ) > 0 )
			{
				m_rewardNotificationString += "\n";
			}
			TS.GiveItemByItemQuery( GetPlayerMainObject(), itemQueryTDBID, amount, ( ( Uint64 )( -1 ) ), "minigame" );
		}
	}

	private function AddHackReward( TS : TransactionSystem, itemTweakID : TweakDBID, optional amount : Uint32 )
	{
		var itemRecord : Item_Record;
		if( amount > ( ( Uint32 )( 0 ) ) )
		{
			itemRecord = TDB.GetItemRecord( itemTweakID );
			if( itemRecord.TagsContains( 'Recipe' ) )
			{
				m_rewardNotificationString += ( GetLocalizedText( "LocKey#49392" ) + " " );
			}
			else if( itemRecord.TagsContains( 'SoftwareShard' ) )
			{
				m_rewardNotificationString += ( GetLocalizedText( "LocKey#46554" ) + " " );
			}
			m_rewardNotificationString += GetLocalizedTextByKey( itemRecord.DisplayName() );
			if( StrLen( m_rewardNotificationString ) > 0 )
			{
				m_rewardNotificationString += "\n";
			}
			TS.GiveItemByTDBID( GetPlayerMainObject(), itemTweakID, 1 );
		}
	}

	private function CleanRewardNotification()
	{
		m_rewardNotificationString = "";
		m_rewardNotificationIcons.Clear();
	}

	private function ShowRewardNotification()
	{
		var notificationEvent : HackingRewardNotificationEvent;
		var uiSystem : UISystem;
		if( StrLen( m_rewardNotificationString ) > 0 )
		{
			uiSystem = GameInstance.GetUISystem( GetGameInstance() );
			notificationEvent = new HackingRewardNotificationEvent;
			notificationEvent.m_text = m_rewardNotificationString;
			notificationEvent.m_icons = m_rewardNotificationIcons;
			uiSystem.QueueEvent( notificationEvent );
		}
	}

	private function ProcessMinigameNetworkActions( device : DeviceComponentPS )
	{
		var i : Int32;
		var minigamePrograms : array< TweakDBID >;
		var activeTraps : array< TweakDBID >;
		var minigameBB : IBlackboard;
		var networkAction : ScriptableDeviceAction;
		var actionName : CName;
		var targetClass : CName;
		var slaveClass : CName;
		var context : GetActionsContext;
		var TS : TransactionSystem;
		var setDetectionEvent : SetDetectionMultiplier;
		TS = GameInstance.GetTransactionSystem( GetGameInstance() );
		minigameBB = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().HackingMinigame );
		minigamePrograms = ( ( array< TweakDBID > )( minigameBB.GetVariant( GetAllBlackboardDefs().HackingMinigame.ActivePrograms ) ) );
		activeTraps = ( ( array< TweakDBID > )( minigameBB.GetVariant( GetAllBlackboardDefs().HackingMinigame.ActiveTraps ) ) );
		FilterRedundantPrograms( minigamePrograms );
		if( minigameBB )
		{
			context.requestType = gamedeviceRequestType.Remote;
			for( i = 0; i < activeTraps.Size(); i += 1 )
			{
				if( activeTraps[ i ] == T"MinigameTraps.MaterialBonus" )
				{
					TS.GiveItemByItemQuery( GetPlayerMainObject(), T"Query.QuickHackMaterial", 1 );
				}
				else if( activeTraps[ i ] == T"MinigameTraps.IncreaseAwareness" )
				{
					setDetectionEvent = new SetDetectionMultiplier;
					setDetectionEvent.multiplier = 10.0;
					( ( SensorDevice )( GameInstance.FindEntityByID( GetGameInstance(), PersistentID.ExtractEntityID( device.GetID() ) ) ) ).QueueEvent( setDetectionEvent );
				}
			}
			for( i = 0; i < minigamePrograms.Size(); i += 1 )
			{
				actionName = TweakDBInterface.GetObjectActionRecord( minigamePrograms[ i ] ).ActionName();
				targetClass = TweakDBInterface.GetCName( minigamePrograms[ i ] + T".targetClass", '' );
				slaveClass = device.GetClassName();
				if( targetClass == slaveClass || targetClass == '' )
				{
					networkAction = ( ( ScriptableDeviceAction )( ( ( ScriptableDeviceComponentPS )( device ) ).GetMinigameActionByName( actionName, context ) ) );
					if( !( networkAction ) )
					{
						networkAction = new PuppetAction;
						networkAction.SetUp( device );
					}
					networkAction.RegisterAsRequester( PersistentID.ExtractEntityID( device.GetID() ) );
					networkAction.SetExecutor( GetPlayer( GetGameInstance() ) );
					networkAction.SetObjectActionID( minigamePrograms[ i ] );
					networkAction.ProcessRPGAction( GetGameInstance() );
				}
			}
		}
	}

	private function ExtractActions() : array< DeviceAction >
	{
		var extractedActions : array< DeviceAction >;
		extractedActions.PushBack( GetActionByName( 'ToggleNetrunnerDive' ) );
		( ( ScriptableDeviceAction )( extractedActions[ 0 ] ) ).RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
		return extractedActions;
	}

	public override function GetQuestActionByName( actionName : CName ) : DeviceAction
	{
		var action : DeviceAction;
		action = super.GetQuestActionByName( actionName );
		if( action == NULL )
		{
			switch( actionName )
			{
				case 'QuestBreachAccessPoint':
					action = ActionQuestBreachAccessPoint();
				break;
				case 'ResetNetworkBreachState':
					action = ActionResetNetworkBreachState();
				break;
			}
		}
		return action;
	}

	protected override function GetQuestActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		super.GetQuestActions( outActions, context );
		outActions.PushBack( ActionQuestBreachAccessPoint() );
		outActions.PushBack( ActionResetNetworkBreachState() );
	}

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

	public function OnResetNetworkBreachState( evt : ResetNetworkBreachState ) : EntityNotificationType
	{
		SetIsBreached( false );
		return EntityNotificationType.DoNotNotifyEntity;
	}

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

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

	public override function FinalizeNetrunnerDive( state : HackingMinigameState )
	{
		super.FinalizeNetrunnerDive( state );
		if( state == HackingMinigameState.Failed )
		{
			SendMinigameFailedToAllNPCs();
		}
	}

	public function OnNPCBreachEvent( evt : NPCBreachEvent ) : EntityNotificationType
	{
		if( evt.state == HackingMinigameState.Succeeded )
		{
			SetIsBreached( true );
			RefreshSlaves_Event();
		}
		else if( evt.state == HackingMinigameState.Failed )
		{
			m_minigameAttempt += 1;
			SendMinigameFailedToAllNPCs();
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected const override function ResolveDive( isRemote : Bool )
	{
		super.ResolveDive( isRemote );
	}

	private const function SendMinigameFailedToAllNPCs()
	{
		var evt : MinigameFailEvent;
		var puppets : array< PuppetDeviceLinkPS >;
		var i : Int32;
		evt = new MinigameFailEvent;
		puppets = GetPuppets();
		for( i = 0; i < puppets.Size(); i += 1 )
		{
			GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( puppets[ i ].GetID() ), evt );
		}
	}

	public export override function OnSetExposeQuickHacks( evt : SetExposeQuickHacks ) : EntityNotificationType
	{
		if( evt.isRemote )
		{
			SetIsBreached( true );
		}
		RefreshSlaves_Event();
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnValidate( evt : Validate ) : EntityNotificationType
	{
		var slaves : array< DeviceComponentPS >;
		var i : Int32;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			if( !( ( ( ScriptableDeviceComponentPS )( slaves[ i ] ) ).IsQuickHacksExposed() ) )
			{
				return EntityNotificationType.DoNotNotifyEntity;
			}
		}
		SetIsBreached( true );
		m_skillCheckContainer.GetHackingSlot().SetIsActive( false );
		m_skillCheckContainer.GetHackingSlot().SetIsPassed( true );
		m_skillCheckContainer.GetHackingSlot().CheckPerformed();
		return EntityNotificationType.SendThisEventToEntity;
	}

	public override function OnQuestRemoveQuickHacks( evt : QuestRemoveQuickHacks ) : EntityNotificationType
	{
		SetIsBreached( false );
		SendActionToAllSlaves( evt );
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	public export override function OnQuestBreachAccessPoint( evt : QuestBreachAccessPoint ) : EntityNotificationType
	{
		ExecutePSAction( ActionSetExposeQuickHacks() );
		m_skillCheckContainer.GetHackingSlot().SetIsActive( false );
		m_skillCheckContainer.GetHackingSlot().SetIsPassed( true );
		m_skillCheckContainer.GetHackingSlot().CheckPerformed();
		TurnAuthorizationModuleOFF();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function OnSpiderbotEnableAccessPoint( evt : SpiderbotEnableAccessPoint ) : EntityNotificationType
	{
		m_isBreached = true;
		m_hasPersonalLinkSlot = false;
		m_skillCheckContainer.GetHackingSlot().SetIsActive( false );
		m_skillCheckContainer.GetHackingSlot().SetIsPassed( true );
		m_skillCheckContainer.GetHackingSlot().CheckPerformed();
		TurnAuthorizationModuleOFF();
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function DebugBreachConnectedDevices()
	{
		RefreshSlaves_Event( false, true );
	}

	public function OnBreachAccessPointEvent( evt : BreachAccessPointEvent ) : EntityNotificationType
	{
		SetIsBreached( true );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected export override function OnRefreshSlavesEvent( evt : RefreshSlavesEvent ) : EntityNotificationType
	{
		if( IsON() || evt.force )
		{
			RefreshSlaves( evt.devices );
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	private const function GetCommunityProxies() : array< CommunityProxyPS >
	{
		var i : Int32;
		var proxies : array< CommunityProxyPS >;
		var slaves : array< DeviceComponentPS >;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			if( ( ( CommunityProxyPS )( slaves[ i ] ) ) )
			{
				proxies.PushBack( ( ( CommunityProxyPS )( slaves[ i ] ) ) );
			}
		}
		return proxies;
	}

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

	protected function IsSpiderbotHackingConditionFullfilled() : Bool
	{
		var statsSystem : StatsSystem;
		var player : PlayerPuppet;
		var checkResult : Bool;
		statsSystem = GameInstance.GetStatsSystem( GetGameInstance() );
		player = GetPlayer( GetGameInstance() );
		if( ( ( Bool )statsSystem.GetStatValue( player.GetEntityID(), gamedataStatType.HasRemoteBotAccessPointBreach ) ) )
		{
			checkResult = true;
		}
		else
		{
			checkResult = false;
		}
		if( !( AIActionHelper.CheckFlatheadStatPoolRequirements( GetGameInstance(), "DeviceAction" ) ) )
		{
			checkResult = false;
		}
		return checkResult;
	}

	public function UploadProgram( programID : Int32 )
	{
		var programToExecute : ProgramAction;
		if( !( m_isBreached ) )
		{
			return;
		}
		switch( programID )
		{
			case 1:
				programToExecute = ActionRevealEnemiesProgram();
			break;
		}
		if( programToExecute )
		{
			ExecutePSAction( programToExecute );
		}
	}

	public override 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 )
	{
		return;
	}

	public const override function GetBlackboardDef() : BackDoorDeviceBlackboardDef
	{
		return GetAllBlackboardDefs().BackdoorBlackboard;
	}

	protected const function CheckMasterRunnerAchievement( minigameProgramsCompleted : Int32 )
	{
		var achievementRequest : AddAchievementRequest;
		var dataTrackingSystem : DataTrackingSystem;
		var achievement : gamedataAchievement;
		dataTrackingSystem = ( ( DataTrackingSystem )( GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).Get( 'DataTrackingSystem' ) ) );
		achievement = gamedataAchievement.MasterRunner;
		if( minigameProgramsCompleted >= 3 )
		{
			achievementRequest = new AddAchievementRequest;
			achievementRequest.achievement = achievement;
			dataTrackingSystem.QueueRequest( achievementRequest );
		}
	}


	private var m_pingedSquads : array< CName >;

	private override function PingSquad()
	{
		var i : Int32;
		var puppets : array< PuppetDeviceLinkPS >;
		var squadName : CName;
		var puppetObject : weak< GameObject >;
		puppets = GetPuppets();
		for( i = 0; i < puppets.Size(); i += 1 )
		{
			puppetObject = ( ( GameObject )( puppets[ i ].GetOwnerEntityWeak() ) );
			if( puppetObject )
			{
				squadName = AISquadHelper.GetSquadName( ( ( ScriptedPuppet )( puppetObject ) ) );
				if( IsSquadMarkedWithPing( squadName ) )
				{
					continue;
				}
				else
				{
					AddPingedSquad( squadName );
					puppets[ i ].PingSquadNetwork();
				}
			}
		}
		ClearPingedSquads();
	}

	private function AddPingedSquad( squadName : CName )
	{
		if( !( m_pingedSquads.Contains( squadName ) ) )
		{
			m_pingedSquads.PushBack( squadName );
		}
	}

	private function RemovePingedSquad( squadName : CName )
	{
		m_pingedSquads.Remove( squadName );
	}

	private function ClearPingedSquads()
	{
		if( m_pingedSquads.Size() > 0 )
		{
			m_pingedSquads.Clear();
		}
	}

	private function IsSquadMarkedWithPing( squadName : CName ) : Bool
	{
		return m_pingedSquads.Contains( squadName );
	}

	protected override function OnFillTakeOverChainBBoardEvent( evt : FillTakeOverChainBBoardEvent ) : EntityNotificationType
	{
		FillTakeOverChainBB();
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public const function CheckConnectedClassTypes() : ConnectedClassTypes
	{
		var data : ConnectedClassTypes;
		var slaves : array< DeviceComponentPS >;
		var puppet : GameObject;
		var i : Int32;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			if( ( data.surveillanceCamera && data.securityTurret ) && data.puppet )
			{
				break;
			}
			if( ( ( ScriptableDeviceComponentPS )( slaves[ i ] ) ) && ( !( ( ( ScriptableDeviceComponentPS )( slaves[ i ] ) ).IsON() ) || ( ( ScriptableDeviceComponentPS )( slaves[ i ] ) ).IsBroken() ) )
			{
				continue;
			}
			if( !( data.surveillanceCamera ) && ( ( SurveillanceCameraControllerPS )( slaves[ i ] ) ) )
			{
				data.surveillanceCamera = true;
			}
			else if( !( data.securityTurret ) && ( ( SecurityTurretControllerPS )( slaves[ i ] ) ) )
			{
				data.securityTurret = true;
			}
			else if( !( data.puppet ) && ( ( PuppetDeviceLinkPS )( slaves[ i ] ) ) )
			{
				puppet = ( ( GameObject )( slaves[ i ].GetOwnerEntityWeak() ) );
				if( puppet && puppet.IsActive() )
				{
					data.puppet = true;
				}
			}
		}
		return data;
	}

}

class SpiderbotOrderDeviceEvent extends Event
{
	var target : weak< GameObject >;
	var overrideMovementTarget : weak< GameObject >;
}

class BreachAccessPointEvent extends Event
{
}

class MinigameFailEvent extends Event
{
}

struct ConnectedClassTypes
{
	var surveillanceCamera : Bool;
	var securityTurret : Bool;
	var puppet : Bool;
}

