enum ELinkType
{
	NETWORK = 0,
	GRID = 1,
	FREE = 2,
	INVALID = 3,
}

enum ENetworkRelation
{
	MASTER = 0,
	SLAVE = 1,
	NONE = 2,
}

enum EPingType
{
	DIRECT = 0,
	SPACE = 1,
}

class ActivateNetworkLinkTaskData extends ScriptTaskData
{
	var linkIndex : Int32;
}

class DeactivateNetworkLinkTaskData extends ScriptTaskData
{
	var linkIndex : Int32;
	var instant : Bool;
}

struct SNetworkLinkData
{
	var beam : FxInstance;
	var fxResource : FxResource;
	var slaveID : EntityID;
	var masterID : EntityID;
	var slavePos : Vector4;
	var masterPos : Vector4;
	var drawLink : Bool;
	default drawLink = true;
	var isActive : Bool;
	var isDynamic : Bool;
	var revealMaster : Bool;
	default revealMaster = true;
	var revealSlave : Bool;
	default revealSlave = true;
	var permanent : Bool;
	default permanent = false;
	var isPing : Bool;
	default isPing = false;
	var isNetrunner : Bool;
	var linkType : ELinkType;
	var priority : EPriority;
	var lifetime : Float;
	default lifetime = -1.0f;
	var delayID : DelayID;
	var weakLink : Bool;
}

class PingCachedData extends IScriptable
{
	var m_sourceID : EntityID;
	var m_pingType : EPingType;
	var m_pingNetworkEffect : EffectInstance;
	var m_timeout : Float;
	var m_ammountOfIntervals : Int32;
	var m_linksCount : Int32;
	var m_currentInterval : Int32;
	default m_currentInterval = 1;
	var m_delayID : DelayID;
	var m_linkType : ELinkType;
	default m_linkType = ELinkType.FREE;
	var m_revealNetwork : Bool;
	var m_linkFXresource : FxResource;
	var m_sourcePosition : Vector4;
	var m_hasActiveVirtualNetwork : Bool;
	var m_virtualNetworkShape : weak< VirtualNetwork_Record >;

	public function Initialize( sourceID : EntityID, timeout : Float, ammountOfIntervals : Int32, pingType : EPingType, gameEffect : EffectInstance, revealNetworkAtEnd : Bool, fxResource : FxResource, position : Vector4, virtualNetworkShapeID : TweakDBID )
	{
		if( ammountOfIntervals <= 0 )
		{
			ammountOfIntervals = 1;
		}
		m_timeout = timeout;
		m_ammountOfIntervals = ammountOfIntervals;
		m_currentInterval = ammountOfIntervals;
		m_linksCount = 0;
		m_sourceID = sourceID;
		m_pingType = pingType;
		m_revealNetwork = revealNetworkAtEnd;
		m_linkFXresource = fxResource;
		m_sourcePosition = position;
		m_virtualNetworkShape = TweakDBInterface.GetVirtualNetworkRecord( virtualNetworkShapeID );
	}

	public function Initialize( timeout : Float, ammountOfIntervals : Int32 )
	{
		if( ammountOfIntervals <= 0 )
		{
			ammountOfIntervals = 1;
		}
		m_timeout = timeout;
		m_ammountOfIntervals = ammountOfIntervals;
		m_currentInterval = ammountOfIntervals;
		m_linksCount = 0;
	}

	public function IncrementLinkCounter()
	{
		m_linksCount += 1;
	}

	public function GetLifetimeValue() : Float
	{
		var lifetime : Float;
		lifetime = RandRangeF( GetCurrentMinValue(), GetCurrentMaxValue() );
		UpdateCurrentInterval();
		return lifetime;
	}

	private function UpdateCurrentInterval()
	{
		if( m_currentInterval == 1 )
		{
			m_currentInterval = m_ammountOfIntervals;
		}
		else
		{
			m_currentInterval -= 1;
		}
	}

	private function GetCurrentMaxValue() : Float
	{
		return ( m_timeout / ( ( Float )( m_ammountOfIntervals ) ) ) * ( ( Float )( m_currentInterval ) );
	}

	private function GetCurrentMinValue() : Float
	{
		var value : Float;
		value = GetCurrentMaxValue() - ( m_timeout / ( ( Float )( m_ammountOfIntervals ) ) );
		if( m_currentInterval == 1 )
		{
			value = value + ( ( m_timeout / ( ( Float )( m_ammountOfIntervals ) ) ) / ( ( Float )( m_ammountOfIntervals ) ) );
		}
		return value;
	}

}

class AddPingedSquadRequest extends ScriptableSystemRequest
{
	var squadName : CName;
}

class RemovePingedSquadRequest extends ScriptableSystemRequest
{
	var squadName : CName;
}

class ClearPingedSquadRequest extends ScriptableSystemRequest
{
	var squadName : CName;
}

class RegisterPingNetworkLinkRequest extends ScriptableSystemRequest
{
	var linksData : array< SNetworkLinkData >;
}

class StartPingingNetworkRequest extends ScriptableSystemRequest
{
	var source : weak< GameObject >;
	var fxResource : FxResource;
	var duration : Float;
	default duration = 2.0f;
	var pingType : EPingType;
	var fakeLinkType : ELinkType;
	var revealNetworkAtEnd : Bool;
	var virtualNetworkShapeID : TweakDBID;
}

class StopPingingNetworkRequest extends ScriptableSystemRequest
{
	var source : weak< GameObject >;
	var pingData : PingCachedData;
}

class RegisterNetworkLinkRequest extends ScriptableSystemRequest
{
	var linksData : array< SNetworkLinkData >;
}

class UnregisterNetworkLinkRequest extends ScriptableSystemRequest
{
	var linksData : array< SNetworkLinkData >;
}

class UnregisterNetworkLinkBetweenTwoEntitiesRequest extends ScriptableSystemRequest
{
	var firstID : EntityID;
	var secondID : EntityID;
	var onlyRemoveWeakLink : Bool;
}

class NewBackdoorDeviceRequest extends ScriptableSystemRequest
{
	var device : ScriptableDeviceComponentPS;
}

class MarkBackdoorAsRevealedRequest extends ScriptableSystemRequest
{
	var device : SharedGameplayPS;
}

class UnregisterAllNetworkLinksRequest extends ScriptableSystemRequest
{
}

class DeactivateAllNetworkLinksRequest extends ScriptableSystemRequest
{
}

class UpdateNetworkVisualisationRequest extends ScriptableSystemRequest
{
}

class UnregisterNetworkLinksByIDRequest extends ScriptableSystemRequest
{
	var ID : EntityID;
}

class UnregisterNetworkLinksByIdAndTypeRequest extends ScriptableSystemRequest
{
	var ID : EntityID;
	var type : ELinkType;
}

class EvaluateVisionModeRequest extends ScriptableSystemRequest
{
	var mode : gameVisionModeType;
}

class RevealNetworkRequestRequest extends ScriptableSystemRequest
{
	var target : EntityID;
	var delay : Float;
}

class ActivateLinksRequest extends ScriptableSystemRequest
{
	var linksIDs : array< Int32 >;
}

class DeactivateLinksRequest extends ScriptableSystemRequest
{
	var linksIDs : array< Int32 >;
	var instant : Bool;
}

class NetworkSystem extends ScriptableSystem
{
	private var m_networkLinks : array< SNetworkLinkData >;
	private var m_networkRevealTargets : array< EntityID >;
	private var m_networkRevealTargetsLastSession : array< EntityID >;
	private var m_sessionStarted : Bool;
	private var m_visionModeChangedCallback : CallbackHandle;
	private var m_focusModeToggleCallback : CallbackHandle;
	private var m_playerSpawnCallback : Uint32;
	private var m_currentPlayerTargetCallbackID : CallbackHandle;
	private var m_lastTargetSlaveID : EntityID;
	private var m_lastTargetMasterID : EntityID;
	private var m_unregisterLinksRequestDelay : DelayID;
	private var m_focusModeActive : Bool;
	private var m_lastBeamResource : FxResource;
	private var m_pingNetworkEffect : EffectInstance;
	private var m_pingCachedData : PingCachedData;
	private var m_lastPingSourceID : EntityID;
	private var m_activePings : array< PingCachedData >;
	private var m_pingedSquads : array< CName >;
	private var m_pingLinksCounter : Int32;
	private var m_networkPresetTBDID : TweakDBID;
	private var m_networkPresetRecord : weak< NetworkPingingParameteres_Record >;
	private var m_backdoors : array< PersistentID >;
	private var m_revealedBackdoorsCount : Int32;
	private var m_debugCashedPingFxResource : FxResource;
	private var m_debugQueryNumber : Int32;
	private var m_activateLinksDelayID : DelayID;
	private var m_deactivateLinksDelayID : DelayID;

	protected function ActivateNetworkLinkByTask( linkIndex : Int32 )
	{
		var taskData : ActivateNetworkLinkTaskData;
		taskData = new ActivateNetworkLinkTaskData;
		taskData.linkIndex = linkIndex;
		GameInstance.GetDelaySystem( GetGameInstance() ).QueueTask( this, taskData, 'ActivateNetworkLinkTask', gameScriptTaskExecutionStage.Any );
	}

	protected export function ActivateNetworkLinkTask( data : ScriptTaskData )
	{
		var taskData : ActivateNetworkLinkTaskData;
		taskData = ( ( ActivateNetworkLinkTaskData )( data ) );
		if( taskData )
		{
			ActivateNetworkLinkByIndex( taskData.linkIndex );
		}
	}

	protected function DeactivateNetworkLinkByTask( linkIndex : Int32, instant : Bool )
	{
		var taskData : DeactivateNetworkLinkTaskData;
		taskData = new DeactivateNetworkLinkTaskData;
		taskData.linkIndex = linkIndex;
		taskData.instant = instant;
		GameInstance.GetDelaySystem( GetGameInstance() ).QueueTask( this, taskData, 'DeactivateNetworkLinkTask', gameScriptTaskExecutionStage.Any );
	}

	protected function DeactivateNetworkLinkTask( data : ScriptTaskData )
	{
		var taskData : DeactivateNetworkLinkTaskData;
		taskData = ( ( DeactivateNetworkLinkTaskData )( data ) );
		if( taskData )
		{
			KillNetworkBeam( taskData.linkIndex, taskData.instant );
		}
	}

	private export override function OnAttach()
	{
		RegisterPlayerSpawnedCallback();
		RegisterFocusModeCallback();
		RegisterPlayerTargetCallback();
		m_networkPresetTBDID = T"Network.ActiveNetworkPresets";
		SetupPingPresetRecord();
	}

	private export override function OnDetach()
	{
		UnregisterFocusModeCallback();
		UnregisterPlayerTargetCallback();
		UnregisterPlayerSpawnedCallback();
	}

	protected function GetPlayerStateMachineBlackboard( playerPuppet : weak< GameObject > ) : IBlackboard
	{
		var blackboard : IBlackboard;
		if( playerPuppet != NULL )
		{
			blackboard = GameInstance.GetBlackboardSystem( GetGameInstance() ).GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		}
		return blackboard;
	}

	private export function OnPlayerSpawnedCallback( playerPuppet : GameObject )
	{
		if( m_visionModeChangedCallback )
		{
			UnregisterVisionModeCallback();
		}
		RegisterVisionModeCallback( playerPuppet );
	}

	protected function RegisterPlayerSpawnedCallback()
	{
		if( m_playerSpawnCallback == ( ( Uint32 )( 0 ) ) )
		{
			m_playerSpawnCallback = GameInstance.GetPlayerSystem( GetGameInstance() ).RegisterPlayerPuppetAttachedCallback( this, 'OnPlayerSpawnedCallback' );
		}
	}

	protected function RegisterVisionModeCallback( player : GameObject )
	{
		var blackboard : IBlackboard;
		if( player != NULL )
		{
			blackboard = GetPlayerStateMachineBlackboard( player );
			if( blackboard && !( m_visionModeChangedCallback ) )
			{
				m_visionModeChangedCallback = blackboard.RegisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Vision, this, 'OnVisionModeChanged' );
			}
		}
	}

	protected function RegisterFocusModeCallback()
	{
		var blackBoard : IBlackboard;
		blackBoard = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().UI_VisionMode );
		if( blackBoard && !( m_focusModeToggleCallback ) )
		{
			m_focusModeToggleCallback = blackBoard.RegisterListenerBool( GetAllBlackboardDefs().UI_VisionMode.isEnabled, this, 'OnFocusModeToggle' );
		}
	}

	protected function RegisterPlayerTargetCallback()
	{
		var blackBoard : IBlackboard;
		blackBoard = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().UI_Scanner );
		if( blackBoard && !( m_currentPlayerTargetCallbackID ) )
		{
			m_currentPlayerTargetCallbackID = blackBoard.RegisterListenerEntityID( GetAllBlackboardDefs().UI_Scanner.ScannedObject, this, 'OnPlayerTargetChanged' );
		}
	}

	protected function UnregisterVisionModeCallback()
	{
		var playerControlledObject : GameObject;
		var blackBoard : IBlackboard;
		playerControlledObject = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject();
		if( playerControlledObject != NULL )
		{
			blackBoard = GetPlayerStateMachineBlackboard( playerControlledObject );
			if( ( blackBoard != NULL ) && m_visionModeChangedCallback )
			{
				blackBoard.UnregisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Vision, m_visionModeChangedCallback );
			}
		}
	}

	protected function UnregisterFocusModeCallback()
	{
		var blackBoard : IBlackboard;
		blackBoard = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().UI_VisionMode );
		if( ( blackBoard != NULL ) && m_focusModeToggleCallback )
		{
			blackBoard.UnregisterListenerBool( GetAllBlackboardDefs().UI_VisionMode.isEnabled, m_focusModeToggleCallback );
		}
	}

	protected function UnregisterPlayerTargetCallback()
	{
		var blackBoard : IBlackboard;
		blackBoard = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().UI_TargetingInfo );
		if( ( blackBoard != NULL ) && m_currentPlayerTargetCallbackID )
		{
			blackBoard.UnregisterListenerEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget, m_currentPlayerTargetCallbackID );
		}
	}

	protected function UnregisterPlayerSpawnedCallback()
	{
		if( m_playerSpawnCallback )
		{
			GameInstance.GetPlayerSystem( GetGameInstance() ).UnregisterPlayerPuppetAttachedCallback( m_playerSpawnCallback );
		}
	}

	private function AddNetworkLink( linkData : SNetworkLinkData )
	{
		if( FxResource.IsValid( linkData.fxResource ) )
		{
			m_lastBeamResource = linkData.fxResource;
		}
		linkData.priority = DeterminNetworkLinkPriority( linkData );
		m_networkLinks.PushBack( linkData );
	}

	private function DeterminNetworkLinkPriority( const linkData : ref< SNetworkLinkData > ) : EPriority
	{
		switch( linkData.linkType )
		{
			case ELinkType.NETWORK:
				if( linkData.isPing )
				{
					return EPriority.Medium;
				}
				else
				{
					return EPriority.Absolute;
				}
			case ELinkType.GRID:
				if( linkData.isPing )
				{
					return EPriority.Low;
				}
				else
				{
					return EPriority.VeryHigh;
				}
			case ELinkType.FREE:
				if( linkData.isPing )
				{
					return EPriority.VeryLow;
				}
				else
				{
					return EPriority.High;
				}
			default:
				return EPriority.VeryLow;
		}
	}

	private function RemoveNetworkLinksByID( ID : EntityID )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( m_networkLinks[ i ].slaveID == ID ) || ( m_networkLinks[ i ].masterID == ID ) )
			{
				KillNetworkBeam( i, false );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemoveNetworkLinksBetweenTwoEntitities( firstID : EntityID, secondID : EntityID, onlyRemoveWeakLink : Bool )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( ( m_networkLinks[ i ].slaveID == firstID ) && ( m_networkLinks[ i ].masterID == secondID ) ) || ( ( m_networkLinks[ i ].slaveID == secondID ) && ( m_networkLinks[ i ].masterID == firstID ) ) )
			{
				if( !( onlyRemoveWeakLink ) || m_networkLinks[ i ].weakLink )
				{
					KillNetworkBeam( i, false );
					m_networkLinks.Erase( i );
				}
			}
		}
	}

	private function RemoveNetworkLinkByData( const linkData : ref< SNetworkLinkData > )
	{
		var i : Int32;
		var instant : Bool;
		var targets : array< EntityID >;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( ( ( m_networkLinks[ i ].linkType == linkData.linkType && ( m_networkLinks[ i ].slaveID == linkData.slaveID ) ) && ( m_networkLinks[ i ].masterID == linkData.masterID ) ) && m_networkLinks[ i ].slavePos == linkData.slavePos ) && m_networkLinks[ i ].masterPos == linkData.masterPos )
			{
				if( linkData.lifetime > 0.0 )
				{
					if( linkData.linkType == ELinkType.NETWORK && linkData.isPing )
					{
						targets.PushBack( linkData.masterID );
						targets.PushBack( linkData.slaveID );
					}
					instant = false;
				}
				RemoveNetworkLink( i, instant );
			}
		}
		RevealNetworkOnCachedTarget( targets );
	}

	private function RemoveNetworkLinkByType( linkType : ELinkType )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_networkLinks[ i ].linkType == linkType )
			{
				RemoveNetworkLink( i, false );
			}
		}
	}

	private function RemoveNetworkLinkByIdAndType( linkType : ELinkType, ID : EntityID )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_networkLinks[ i ].linkType == linkType && ( ( m_networkLinks[ i ].slaveID == ID ) || ( m_networkLinks[ i ].masterID == ID ) ) )
			{
				RemoveNetworkLink( i, false );
			}
		}
	}

	private function RemoveNetworkLink( index : Int32, instant : Bool )
	{
		if( ( index < 0 ) || ( index >= m_networkLinks.Size() ) )
		{
			return;
		}
		if( m_networkLinks[ index ].lifetime > 0.0 )
		{
			CancelNetworkLinkDelay( m_networkLinks[ index ] );
		}
		KillNetworkBeam( index, instant );
		m_networkLinks.Erase( index );
	}

	private function UnregisterNetworkLinkWithDelay( const linkData : ref< SNetworkLinkData > ) : DelayID
	{
		var unregisterReq : UnregisterNetworkLinkRequest;
		unregisterReq = new UnregisterNetworkLinkRequest;
		unregisterReq.linksData.PushBack( linkData );
		unregisterReq.linksData[ 0 ].delayID = GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), unregisterReq, linkData.lifetime, false );
		return unregisterReq.linksData[ 0 ].delayID;
	}

	private function RegisterNetworkLinkWithDelay( const linkData : ref< SNetworkLinkData >, delay : Float )
	{
		var registerReq : RegisterNetworkLinkRequest;
		registerReq = new RegisterNetworkLinkRequest;
		registerReq.linksData.PushBack( linkData );
		GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), registerReq, delay, false );
	}

	public const function HasNetworkLink( const linkData : ref< SNetworkLinkData >, out index : Int32 ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ( ( ( linkData.slavePos == m_networkLinks[ i ].slavePos && linkData.masterPos == m_networkLinks[ i ].masterPos ) && ( linkData.slaveID == m_networkLinks[ i ].slaveID ) ) && ( linkData.masterID == m_networkLinks[ i ].masterID ) ) && linkData.linkType == m_networkLinks[ i ].linkType ) && linkData.isPing == m_networkLinks[ i ].isPing )
			{
				index = i;
				return true;
			}
		}
		index = -1;
		return false;
	}

	public const function HasNetworkLink( const linkData : ref< SNetworkLinkData > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ( ( linkData.slavePos == m_networkLinks[ i ].slavePos && linkData.masterPos == m_networkLinks[ i ].masterPos ) && ( linkData.slaveID == m_networkLinks[ i ].slaveID ) ) && ( linkData.masterID == m_networkLinks[ i ].masterID ) ) && linkData.linkType == m_networkLinks[ i ].linkType )
			{
				return true;
			}
		}
		return false;
	}

	private function UpdateNetworkLinkData( linkData : SNetworkLinkData, indexToUpdate : Int32 )
	{
		if( !( FxResource.IsValid( linkData.fxResource ) ) )
		{
			return;
		}
		if( !( CompareLinksData( linkData, m_networkLinks[ indexToUpdate ] ) ) )
		{
			CancelNetworkLinkDelay( m_networkLinks[ indexToUpdate ] );
			KillNetworkBeam( indexToUpdate, true );
			linkData.priority = DeterminNetworkLinkPriority( linkData );
			if( linkData.lifetime > 0.0 )
			{
				linkData.delayID = UnregisterNetworkLinkWithDelay( linkData );
			}
			m_networkLinks[ indexToUpdate ] = linkData;
		}
	}

	private function CancelNetworkLinkDelay( const linkData : ref< SNetworkLinkData > )
	{
		var invalidDealyID : DelayID;
		if( ( linkData.lifetime > 0.0 ) && ( linkData.delayID != invalidDealyID ) )
		{
			GameInstance.GetDelaySystem( GetGameInstance() ).CancelDelay( linkData.delayID );
		}
	}

	private const function CompareLinks( const linkData1 : ref< SNetworkLinkData >, const linkData2 : ref< SNetworkLinkData > ) : Bool
	{
		return ( ( ( ( ( ( ( ( linkData1.slaveID == linkData2.slaveID ) && ( linkData1.masterID == linkData2.masterID ) ) && linkData1.linkType == linkData2.linkType ) && linkData1.fxResource == linkData2.fxResource ) && linkData1.isDynamic == linkData2.isDynamic ) && linkData1.drawLink == linkData2.drawLink ) && linkData1.isPing == linkData2.isPing ) && linkData1.revealSlave == linkData2.revealSlave ) && linkData1.revealMaster == linkData2.revealMaster;
	}

	private const function CompareLinksData( const linkData1 : ref< SNetworkLinkData >, const linkData2 : ref< SNetworkLinkData > ) : Bool
	{
		return ( ( ( ( linkData1.fxResource == linkData2.fxResource && linkData1.isDynamic == linkData2.isDynamic ) && linkData1.drawLink == linkData2.drawLink ) && linkData1.isPing == linkData2.isPing ) && linkData1.revealSlave == linkData2.revealSlave ) && linkData1.revealMaster == linkData2.revealMaster;
	}

	public const function HasNetworkLinkWithHigherPriority( const linkData : ref< SNetworkLinkData > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ( linkData.slavePos == m_networkLinks[ i ].slavePos && linkData.masterPos == m_networkLinks[ i ].masterPos ) && ( linkData.slaveID == m_networkLinks[ i ].slaveID ) ) && ( linkData.masterID == m_networkLinks[ i ].masterID ) )
			{
				if( ( ( Int32 )( m_networkLinks[ i ].priority ) ) > ( ( Int32 )( linkData.priority ) ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	public const function HasNetworkLink( ID : EntityID ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ID == m_networkLinks[ i ].slaveID ) || ( ID == m_networkLinks[ i ].masterID ) )
			{
				return true;
			}
		}
		return false;
	}

	public const function HasNetworkLink( ID : EntityID, ignorePingLinks : Bool ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ignorePingLinks && m_networkLinks[ i ].isPing )
			{
				continue;
			}
			else
			{
				if( ( ID == m_networkLinks[ i ].slaveID ) || ( ID == m_networkLinks[ i ].masterID ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	public const function HasNetworkLink( masterID : EntityID, slaveID : EntityID, linkType : ELinkType ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ( slaveID == m_networkLinks[ i ].slaveID ) && ( masterID == m_networkLinks[ i ].masterID ) ) && m_networkLinks[ i ].linkType == linkType )
			{
				return true;
			}
		}
		return false;
	}

	private function HasAnyActiveNetworkLink( ID : EntityID ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ID == m_networkLinks[ i ].slaveID ) || ( ID == m_networkLinks[ i ].masterID ) )
			{
				if( m_networkLinks[ i ].isActive )
				{
					return true;
				}
			}
		}
		return false;
	}

	private function GetNetworkBeam( const linkData : ref< SNetworkLinkData > ) : FxInstance
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( ( linkData.slavePos == m_networkLinks[ i ].slavePos && linkData.masterPos == m_networkLinks[ i ].masterPos ) && ( linkData.slaveID == m_networkLinks[ i ].slaveID ) ) && ( linkData.masterID == m_networkLinks[ i ].masterID ) )
			{
				if( m_networkLinks[ i ].beam != NULL )
				{
					return m_networkLinks[ i ].beam;
				}
			}
		}
		return NULL;
	}

	private function OnNewBackdoorDeviceRequest( request : NewBackdoorDeviceRequest )
	{
		if( !( m_backdoors.Contains( request.device.GetID() ) ) )
		{
			m_backdoors.PushBack( request.device.GetID() );
		}
	}

	private function OnMarkBackdoorAsRevealedRequest( request : MarkBackdoorAsRevealedRequest )
	{
		if( ( m_backdoors.Size() > 0 ) && m_backdoors.Contains( request.device.GetID() ) )
		{
			m_revealedBackdoorsCount += 1;
		}
	}

	private export function OnUnregisterAllNetworkLinksRequest( request : UnregisterAllNetworkLinksRequest )
	{
		ResolveNetworkSystemCleanupDelay();
		CleanNetwork();
	}

	private function OnDeactivateAllNetworkLinksRequest( request : DeactivateAllNetworkLinksRequest )
	{
		KillAllNetworkBeams();
		m_networkLinks.Clear();
	}

	private function KillAllNetworkBeams()
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			KillNetworkBeam( i, false );
		}
	}

	private function ResolveNetworkSystemCleanupDelay()
	{
		var defaultDelay : DelayID;
		if( m_unregisterLinksRequestDelay != defaultDelay )
		{
			GameInstance.GetDelaySystem( GetGameInstance() ).CancelDelay( m_unregisterLinksRequestDelay );
			GameInstance.GetDelaySystem( GetGameInstance() ).CancelCallback( m_unregisterLinksRequestDelay );
			m_unregisterLinksRequestDelay = defaultDelay;
		}
	}

	private function CleanNetwork()
	{
		var i : Int32;
		var invalidID : EntityID;
		m_lastTargetMasterID = invalidID;
		m_lastTargetSlaveID = invalidID;
		m_sessionStarted = false;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_networkLinks[ i ].permanent )
			{
				continue;
			}
			KillNetworkBeam( i, false );
			if( !( IsTagged( m_networkLinks[ i ].masterID ) ) && !( IsTagged( m_networkLinks[ i ].slaveID ) ) )
			{
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemoveAllNetworkLinks()
	{
		KillAllNetworkBeams();
		m_networkLinks.Clear();
	}

	private function OnUnregisterNetworkLinksByIDRequest( request : UnregisterNetworkLinksByIDRequest )
	{
		RemoveNetworkLinksByID( request.ID );
	}

	private export function OnUnregisterNetworkLinksByIdAndTypeRequest( request : UnregisterNetworkLinksByIdAndTypeRequest )
	{
		RemoveNetworkLinkByIdAndType( request.type, request.ID );
	}

	private function OnUnregisterNetworkLinkRequest( request : UnregisterNetworkLinkRequest )
	{
		var i : Int32;
		for( i = 0; i < request.linksData.Size(); i += 1 )
		{
			RemoveNetworkLinkByData( request.linksData[ i ] );
		}
	}

	private function OnUnregisterNetworkLinkBetweenTwoEntitiesRequest( request : UnregisterNetworkLinkBetweenTwoEntitiesRequest )
	{
		RemoveNetworkLinksBetweenTwoEntitities( request.firstID, request.secondID, request.onlyRemoveWeakLink );
	}

	private function KillNetworkBeamsByID( ID : EntityID )
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( m_networkLinks[ i ].slaveID == ID ) || ( m_networkLinks[ i ].masterID == ID ) )
			{
				KillNetworkBeam( i, false );
			}
		}
	}

	private function KillNetworkBeamByIndex( index : Int32 )
	{
		KillNetworkBeam( index, false );
	}

	private function KillNetworkBeamsByID( slaveID : EntityID, masterID : EntityID )
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( m_networkLinks[ i ].slaveID == slaveID ) && ( m_networkLinks[ i ].masterID == masterID ) )
			{
				KillNetworkBeam( i, false );
			}
		}
	}

	private function KillNetworkBeamByData( const linkData : ref< SNetworkLinkData > )
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( m_networkLinks[ i ].linkType == linkData.linkType && ( m_networkLinks[ i ].slaveID == linkData.slaveID ) ) && ( m_networkLinks[ i ].masterID == linkData.masterID ) )
			{
				KillNetworkBeam( i, false );
				return;
			}
		}
	}

	private function KillNetworkBeam( index : Int32, instant : Bool )
	{
		if( ( index < 0 ) || ( index >= m_networkLinks.Size() ) )
		{
			return;
		}
		if( ShouldForceInstantBeamKill() )
		{
			instant = true;
		}
		if( m_networkLinks[ index ].beam != NULL )
		{
			if( instant )
			{
				m_networkLinks[ index ].beam.Kill();
			}
			else
			{
				m_networkLinks[ index ].beam.BreakLoop();
			}
			m_networkLinks[ index ].beam = NULL;
			if( m_networkLinks[ index ].isPing )
			{
				DecreasePingLinbksCounter();
			}
		}
		ResolveConnectionHighlight( false, m_networkLinks[ index ] );
		m_networkLinks[ index ].isActive = false;
	}

	private export function OnRegisterNetworkLinkRequest( request : RegisterNetworkLinkRequest )
	{
		var i : Int32;
		var totalIinks : Int32;
		var excessLnks : array< SNetworkLinkData >;
		var requestToSend : RegisterNetworkLinkRequest;
		for( i = 0; i < request.linksData.Size(); i += 1 )
		{
			if( totalIinks > GetMaxLinksRegisteredAtOnce() )
			{
				excessLnks.PushBack( request.linksData[ i ] );
			}
			else
			{
				RegisterNetworkLink( request.linksData[ i ] );
			}
			totalIinks += 1;
		}
		if( excessLnks.Size() > 0 )
		{
			requestToSend = new RegisterNetworkLinkRequest;
			requestToSend.linksData = excessLnks;
			GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequestNextFrame( GetClassName(), requestToSend );
		}
		else
		{
			UpdateNetworkVisualisation();
		}
	}

	private function RegisterNetworkLink( linkData : SNetworkLinkData )
	{
		var indexToUpdate : Int32;
		var currentTarget : EntityID;
		var evaluateTargets : Bool;
		if( !( HasNetworkLink( linkData, indexToUpdate ) ) )
		{
			if( linkData.lifetime > 0.0 )
			{
				linkData.delayID = UnregisterNetworkLinkWithDelay( linkData );
				if( linkData.linkType == ELinkType.NETWORK && linkData.isPing )
				{
					ResolveNetworkRevealTarget( linkData );
				}
			}
			AddNetworkLink( linkData );
			if( !( linkData.isPing ) && linkData.linkType != ELinkType.FREE )
			{
				currentTarget = GetCurrentTargetID();
				if( currentTarget == linkData.slaveID )
				{
					m_lastTargetSlaveID = currentTarget;
					evaluateTargets = true;
				}
				else if( currentTarget == linkData.masterID )
				{
					m_lastTargetMasterID = currentTarget;
					evaluateTargets = true;
				}
				if( evaluateTargets )
				{
					EvaluatelastTargets( currentTarget );
				}
			}
		}
		else if( indexToUpdate >= 0 )
		{
			UpdateNetworkLinkData( linkData, indexToUpdate );
		}
	}

	private export function OnUpdateNetworkVisualisationRequest( request : UpdateNetworkVisualisationRequest )
	{
		UpdateNetworkVisualisation();
	}

	private function UpdateNetworkVisualisation()
	{
		var i : Int32;
		var hasContext : Bool;
		var toDeactivate : array< Int32 >;
		var toActivate : array< Int32 >;
		hasContext = IsCurrentTargetValid();
		if( m_activateLinksDelayID != GetInvalidDelayID() )
		{
			GameInstance.GetDelaySystem( GetGameInstance() ).CancelDelay( m_activateLinksDelayID );
			m_activateLinksDelayID = GetInvalidDelayID();
		}
		if( m_deactivateLinksDelayID != GetInvalidDelayID() )
		{
			GameInstance.GetDelaySystem( GetGameInstance() ).CancelDelay( m_deactivateLinksDelayID );
			m_deactivateLinksDelayID = GetInvalidDelayID();
		}
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( HasNetworkLinkWithHigherPriority( m_networkLinks[ i ] ) )
			{
				toDeactivate.PushBack( i );
				continue;
			}
			if( m_networkLinks[ i ].isPing )
			{
				toActivate.PushBack( i );
				continue;
			}
			if( m_networkLinks[ i ].isNetrunner && m_focusModeActive )
			{
				toActivate.PushBack( i );
				continue;
			}
			if( m_networkLinks[ i ].isNetrunner && !( m_focusModeActive ) )
			{
				toDeactivate.PushBack( i );
				continue;
			}
			if( hasContext )
			{
				if( IsCurrentTarget( m_networkLinks[ i ].slaveID ) )
				{
					toActivate.PushBack( i );
				}
				else if( ShouldShowLinksOnMaster() && IsCurrentTarget( m_networkLinks[ i ].masterID ) )
				{
					toActivate.PushBack( i );
				}
				else if( m_networkLinks[ i ].isActive )
				{
					toDeactivate.PushBack( i );
				}
			}
			else
			{
				if( IsLastSlaveTarget( m_networkLinks[ i ].slaveID ) || IsTagged( m_networkLinks[ i ].slaveID ) )
				{
					toActivate.PushBack( i );
				}
				else if( ShouldShowLinksOnMaster() && ( IsLastMasterTarget( m_networkLinks[ i ].masterID ) || IsTagged( m_networkLinks[ i ].masterID ) ) )
				{
					toActivate.PushBack( i );
				}
				else if( m_networkLinks[ i ].isActive )
				{
					toDeactivate.PushBack( i );
				}
			}
		}
		DeactivateNetworkLinks( toDeactivate, hasContext );
		ActivateNetworkLinks( toActivate );
	}

	protected function OnActivateNetworkLinksRequest( request : ActivateLinksRequest )
	{
		m_activateLinksDelayID = GetInvalidDelayID();
		ActivateNetworkLinks( request.linksIDs );
	}

	private function ActivateNetworkLinks( const toActivate : ref< array< Int32 > > )
	{
		var i : Int32;
		var totalIinks : Int32;
		var excessLnks : array< Int32 >;
		var request : ActivateLinksRequest;
		for( i = 0; i < toActivate.Size(); i += 1 )
		{
			if( totalIinks > NetworkSystem.GetMaxLinksDrawnAtOnce() )
			{
				excessLnks.PushBack( toActivate[ i ] );
			}
			else
			{
				ActivateNetworkLinkByTask( toActivate[ i ] );
			}
			totalIinks += 1;
		}
		if( excessLnks.Size() > 0 )
		{
			request = new ActivateLinksRequest;
			request.linksIDs = excessLnks;
			m_activateLinksDelayID = GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), request, 0.03, false );
		}
	}

	protected function OnDeactivateLinksRequest( request : DeactivateLinksRequest )
	{
		m_deactivateLinksDelayID = GetInvalidDelayID();
		DeactivateNetworkLinks( request.linksIDs, request.instant );
	}

	private function DeactivateNetworkLinks( const toDeactivate : ref< array< Int32 > >, hasContext : Bool )
	{
		var i : Int32;
		var totalIinks : Int32;
		var excessLnks : array< Int32 >;
		var request : DeactivateLinksRequest;
		for( i = 0; i < toDeactivate.Size(); i += 1 )
		{
			if( totalIinks > NetworkSystem.GetMaxLinksDeactivatedAtOnce() )
			{
				excessLnks.PushBack( toDeactivate[ i ] );
			}
			else
			{
				DeactivateNetworkLinkByTask( toDeactivate[ i ], hasContext );
			}
			totalIinks += 1;
		}
		if( excessLnks.Size() > 0 )
		{
			request = new DeactivateLinksRequest;
			request.linksIDs = excessLnks;
			request.instant = hasContext;
			m_deactivateLinksDelayID = GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), request, 0.03, false );
		}
	}

	private function DeterminNetworkLinkVisibility( linkIndex : Int32 ) : Bool
	{
		if( !( m_networkLinks[ linkIndex ].drawLink ) )
		{
			return false;
		}
		else
		{
			if( IsCurrentTarget( m_networkLinks[ linkIndex ].masterID ) )
			{
				return false;
			}
			else if( IsLastMasterTarget( m_networkLinks[ linkIndex ].masterID ) && !( IsLastSlaveTarget( m_networkLinks[ linkIndex ].slaveID ) ) )
			{
				return false;
			}
			else
			{
				return true;
			}
		}
	}

	private function ActivateNetworkLinkByIndex( linkIndex : Int32 )
	{
		if( ( linkIndex < 0 ) || ( linkIndex >= m_networkLinks.Size() ) )
		{
			return;
		}
		if( m_networkLinks[ linkIndex ].drawLink && !( DrawNetworkBeamByIndex( linkIndex ) ) )
		{
			return;
		}
		ResolveConnectionHighlight( true, m_networkLinks[ linkIndex ] );
		m_networkLinks[ linkIndex ].isActive = true;
	}

	private function DrawNetworkBeamByIndex( linkIndex : Int32 ) : Bool
	{
		var targetWorldPosition : WorldPosition;
		var rootWorldPosition : WorldPosition;
		var rootEntityWorldTransform : WorldTransform;
		var beam : FxInstance;
		var beamResource : FxResource;
		var slave : GameObject;
		var master : GameObject;
		var masterTransform : WorldTransform;
		var slaveTransform : WorldTransform;
		var rootEnttity : GameObject;
		var isDynamic : Bool;
		var attachementType : entAttachmentTarget;
		var isMasterRoot : Bool;
		beam = GetNetworkBeam( m_networkLinks[ linkIndex ] );
		if( beam == NULL )
		{
			if( !( FxResource.IsValid( m_networkLinks[ linkIndex ].fxResource ) ) )
			{
				beamResource = m_lastBeamResource;
			}
			else
			{
				beamResource = m_networkLinks[ linkIndex ].fxResource;
			}
			if( !( FxResource.IsValid( beamResource ) ) )
			{
				return false;
			}
			if( Vector4.IsZero( m_networkLinks[ linkIndex ].masterPos ) && Vector4.IsZero( m_networkLinks[ linkIndex ].slavePos ) )
			{
				return false;
			}
			isDynamic = m_networkLinks[ linkIndex ].isDynamic;
			if( isDynamic )
			{
				if( EntityID.IsDefined( m_networkLinks[ linkIndex ].slaveID ) )
				{
					slave = ( ( GameObject )( GameInstance.FindEntityByID( GetGameInstance(), m_networkLinks[ linkIndex ].slaveID ) ) );
				}
				if( EntityID.IsDefined( m_networkLinks[ linkIndex ].masterID ) )
				{
					master = ( ( GameObject )( GameInstance.FindEntityByID( GetGameInstance(), m_networkLinks[ linkIndex ].masterID ) ) );
				}
				if( ( slave == NULL ) && ( master == NULL ) )
				{
					return false;
				}
				else if( ( slave != NULL ) && ( master != NULL ) )
				{
					if( IsCurrentTarget( m_networkLinks[ linkIndex ].masterID ) )
					{
						rootEnttity = master;
						isMasterRoot = true;
					}
					else
					{
						rootEnttity = slave;
					}
				}
				else if( slave != NULL )
				{
					rootEnttity = slave;
				}
				else
				{
					rootEnttity = master;
					isMasterRoot = true;
				}
				WorldPosition.SetVector4( rootWorldPosition, rootEnttity.GetNetworkBeamEndpoint() );
			}
			else
			{
				if( IsCurrentTarget( m_networkLinks[ linkIndex ].masterID ) )
				{
					WorldPosition.SetVector4( rootWorldPosition, m_networkLinks[ linkIndex ].masterPos );
					isMasterRoot = true;
				}
				else
				{
					WorldPosition.SetVector4( rootWorldPosition, m_networkLinks[ linkIndex ].slavePos );
				}
				if( isMasterRoot )
				{
					if( Vector4.IsZero( m_networkLinks[ linkIndex ].slavePos ) )
					{
						return false;
					}
				}
				else
				{
					if( Vector4.IsZero( m_networkLinks[ linkIndex ].masterPos ) )
					{
						return false;
					}
				}
			}
			WorldTransform.SetWorldPosition( rootEntityWorldTransform, rootWorldPosition );
			beam = GameInstance.GetFxSystem( GetGameInstance() ).SpawnEffect( beamResource, rootEntityWorldTransform, true );
			if( isDynamic )
			{
				if( slave && IsNameValid( slave.GetNetworkLinkSlotName() ) )
				{
					if( slave == rootEnttity )
					{
						attachementType = entAttachmentTarget.Transform;
					}
					else
					{
						attachementType = entAttachmentTarget.TargetPosition;
					}
					beam.AttachToSlot( slave, attachementType, slave.GetNetworkLinkSlotName( slaveTransform ) );
				}
				if( master && IsNameValid( master.GetNetworkLinkSlotName() ) )
				{
					if( master == rootEnttity )
					{
						attachementType = entAttachmentTarget.Transform;
					}
					else
					{
						attachementType = entAttachmentTarget.TargetPosition;
					}
					beam.AttachToSlot( master, attachementType, master.GetNetworkLinkSlotName( masterTransform ) );
				}
				if( ( slave == rootEnttity ) && ( master == NULL ) )
				{
					WorldPosition.SetVector4( targetWorldPosition, m_networkLinks[ linkIndex ].masterPos );
					beam.UpdateTargetPosition( targetWorldPosition );
				}
				else if( ( master == rootEnttity ) && ( slave == NULL ) )
				{
					WorldPosition.SetVector4( targetWorldPosition, m_networkLinks[ linkIndex ].slavePos );
					beam.UpdateTargetPosition( targetWorldPosition );
				}
			}
			else
			{
				if( isMasterRoot )
				{
					WorldPosition.SetVector4( targetWorldPosition, m_networkLinks[ linkIndex ].slavePos );
					beam.UpdateTargetPosition( targetWorldPosition );
				}
				else
				{
					WorldPosition.SetVector4( targetWorldPosition, m_networkLinks[ linkIndex ].masterPos );
					beam.UpdateTargetPosition( targetWorldPosition );
				}
			}
			m_networkLinks[ linkIndex ].beam = beam;
			m_networkLinks[ linkIndex ].fxResource = beamResource;
			if( m_networkLinks[ linkIndex ].isPing )
			{
				IncreasePingLinbksCounter();
			}
			return true;
		}
		else
		{
			return false;
		}
	}

	private function ResolveConnectionHighlight( enable : Bool, const linkData : ref< SNetworkLinkData > )
	{
		var slaveID : EntityID;
		var masterID : EntityID;
		slaveID = linkData.slaveID;
		masterID = linkData.masterID;
		if( ( enable && !( linkData.isActive ) ) || ( !( enable ) && linkData.isActive ) )
		{
			if( linkData.revealSlave && EntityID.IsDefined( slaveID ) )
			{
				SendConnectionHighlightEvent( enable, slaveID, masterID, linkData );
			}
			if( linkData.revealMaster && EntityID.IsDefined( masterID ) )
			{
				SendConnectionHighlightEvent( enable, masterID, slaveID, linkData );
			}
		}
	}

	private function SendConnectionHighlightEvent( enable : Bool, target : EntityID, source : EntityID, const linkData : ref< SNetworkLinkData > )
	{
		var evt : RevealDeviceRequest;
		evt = new RevealDeviceRequest;
		evt.shouldReveal = enable;
		evt.sourceID = source;
		evt.linkData = linkData;
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueueEntityEvent( target, evt );
	}

	public static function SendEvaluateVisionModeRequest( instance : GameInstance, mode : gameVisionModeType )
	{
		var networkSystem : NetworkSystem;
		var request : EvaluateVisionModeRequest;
		networkSystem = ( ( NetworkSystem )( GameInstance.GetScriptableSystemsContainer( instance ).Get( 'NetworkSystem' ) ) );
		if( networkSystem )
		{
			request = new EvaluateVisionModeRequest;
			request.mode = mode;
			networkSystem.QueueRequest( request );
		}
	}

	private function OnEvaluateVisionModeRequest( request : EvaluateVisionModeRequest )
	{
		EvaluateVisionMode( request.mode );
	}

	protected event OnVisionModeChanged( value : Int32 )
	{
		EvaluateVisionMode( ( ( gameVisionModeType )( value ) ) );
	}

	private function EvaluateVisionMode( visionType : gameVisionModeType )
	{
		var unregisterLinkRequest : UnregisterAllNetworkLinksRequest;
		var updateNetworkRequest : UpdateNetworkVisualisationRequest;
		var duration : Float;
		if( visionType == gameVisionModeType.Default )
		{
			m_focusModeActive = false;
			if( !( m_sessionStarted ) )
			{
				return;
			}
			unregisterLinkRequest = new UnregisterAllNetworkLinksRequest;
			duration = GetRevealLinksAfterLeavingFocusDuration();
			if( ( ( Bool )duration ) )
			{
				m_unregisterLinksRequestDelay = GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), unregisterLinkRequest, duration, false );
			}
			else
			{
				ResolveNetworkSystemCleanupDelay();
				CleanNetwork();
			}
		}
		else if( visionType == gameVisionModeType.Focus )
		{
			m_focusModeActive = true;
			m_sessionStarted = true;
			ResolveNetworkSystemCleanupDelay();
			updateNetworkRequest = new UpdateNetworkVisualisationRequest;
			QueueRequest( updateNetworkRequest );
		}
		UpdateNetworkVisualisation();
	}

	protected event OnFocusModeToggle( value : Bool )
	{
		var unregisterLinkRequest : UnregisterAllNetworkLinksRequest;
		m_focusModeActive = value;
		if( !( value ) )
		{
			unregisterLinkRequest = new UnregisterAllNetworkLinksRequest;
			QueueRequest( unregisterLinkRequest );
		}
	}

	protected event OnPlayerTargetChanged( value : EntityID )
	{
		var updateNetworkRequest : UpdateNetworkVisualisationRequest;
		EvaluatelastTargets( value );
		if( m_focusModeActive )
		{
			updateNetworkRequest = new UpdateNetworkVisualisationRequest;
			QueueRequest( updateNetworkRequest );
		}
	}

	private function EvaluatelastTargets( newTarget : EntityID ) : Bool
	{
		var emptyID : EntityID;
		var newTargetSet : Bool;
		var newTargetObject : weak< GameObject >;
		if( !( EntityID.IsDefined( newTarget ) ) )
		{
			return false;
		}
		newTargetObject = GetObjectFromID( newTarget );
		if( ( newTargetObject != NULL ) && !( newTargetObject.CanOverrideNetworkContext() ) )
		{
			return false;
		}
		if( EntityID.IsDefined( m_lastTargetSlaveID ) && !( EntityID.IsDefined( m_lastTargetMasterID ) ) )
		{
			if( newTarget != m_lastTargetSlaveID )
			{
				if( GetNetworkRelation( newTarget, m_lastTargetSlaveID ) == ENetworkRelation.MASTER )
				{
					m_lastTargetMasterID = newTarget;
					newTargetSet = true;
				}
				else if( GetNetworkRelation( newTarget, m_lastTargetSlaveID ) == ENetworkRelation.SLAVE )
				{
					m_lastTargetMasterID = m_lastTargetSlaveID;
					m_lastTargetSlaveID = newTarget;
					newTargetSet = true;
				}
				else
				{
					m_lastTargetSlaveID = emptyID;
				}
			}
		}
		else if( !( EntityID.IsDefined( m_lastTargetSlaveID ) ) && EntityID.IsDefined( m_lastTargetMasterID ) )
		{
			if( newTarget != m_lastTargetMasterID )
			{
				if( GetNetworkRelation( newTarget, m_lastTargetMasterID ) == ENetworkRelation.SLAVE )
				{
					m_lastTargetSlaveID = newTarget;
					newTargetSet = true;
				}
				else if( GetNetworkRelation( newTarget, m_lastTargetMasterID ) == ENetworkRelation.MASTER )
				{
					m_lastTargetSlaveID = m_lastTargetMasterID;
					m_lastTargetMasterID = newTarget;
					newTargetSet = true;
				}
				else
				{
					m_lastTargetMasterID = emptyID;
				}
			}
		}
		else if( EntityID.IsDefined( m_lastTargetSlaveID ) && EntityID.IsDefined( m_lastTargetMasterID ) )
		{
			if( newTarget != m_lastTargetMasterID )
			{
				if( GetNetworkRelation( newTarget, m_lastTargetMasterID ) == ENetworkRelation.SLAVE )
				{
					m_lastTargetSlaveID = newTarget;
					newTargetSet = true;
				}
				else if( GetNetworkRelation( newTarget, m_lastTargetMasterID ) == ENetworkRelation.MASTER )
				{
					m_lastTargetSlaveID = m_lastTargetMasterID;
					m_lastTargetMasterID = newTarget;
					newTargetSet = true;
				}
				else
				{
					m_lastTargetMasterID = emptyID;
				}
			}
			if( newTarget != m_lastTargetSlaveID )
			{
				if( GetNetworkRelation( newTarget, m_lastTargetSlaveID ) == ENetworkRelation.MASTER )
				{
					m_lastTargetMasterID = newTarget;
					newTargetSet = true;
				}
				else if( GetNetworkRelation( newTarget, m_lastTargetSlaveID ) == ENetworkRelation.SLAVE )
				{
					m_lastTargetMasterID = m_lastTargetSlaveID;
					m_lastTargetSlaveID = newTarget;
					newTargetSet = true;
				}
				else
				{
					m_lastTargetSlaveID = emptyID;
				}
			}
		}
		if( !( EntityID.IsDefined( m_lastTargetSlaveID ) ) && !( EntityID.IsDefined( m_lastTargetMasterID ) ) )
		{
			if( IsSlaveInNetwork( newTarget ) )
			{
				m_lastTargetSlaveID = newTarget;
				newTargetSet = true;
			}
			if( IsMasterInNetwork( newTarget ) )
			{
				m_lastTargetMasterID = newTarget;
				newTargetSet = true;
			}
		}
		if( newTargetSet )
		{
			if( AllowSimultanousPinging() )
			{
				return newTargetSet;
			}
			RemoveAllActivePings();
		}
		return newTargetSet;
	}

	private function GetNetworkRelation( sourceID : EntityID, targetID : EntityID ) : ENetworkRelation
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( ( m_networkLinks[ i ].slaveID == sourceID ) && ( m_networkLinks[ i ].masterID == targetID ) )
			{
				return ENetworkRelation.SLAVE;
			}
			else if( ( m_networkLinks[ i ].masterID == sourceID ) && ( m_networkLinks[ i ].slaveID == targetID ) )
			{
				return ENetworkRelation.MASTER;
			}
		}
		return ENetworkRelation.NONE;
	}

	private function EvaluateLastSlaveTarget( masterID : EntityID )
	{
		var i : Int32;
		var currSlaveID : EntityID;
		var currMasterID : EntityID;
		var emptyID : EntityID;
		if( !( EntityID.IsDefined( masterID ) ) || ( masterID == emptyID ) )
		{
			return;
		}
		if( m_networkLinks.Size() == 0 )
		{
			return;
		}
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			currMasterID = m_networkLinks[ i ].masterID;
			currSlaveID = m_networkLinks[ i ].slaveID;
			if( ( masterID == currMasterID ) && ( currSlaveID == m_lastTargetSlaveID ) )
			{
				return;
			}
		}
		m_lastTargetSlaveID = emptyID;
	}

	private function EvaluateLastMasterTarget( slaveID : EntityID )
	{
		var i : Int32;
		var currSlaveID : EntityID;
		var currMasterID : EntityID;
		var emptyID : EntityID;
		if( !( EntityID.IsDefined( slaveID ) ) || ( slaveID == emptyID ) )
		{
			return;
		}
		if( m_networkLinks.Size() == 0 )
		{
			return;
		}
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			currMasterID = m_networkLinks[ i ].masterID;
			currSlaveID = m_networkLinks[ i ].slaveID;
			if( ( slaveID == currSlaveID ) && ( currMasterID == m_lastTargetMasterID ) )
			{
				return;
			}
		}
		m_lastTargetMasterID = emptyID;
	}

	private function IsMaster( targetEntityID : EntityID ) : Bool
	{
		var masterID : EntityID;
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			masterID = m_networkLinks[ i ].masterID;
			if( masterID == targetEntityID )
			{
				return true;
			}
		}
		return false;
	}

	private function IsLastSlaveTarget( entityID : EntityID ) : Bool
	{
		return entityID == m_lastTargetSlaveID;
	}

	private function IsLastMasterTarget( entityID : EntityID ) : Bool
	{
		return entityID == m_lastTargetMasterID;
	}

	private function IsCurrentTarget( entityID : EntityID ) : Bool
	{
		return entityID == GetCurrentTargetID();
	}

	private function IsCurrentTargetValid() : Bool
	{
		var currentTarget : weak< GameObject >;
		currentTarget = GetCurrentTarget();
		return ( currentTarget != NULL ) && currentTarget.CanOverrideNetworkContext();
	}

	private function IsCurrentTargetValidInNetwork() : Bool
	{
		var emptyID : EntityID;
		var currentTargetID : EntityID;
		currentTargetID = GetCurrentTargetID();
		return ( EntityID.IsDefined( currentTargetID ) && ( currentTargetID != emptyID ) ) && IsInNetwork( currentTargetID );
	}

	private function IsInNetwork( id : EntityID ) : Bool
	{
		var slaveEntityID : EntityID;
		var masterEntityID : EntityID;
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			slaveEntityID = m_networkLinks[ i ].slaveID;
			masterEntityID = m_networkLinks[ i ].masterID;
			if( ( slaveEntityID == id ) || ( masterEntityID == id ) )
			{
				return true;
			}
		}
		return false;
	}

	private function IsSlaveInNetwork( id : EntityID ) : Bool
	{
		var slaveEntityID : EntityID;
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			slaveEntityID = m_networkLinks[ i ].slaveID;
			if( slaveEntityID == id )
			{
				return true;
			}
		}
		return false;
	}

	private function GetAllSlavesOfMaster( masterID : EntityID ) : array< EntityID >
	{
		var slaves : array< EntityID >;
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( m_networkLinks[ i ].masterID == masterID )
			{
				if( EntityID.IsDefined( m_networkLinks[ i ].slaveID ) )
				{
					slaves.PushBack( m_networkLinks[ i ].slaveID );
				}
			}
		}
		return slaves;
	}

	private function IsMasterInNetwork( id : EntityID ) : Bool
	{
		var masterEntityID : EntityID;
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			masterEntityID = m_networkLinks[ i ].masterID;
			if( masterEntityID == id )
			{
				return true;
			}
		}
		return false;
	}

	private function GetAllMastersOfSlave( slaveID : EntityID ) : array< EntityID >
	{
		var masters : array< EntityID >;
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( m_networkLinks[ i ].slaveID == slaveID )
			{
				if( EntityID.IsDefined( m_networkLinks[ i ].masterID ) )
				{
					masters.PushBack( m_networkLinks[ i ].masterID );
				}
			}
		}
		return masters;
	}

	private function GetCurrentTargetID() : EntityID
	{
		return GetHudManager().GetCurrentTargetID();
	}

	private function GetCurrentTarget() : weak< GameObject >
	{
		var target : weak< GameObject >;
		var entityID : EntityID;
		entityID = GetCurrentTargetID();
		if( EntityID.IsDefined( entityID ) )
		{
			target = ( ( GameObject )( GameInstance.FindEntityByID( GetGameInstance(), entityID ) ) );
		}
		return target;
	}

	private const function GetObjectFromID( entityID : EntityID ) : weak< GameObject >
	{
		var target : weak< GameObject >;
		if( EntityID.IsDefined( entityID ) )
		{
			target = ( ( GameObject )( GameInstance.FindEntityByID( GetGameInstance(), entityID ) ) );
		}
		return target;
	}

	private function IsTagged( id : EntityID ) : Bool
	{
		var target : weak< GameObject >;
		target = GetObjectFromID( id );
		if( target )
		{
			return GameInstance.GetVisionModeSystem( GetGameInstance() ).GetScanningController().IsTagged( target );
		}
		return false;
	}

	private function OnRegisterPingLinkRequest( request : RegisterPingNetworkLinkRequest )
	{
		var linkData : SNetworkLinkData;
		var ping : PingCachedData;
		var i : Int32;
		for( i = 0; i < request.linksData.Size(); i += 1 )
		{
			if( !( IsFreeLinkLimitReached( request.linksData[ i ] ) ) )
			{
				if( IsPingLinksLimitReached() )
				{
					KillSingleOldestFreeLink();
				}
				linkData = request.linksData[ i ];
				ping = GetActivePing( linkData.masterID );
				if( ( ping != NULL ) && ( linkData.lifetime <= 0.0 ) )
				{
					linkData.lifetime = ping.GetLifetimeValue();
				}
				RegisterNetworkLink( linkData );
			}
		}
		UpdateNetworkVisualisation();
	}

	private function KillSingleOldestFreeLink()
	{
		var i : Int32;
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( m_networkLinks[ i ].linkType == ELinkType.FREE )
			{
				KillNetworkBeam( i, false );
				return;
			}
		}
	}

	private function KillSingleOldestFreeLinkWitoutRevealPing()
	{
		var i : Int32;
		var ping : PingCachedData;
		ping = GetLastActivePingWithRevealNetwork();
		if( ping != NULL )
		{
			for( i = 0; i < m_networkLinks.Size(); i += 1 )
			{
				if( m_networkLinks[ i ].linkType == ELinkType.FREE && ( m_networkLinks[ i ].masterID != ping.m_sourceID ) )
				{
					KillNetworkBeam( i, false );
					return;
				}
			}
		}
		else
		{
			KillSingleOldestFreeLink();
		}
	}

	private const function IsFreeLinkLimitReached( const linkData : ref< SNetworkLinkData > ) : Bool
	{
		var sourceLinkCount : Int32;
		var targetLinkCount : Int32;
		var i : Int32;
		if( ( !( linkData.isActive ) || !( EntityID.IsDefined( linkData.slaveID ) ) ) || !( EntityID.IsDefined( linkData.masterID ) ) )
		{
			return false;
		}
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( m_networkLinks[ i ].slaveID == linkData.slaveID )
			{
				if( m_networkLinks[ i ].linkType == ELinkType.FREE )
				{
					targetLinkCount += 1;
				}
			}
			if( m_networkLinks[ i ].masterID == linkData.masterID )
			{
				if( m_networkLinks[ i ].linkType == ELinkType.FREE )
				{
					sourceLinkCount += 1;
				}
			}
			if( ( sourceLinkCount > GetMaximumNumberOfFreeLinksPerTarget() ) || ( targetLinkCount > GetMaximumNumberOfFreeLinksPerTarget() ) )
			{
				return true;
			}
		}
		return false;
	}

	private function OnStartPingingNetworkRequest( request : StartPingingNetworkRequest )
	{
		if( IsActivePingsLimitReached() )
		{
			return;
		}
		if( request.pingType == EPingType.DIRECT )
		{
			if( AllowSimultanousPinging() )
			{
				RemoveActivePingBySourceAndType( request.source.GetEntityID(), EPingType.SPACE );
				if( HasActivePing( request.source.GetEntityID() ) )
				{
					return;
				}
			}
			else
			{
				RemoveAllActivePings();
			}
		}
		if( request.duration <= 0.0 )
		{
			return;
		}
		if( request.pingType == EPingType.SPACE )
		{
			if( HasActivePing( request.source.GetEntityID() ) )
			{
				return;
			}
		}
		AddActivePing( request.source, request.pingType, request.duration, request.fakeLinkType, request.revealNetworkAtEnd, request.fxResource, request.virtualNetworkShapeID );
	}

	private function OnStopingingNetworkRequest( request : StopPingingNetworkRequest )
	{
		var invalidID : DelayID;
		if( request.pingData != NULL )
		{
			request.pingData.m_delayID = invalidID;
			if( request.pingData.m_pingType == EPingType.SPACE && request.pingData.m_revealNetwork )
			{
				SendRevealNetworkEvent( request.source.GetEntityID() );
			}
			if( request.pingData.m_pingType == EPingType.DIRECT && request.pingData.m_revealNetwork )
			{
				SendRevealNetworkGridRequest( request.source.GetEntityID() );
			}
		}
		if( !( HasAnyActivePingWithRevealNetwork() ) )
		{
			RemoveAllActiveFakePings();
		}
		else if( request.pingData.m_revealNetwork )
		{
			RemoveActivePingBySource( request.source.GetEntityID() );
		}
	}

	private function AddActivePing( source : GameObject, pingType : EPingType, duration : Float, linkType : ELinkType, revealNetworkAtEnd : Bool, fxResource : FxResource, virtualNetworkShapeID : TweakDBID )
	{
		var pingData : PingCachedData;
		var pingNetworkEffect : EffectInstance;
		var stopPingRequest : StopPingingNetworkRequest;
		if( source == NULL )
		{
			return;
		}
		if( !( HasActivePing( source.GetEntityID() ) ) )
		{
			pingData = new PingCachedData;
			m_networkRevealTargetsLastSession.Clear();
			pingNetworkEffect = GameInstance.GetGameEffectSystem( GetGameInstance() ).CreateEffectStatic( 'pingNetworkEffect', 'ping_netwrok', source );
			EffectData.SetFloat( pingNetworkEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.radius, GetPingRange() );
			EffectData.SetFloat( pingNetworkEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.duration, duration );
			EffectData.SetVector( pingNetworkEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, source.GetWorldPosition() );
			pingData.Initialize( source.GetEntityID(), duration, GetAmmountOfPingDurationIntervals(), pingType, pingNetworkEffect, revealNetworkAtEnd, fxResource, source.GetNetworkBeamEndpoint(), virtualNetworkShapeID );
			if( duration > 0.0 )
			{
				stopPingRequest = new StopPingingNetworkRequest;
				stopPingRequest.source = source;
				pingData.m_delayID = GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), stopPingRequest, duration + 0.1, false );
				stopPingRequest.pingData = pingData;
			}
			m_activePings.PushBack( pingData );
			m_lastPingSourceID = source.GetEntityID();
			if( ShouldPulsRealObject() )
			{
				pingNetworkEffect.Run();
			}
			if( ShouldUsePulseOnPing() && pingData.m_revealNetwork )
			{
				GameInstance.GetVisionModeSystem( GetGameInstance() ).GetScanningController().PulseScan( GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject(), GetPulseRange(), , source.GetWorldPosition() );
			}
			CreateVirtualNetwork( pingData );
		}
	}

	private function CreateVirtualNetwork( sourcePing : PingCachedData )
	{
		var linkData : SNetworkLinkData;
		var sourceObject : weak< GameObject >;
		var i : Int32;
		var k : Int32;
		var lastPos : Vector4;
		var sphereCentre : Vector4;
		var localPing : PingCachedData;
		var paths : array< weak< VirtualNetworkPath_Record > >;
		var points : array< Vector3 >;
		var currentPoint : Vector4;
		var scale : Float;
		var playerPos : Vector4;
		var networkRotation : Quaternion;
		var drawingStarted : Bool;
		var segmentMarker : Vector4;
		var registerDelay : Float;
		var virtualNetworkRecord : weak< VirtualNetwork_Record >;
		if( !( IsVirtualNetworkWithinDistanceLimit( sourcePing ) ) )
		{
			return;
		}
		sourceObject = GetObjectFromID( sourcePing.m_sourceID );
		if( sourceObject == NULL )
		{
			return;
		}
		playerPos = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerMainGameObject().GetWorldPosition();
		sphereCentre = sourceObject.GetNetworkBeamEndpoint();
		if( sourcePing.m_virtualNetworkShape != NULL )
		{
			virtualNetworkRecord = sourcePing.m_virtualNetworkShape;
		}
		else
		{
			virtualNetworkRecord = GetVirtualNetworkRecord();
		}
		virtualNetworkRecord.Paths( paths );
		currentPoint = sphereCentre - playerPos;
		currentPoint.Z = 0.0;
		currentPoint = Vector4.Normalize( currentPoint );
		networkRotation = Quaternion.BuildFromDirectionVector( currentPoint, Vector4( 0.0, 0.0, 1.0, 0.0 ) );
		linkData.masterID = sourcePing.m_sourceID;
		linkData.drawLink = true;
		linkData.linkType = ELinkType.FREE;
		linkData.isDynamic = false;
		linkData.revealMaster = false;
		linkData.revealSlave = false;
		linkData.fxResource = sourcePing.m_linkFXresource;
		linkData.isPing = true;
		linkData.permanent = true;
		sourcePing.m_hasActiveVirtualNetwork = true;
		localPing = new PingCachedData;
		localPing.m_timeout = sourcePing.m_timeout;
		localPing.m_ammountOfIntervals = GetMaxNumberOfSegmentsForVirtualNetwork( virtualNetworkRecord );
		segmentMarker = GetVirtualNetworkSegmentMarker( virtualNetworkRecord );
		for( i = 0; i < paths.Size(); i += 1 )
		{
			points = paths[ i ].Points();
			drawingStarted = false;
			if( points.Size() < 2 )
			{
				continue;
			}
			localPing.m_currentInterval = points.Size() - 1;
			scale = virtualNetworkRecord.Scale();
			for( k = 0; k < points.Size(); k += 1 )
			{
				currentPoint = Vector4( points[ k ].X, points[ k ].Y, points[ k ].Z, 0.0 );
				if( currentPoint == segmentMarker )
				{
					if( drawingStarted )
					{
						localPing.GetLifetimeValue();
					}
					continue;
				}
				currentPoint *= scale;
				currentPoint = networkRotation * currentPoint;
				currentPoint += sphereCentre;
				if( virtualNetworkRecord.OffsetMultiplier() > 1.0 )
				{
					currentPoint *= virtualNetworkRecord.OffsetMultiplier();
				}
				if( !( drawingStarted ) )
				{
					drawingStarted = true;
					lastPos = currentPoint;
					continue;
				}
				else
				{
					linkData.slavePos = currentPoint;
					linkData.masterPos = lastPos;
					linkData.lifetime = localPing.GetLifetimeValue();
					if( lastPos == sphereCentre )
					{
						linkData.isDynamic = sourceObject.IsNetworkLinkDynamic();
					}
					if( GetSpacePingAppearModifier() > 0.0 )
					{
						registerDelay = ( 0.0 + ( localPing.m_timeout - linkData.lifetime ) ) / GetSpacePingAppearModifier();
						RegisterNetworkLinkWithDelay( linkData, registerDelay );
					}
					else
					{
						RegisterNetworkLink( linkData );
					}
					linkData.isDynamic = false;
					lastPos = linkData.slavePos;
				}
			}
		}
		if( IsPingLinksLimitReached() )
		{
			KillSingleOldestFreeLink();
		}
		UpdateNetworkVisualisation();
	}

	private function IsVirtualNetworkWithinDistanceLimit( sourcePing : PingCachedData ) : Bool
	{
		var i : Int32;
		var distance : Float;
		var minDistance : Float;
		minDistance = GetVirtualNetworkRecord().MinDistanceToOther();
		for( i = 0; i < m_activePings.Size(); i += 1 )
		{
			if( ( sourcePing == m_activePings[ i ] ) || !( m_activePings[ i ].m_hasActiveVirtualNetwork ) )
			{
				continue;
			}
			distance = Vector4.Distance( sourcePing.m_sourcePosition, m_activePings[ i ].m_sourcePosition );
			if( distance < minDistance )
			{
				return false;
			}
		}
		return true;
	}

	private function CreateVirtualLinksForPing1( ping : PingCachedData )
	{
		var linkData : SNetworkLinkData;
		var sourceObject : weak< GameObject >;
		var i : Int32;
		var k : Int32;
		var lastPos : Vector4;
		var sphereCentre : Vector4;
		var currentRadius : Float;
		var radius : Float;
		var direction : Vector4;
		var zeroVec : Vector4;
		var allVertices : array< Vector4 >;
		var lastRadius : Float;
		sourceObject = GetObjectFromID( ping.m_sourceID );
		if( sourceObject == NULL )
		{
			return;
		}
		sphereCentre = sourceObject.GetNetworkBeamEndpoint();
		linkData.masterID = ping.m_sourceID;
		linkData.drawLink = true;
		linkData.linkType = ELinkType.FREE;
		linkData.isDynamic = false;
		linkData.revealMaster = false;
		linkData.revealSlave = false;
		linkData.fxResource = ping.m_linkFXresource;
		linkData.isPing = true;
		linkData.permanent = true;
		radius = GetVirtualLinksSphereRadius() / ( ( Float )( GetVirtualLinkDepth() ) );
		for( i = 0; i < GetNumberOfVirtualLinksPerObject(); i += 1 )
		{
			direction = GetRandomPointOnSphere( zeroVec, 1.0, i );
			for( k = 0; k < GetVirtualLinkDepth(); k += 1 )
			{
				currentRadius += ( radius * RandRangeF( 0.89999998, 1.10000002 ) );
				linkData.slavePos = GetRandomPoint( direction, currentRadius, GetVirtualLinkAngleTollerance() );
				linkData.slavePos = linkData.slavePos + sphereCentre;
				if( k == 0 )
				{
					linkData.masterPos = sphereCentre;
					linkData.isDynamic = sourceObject.IsNetworkLinkDynamic();
				}
				else
				{
					linkData.masterPos = lastPos;
				}
				lastPos = linkData.slavePos;
				linkData.lifetime = ping.GetLifetimeValue();
				allVertices.PushBack( lastPos );
				if( i > 0 )
				{
					CreateForksForVirtualLink1( linkData, ping, sphereCentre, lastRadius, direction, allVertices );
				}
				RegisterNetworkLink( linkData );
				lastRadius = currentRadius;
			}
			currentRadius = 0.0;
			lastRadius = 0.0;
		}
		if( IsPingLinksLimitReached() )
		{
			KillSingleOldestFreeLinkWitoutRevealPing();
		}
		UpdateNetworkVisualisation();
	}

	private function CreateVirtualLinksForPing( ping : PingCachedData )
	{
		var linkData : SNetworkLinkData;
		var sourceObject : weak< GameObject >;
		var i : Int32;
		var k : Int32;
		var lastPos : Vector4;
		var sphereCentre : Vector4;
		var currentRadius : Float;
		var radius : Float;
		sourceObject = GetObjectFromID( ping.m_sourceID );
		if( sourceObject == NULL )
		{
			return;
		}
		sphereCentre = sourceObject.GetNetworkBeamEndpoint();
		linkData.masterID = ping.m_sourceID;
		linkData.drawLink = true;
		linkData.linkType = ELinkType.FREE;
		linkData.isDynamic = sourceObject.IsNetworkLinkDynamic();
		linkData.revealMaster = false;
		linkData.revealSlave = false;
		linkData.fxResource = ping.m_linkFXresource;
		linkData.isPing = true;
		linkData.permanent = true;
		radius = GetVirtualLinksSphereRadius() / ( ( Float )( GetVirtualLinkDepth() ) );
		for( i = 0; i < GetNumberOfVirtualLinksPerObject(); i += 1 )
		{
			for( k = 0; k < GetVirtualLinkDepth(); k += 1 )
			{
				currentRadius += ( radius * RandRangeF( 0.89999998, 1.10000002 ) );
				linkData.slavePos = GetRandomPointOnSphere( sphereCentre, currentRadius, i );
				if( k == 0 )
				{
					linkData.masterPos = sphereCentre;
				}
				else
				{
					linkData.masterPos = lastPos;
				}
				lastPos = linkData.slavePos;
				linkData.lifetime = ping.GetLifetimeValue();
				if( IsPingLinksLimitReached() )
				{
					KillSingleOldestFreeLinkWitoutRevealPing();
				}
				RegisterNetworkLink( linkData );
			}
			currentRadius = 0.0;
		}
		UpdateNetworkVisualisation();
	}

	private function CreateForksForVirtualLink1( linkData : SNetworkLinkData, ping : PingCachedData, sphereCentre : Vector4, radius : Float, direction : Vector4, const vertices : ref< array< Vector4 > > )
	{
		var numberOfForks : Int32;
		var baseRadius : Float;
		var linkDirection : Vector4;
		var foundVertice : Vector4;
		var currentRadius : Float;
		var i : Int32;
		numberOfForks = RandRange( 0, GetMaxAmountOfVirtualLinkForks() );
		baseRadius = GetVirtualLinksSphereRadius() / ( ( Float )( GetVirtualLinkDepth() ) );
		currentRadius = radius;
		linkDirection = Vector4.Normalize( linkData.slavePos - linkData.masterPos );
		for( i = 0; i < numberOfForks; i += 1 )
		{
			currentRadius += ( baseRadius * RandRangeF( 0.89999998, 1.20000005 ) );
			foundVertice = FindBestMatchingVertice( linkData.slavePos, linkDirection, Rad2Deg( GetVirtualLinkForkAngleTollerance() ), GetVirtualLinksSphereRadius() * RandRangeF( 1.0, 1.20000005 ), foundVertice, vertices );
			if( Vector4.IsZero( foundVertice ) )
			{
			}
			else
			{
				linkData.slavePos = foundVertice;
			}
			RegisterNetworkLink( linkData );
			currentRadius = radius;
		}
	}

	private function CreateForksForVirtualLink1( linkData : SNetworkLinkData, ping : PingCachedData, sphereCentre : Vector4, radius : Float, direction : Vector4, connectionPoint : Vector4 )
	{
		var numberOfForks : Int32;
		var baseRadius : Float;
		var forkToConnect : Int32;
		var i : Int32;
		numberOfForks = RandRange( 0, GetMaxAmountOfVirtualLinkForks() );
		if( numberOfForks == 0 )
		{
			numberOfForks = 1;
		}
		if( numberOfForks > 0 )
		{
			forkToConnect = RandRange( 0, numberOfForks );
		}
		baseRadius = GetVirtualLinksSphereRadius() / ( ( Float )( GetVirtualLinkDepth() ) );
		for( i = 0; i < numberOfForks; i += 1 )
		{
			if( !( Vector4.IsZero( connectionPoint ) ) && ( forkToConnect == i ) )
			{
				linkData.slavePos = connectionPoint;
			}
			else
			{
				radius = baseRadius * RandRangeF( 0.80000001, 1.20000005 );
				linkData.slavePos = sphereCentre + GetRandomPoint( direction, radius, GetVirtualLinkForkAngleTollerance() );
			}
			RegisterNetworkLink( linkData );
		}
	}

	private function CreateForksForVirtualLink( linkData : SNetworkLinkData, ping : PingCachedData, sphereCentre : Vector4, radius : Float, slice : Int32 )
	{
		var currentRadius : Float;
		var numberOfForks : Int32;
		var baseRadius : Float;
		var i : Int32;
		numberOfForks = RandRange( 0, 4 );
		baseRadius = GetVirtualLinksSphereRadius() / ( ( Float )( GetVirtualLinkDepth() ) );
		currentRadius += ( radius + ( RandF() * baseRadius ) );
		for( i = 0; i < numberOfForks; i += 1 )
		{
			linkData.slavePos = GetRandomPointOnSphere( sphereCentre, currentRadius, slice );
			RegisterNetworkLink( linkData );
		}
	}

	private function RemoveActivePing( index : Int32 )
	{
		var invalidID : DelayID;
		m_activePings[ index ].m_pingNetworkEffect.Terminate();
		if( m_activePings[ index ].m_delayID != invalidID )
		{
			GameInstance.GetDelaySystem( GetGameInstance() ).CancelDelay( m_activePings[ index ].m_delayID );
		}
		m_activePings[ index ] = NULL;
		m_activePings.Erase( index );
	}

	private function RemoveActivePingBySource( sourceID : EntityID )
	{
		var i : Int32;
		var removeLinks : Bool;
		var isDirect : Bool;
		var linkType : ELinkType;
		for( i = m_activePings.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_activePings[ i ].m_sourceID == sourceID )
			{
				isDirect = m_activePings[ i ].m_pingType == EPingType.DIRECT;
				linkType = m_activePings[ i ].m_linkType;
				RemoveActivePing( i );
				removeLinks = true;
				break;
			}
		}
		if( !( HasAnyActivePing() ) )
		{
			RemoveAllPingLinksByType( linkType );
		}
		else if( removeLinks )
		{
			for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
			{
				if( isDirect )
				{
					if( ( m_networkLinks[ i ].isPing && m_networkLinks[ i ].linkType == linkType ) && ( ( m_networkLinks[ i ].masterID == sourceID ) || ( m_networkLinks[ i ].slaveID == sourceID ) ) )
					{
						KillNetworkBeam( i, true );
						m_networkLinks.Erase( i );
					}
				}
				else if( ( m_networkLinks[ i ].masterID == sourceID ) && ( m_networkLinks[ i ].isPing && m_networkLinks[ i ].linkType == linkType ) )
				{
					KillNetworkBeam( i, true );
					m_networkLinks.Erase( i );
				}
			}
		}
	}

	private function RemoveAllActivePings()
	{
		var i : Int32;
		var removeLinks : Bool;
		removeLinks = true;
		for( i = m_activePings.Size() - 1; i >= 0; i -= 1 )
		{
			RemoveActivePing( i );
		}
		if( removeLinks )
		{
			RemoveAllPingLinks();
		}
	}

	private function RemoveAllActiveFakePings()
	{
		var i : Int32;
		var resources : array< FxResource >;
		var lastResource : FxResource;
		for( i = m_activePings.Size() - 1; i >= 0; i -= 1 )
		{
			if( !( m_activePings[ i ].m_revealNetwork ) )
			{
				if( m_activePings[ i ].m_linkFXresource != lastResource )
				{
					resources.PushBack( m_activePings[ i ].m_linkFXresource );
					lastResource = m_activePings[ i ].m_linkFXresource;
				}
				RemoveActivePing( i );
			}
		}
		for( i = 0; i < resources.Size(); i += 1 )
		{
			RemovePingLinksByFxResource( resources[ i ], true );
		}
	}

	private function RemoveAllPingLinks()
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_networkLinks[ i ].isPing )
			{
				KillNetworkBeam( i, false );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemoveAllPingLinksByType( linkType : ELinkType )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_networkLinks[ i ].isPing && m_networkLinks[ i ].linkType == linkType )
			{
				KillNetworkBeam( i, false );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemovePingLinksBySourceAndType( linkType : ELinkType, sourceID : EntityID )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( ( m_networkLinks[ i ].masterID == sourceID ) && m_networkLinks[ i ].isPing ) && m_networkLinks[ i ].linkType == linkType )
			{
				KillNetworkBeam( i, false );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemovePingLinksBySource( sourceID : EntityID, intant : Bool )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( m_networkLinks[ i ].masterID == sourceID ) && m_networkLinks[ i ].isPing )
			{
				KillNetworkBeam( i, intant );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemovePingLinksBySourceAndFxResource( sourceID : EntityID, fxResource : FxResource, intant : Bool )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( ( ( m_networkLinks[ i ].masterID == sourceID ) || ( m_networkLinks[ i ].slaveID == sourceID ) ) && m_networkLinks[ i ].isPing ) && m_networkLinks[ i ].fxResource == fxResource )
			{
				KillNetworkBeam( i, intant );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemovePingLinksByFxResource( fxResource : FxResource, intant : Bool )
	{
		var i : Int32;
		for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_networkLinks[ i ].isPing && m_networkLinks[ i ].fxResource == fxResource )
			{
				KillNetworkBeam( i, intant );
				m_networkLinks.Erase( i );
			}
		}
	}

	private function RemoveActivePingBySourceAndType( sourceID : EntityID, pingType : EPingType )
	{
		var i : Int32;
		var removeLinks : Bool;
		var isDirect : Bool;
		var linkType : ELinkType;
		for( i = m_activePings.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( m_activePings[ i ].m_sourceID == sourceID ) && m_activePings[ i ].m_pingType == pingType )
			{
				isDirect = m_activePings[ i ].m_pingType == EPingType.DIRECT;
				linkType = m_activePings[ i ].m_linkType;
				RemoveActivePing( i );
				removeLinks = true;
				break;
			}
		}
		if( !( HasAnyActivePing() ) )
		{
			RemoveAllPingLinksByType( linkType );
		}
		else if( removeLinks )
		{
			for( i = m_networkLinks.Size() - 1; i >= 0; i -= 1 )
			{
				if( isDirect )
				{
					if( ( m_networkLinks[ i ].isPing && m_networkLinks[ i ].linkType == linkType ) && ( ( m_networkLinks[ i ].masterID == sourceID ) || ( m_networkLinks[ i ].slaveID == sourceID ) ) )
					{
						KillNetworkBeam( i, false );
						m_networkLinks.Erase( i );
					}
				}
				else if( ( m_networkLinks[ i ].masterID == sourceID ) && ( m_networkLinks[ i ].isPing && m_networkLinks[ i ].linkType == linkType ) )
				{
					KillNetworkBeam( i, false );
					m_networkLinks.Erase( i );
				}
			}
		}
	}

	public const function HasActivePing( sourceID : EntityID ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_activePings.Size(); i += 1 )
		{
			if( m_activePings[ i ].m_sourceID == sourceID )
			{
				return true;
			}
		}
		return false;
	}

	private function GetActivePing( sourceID : EntityID ) : PingCachedData
	{
		var i : Int32;
		for( i = 0; i < m_activePings.Size(); i += 1 )
		{
			if( m_activePings[ i ].m_sourceID == sourceID )
			{
				return m_activePings[ i ];
			}
		}
		return NULL;
	}

	public const function GetInitialPingSourceID() : EntityID
	{
		var entityID : EntityID;
		if( m_activePings[ 0 ] != NULL )
		{
			entityID = m_activePings[ 0 ].m_sourceID;
		}
		return entityID;
	}

	public const function GetInitialPingSource() : weak< GameObject >
	{
		var object : weak< GameObject >;
		if( m_activePings[ 0 ] != NULL )
		{
			object = GetObjectFromID( m_activePings[ 0 ].m_sourceID );
		}
		return object;
	}

	public const function GetPingType( sourceID : EntityID ) : EPingType
	{
		var type : EPingType;
		var i : Int32;
		for( i = 0; i < m_activePings.Size(); i += 1 )
		{
			if( m_activePings[ i ].m_sourceID == sourceID )
			{
				type = m_activePings[ i ].m_pingType;
			}
		}
		return type;
	}

	private function HasAnyActivePing() : Bool
	{
		return m_activePings.Size() > 0;
	}

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

	public const function HasActivePingWithRevealNetwork( sourceID : EntityID ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_activePings.Size(); i += 1 )
		{
			if( m_activePings[ i ].m_revealNetwork && ( m_activePings[ i ].m_sourceID == sourceID ) )
			{
				return true;
			}
		}
		return false;
	}

	public const function GetLastActivePingWithRevealNetwork() : PingCachedData
	{
		var i : Int32;
		var ping : PingCachedData;
		for( i = 0; i < m_activePings.Size(); i += 1 )
		{
			if( m_activePings[ i ].m_revealNetwork )
			{
				ping = m_activePings[ i ];
			}
		}
		return ping;
	}

	private function IncreasePingLinbksCounter()
	{
		m_pingLinksCounter += 1;
	}

	private function DecreasePingLinbksCounter()
	{
		m_pingLinksCounter -= 1;
	}

	public const function IsPingLinksLimitReached() : Bool
	{
		return m_pingLinksCounter >= GetMaxFreePingLinks();
	}

	private const function IsActivePingsLimitReached() : Bool
	{
		return m_activePings.Size() > GetMaximumNumberOfActivePings();
	}

	private function OnAddPingedSquadRequest( request : AddPingedSquadRequest )
	{
		AddPingedSquad( request.squadName );
	}

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

	private function OnRemovePingedSquadRequest( request : RemovePingedSquadRequest )
	{
		RemovePingedSquad( request.squadName );
	}

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

	private function OnClearPingedSquadRequest( request : ClearPingedSquadRequest )
	{
		ClearPingedSquads();
	}

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

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

	private function HasDiffrentParentsThanTargets( sourceID : EntityID, const targets : ref< array< EntityID > > ) : Bool
	{
		var i : Int32;
		var k : Int32;
		var l : Int32;
		var sourceParents : array< LazyDevice >;
		var targetParents : array< LazyDevice >;
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetLazyParents( sourceID, sourceParents );
		if( sourceParents.Size() == 0 )
		{
			return false;
		}
		for( i = 0; i < targets.Size(); i += 1 )
		{
			GameInstance.GetDeviceSystem( GetGameInstance() ).GetLazyParents( targets[ i ], targetParents );
			for( k = 0; k < targetParents.Size(); k += 1 )
			{
				for( l = 0; l < sourceParents.Size(); l += 1 )
				{
					if( sourceParents[ l ].GetPersistentID() == targetParents[ k ].GetPersistentID() )
					{
						return false;
					}
				}
			}
		}
		return true;
	}

	private function HasDiffrentChildrenThanTargets( sourceID : EntityID, const targets : ref< array< EntityID > > ) : Bool
	{
		var i : Int32;
		var k : Int32;
		var l : Int32;
		var sourceChildren : array< LazyDevice >;
		var targetChildren : array< LazyDevice >;
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetLazyChildren( sourceID, sourceChildren );
		if( sourceChildren.Size() == 0 )
		{
			return false;
		}
		for( i = 0; i < targets.Size(); i += 1 )
		{
			GameInstance.GetDeviceSystem( GetGameInstance() ).GetLazyChildren( targets[ i ], targetChildren );
			for( k = 0; k < targetChildren.Size(); k += 1 )
			{
				for( l = 0; l < sourceChildren.Size(); l += 1 )
				{
					if( sourceChildren[ l ].GetPersistentID() == targetChildren[ k ].GetPersistentID() )
					{
						return false;
					}
				}
			}
		}
		return true;
	}

	private function ResolveNetworkRevealTarget( const linkData : ref< SNetworkLinkData > )
	{
		var ignoreNetworkReveal : Bool;
		var currentTargets : array< EntityID >;
		var hasTargets : Bool;
		currentTargets = m_networkRevealTargets;
		hasTargets = currentTargets.Size() > 0;
		if( HasActivePingWithRevealNetwork( linkData.slaveID ) )
		{
			if( hasTargets )
			{
				ignoreNetworkReveal = m_networkRevealTargetsLastSession.Contains( linkData.masterID ) || ( !( HasDiffrentChildrenThanTargets( linkData.masterID, currentTargets ) ) && !( HasDiffrentParentsThanTargets( linkData.masterID, currentTargets ) ) );
			}
			if( !( ignoreNetworkReveal ) )
			{
				AddNetworkRevealTarget( linkData.masterID );
				ignoreNetworkReveal = false;
			}
		}
		if( HasActivePingWithRevealNetwork( linkData.masterID ) )
		{
			if( hasTargets )
			{
				ignoreNetworkReveal = m_networkRevealTargetsLastSession.Contains( linkData.slaveID ) || ( !( HasDiffrentChildrenThanTargets( linkData.slaveID, currentTargets ) ) && !( HasDiffrentParentsThanTargets( linkData.slaveID, currentTargets ) ) );
			}
			if( !( ignoreNetworkReveal ) )
			{
				AddNetworkRevealTarget( linkData.slaveID );
				ignoreNetworkReveal = false;
			}
		}
	}

	private function AddNetworkRevealTarget( target : EntityID )
	{
		if( !( m_networkRevealTargets.Contains( target ) ) )
		{
			m_networkRevealTargets.PushBack( target );
		}
	}

	private function RemoveNetworkRevealTarget( index : Int32 )
	{
		if( ( index >= 0 ) && ( index < m_networkRevealTargets.Size() ) )
		{
			m_networkRevealTargets.Erase( index );
		}
	}

	private function RevealNetworkOnCachedTarget( const targets : ref< array< EntityID > > )
	{
		var i : Int32;
		var k : Int32;
		if( targets.Size() == 0 )
		{
			return;
		}
		for( i = m_networkRevealTargets.Size() - 1; i >= 0; i -= 1 )
		{
			for( k = 0; k < targets.Size(); k += 1 )
			{
				if( m_networkRevealTargets[ i ] == targets[ k ] )
				{
					if( EntityID.IsDefined( m_networkRevealTargets[ i ] ) && !( HasActivePing( targets[ k ] ) ) )
					{
						SendRevealNetworkEvent( m_networkRevealTargets[ i ] );
					}
					m_networkRevealTargetsLastSession.PushBack( m_networkRevealTargets[ i ] );
					m_networkRevealTargets.Erase( i );
				}
			}
		}
	}

	private function RevealEntireNetworkOnTarget( target : EntityID )
	{
		var i : Int32;
		SendRevealNetworkEvent( target );
		for( i = 0; i < m_networkLinks.Size(); i += 1 )
		{
			if( m_networkLinks[ i ].slaveID == target )
			{
				if( EntityID.IsDefined( m_networkLinks[ i ].masterID ) )
				{
					SendRevealNetworkEvent( m_networkLinks[ i ].masterID );
					break;
				}
			}
			else if( m_networkLinks[ i ].masterID == target )
			{
				if( EntityID.IsDefined( m_networkLinks[ i ].slaveID ) )
				{
					SendRevealNetworkEvent( m_networkLinks[ i ].slaveID );
					break;
				}
			}
		}
	}

	private function OnRevealNetworkRequestRequest( request : RevealNetworkRequestRequest )
	{
		SendRevealNetworkEvent( request.target, request.delay );
	}

	private function SendRevealNetworkEvent( target : EntityID, optional delay : Float )
	{
		var evt : RevealNetworkGridOnPulse;
		var request : RevealNetworkRequestRequest;
		evt = new RevealNetworkGridOnPulse;
		evt.duration = GetPingRevealDuration();
		evt.revealSlave = ShouldRevealSlaveOnPulse();
		evt.revealMaster = ShouldRevealMasterOnPulse();
		if( delay > 0.0 )
		{
			request = new RevealNetworkRequestRequest;
			request.target = target;
			request.delay = 0.0;
			GameInstance.GetDelaySystem( GetGameInstance() ).DelayScriptableSystemRequest( GetClassName(), request, delay, false );
		}
		else
		{
			GameInstance.GetPersistencySystem( GetGameInstance() ).QueueEntityEvent( target, evt );
		}
	}

	private function SendRevealNetworkGridRequest( target : EntityID )
	{
		var evt : RevealNetworkGridNetworkRequest;
		evt = new RevealNetworkGridNetworkRequest;
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueueEntityEvent( target, evt );
	}

	private function IsIdValid( id : EntityID ) : Bool
	{
		var emptyID : EntityID;
		return EntityID.IsDefined( id ) && ( id != emptyID );
	}

	private const function GetVirtualNetworkRecord() : weak< VirtualNetwork_Record >
	{
		var record : VirtualNetwork_Record;
		record = GetPingPresetRecord().VirtualNetwork();
		return record;
	}

	private const function GetVirtualNetworkSegmentMarker( virtualNetworkRecord : weak< VirtualNetwork_Record > ) : Vector4
	{
		var segmentMarker : Vector3;
		segmentMarker = virtualNetworkRecord.SegmentMarker();
		return Vector4( segmentMarker.X, segmentMarker.Y, segmentMarker.Z, 0.0 );
	}

	private function GetMaxNumberOfSegmentsForVirtualNetwork( virtualNetworkRecord : weak< VirtualNetwork_Record > ) : Int32
	{
		var i : Int32;
		var paths : array< weak< VirtualNetworkPath_Record > >;
		var points : array< Vector3 >;
		var segments : Int32;
		var currentSegments : Int32;
		virtualNetworkRecord.Paths( paths );
		for( i = 0; i < paths.Size(); i += 1 )
		{
			points = paths[ i ].Points();
			currentSegments = points.Size() - 1;
			if( i == 0 )
			{
				segments = currentSegments;
			}
			else
			{
				if( currentSegments > segments )
				{
					segments = currentSegments;
				}
			}
		}
		return segments;
	}

	private const function GetPingRange() : Float
	{
		return GetPingPresetRecord().PingRange();
	}

	public const function GetSpacePingDuration() : Float
	{
		return GetPingPresetRecord().SpacePingDuration();
	}

	public const function GetSpacePingAppearModifier() : Float
	{
		return GetPingPresetRecord().SpacePingAppearModifier();
	}

	public const function GetNetworkReavealDuration() : Float
	{
		return GetPingPresetRecord().NetworkRevealDuration();
	}

	public const function ShouldNetworkElementsPersistAfterFocus() : Bool
	{
		return GetPingPresetRecord().ShouldNetworkElementsPersistAfterFocus();
	}

	private const function ShouldForceInstantBeamKill() : Bool
	{
		return GetPingPresetRecord().ForceInstantBeamKill();
	}

	public const function GetRevealMasterAfterLeavingFocusDuration() : Float
	{
		var duration : Float;
		var player : PlayerPuppet;
		player = GetPlayer( GetGameInstance() );
		if( player )
		{
			duration = player.GetPingDuration();
		}
		if( duration <= 0.0 )
		{
			duration = GetPingPresetRecord().RevealMasterAfterLeavingFocusDuration();
		}
		return duration;
	}

	public const function GetRevealLinksAfterLeavingFocusDuration() : Float
	{
		return GetPingPresetRecord().RevealLinksAfterLeavingFocusDuration();
	}

	private const function GetPingRevealDuration() : Float
	{
		return GetPingPresetRecord().NetworkRevealDuration();
	}

	private const function GetAmmountOfPingDurationIntervals() : Int32
	{
		return GetPingPresetRecord().AmmountOfIntervals();
	}

	private const function GetMaxFreePingLinks() : Int32
	{
		return GetPingPresetRecord().MaxFreePingLinks();
	}

	private constexpr static function GetMaxLinksDrawnInTotal() : Int32
	{
		return 23;
	}

	private constexpr static function GetMaxLinksDrawnAtOnce() : Int32
	{
		return 10;
	}

	private constexpr static function GetMaxLinksDeactivatedAtOnce() : Int32
	{
		return 10;
	}

	private constexpr static function GetMaxLinksRegisteredAtOnce() : Int32
	{
		return 10;
	}

	private constexpr static function GetMaximumNumberOfFreeLinksPerTarget() : Int32
	{
		return 20;
	}

	private constexpr static function GetMaximumNumberOfActivePings() : Int32
	{
		return 10;
	}

	private constexpr static function GetNumberOfVirtualLinksPerObject() : Int32
	{
		return 10;
	}

	private constexpr static function GetVirtualLinkDepth() : Int32
	{
		return 6;
	}

	private const function GetVirtualLinkAngleTollerance() : Float
	{
		return Deg2Rad( 45.0 );
	}

	private const function GetVirtualLinkForkAngleTollerance() : Float
	{
		return Deg2Rad( 45.0 );
	}

	private constexpr static function GetMaxAmountOfVirtualLinkForks() : Int32
	{
		return 2;
	}

	private constexpr static function GetVirtualLinksSphereRadius() : Float
	{
		return 25.0;
	}

	private const function ShouldRevealMasterOnPulse() : Bool
	{
		return GetPingPresetRecord().RevealMaster();
	}

	private const function ShouldRevealSlaveOnPulse() : Bool
	{
		return GetPingPresetRecord().RevealSlave();
	}

	public const function SuppressPingIfBackdoorsFound() : Bool
	{
		return GetPingPresetRecord().SuppressPingIfBackdoorsFound();
	}

	public const function AllowSimultanousPinging() : Bool
	{
		return GetPingPresetRecord().AllowSimultanousPinging();
	}

	public const function ShouldRevealNetworkAfterPulse() : Bool
	{
		return GetPingPresetRecord().ShouldRevealNetworkAfterPulse();
	}

	public const function ShouldUsePulseOnPing() : Bool
	{
		return GetPingPresetRecord().UsePulse();
	}

	public const function GetPulseRange() : Float
	{
		return GetPingPresetRecord().PulseRange();
	}

	public const function ShouldPulsRealObject() : Bool
	{
		return GetPingPresetRecord().PulseRealObjects();
	}

	public const function ShouldShowLinksOnMaster() : Bool
	{
		return GetPingPresetRecord().ReavealNetworkOnMaster();
	}

	public const function QuickHacksExposedByDefault() : Bool
	{
		return GetPingPresetRecord().QuickHacksExposedByDefaul();
	}

	public static function QuickHacksExposedByDefault( game : GameInstance ) : Bool
	{
		var networkSystem : NetworkSystem;
		if( GameInstance.IsValid( game ) )
		{
			networkSystem = ( ( NetworkSystem )( GameInstance.GetScriptableSystemsContainer( game ).Get( 'NetworkSystem' ) ) );
			return networkSystem.QuickHacksExposedByDefault();
		}
		return false;
	}

	public const function ShouldShowOnlyTargetQuickHacks() : Bool
	{
		return GetPingPresetRecord().ShowOnlyTargetQuickHacks();
	}

	public static function ShouldShowOnlyTargetQuickHacks( game : GameInstance ) : Bool
	{
		var networkSystem : NetworkSystem;
		if( GameInstance.IsValid( game ) )
		{
			networkSystem = ( ( NetworkSystem )( GameInstance.GetScriptableSystemsContainer( game ).Get( 'NetworkSystem' ) ) );
			return networkSystem.ShouldShowOnlyTargetQuickHacks();
		}
		return false;
	}

	private const function GetPingPresetRecord() : weak< NetworkPingingParameteres_Record >
	{
		return m_networkPresetRecord;
	}

	private function SetupPingPresetRecord()
	{
		if( TDBID.IsValid( m_networkPresetTBDID ) )
		{
			m_networkPresetRecord = TweakDBInterface.GetNetworkPresetBinderParametersRecord( m_networkPresetTBDID ).PingPresetID();
		}
	}

	public const function GetLastPingSourceID() : EntityID
	{
		return m_lastPingSourceID;
	}

	private function FindBestMatchingVertice( point : Vector4, direction : Vector4, angle : Float, radius : Float, excludeVertice : Vector4, const vertices : ref< array< Vector4 > > ) : Vector4
	{
		var i : Int32;
		var foundVertice : Vector4;
		var bestAngle : Float;
		var foundVerticeDirection : Vector4;
		var currentAngle : Float;
		var distance : Float;
		bestAngle = angle;
		for( i = 0; i < vertices.Size(); i += 1 )
		{
			if( vertices[ i ] == excludeVertice )
			{
				continue;
			}
			distance = Vector4.Distance( point, vertices[ i ] );
			if( distance > radius )
			{
				continue;
			}
			foundVerticeDirection = Vector4.Normalize( vertices[ i ] - point );
			currentAngle = Vector4.GetAngleBetween( direction, foundVerticeDirection );
			if( currentAngle > angle )
			{
				continue;
			}
			if( currentAngle < bestAngle )
			{
				bestAngle = currentAngle;
				foundVertice = vertices[ i ];
			}
		}
		return foundVertice;
	}

	private function GetRandomPoint( direction : Vector4, radius : Float, angle : Float ) : Vector4
	{
		var point : Vector4;
		var orientation : EulerAngles;
		var halfAngle : Float;
		var quat : Quaternion;
		var minAngle : Float;
		minAngle = Deg2Rad( 20.0 );
		point = direction;
		if( angle > 0.0 )
		{
			halfAngle = angle * 0.5;
			orientation.Pitch = RandRangeF( -( halfAngle ), halfAngle );
			orientation.Yaw = RandRangeF( -( halfAngle ), halfAngle );
			orientation.Pitch = ( SgnF( orientation.Pitch ) * minAngle ) + orientation.Pitch;
			orientation.Yaw = ( SgnF( orientation.Yaw ) * minAngle ) + orientation.Yaw;
			Quaternion.SetYRot( quat, orientation.Pitch );
			point = quat * point;
			Quaternion.SetZRot( quat, orientation.Yaw );
			point = quat * point;
			point = Vector4.Normalize( point );
		}
		point = point * radius;
		return point;
	}

	private function GetRandomPointOnSphere( sphereCentre : Vector4, radius : Float, slice : Int32 ) : Vector4
	{
		var theta : Float;
		var phi : Float;
		var point : Vector4;
		var minAngleTheta : Float;
		var maxAngleTheta : Float;
		var minAnglePhi : Float;
		var maxAnglePhi : Float;
		var angleIncrementTheta : Float;
		var angleIncrementPhi : Float;
		if( GetNumberOfVirtualLinksPerObject() > 0 )
		{
			angleIncrementPhi = ( 2.0 * Pi() ) / ( ( Float )( GetNumberOfVirtualLinksPerObject() ) );
			angleIncrementTheta = HalfPi() / ( ( Float )( GetNumberOfVirtualLinksPerObject() ) );
			minAngleTheta = ( ( Float )( slice ) ) * angleIncrementTheta;
			maxAngleTheta = ( ( Float )( ( slice + 1 ) ) ) * angleIncrementTheta;
			minAnglePhi = ( ( Float )( slice ) ) * angleIncrementPhi;
			maxAnglePhi = ( ( Float )( ( slice + 1 ) ) ) * angleIncrementPhi;
			theta = RandRangeF( minAngleTheta, maxAngleTheta );
			phi = RandRangeF( minAnglePhi, maxAnglePhi );
			point.X = sphereCentre.X + ( ( radius * SinF( phi ) ) * CosF( theta ) );
			point.Y = sphereCentre.Y + ( ( radius * SinF( phi ) ) * SinF( theta ) );
			point.Z = sphereCentre.Z + ( radius * CosF( theta ) );
		}
		return point;
	}

	private function GetRandomPointOnSphere( sphereCentre : Vector4, radius : Float ) : Vector4
	{
		var u : Float;
		var v : Float;
		var theta : Float;
		var phi : Float;
		var point : Vector4;
		u = RandF();
		v = RandF();
		theta = ( 2.0 * Pi() ) * u;
		phi = AcosF( ( 2.0 * v ) - 1.0 );
		point.X = sphereCentre.X + ( ( radius * SinF( phi ) ) * CosF( theta ) );
		point.Y = sphereCentre.Y + ( ( radius * SinF( phi ) ) * SinF( theta ) );
		point.Z = sphereCentre.Z + ( radius * CosF( phi ) );
		return point;
	}

	private function GetRandomPointOnSphereQuadrant0() : Vector4
	{
		var u : Float;
		var v : Float;
		var cosPhi : Float;
		var sinPhi : Float;
		var theta : Float;
		var point : Vector4;
		u = RandF();
		v = RandF();
		theta = ( Pi() * u ) * 0.5;
		cosPhi = SqrtF( 1.0 - v );
		sinPhi = SqrtF( 1.0 - ( cosPhi * cosPhi ) );
		point.X = sinPhi * CosF( theta );
		point.Y = sinPhi * SinF( theta );
		point.Z = cosPhi;
		return point;
	}

	private function GetRandomPointOnSphereInFacingQuadrant( sphereCenter : Vector4, radius : Float, facePoint : Vector4 ) : Vector4
	{
		var point : Vector4;
		point = GetRandomPointOnSphereQuadrant0();
		if( facePoint.X < sphereCenter.X )
		{
			point.X = -( point.X );
		}
		if( facePoint.Y < sphereCenter.Y )
		{
			point.Y = -( point.Y );
		}
		if( facePoint.Z < sphereCenter.Z )
		{
			point.Z = -( point.Z );
		}
		point.X = sphereCenter.X + ( point.X * radius );
		point.Y = sphereCenter.Y + ( point.Y * radius );
		point.Z = sphereCenter.Z + ( point.Z * radius );
		return point;
	}

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

}

