class RemotelyConnectToAccessPoint extends EffectExecutor_Scripted
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var entity : Entity;
		var debugRemoteConnectionEvent : DebugRemoteConnectionEvent;
		entity = EffectExecutionScriptContext.GetTarget( applierCtx );
		debugRemoteConnectionEvent = new DebugRemoteConnectionEvent;
		entity.QueueEvent( debugRemoteConnectionEvent );
		return true;
	}

}

class PuppetForceVisionAppearanceData extends IScriptable
{
	var m_highlightType : EFocusForcedHighlightType;
	default m_highlightType = EFocusForcedHighlightType.INVALID;
	var m_outlineType : EFocusOutlineType;
	var m_stimRecord : weak< Stim_Record >;
	var m_transitionTime : Float;
	default m_transitionTime = 0.5f;
	var m_priority : EPriority;
	default m_priority = EPriority.VeryHigh;
	var m_targets : array< weak< ScriptedPuppet > >;
	var m_highlightedTargets : array< weak< ScriptedPuppet > >;
	var m_investigationSlots : Int32;
	var m_sourceHighlighted : Bool;
	var m_effectName : String;
}

class EffectExecutor_PuppetForceVisionAppearance extends EffectExecutor_Scripted
{

	[ profile = "" ]
	private function GetForceVisionAppearanceData( ctx : EffectScriptContext ) : PuppetForceVisionAppearanceData
	{
		var data : PuppetForceVisionAppearanceData;
		var dataVariant : Variant;
		EffectData.GetVariant( EffectScriptContext.GetSharedData( ctx ), GetAllBlackboardDefs().EffectSharedData.forceVisionAppearanceData, dataVariant );
		data = ( ( PuppetForceVisionAppearanceData )dataVariant );
		if( data == NULL )
		{
			data = new PuppetForceVisionAppearanceData;
		}
		return data;
	}

	[ profile = "" ]
	private function SetForceVisionAppearanceData( ctx : EffectScriptContext, data : PuppetForceVisionAppearanceData )
	{
		EffectData.SetVariant( EffectScriptContext.GetSharedData( ctx ), GetAllBlackboardDefs().EffectSharedData.forceVisionAppearanceData, data );
	}

	[ profile = "" ]
	private function GetHighlightType( ctx : EffectScriptContext ) : EFocusForcedHighlightType
	{
		return GetForceVisionAppearanceData( ctx ).m_highlightType;
	}

	[ profile = "" ]
	private function GetOutlineType( ctx : EffectScriptContext ) : EFocusOutlineType
	{
		return GetForceVisionAppearanceData( ctx ).m_outlineType;
	}

	[ profile = "" ]
	private function GetTransitionTime( ctx : EffectScriptContext ) : Float
	{
		return GetForceVisionAppearanceData( ctx ).m_transitionTime;
	}

	[ profile = "" ]
	private function GetPriority( ctx : EffectScriptContext ) : EPriority
	{
		return GetForceVisionAppearanceData( ctx ).m_priority;
	}

	[ profile = "" ]
	private function IsSourceHighlighted( ctx : EffectScriptContext ) : Bool
	{
		return GetForceVisionAppearanceData( ctx ).m_sourceHighlighted;
	}

	[ profile = "" ]
	private function GetEffectName( ctx : EffectScriptContext ) : String
	{
		return GetForceVisionAppearanceData( ctx ).m_effectName;
	}

	[ profile = "" ]
	public export function Init( ctx : EffectScriptContext ) : Bool
	{
		var source : GameObject;
		var communicationEvent : CommunicationEvent;
		source = ( ( GameObject )( EffectScriptContext.GetSource( ctx ) ) );
		if( source )
		{
			communicationEvent = new CommunicationEvent;
			communicationEvent.name = 'ResetInvestigators';
			source.QueueEvent( communicationEvent );
		}
		return true;
	}

	public export function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var source : GameObject;
		source = ( ( GameObject )( EffectScriptContext.GetSource( ctx ) ) );
		UpdateSourceHighlight( source, ctx );
		return true;
	}

	[ profile = "" ]
	public export function TargetAcquired( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var target : ScriptedPuppet;
		var source : GameObject;
		target = ( ( ScriptedPuppet )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		source = ( ( GameObject )( EffectScriptContext.GetSource( ctx ) ) );
		SendForceVisionApperaceEvent( true, target, source, ctx );
	}

	[ profile = "" ]
	public export function TargetLost( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var target : ScriptedPuppet;
		var source : GameObject;
		target = ( ( ScriptedPuppet )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		source = ( ( GameObject )( EffectScriptContext.GetSource( ctx ) ) );
		SendForceVisionApperaceEvent( false, target, source, ctx );
	}

	[ profile = "" ]
	private function IsSourceValid( source : GameObject ) : Bool
	{
		var device : Device;
		device = ( ( Device )( source ) );
		if( device != NULL )
		{
			return device.GetCurrentGameplayRole() != EGameplayRole.None && device.IsActive();
		}
		else
		{
			return true;
		}
	}

	[ profile = "" ]
	private function UpdateSourceHighlight( source : weak< GameObject >, ctx : EffectScriptContext )
	{
		var isSourceHighlighted : Bool;
		var isSourceValid : Bool;
		isSourceHighlighted = IsSourceHighlighted( ctx );
		isSourceValid = IsSourceValid( source );
		if( !( isSourceHighlighted ) && isSourceValid )
		{
			SendForceVisionApperaceEvent( true, source, source, ctx );
		}
		else if( isSourceHighlighted && !( isSourceValid ) )
		{
			SendForceVisionApperaceEvent( false, source, source, ctx );
		}
	}

	[ profile = "" ]
	private function SendForceVisionApperaceEvent( enable : Bool, owner : weak< GameObject >, source : weak< GameObject >, ctx : EffectScriptContext )
	{
		var evt : ForceVisionApperanceEvent;
		var highlight : FocusForcedHighlightData;
		var addTargetEvent : AddForceHighlightTargetEvent;
		var puppet : ScriptedPuppet;
		var updateInvestEvt : UpdateWillingInvestigators;
		var bbData : PuppetForceVisionAppearanceData;
		if( ( owner == NULL ) || ( source == NULL ) )
		{
			return;
		}
		evt = new ForceVisionApperanceEvent;
		highlight = new FocusForcedHighlightData;
		updateInvestEvt = new UpdateWillingInvestigators;
		highlight.sourceID = source.GetEntityID();
		highlight.sourceName = StringToName( GetEffectName( ctx ) );
		highlight.highlightType = GetHighlightType( ctx );
		highlight.outlineType = GetOutlineType( ctx );
		highlight.inTransitionTime = GetTransitionTime( ctx );
		highlight.priority = GetPriority( ctx );
		highlight.isRevealed = highlight.highlightType == EFocusForcedHighlightType.DISTRACTION || highlight.outlineType == EFocusOutlineType.DISTRACTION;
		evt.forcedHighlight = highlight;
		evt.apply = enable;
		owner.QueueEvent( evt );
		if( owner == source )
		{
			bbData = GetForceVisionAppearanceData( ctx );
			bbData.m_sourceHighlighted = enable;
			SetForceVisionAppearanceData( ctx, bbData );
			return;
		}
		puppet = ( ( ScriptedPuppet )( owner ) );
		if( enable )
		{
			if( puppet )
			{
				bbData = GetForceVisionAppearanceData( ctx );
				if( owner != source )
				{
					updateInvestEvt.investigator = owner.GetEntityID();
					source.QueueEvent( updateInvestEvt );
				}
			}
			addTargetEvent = new AddForceHighlightTargetEvent;
			addTargetEvent.targetID = owner.GetEntityID();
			addTargetEvent.effecName = highlight.sourceName;
			source.QueueEvent( addTargetEvent );
		}
	}

}

class ApplyJammer extends EffectExecutor_Scripted
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var entity : Entity;
		var evt : SetJammedEvent;
		evt = new SetJammedEvent;
		entity = ( ( SensorDevice )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( entity )
		{
			evt.newJammedState = true;
			evt.instigator = ( ( WeaponObject )( EffectScriptContext.GetInstigator( ctx ) ) );
			entity.QueueEvent( evt );
			return true;
		}
		return false;
	}

}

class ApplyJammerFromCw extends EffectExecutor_Scripted
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var evt : SensorJammed;
		var entity : Entity;
		evt = new SensorJammed;
		evt.sensor = ( ( SensorDevice )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		entity = EffectScriptContext.GetWeapon( ctx );
		entity.QueueEvent( evt );
		return true;
	}

}

class EMP extends EffectExecutor_Scripted
{

	public constexpr function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		return true;
	}

	public function TargetAcquired( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var target : GameObject;
		var device : Device;
		var unpowerEVT : EMPHitEvent;
		target = ( ( GameObject )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( target == NULL )
		{
			return;
		}
		if( ( ( ScriptedPuppet )( target ) ) )
		{
			StatusEffectHelper.ApplyStatusEffect( target, T"BaseStatusEffect.EMP" );
		}
		device = ( ( Device )( target ) );
		if( device != NULL )
		{
			unpowerEVT = new EMPHitEvent;
			device.QueueEvent( unpowerEVT );
		}
	}

}

class EMPExplosion extends EffectExecutor_Scripted
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var device : Device;
		var unpowerEVT : EMPHitEvent;
		device = ( ( Device )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( device != NULL )
		{
			unpowerEVT = new EMPHitEvent;
			device.QueueEvent( unpowerEVT );
		}
		return true;
	}

}

class EffectExecutor_PingNetwork extends EffectExecutor_Scripted
{
	private editable var m_fxResource : FxResource;

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var registerLinkRequest : RegisterPingNetworkLinkRequest;
		var linkData : SNetworkLinkData;
		var source : GameObject;
		var target : GameObject;
		var fxResource : FxResource;
		var fxVariant : Variant;
		var duration : Float;
		if( GetNetworkSystem( ctx ).IsPingLinksLimitReached() || !( GetNetworkSystem( ctx ).HasAnyActivePingWithRevealNetwork() ) )
		{
			return true;
		}
		source = ( ( GameObject )( EffectScriptContext.GetSource( ctx ) ) );
		target = ( ( GameObject )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( !( IsTargetValid( target, source, ctx ) ) )
		{
			return true;
		}
		EffectData.GetVariant( EffectScriptContext.GetSharedData( ctx ), GetAllBlackboardDefs().EffectSharedData.fxResource, fxVariant );
		fxResource = ( ( FxResource )fxVariant );
		if( !( FxResource.IsValid( fxResource ) ) )
		{
			fxResource = m_fxResource;
		}
		EffectData.GetFloat( EffectScriptContext.GetSharedData( ctx ), GetAllBlackboardDefs().EffectSharedData.duration, duration );
		registerLinkRequest = new RegisterPingNetworkLinkRequest;
		linkData.masterID = source.GetEntityID();
		linkData.drawLink = true;
		linkData.linkType = ELinkType.FREE;
		linkData.isDynamic = target.IsNetworkLinkDynamic() || source.IsNetworkLinkDynamic();
		linkData.revealMaster = false;
		linkData.revealSlave = false;
		linkData.fxResource = fxResource;
		linkData.isPing = true;
		linkData.permanent = GetNetworkSystem( ctx ).GetPingType( source.GetEntityID() ) == EPingType.SPACE;
		linkData.masterPos = source.GetNetworkBeamEndpoint();
		linkData.slavePos = target.GetNetworkBeamEndpoint();
		linkData.slaveID = target.GetEntityID();
		if( !( GetNetworkSystem( ctx ).HasNetworkLink( linkData ) ) )
		{
			registerLinkRequest.linksData.PushBack( linkData );
		}
		if( registerLinkRequest.linksData.Size() > 0 )
		{
			GetNetworkSystem( ctx ).QueueRequest( registerLinkRequest );
		}
		return true;
	}

	public function TargetLost( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) {}

	private function ShouldRevealObject( object : GameObject ) : Bool
	{
		var device : Device;
		device = ( ( Device )( object ) );
		if( device )
		{
			return device.GetDevicePS().HasNetworkBackdoor();
		}
		else
		{
			return false;
		}
	}

	private function GetFxResource( object : GameObject ) : FxResource
	{
		var device : Device;
		var resource : FxResource;
		device = ( ( Device )( object ) );
		if( device )
		{
			if( device.GetDevicePS().IsBreached() )
			{
				resource = device.GetFxResourceByKey( 'networkLinkBreached' );
			}
			else
			{
				resource = device.GetFxResourceByKey( 'networkLinkDefault' );
			}
		}
		return resource;
	}

	private function IsTargetValid( target : GameObject, source : GameObject, ctx : EffectScriptContext ) : Bool
	{
		if( ( source == NULL ) || ( target == NULL ) )
		{
			return false;
		}
		if( ( ( ( ScriptedPuppet )( target ) ) == NULL ) && ( ( ( Device )( target ) ) == NULL ) )
		{
			return false;
		}
		if( source == target )
		{
			return false;
		}
		if( ( ( PlayerPuppet )( target ) ) || ( ( PlayerPuppet )( source ) ) )
		{
			return false;
		}
		if( ( ( ItemObject )( target ) ) || ( ( ItemObject )( source ) ) )
		{
			return false;
		}
		if( ( ( ( ScriptedPuppet )( target ) ) && ( ( Device )( source ) ) ) || ( ( ( ScriptedPuppet )( source ) ) && ( ( Device )( target ) ) ) )
		{
			return false;
		}
		if( ( ( ( Device )( target ) ) && !( ( ( Device )( target ) ).ShouldRevealDevicesGrid() ) ) || ( ( ( Device )( source ) ) && !( ( ( Device )( source ) ).ShouldRevealDevicesGrid() ) ) )
		{
			return false;
		}
		return true;
	}

	protected const function GetNetworkSystem( ctx : EffectScriptContext ) : NetworkSystem
	{
		return ( ( NetworkSystem )( GameInstance.GetScriptableSystemsContainer( EffectScriptContext.GetGameInstance( ctx ) ).Get( 'NetworkSystem' ) ) );
	}

}

class EffectExecutor_MuteBubble extends EffectExecutor_Scripted
{

	public constexpr function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		return true;
	}

	public function TargetAcquired( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		if( IsTargetValid( ctx, applierCtx ) )
		{
			GameInstance.GetStatusEffectSystem( EffectScriptContext.GetGameInstance( ctx ) ).ApplyStatusEffect( EffectExecutionScriptContext.GetTarget( applierCtx ).GetEntityID(), T"BaseStatusEffect.MuteAudioStims" );
			GameInstance.GetStatusEffectSystem( EffectScriptContext.GetGameInstance( ctx ) ).ApplyStatusEffect( EffectExecutionScriptContext.GetTarget( applierCtx ).GetEntityID(), T"BaseStatusEffect.JamCommuniations" );
		}
	}

	public function TargetLost( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		GameInstance.GetStatusEffectSystem( EffectScriptContext.GetGameInstance( ctx ) ).RemoveStatusEffect( EffectExecutionScriptContext.GetTarget( applierCtx ).GetEntityID(), T"BaseStatusEffect.MuteAudioStims" );
		GameInstance.GetStatusEffectSystem( EffectScriptContext.GetGameInstance( ctx ) ).RemoveStatusEffect( EffectExecutionScriptContext.GetTarget( applierCtx ).GetEntityID(), T"BaseStatusEffect.JamCommuniations" );
	}

	private const function IsTargetValid( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var statusEffect : Bool;
		statusEffect = GameInstance.GetStatusEffectSystem( EffectScriptContext.GetGameInstance( ctx ) ).HasStatusEffect( EffectExecutionScriptContext.GetTarget( applierCtx ).GetEntityID(), T"BaseStatusEffect.MuteAudioStims" );
		return EffectExecutionScriptContext.GetTarget( applierCtx ) && !( statusEffect );
	}

}

abstract class EffectExecutor_Device extends EffectExecutor_Scripted
{
	editable var m_maxDelay : Float;
	default m_maxDelay = 0.0f;

	protected function QueueEventOnDevice( device : weak< InteractiveDevice >, evt : ActionBool )
	{
		var delay : Float;
		delay = ( ( m_maxDelay > 0.0 ) ? ( RandRangeF( 0.0, m_maxDelay ) ) : ( 0.0 ) );
		if( delay > 0.0 )
		{
			GameInstance.GetDelaySystem( device.GetGame() ).DelayPSEvent( device.GetDevicePS().GetID(), device.GetDevicePS().GetClassName(), evt, delay );
		}
		else
		{
			GameInstance.GetPersistencySystem( device.GetGame() ).QueuePSEvent( device.GetDevicePS().GetID(), device.GetDevicePS().GetClassName(), evt );
		}
	}

}

class EffectExecutor_SetDeviceOFF extends EffectExecutor_Device
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var device : weak< InteractiveDevice >;
		var evt : DeactivateDevice;
		device = ( ( InteractiveDevice )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( device )
		{
			if( ( ( RoadBlockTrap )( device ) ) )
			{
				return false;
			}
			evt = new DeactivateDevice;
			evt.SetProperties();
			QueueEventOnDevice( device, evt );
		}
		return true;
	}

}

class EffectExecutor_SetDeviceON extends EffectExecutor_Device
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var device : weak< InteractiveDevice >;
		var evt : ActivateDevice;
		var owner : Entity;
		owner = EffectScriptContext.GetInstigator( ctx );
		device = ( ( InteractiveDevice )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( device )
		{
			if( ( ( RoadBlockTrap )( device ) ) )
			{
			}
			else if( owner && ( Vector4.Distance( owner.GetWorldPosition(), device.GetWorldPosition() ) <= 3.0 ) )
			{
				return true;
			}
			evt = new ActivateDevice;
			evt.SetProperties();
			QueueEventOnDevice( device, evt );
		}
		return true;
	}

}

class EffectExecutor_ToggleDevice extends EffectExecutor_Device
{

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var device : weak< InteractiveDevice >;
		var evt : ToggleActivate;
		device = ( ( InteractiveDevice )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( device )
		{
			evt = new ToggleActivate;
			QueueEventOnDevice( device, evt );
		}
		return true;
	}

}

class EffectExecutor_GrenadeTargetTracker extends EffectExecutor_Scripted
{
	const editable var m_potentialTargetSlots : array< CName >;

	public constexpr function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		return true;
	}

	public function TargetAcquired( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var targetAcquiredEvent : GrenadeTrackerTargetAcquiredEvent;
		var target : NPCPuppet;
		var targetSlot : CName;
		target = ( ( NPCPuppet )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( IsTargetValid( target, ctx, applierCtx, targetSlot ) )
		{
			targetAcquiredEvent = new GrenadeTrackerTargetAcquiredEvent;
			targetAcquiredEvent.target = ( ( ScriptedPuppet )( target ) );
			targetAcquiredEvent.targetSlot = targetSlot;
			EffectScriptContext.GetSource( ctx ).QueueEvent( targetAcquiredEvent );
		}
	}

	public function TargetLost( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var targetLostEvent : GrenadeTrackerTargetLostEvent;
		var target : NPCPuppet;
		target = ( ( NPCPuppet )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( target )
		{
			targetLostEvent = new GrenadeTrackerTargetLostEvent;
			targetLostEvent.target = ( ( ScriptedPuppet )( target ) );
			EffectScriptContext.GetSource( ctx ).QueueEvent( targetLostEvent );
		}
	}

	private const function IsTargetValid( target : NPCPuppet, ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext, out targetSlot : CName ) : Bool
	{
		if( !( target ) )
		{
			return false;
		}
		if( ( ( ( ( ( ScriptedPuppet.IsAlive( target ) && !( ScriptedPuppet.IsDefeated( target ) ) ) && !( ScriptedPuppet.IsUnconscious( target ) ) ) && !( ScriptedPuppet.IsPlayerCompanion( target ) ) ) && target.GetAttitudeTowards( ( ( GameObject )( EffectScriptContext.GetInstigator( ctx ) ) ) ) != EAIAttitude.AIA_Friendly ) && !( target.IsCrowd() ) ) && !( target.IsCharacterCivilian() ) )
		{
			return true;
		}
		return false;
	}

	private const function IsTargetReachable( ctx : EffectScriptContext, target : NPCPuppet, out targetSlot : CName ) : Bool
	{
		var sourcePosition : Vector4;
		var slotComponent : SlotComponent;
		var slotTransform : WorldTransform;
		var startPoint : Vector4;
		var endPoint : Vector4;
		var i : Int32;
		if( m_potentialTargetSlots.Size() == 0 )
		{
			return true;
		}
		sourcePosition = EffectScriptContext.GetSource( ctx ).GetWorldPosition();
		slotComponent = target.GetSlotComponent();
		for( i = 0; i < m_potentialTargetSlots.Size(); i += 1 )
		{
			if( slotComponent.GetSlotTransform( m_potentialTargetSlots[ i ], slotTransform ) )
			{
				if( GetAngleBetweenSourceUpAndTarget( sourcePosition, WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) ) ) > 150.0 )
				{
					startPoint = sourcePosition + Vector4( 0.0, 0.0, -0.5, 0.0 );
				}
				else
				{
					startPoint = sourcePosition + Vector4( 0.0, 0.0, 0.5, 0.0 );
				}
				endPoint = WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) ) + ( Vector4.Normalize( startPoint - WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) ) ) * 0.30000001 );
				if( IsPointReachable( ctx, startPoint, endPoint ) )
				{
					targetSlot = m_potentialTargetSlots[ i ];
					return true;
				}
			}
		}
		return false;
	}

	private const function GetAngleBetweenSourceUpAndTarget( sourcePosition : Vector4, targetPosition : Vector4 ) : Float
	{
		var angle : Float;
		var vectorToTarget : Vector4;
		vectorToTarget = targetPosition - sourcePosition;
		angle = Vector4.GetAngleBetween( Vector4( 0.0, 0.0, 1.0, 0.0 ), vectorToTarget );
		return angle;
	}

	private const function IsPointReachable( ctx : EffectScriptContext, startPoint : Vector4, endPoint : Vector4 ) : Bool
	{
		var raycastResult : TraceResult;
		GameInstance.GetSpatialQueriesSystem( EffectScriptContext.GetGameInstance( ctx ) ).SyncRaycastByCollisionPreset( startPoint, endPoint, 'World Static', raycastResult );
		return !( TraceResult.IsValid( raycastResult ) );
	}

}

class EffectExecutor_TrackTargets extends EffectExecutor_Scripted
{

	public constexpr function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		return true;
	}

	public function TargetAcquired( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var targetAcquiredEvent : TargetAcquiredEvent;
		var target : GameObject;
		var source : Entity;
		target = ( ( GameObject )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		source = EffectScriptContext.GetSource( ctx );
		if( IsTargetValid( target, ctx, applierCtx ) )
		{
			targetAcquiredEvent = new TargetAcquiredEvent;
			targetAcquiredEvent.target = ( ( ScriptedPuppet )( target ) );
			source.QueueEvent( targetAcquiredEvent );
		}
	}

	public function TargetLost( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext )
	{
		var targetLostEvent : TargetLostEvent;
		var target : GameObject;
		var source : Entity;
		target = ( ( GameObject )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		source = EffectScriptContext.GetSource( ctx );
		if( IsTargetValid( target, ctx, applierCtx ) )
		{
			targetLostEvent = new TargetLostEvent;
			targetLostEvent.target = ( ( ScriptedPuppet )( target ) );
			source.QueueEvent( targetLostEvent );
		}
	}

	private const function IsTargetValid( target : GameObject, ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		if( ScriptedPuppet.IsAlive( ( ( ScriptedPuppet )( target ) ) ) )
		{
			return true;
		}
		return false;
	}

}

class EffectExecutor_SendActionSignal extends EffectExecutor_Scripted
{
	editable var m_signalName : CName;
	editable var m_signalDuration : Float;
	default m_signalDuration = 0.0f;

	public export function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var target : NPCPuppet;
		target = ( ( NPCPuppet )( EffectExecutionScriptContext.GetTarget( applierCtx ) ) );
		if( target )
		{
			ScriptedPuppet.SendActionSignal( target, m_signalName, m_signalDuration );
		}
		return true;
	}

}

class EffectExecutor_VisualEffectAtTarget extends EffectExecutor_Scripted
{
	editable var m_effect : FxResource;
	editable var m_ignoreTimeDilation : Bool;

	public function Process( ctx : EffectScriptContext, applierCtx : EffectExecutionScriptContext ) : Bool
	{
		var target : Entity;
		var worldPosition : WorldPosition;
		var transform : WorldTransform;
		target = EffectExecutionScriptContext.GetTarget( applierCtx );
		if( target )
		{
			WorldPosition.SetVector4( worldPosition, target.GetWorldPosition() );
			WorldTransform.SetWorldPosition( transform, worldPosition );
			WorldTransform.SetOrientation( transform, target.GetWorldOrientation() );
			EffectScriptContext.SpawnEffect( ctx, m_effect, transform, m_ignoreTimeDilation );
			return true;
		}
		return false;
	}

	public function Preload( ctx : EffectPreloadScriptContext )
	{
		EffectPreloadScriptContext.PreloadFxResource( ctx, m_effect );
	}

}

