import class PlayerProximityStartEvent extends Event
{
	import var profile : CName;
}

import class PlayerProximityStopEvent extends Event
{
	import var profile : CName;
}

importonly abstract class ReactionChangeRequestEvent extends Event
{
	import var reactionPresetRecord : ReactionPreset_Record;
}

importonly class AIPuppetSwappedEvent extends Event
{
}

importonly class AIPuppetTeleportedEvent extends Event
{
}

class SecuritySystemOutputTaskData extends ScriptTaskData
{
	var cachedEvt : SecuritySystemOutput;
}

class StimEventTaskData extends ScriptTaskData
{
	var cachedEvt : StimuliEvent;
	var delayed : Bool;
	editable var id : Uint32;
}

class AnimFeature_FacialReaction extends AnimFeature
{
	editable var category : Int32;
	editable var idle : Int32;
}

struct StimParams
{
	var reactionOutput : ReactionOutput;
	var stimData : StimEventData;
}

class ReprimandEscalationEvent extends Event
{
	var startReprimand : Bool;
	var startDeescalate : Bool;
}

class IgnoreListEvent extends Event
{
	var bodyID : EntityID;
	var removeEvent : Bool;
}

class IncapacitatedEvent extends Event
{
}

class HandleRagdollOnDeathEvent extends Event
{
	var handleUncontrolledMovement : Bool;
}

import class StanceStateChangeEvent extends Event
{
	import var state : gamedataNPCStanceState;
}

class CrowdCallingPoliceEvent extends Event
{
}

class SwapPresetEvent extends Event
{
	var mappingName : String;
}

class ReevaluatePresetEvent extends Event
{
}

class EndLookatEvent extends Event
{
	var repeat : Bool;
}

class DisableUndeadAnimFeatureEvent extends Event
{
}

class TerminateReactionLookatEvent extends Event
{
}

class RepeatLookatEvent extends Event
{
	var target : weak< Entity >;
}

class CommunicationEvent extends Event
{
	var name : CName;
	var sender : EntityID;
}

class DelayedCrowdReactionEvent extends Event
{
	var stimEvent : StimuliEvent;
	var vehicleFearPhase : Int32;
}

class DelayStimEvent extends Event
{
	var stimEvent : StimuliEvent;
	var fullEventPipeline : Bool;
}

class TriggerDelayedReactionEvent extends DelayedCrowdReactionEvent
{
	var initAnim : Bool;
	var behavior : gamedataOutput;
}

class ExitWorkspotSequenceEvent extends Event
{
}

class DeescalateFearInVehicle extends Event
{
}

class CrowdSettingsEvent extends Event
{
	var movementType : CName;
}

class ProximityLookatEvent extends Event
{
}

class AddInvestigatorEvent extends Event
{
	var investigator : EntityID;
}

class SetBodyPositionEvent extends Event
{
	var bodyPosition : Vector4;
	var bodyPositionID : EntityID;
	var pickedUp : Bool;
}

class ResetLookatReactionEvent extends Event
{
}

class ResetFacialEvent extends Event
{
}

class SpreadFearEvent extends Event
{
	var player : Bool;
	var phase : Int32;
}

class DisturbingComfortZone extends Event
{
}

class CheckComfortZoneEvent extends Event
{
}

class StalkEvent extends Event
{
}

class DistrurbComfortZoneAggressiveEvent extends Event
{
}

class ResetReactionEvent extends Event
{
	var data : AIReactionData;
}

class CleanEnvironmentalHazardEvent extends Event
{
	var stimEvent : StimuliEvent;
}

class StimThresholdEvent extends Event
{
	var reset : Bool;
	var timeThreshold : Float;
}

class StealthStimThresholdEvent extends Event
{
	var reset : Bool;
	var timeThreshold : Float;
}

class BodyInvestigatedEvent extends Event
{
}

class PlayerMuntedToMyVehicle extends Event
{
	var player : weak< PlayerPuppet >;

	public static function Create( player : weak< PlayerPuppet > ) : PlayerMuntedToMyVehicle
	{
		var evt : PlayerMuntedToMyVehicle;
		evt = new PlayerMuntedToMyVehicle;
		evt.player = player;
		return evt;
	}

}

struct StimEventData
{
	var source : weak< GameObject >;
	var stimType : gamedataStimType;
}

struct ReactionOutput
{
	var reactionBehavior : gamedataOutput;
	var reactionPriority : Int32;
	var AIbehaviorPriority : Float;
	var reactionCooldown : Float;
	var startedInWorkspot : Bool;
	var workspotReaction : Bool;
	var workspotReactionType : CName;
}

struct LookAtData
{
	var idle : Int32;
	var category : Int32;
	var personality : gamedataStatType;
}

class ReactionBehaviorStatus extends Event
{
	var status : AIbehaviorUpdateOutcome;
	var reactionData : AIReactionData;
}

class AIReactionData
{
	var reactionPriority : Int32;
	var reactionBehaviorName : gamedataOutput;
	var reactionBehaviorAIPriority : Float;
	var reactionCooldown : Float;
	var stimTarget : weak< GameObject >;
	var stimSource : Vector4;
	var stimType : gamedataStimType;
	var stimPriority : gamedataStimPriority;
	var stimRecord : Stim_Record;
	var stimInvestigateData : stimInvestigateData;
	var stimEventData : StimEventData;
	var stimPropagation : gamedataStimPropagation;
	var initAnimInWorkspot : Bool;
	var skipInitialAnimation : Bool;
	var validTillTimeStamp : Float;
	var recentReactionTimeStamp : Float;
	var escalateProvoke : Bool;
}

class StimFilters
{

	public static function Debug_IsAllowed( stimType : gamedataStimType ) : Bool
	{
		if( IsFinal() )
		{
			return true;
		}
		return true;
	}

	public static function IsGunshot( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.Gunshot || stimType == gamedataStimType.SilencedGunshot;
	}

	public static function IsProjectile( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.Bullet || stimType == gamedataStimType.ProjectileDistraction;
	}

	public static function IsIllegalAction( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.IllegalAction || stimType == gamedataStimType.IllegalActionNoCombat;
	}

	public static function IsIllegal( stimType : gamedataStimType ) : Bool
	{
		return IsIllegalAction( stimType ) || stimType == gamedataStimType.IllegalInteraction;
	}

	public static function IsIgnoredWhileGrappled( stimType : gamedataStimType ) : Bool
	{
		return stimType != gamedataStimType.Combat;
	}

	public static function IsIgnoredFromSameSource( stimType : gamedataStimType ) : Bool
	{
		return ( stimType == gamedataStimType.Distract || stimType == gamedataStimType.CarAlarm ) || stimType == gamedataStimType.Recon;
	}

	public static function IsIgnoredInVehicle( stimType : gamedataStimType ) : Bool
	{
		return ( stimType == gamedataStimType.CrowdIllegalAction || stimType == gamedataStimType.CrimeWitness ) || stimType == gamedataStimType.SpreadFear;
	}

	public static function ShouldBeInPublicZone( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.WeaponDisplayed;
	}

	public static function ShouldBeFriendly( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.DeadBody;
	}

	public static function ShouldBeDetected( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.IllegalInteraction || stimType == gamedataStimType.CarryBody;
	}

	public static function IsForTheDead( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.UndeadCall;
	}

	public static function AllowFriendlyInCombat( stimType : gamedataStimType ) : Bool
	{
		return ( ( stimType == gamedataStimType.Terror || stimType == gamedataStimType.CombatCall ) || stimType == gamedataStimType.CombatWhistle ) || stimType == gamedataStimType.EnvironmentalHazard;
	}

	public static function CanTriggerAllyHelp( stimType : gamedataStimType ) : Bool
	{
		return ( ( ( ( ( ( StimFilters.IsGunshot( stimType ) || StimFilters.IsProjectile( stimType ) ) || stimType == gamedataStimType.Explosion ) || stimType == gamedataStimType.MeleeHit ) || stimType == gamedataStimType.VehicleHit ) || stimType == gamedataStimType.Alarm ) || stimType == gamedataStimType.Call ) || stimType == gamedataStimType.Dying;
	}

	public static function CanTriggerNeutralHelp( stimType : gamedataStimType ) : Bool
	{
		return ( StimFilters.IsGunshot( stimType ) || stimType == gamedataStimType.Bullet ) || stimType == gamedataStimType.Explosion;
	}

	public static function CanTriggerEnemyHostility( stimType : gamedataStimType ) : Bool
	{
		return CanTriggerNeutralHelp( stimType );
	}

	public static function CanProcessPastAllyHelp( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.Call || stimType == gamedataStimType.Dying;
	}

	public static function CanTriggerCombatDirectly( stimType : gamedataStimType ) : Bool
	{
		return ( ( ( StimFilters.IsGunshot( stimType ) || ( StimFilters.IsIllegal( stimType ) && stimType != gamedataStimType.IllegalActionNoCombat ) ) || stimType == gamedataStimType.Combat ) || stimType == gamedataStimType.Bullet ) || stimType == gamedataStimType.CarryBody;
	}

	public static function CanTriggerAlertedDirectly( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.CombatHit;
	}

	public static function CanTriggerPreventionCombatDirectly( stimType : gamedataStimType ) : Bool
	{
		return ( ( stimType == gamedataStimType.VehicleHit || stimType == gamedataStimType.HijackVehicle ) || stimType == gamedataStimType.CrimeWitness ) || stimType == gamedataStimType.CombatHit;
	}

	public static function CanTriggerPreventionCombatDirectlyEvenInvisible( stimType : gamedataStimType ) : Bool
	{
		return stimType == gamedataStimType.VehicleHit;
	}

	public static function CanBeIgnoredInCombat( stimType : gamedataStimType ) : Bool
	{
		return ( ( StimFilters.IsGunshot( stimType ) || StimFilters.IsProjectile( stimType ) ) || StimFilters.IsIllegal( stimType ) ) || stimType == gamedataStimType.Explosion;
	}

}

enum EReactLogSource
{
	Undefined = 0,
	Detected = 1,
	StimEvent = 2,
	BehaviorCombatCheck = 3,
	BehaviorCombatTrigger = 4,
}

class ReactionManagerComponent extends ScriptableComponent
{
	private var m_activeReaction : AIReactionData;
	private var m_desiredReaction : AIReactionData;
	private var m_stimuliCache : array< StimEventTaskData >;
	private var m_reactionCache : array< AIReactionData >;
	private var m_reactionPreset : ReactionPreset_Record;
	private var m_puppetReactionBlackboard : IBlackboard;
	private var m_receivedStimType : gamedataStimType;
	private var m_receivedStimPropagation : gamedataStimPropagation;
	private var m_inCrowd : Bool;
	private var m_inTrafficLane : Bool;
	private var m_desiredFearPhase : Int32;
	default m_desiredFearPhase = -1;
	private var m_previousFearPhase : Int32;
	private var m_NPCRadius : Float;
	default m_NPCRadius = 0.3f;
	private var m_bumpTriggerDistanceBufferMounted : Float;
	default m_bumpTriggerDistanceBufferMounted = 1.0f;
	private var m_bumpTriggerDistanceBufferCrouched : Float;
	default m_bumpTriggerDistanceBufferCrouched = -0.11f;
	private var m_delayReactionEventID : DelayID;
	private var m_delay : Vector2;
	private var m_delayDetectionEventID : DelayID;
	private var m_delayStimEventID : DelayID;
	private var m_resetReactionDataID : DelayID;
	private var m_callingPoliceID : DelayID;
	private var m_lookatEvent : LookAtAddEvent;
	private var m_ignoreList : array< EntityID >;
	private var m_investigationList : array< StimEventData >;
	private var m_pendingReaction : AIReactionData;
	private var m_ovefloodCooldown : Float;
	default m_ovefloodCooldown = 1.f;
	private var m_stanceState : gamedataNPCStanceState;
	default m_stanceState = gamedataNPCStanceState.Stand;
	private var m_highLevelState : gamedataNPCHighLevelState;
	default m_highLevelState = gamedataNPCHighLevelState.Relaxed;
	private var m_aiRole : EAIRole;
	private var m_pendingBehaviorCb : CallbackHandle;
	private var m_inPendingBehavior : Bool;
	private var m_cacheSecuritySysOutput : SecuritySystemOutput;
	private var m_environmentalHazards : array< StimuliEvent >;
	private var m_environmentalHazardsDelayIDs : array< DelayID >;
	private var m_stolenVehicle : weak< VehicleObject >;
	private var m_isAlertedByDeadBody : Bool;
	default m_isAlertedByDeadBody = false;
	private var m_isInCrosswalk : Bool;
	private var m_beignHijacked : Bool;
	private var m_owner_id : EntityID;
	private var m_presetName : CName;
	private var m_updateByActive : Bool;
	private var m_personalities : array< gamedataStatType >;
	private var m_workspotReactionPlayed : Bool;
	private var m_inReactionSequence : Bool;
	private var m_playerProximity : Bool;
	private var m_fearToIdleDistance : Vector2;
	private var m_exitWorkspotAim : Vector2;
	private var m_bumpedRecently : Int32;
	private var m_bumpTimestamp : Float;
	private var m_crowdAimingReactionDistance : Float;
	default m_crowdAimingReactionDistance = 20.f;
	private var m_fearInPlaceAroundDistance : Float;
	private var m_lookatRepeat : Bool;
	private var m_disturbingComfortZoneInProgress : Bool;
	private var m_entereProximityRecently : Int32;
	private var m_comfortZoneTimestamp : Float;
	private var m_disturbComfortZoneEventId : DelayID;
	private var m_checkComfortZoneEventId : DelayID;
	private var m_spreadingFearEventId : DelayID;
	private var m_proximityLookatEventId : DelayID;
	private var m_resetFacialEventId : DelayID;
	private var m_exitWorkspotSequenceEventId : DelayID;
	private var m_exitFearInVehicleEventId : DelayID;
	private var m_fastWalk : Bool;
	default m_fastWalk = true;
	private var m_createThreshold : Bool;
	default m_createThreshold = true;
	private var m_initialized : Bool;
	private var m_initCrowd : Bool;
	private var m_facialCooldown : Float;
	private var m_disturbComfortZoneAggressiveEventId : DelayID;
	private var m_backOffInProgress : Bool;
	private var m_backOffTimestamp : Float;
	private var m_crowdFearStage : gameFearStage;
	default m_crowdFearStage = gameFearStage.Relaxed;
	private var m_fearLocomotionWrapper : Bool;
	private var m_successfulFearDeescalation : Float;
	private var m_willingToCallPolice : Bool;
	private var m_deadBodyInvestigators : array< EntityID >;
	private var m_deadBodyStartingPosition : Vector4;
	private var m_currentStimThresholdValue : Int32;
	private var m_timeStampThreshold : Float;
	private var m_currentStealthStimThresholdValue : Int32;
	private var m_stealthTimeStampThreshold : Float;
	private var m_driverAllowedToGetAggressive : Bool;
	default m_driverAllowedToGetAggressive = false;
	private var m_driverIsAggressive : Bool;
	default m_driverIsAggressive = false;
	private var m_logSource : EReactLogSource;
	default m_logSource = EReactLogSource.Undefined;
	private var m_gracePeriodDuration : Float;
	default m_gracePeriodDuration = 5.f;
	private var m_recentAlertObject : weak< GameObject >;
	private var m_recentAlertTimeStamp : Float;

	private const function LogEnabled() : Bool
	{
		return AIComponent.DebugLogEnabled( GetOwnerPuppet() );
	}

	private const function Log( category : CName, message : String )
	{
		var aiComponent : AIHumanComponent;
		aiComponent = GetOwnerPuppet().GetAIControllerComponent();
		if( aiComponent )
		{
			aiComponent.DebugLog( category, message );
		}
	}

	private const function LogStim( category : CName, stimType : gamedataStimType, stimPropagation : gamedataStimPropagation, message : String )
	{
		var logMessage : String;
		logMessage = ( "(" + EnumValueToString( "gamedataStimPropagation", ( ( Int32 )( stimPropagation ) ) ) ) + ") ";
		logMessage += EnumValueToString( "gamedataStimType", ( ( Int32 )( stimType ) ) );
		logMessage += ": ";
		logMessage += message;
		Log( category, logMessage );
	}

	private const function LogReactionData( category : CName, reactionData : AIReactionData, message : String )
	{
		if( reactionData )
		{
			LogStim( category, reactionData.stimType, reactionData.stimPropagation, message );
			return;
		}
		Log( category, "NO REACTION DATA: " + message );
	}

	private const function LogReaction( category : CName, message : String )
	{
		switch( m_logSource )
		{
			case EReactLogSource.Detected:
				Log( category, "OnDetected: " + message );
			break;
			case EReactLogSource.StimEvent:
				LogStim( category, m_receivedStimType, m_receivedStimPropagation, message );
			break;
			case EReactLogSource.BehaviorCombatCheck:
			case EReactLogSource.BehaviorCombatTrigger:
				LogReactionData( category, GetActiveOrDesiredReactionData(), message );
			break;
			default:
				Log( category, message );
			break;
		}
	}

	private const function LogCategory( suffix : String ) : CName
	{
		var cat : String;
		cat = "reactions";
		switch( m_logSource )
		{
			case EReactLogSource.Detected:
				cat += ".detect";
			break;
			case EReactLogSource.StimEvent:
				cat += ".stim";
			break;
			case EReactLogSource.BehaviorCombatCheck:
			case EReactLogSource.BehaviorCombatTrigger:
				cat += ".behavior";
			break;
			default:
				break;
		}
		if( StrLen( suffix ) > 0 )
		{
			cat += ( "." + suffix );
		}
		return StringToName( cat );
	}

	public function LogStart( source : EReactLogSource, message : String )
	{
		m_logSource = source;
		if( !( LogEnabled() ) )
		{
			return;
		}
		LogReaction( LogCategory( "start" ), message );
	}

	public const function LogInfo( message : String )
	{
		if( !( LogEnabled() ) )
		{
			return;
		}
		LogReaction( LogCategory( "info" ), message );
	}

	public const function LogSuccess( message : String )
	{
		if( !( LogEnabled() ) )
		{
			return;
		}
		LogReaction( LogCategory( "success" ), message );
	}

	public const function LogFailure( message : String )
	{
		if( !( LogEnabled() ) )
		{
			return;
		}
		LogReaction( LogCategory( "failure" ), message );
	}

	protected function HandleStimEventByTask( stimEvent : StimuliEvent, optional delayed : Bool )
	{
		var data : StimEventTaskData;
		data = new StimEventTaskData;
		data.cachedEvt = stimEvent;
		data.delayed = delayed;
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).QueueTask( this, data, 'HandleStimEventTask', gameScriptTaskExecutionStage.PostPhysics );
	}

	protected export function HandleStimEventTask( data : ScriptTaskData )
	{
		var stimData : StimEventTaskData;
		stimData = ( ( StimEventTaskData )( data ) );
		if( stimData )
		{
			HandleStimEvent( stimData );
		}
	}

	protected function HandleStimEvent( stimData : StimEventTaskData )
	{
		var stimParams : StimParams;
		var ownerPuppet : ScriptedPuppet;
		var localPlayer : GameObject;
		var resetLookatReaction : ResetLookatReactionEvent;
		var stimType : gamedataStimType;
		var stimEvent : StimuliEvent;
		if( !( stimData ) || !( stimData.cachedEvt ) )
		{
			return;
		}
		stimEvent = stimData.cachedEvt;
		stimType = stimEvent.GetStimType();
		m_receivedStimType = stimType;
		m_receivedStimPropagation = stimEvent.stimPropagation;
		if( !( IsEnabled() ) && !( StimFilters.IsForTheDead( stimType ) ) )
		{
			m_receivedStimType = gamedataStimType.Invalid;
			m_receivedStimPropagation = gamedataStimPropagation.Invalid;
			return;
		}
		if( !( m_initialized ) )
		{
			Initialiaze();
			m_initialized = true;
		}
		ownerPuppet = GetOwnerPuppet();
		if( stimType == gamedataStimType.AudioEnemyPing )
		{
			if( ScriptedPuppet.IsActive( ownerPuppet ) && ownerPuppet.IsAggressive() )
			{
				localPlayer = GameInstance.GetPlayerSystem( ownerPuppet.GetGame() ).GetLocalPlayerMainGameObject();
				if( !( SourceAttitude( localPlayer, EAIAttitude.AIA_Friendly ) ) )
				{
					GameInstance.GetAudioSystem( ownerPuppet.GetGame() ).RegisterEnemyPingStim( ownerPuppet.GetHighLevelStateFromBlackboard(), ownerPuppet.IsPrevention() );
				}
			}
			return;
		}
		if( stimData.delayed )
		{
			LogStart( EReactLogSource.StimEvent, "processing delayed stim event" );
		}
		else
		{
			LogStart( EReactLogSource.StimEvent, "processing stim event" );
		}
		if( ShouldEventBeProcessed( stimEvent ) )
		{
			if( ( !( ownerPuppet.IsPrevention() ) && ownerPuppet.GetCrowdMemberComponent() ) && ( m_inCrowd || ownerPuppet.IsCharacterCivilian() ) )
			{
				LogInfo( "processing crowd stim" );
				if( ShouldStimBeProcessedByCrowd( stimEvent ) )
				{
					LogSuccess( "handling crowd reaction" );
					HandleCrowdReaction( stimEvent );
				}
				if( stimEvent.IsTagInStimuli( 'Safe' ) )
				{
					LogInfo( "resetting lookat with delay" );
					resetLookatReaction = new ResetLookatReactionEvent;
					GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), resetLookatReaction, 1.0 );
				}
				m_receivedStimType = gamedataStimType.Invalid;
				m_receivedStimPropagation = gamedataStimPropagation.Invalid;
				return;
			}
			LogInfo( "processing stim" );
			if( ShouldStimBeProcessed( stimEvent, stimData.delayed ) )
			{
				stimParams = ProcessStimParams( stimEvent );
				ProcessReactionOutput( stimData, stimParams );
			}
		}
		if( ( stimType == gamedataStimType.StopedAiming || stimType == gamedataStimType.ReevaluateDetectionOverwrite ) && m_reactionPreset.IsAggressive() )
		{
			ownerPuppet.GetSensesComponent().ReevaluateDetectionOverwrite( stimEvent.sourceObject );
			LogSuccess( "reevaluated detection overwrite" );
		}
		if( stimType == gamedataStimType.EnvironmentalHazard )
		{
			ProcessEnvironmentalHazard( stimEvent );
		}
		if( StimFilters.IsForTheDead( stimEvent.GetStimType() ) )
		{
			ProcessStimForTheDead( stimEvent );
		}
		m_receivedStimType = gamedataStimType.Invalid;
		m_receivedStimPropagation = gamedataStimPropagation.Invalid;
	}

	protected function ReactToSecuritySystemOutputByTask( evt : SecuritySystemOutput )
	{
		var data : SecuritySystemOutputTaskData;
		data = new SecuritySystemOutputTaskData;
		data.cachedEvt = evt;
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).QueueTask( this, data, 'ReactToSecuritySystemOutputTask', gameScriptTaskExecutionStage.Any );
	}

	protected export function ReactToSecuritySystemOutputTask( data : ScriptTaskData )
	{
		var taskData : SecuritySystemOutputTaskData;
		taskData = ( ( SecuritySystemOutputTaskData )( data ) );
		if( taskData )
		{
			ReactToSecurityOutput( taskData.cachedEvt );
		}
	}

	public export function OnGameAttach()
	{
		m_owner_id = GetOwner().GetEntityID();
		m_puppetReactionBlackboard = IBlackboard.Create( GetAllBlackboardDefs().PuppetReaction );
	}

	protected event OnPlayerMuntedToMyVehicle( evt : PlayerMuntedToMyVehicle )
	{
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		if( ( ownerPuppet.IsAggressive() && ScriptedPuppet.IsActive( ownerPuppet ) ) && AIActionHelper.TryChangingAttitudeToHostile( ownerPuppet, evt.player ) )
		{
			if( !( GetOwnerPuppet().GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().PuppetState.WorkspotAnimationInProgress ) ) )
			{
				GetOwner().QueueEvent( AIEvents.ExitVehicleEvent() );
				TargetTrackingExtension.InjectThreat( ownerPuppet, evt.player );
			}
		}
	}

	protected event OnSenseVisibilityEvent( evt : SenseVisibilityEvent )
	{
		var broadcaster : StimBroadcasterComponent;
		var ownerPuppet : ScriptedPuppet;
		var owner : GameObject;
		ownerPuppet = GetOwnerPuppet();
		owner = GetOwner();
		if( !( evt.target ) || !( evt.target.IsPlayer() ) )
		{
			return false;
		}
		if( ownerPuppet.IsPrevention() || NPCManager.HasTag( ownerPuppet.GetRecordID(), 'TriggerPrevention' ) )
		{
			if( evt.isVisible )
			{
				PreventionSystem.RegisterAsViewerToPreventionSystem( owner.GetGame(), ownerPuppet );
			}
			else
			{
				PreventionSystem.UnRegisterAsViewerToPreventionSystem( owner.GetGame(), ownerPuppet );
			}
		}
		if( !( evt.isVisible ) )
		{
			( ( NPCPuppet )( owner ) ).GetComfortZoneComponent().Toggle( false );
			return false;
		}
		if( !( CanTriggerReprimandOrder() ) )
		{
			return false;
		}
		if( !( m_activeReaction ) )
		{
			broadcaster = evt.target.GetStimBroadcasterComponent();
			broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.AskToFollowOrder, owner );
		}
		if( m_highLevelState != gamedataNPCHighLevelState.Combat )
		{
			( ( NPCPuppet )( owner ) ).GetComfortZoneComponent().Toggle( true );
		}
	}

	protected event OnLookedAtEvent( evt : LookedAtEvent )
	{
		var broadcaster : StimBroadcasterComponent;
		broadcaster = GetPlayerSystem().GetLocalPlayerControlledGameObject().GetStimBroadcasterComponent();
		if( ( ( broadcaster && evt.isLookedAt ) && !( m_activeReaction ) ) && CanTriggerReprimandOrder() )
		{
			broadcaster.SendDrirectStimuliToTarget( GetOwner(), gamedataStimType.AskToFollowOrder, GetOwner() );
		}
	}

	protected event OnDetectedEvent( evt : OnDetectedEvent )
	{
		var broadcaster : StimBroadcasterComponent;
		var securitySystem : SecuritySystemControllerPS;
		var securitySystemInput : SecuritySystemInput;
		var deviceLink : PuppetDeviceLinkPS;
		var ownerPuppet : ScriptedPuppet;
		var owner : GameObject;
		var scriptedPuppetTarget : ScriptedPuppet;
		var targetOfSource : weak< GameObject >;
		var onlyAlertNoThreat : Bool;
		var investigateData : stimInvestigateData;
		var ignoreListEvent : IgnoreListEvent;
		ownerPuppet = GetOwnerPuppet();
		owner = GetOwner();
		onlyAlertNoThreat = false;
		LogStart( EReactLogSource.Detected, "handling event" );
		if( ScriptedPuppet.IsBlinded( ownerPuppet ) )
		{
			LogFailure( "owner is blinded" );
			return false;
		}
		if( !( evt.target ) )
		{
			LogFailure( "no target" );
			return false;
		}
		if( !( evt.isVisible ) )
		{
			LogFailure( "target is not visible" );
			return false;
		}
		if( evt.target.IsPlayer() )
		{
			deviceLink = ( ( PuppetDeviceLinkPS )( owner.GetDeviceLink() ) );
			if( deviceLink )
			{
				deviceLink.NotifyAboutSpottingPlayer( true );
			}
			if( ( IsPlayerAiming() && IsReactionAvailableInPreset( gamedataStimType.AimingAt ) ) || DidTargetMakeMeAlerted( evt.target ) )
			{
				AIActionHelper.TryStartCombatWithTarget( ownerPuppet, evt.target );
				LogSuccess( "starting combat with target (target aiming at owner)" );
				return false;
			}
			broadcaster = evt.target.GetStimBroadcasterComponent();
			securitySystem = ownerPuppet.GetSecuritySystem();
			if( m_reactionPreset.Type() == gamedataReactionPresetType.Civilian_Guard || m_reactionPreset.Type() == gamedataReactionPresetType.Civilian_Grabbable )
			{
				broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.SecurityBreach, owner );
				LogSuccess( "broadcasted SecurityBreach to owner" );
			}
			else if( securitySystem )
			{
				securitySystemInput = deviceLink.ActionSecurityBreachNotification( evt.target.GetWorldPosition(), evt.target, ESecurityNotificationType.DEFAULT );
				if( securitySystem.DetermineSecuritySystemState( securitySystemInput, true ) == ESecuritySystemState.COMBAT )
				{
					AIActionHelper.TryStartCombatWithTarget( ownerPuppet, evt.target );
					LogSuccess( "starting combat with target (security system in combat)" );
				}
				else
				{
					ownerPuppet.TriggerSecuritySystemNotification( evt.target.GetWorldPosition(), evt.target, ESecurityNotificationType.DEFAULT );
					LogSuccess( "triggered security system notification" );
				}
			}
			if( IsTargetArmed( evt.target ) && broadcaster )
			{
				broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.WeaponDisplayed, owner );
				LogSuccess( "broadcasted WeaponDisplayed to owner" );
			}
			if( StatusEffectSystem.ObjectHasStatusEffect( evt.target, T"PreventionStatusEffect.PerformingIllegalAction" ) && IsReactionAvailableInPreset( gamedataStimType.IllegalAction ) )
			{
				broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.IllegalAction, owner );
				LogSuccess( "broadcasted IllegalAction to owner" );
			}
			return false;
		}
		if( ( ( m_highLevelState == gamedataNPCHighLevelState.Combat && ownerPuppet.IsPrevention() ) && ( ( ScriptedPuppet )( evt.target ) ).GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Combat ) && SourceAttitude( evt.target, EAIAttitude.AIA_Neutral ) )
		{
			AIActionHelper.TryStartCombatWithTarget( ownerPuppet, evt.target );
			LogSuccess( "starting combat with target (already in combat)" );
			return false;
		}
		if( ( ( ( ( m_highLevelState != gamedataNPCHighLevelState.Combat && evt.target.IsPuppet() ) && !( evt.target.IsDead() ) ) && !( ownerPuppet.IsCharacterCivilian() ) ) && !( ownerPuppet.IsCharacterChildren() ) ) && SourceAttitude( evt.target, EAIAttitude.AIA_Friendly ) )
		{
			targetOfSource = GetCombatTarget( evt.target );
			if( ( ( IsTargetSquadAlly( evt.target ) || IsTargetInSameSecuritySystem( evt.target ) ) || ShouldHelpTargetFromSameAttitudeGroup( evt.target, targetOfSource ) ) || ShouldHelpCausePlayerGotTooClose( targetOfSource, onlyAlertNoThreat ) )
			{
				scriptedPuppetTarget = ( ( ScriptedPuppet )( evt.target ) );
				if( scriptedPuppetTarget.GetStimReactionComponent().GetReactionPreset().Type() == gamedataReactionPresetType.Civilian_Guard || scriptedPuppetTarget.GetStimReactionComponent().GetReactionPreset().Type() == gamedataReactionPresetType.Civilian_Grabbable )
				{
					if( scriptedPuppetTarget.GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Fear && m_highLevelState == gamedataNPCHighLevelState.Relaxed )
					{
						NPCPuppet.ChangeHighLevelState( owner, gamedataNPCHighLevelState.Alerted );
						LogSuccess( "entering alerted state (ally in fear)" );
					}
				}
				else
				{
					LogInfo( "HelpAlly attempt - friend detected" );
					HelpAlly( scriptedPuppetTarget, targetOfSource, onlyAlertNoThreat );
				}
			}
		}
		if( ( evt.description != 'Dead_Body' && evt.description != 'Unconscious' ) && evt.description != 'HeartAttack' )
		{
			return false;
		}
		LogInfo( "handling dead/defeated body" );
		if( HasCombatTarget() )
		{
			if( m_ignoreList.Contains( evt.target.GetEntityID() ) )
			{
				LogSuccess( "body already ignored" );
			}
			else
			{
				m_ignoreList.PushBack( ignoreListEvent.bodyID );
				ignoreListEvent = new IgnoreListEvent;
				ignoreListEvent.bodyID = evt.target.GetEntityID();
				SendIgnoreEventToSquad( ignoreListEvent );
				LogSuccess( "new body ignored" );
			}
			return false;
		}
		broadcaster = evt.target.GetStimBroadcasterComponent();
		if( broadcaster )
		{
			if( evt.description == 'Dead_Body' )
			{
				investigateData.attackInstigator = EntityGameInterface.GetEntity( GameInstance.GetPlayerSystem( owner.GetGame() ).GetLocalPlayerControlledGameObject().GetEntity() );
			}
			broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.DeadBody, owner, investigateData );
			LogSuccess( "broadcasted DeadBody to owner" );
		}
	}

	protected event OnSecurityAreaCrossingPerimeter( evt : SecurityAreaCrossingPerimeter )
	{
		var target : weak< GameObject >;
		if( !( IsEnabled() ) )
		{
			return NULL;
		}
		if( GetOwner() )
		{
			target = evt.GetWhoBreached();
		}
		if( ( target && target.IsPlayer() ) && IsTargetDetected( target ) )
		{
			if( m_reactionPreset.Type() != gamedataReactionPresetType.Civilian_Guard || m_reactionPreset.Type() != gamedataReactionPresetType.Civilian_Grabbable )
			{
				GetOwnerPuppet().TriggerSecuritySystemNotification( target.GetWorldPosition(), target, ESecurityNotificationType.DEFAULT );
			}
		}
	}

	protected event OnSecuritySystemOutput( evt : SecuritySystemOutput )
	{
		var debugFact : CName;
		if( GetOwnerPuppet().GetAreIncomingSecuritySystemEventsSuppressed() )
		{
			return NULL;
		}
		if( !( IsFinal() ) )
		{
			debugFact = StringToName( EntityID.ToDebugStringDecimal( m_owner_id ) );
			AddFact( GetOwnerPuppet().GetGame(), debugFact );
		}
		ReactToSecuritySystemOutputByTask( evt );
	}

	protected event OnReprimandEscalationEvent( evt : ReprimandEscalationEvent )
	{
		if( evt.startReprimand )
		{
			StartEscalateReprimand();
		}
		else if( evt.startDeescalate )
		{
			DeescalateReprimand();
		}
		else
		{
			ReprimandEscalation();
		}
	}

	private function StartEscalateReprimand()
	{
		var statPoolSys : StatPoolsSystem;
		statPoolSys = GameInstance.GetStatPoolsSystem( GetOwner().GetGame() );
		statPoolSys.RequestAddingStatPool( GetOwner().GetEntityID(), T"BaseStatPools.ReprimandEscalation", true );
	}

	private function ReprimandEscalation()
	{
		var statPoolSys : StatPoolsSystem;
		var statPoolMod : StatPoolModifier;
		var owner : GameObject;
		var ownerId : EntityID;
		owner = GetOwner();
		ownerId = owner.GetEntityID();
		statPoolSys = GameInstance.GetStatPoolsSystem( owner.GetGame() );
		statPoolSys.GetModifier( ownerId, gamedataStatPoolType.ReprimandEscalation, gameStatPoolModificationTypes.Regeneration, statPoolMod );
		statPoolMod.enabled = true;
		statPoolSys.RequestSettingModifier( ownerId, gamedataStatPoolType.ReprimandEscalation, gameStatPoolModificationTypes.Regeneration, statPoolMod );
	}

	private function DeescalateReprimand()
	{
		var statPoolSys : StatPoolsSystem;
		var regenMod : StatPoolModifier;
		var decayMod : StatPoolModifier;
		var owner : GameObject;
		var ownerId : EntityID;
		owner = GetOwner();
		ownerId = owner.GetEntityID();
		statPoolSys = GameInstance.GetStatPoolsSystem( owner.GetGame() );
		statPoolSys.GetModifier( ownerId, gamedataStatPoolType.ReprimandEscalation, gameStatPoolModificationTypes.Regeneration, regenMod );
		regenMod.enabled = false;
		statPoolSys.RequestSettingModifier( ownerId, gamedataStatPoolType.ReprimandEscalation, gameStatPoolModificationTypes.Regeneration, regenMod );
		statPoolSys.GetModifier( ownerId, gamedataStatPoolType.ReprimandEscalation, gameStatPoolModificationTypes.Decay, decayMod );
		decayMod.enabled = true;
		statPoolSys.RequestSettingModifier( ownerId, gamedataStatPoolType.ReprimandEscalation, gameStatPoolModificationTypes.Decay, decayMod );
	}

	protected event OnReprimandUpdate( evt : ReprimandUpdate )
	{
		var trespasserID : EntityID;
		var trespasser : GameObject;
		var reprimandUpdateEvent : StimuliEvent;
		var broadcaster : StimBroadcasterComponent;
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		if( !( IsEnabled() ) )
		{
			return false;
		}
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		if( ownerPuppet.GetAreIncomingSecuritySystemEventsSuppressed() )
		{
			return false;
		}
		if( evt.reprimandInstructions == EReprimandInstructions.CONCLUDE_SUCCESSFUL )
		{
			reprimandUpdateEvent = new StimuliEvent;
			reprimandUpdateEvent.name = 'targetComplies';
			owner.QueueEvent( reprimandUpdateEvent );
		}
		if( evt.reprimandInstructions == EReprimandInstructions.CONCLUDE_FAILED )
		{
			reprimandUpdateEvent = new StimuliEvent;
			reprimandUpdateEvent.name = 'concludeFailed';
			owner.QueueEvent( reprimandUpdateEvent );
		}
		if( evt.reprimandInstructions == EReprimandInstructions.RELEASE_TO_ANOTHER_ENTITY )
		{
			reprimandUpdateEvent = new StimuliEvent;
			reprimandUpdateEvent.name = 'exitReprimand';
		}
		trespasserID = evt.target;
		trespasser = ( ( GameObject )( GameInstance.FindEntityByID( owner.GetGame(), trespasserID ) ) );
		if( !( trespasser ) )
		{
			return false;
		}
		broadcaster = trespasser.GetStimBroadcasterComponent();
		if( VehicleComponent.IsMountedToVehicle( owner.GetGame(), trespasser ) )
		{
			AIActionHelper.TryStartCombatWithTarget( ownerPuppet, trespasser );
		}
		if( evt.reprimandInstructions == EReprimandInstructions.INITIATE_FIRST )
		{
			if( RecentReaction( gamedataOutput.AskToHolster ) )
			{
				broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.ReprimandFinalWarning, owner );
			}
			else
			{
				broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.Reprimand, owner );
			}
		}
		if( evt.reprimandInstructions == EReprimandInstructions.INITIATE_SUCCESSIVE )
		{
			broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.ReprimandFinalWarning, owner );
		}
		if( evt.reprimandInstructions == EReprimandInstructions.TAKEOVER )
		{
			broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.Reprimand, owner );
		}
	}

	private function RecentReaction( behaviorName : gamedataOutput ) : Bool
	{
		var reactionData : AIReactionData;
		var i : Int32;
		var simTime : Float;
		simTime = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) );
		for( i = 0; i < m_reactionCache.Size(); i += 1 )
		{
			reactionData = m_reactionCache[ i ];
			if( reactionData.reactionBehaviorName == behaviorName && ( reactionData.recentReactionTimeStamp >= simTime ) )
			{
				return true;
			}
		}
		return false;
	}

	protected event OnSuspiciousObjectEvent( evt : SuspiciousObjectEvent )
	{
		if( evt.target.IsPlayer() )
		{
			AIActionHelper.TryChangingAttitudeToHostile( ( ( ScriptedPuppet )( GetOwner() ) ), evt.target );
		}
	}

	private function GetCombatTarget( source : weak< GameObject >, optional attacker : weak< Entity > ) : weak< GameObject >
	{
		var aiComponent : AIHumanComponent;
		var commandCombatTargetVariant : Variant;
		var threat : TrackedLocation;
		if( attacker )
		{
			return ( ( weak< weak< GameObject > > )( attacker ) );
		}
		aiComponent = ( ( ScriptedPuppet )( source ) ).GetAIControllerComponent();
		if( aiComponent )
		{
			commandCombatTargetVariant = aiComponent.GetBehaviorArgument( 'CommmandCombatTarget' );
			if( commandCombatTargetVariant.IsValid() )
			{
				return ( ( weak< weak< GameObject > > )commandCombatTargetVariant );
			}
		}
		if( source.GetTargetTrackerComponent().GetTopHostileThreat( false, threat ) )
		{
			return ( ( weak< weak< GameObject > > )( threat.entity ) );
		}
		return NULL;
	}

	private function IsSourceGrenade( stimEvent : StimuliEvent ) : Bool
	{
		return ( ( BaseGrenade )( stimEvent.sourceObject ) );
	}

	private function GetGrenadeInstigator( stimEvent : StimuliEvent ) : weak< GameObject >
	{
		return ( ( BaseGrenade )( stimEvent.sourceObject ) ).GetUser();
	}

	private function JoinSearchWithAlert( ownerPuppet : ScriptedPuppet, target : weak< GameObject >, timeToLive : Float )
	{
		TriggerAlerted( target );
		if( !( TargetTrackingExtension.HasHostileThreat( ownerPuppet ) ) )
		{
			LogInfo( "HelpAlly - injected positional threat" );
			TargetTrackingExtension.InjectThreat( ownerPuppet, target.GetWorldPosition(), timeToLive );
		}
	}

	private function HelpAllyWithAlert( ownerPuppet : ScriptedPuppet, target : weak< GameObject >, timeToLive : Float )
	{
		JoinSearchWithAlert( ownerPuppet, target, timeToLive );
	}

	private function HelpAlly( ally : weak< GameObject >, targetOfAlly : weak< GameObject >, onlyAlertNoThreat : Bool, optional dontTrySquad : Bool )
	{
		var threat : TrackedLocation;
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		if( !( ally ) )
		{
			LogFailure( "HelpAlly failure - no ally" );
			return;
		}
		if( m_reactionPreset.Type() == gamedataReactionPresetType.NoReaction )
		{
			LogFailure( "HelpAlly failure - NoReaction reaction preset" );
			return;
		}
		if( targetOfAlly && ally.GetTargetTrackerComponent().ThreatFromEntity( targetOfAlly, threat ) )
		{
			if( threat.entity && AIActionHelper.TryChangingAttitudeToHostile( ownerPuppet, ( ( GameObject )( threat.entity ) ) ) )
			{
				if( onlyAlertNoThreat )
				{
					HelpAllyWithAlert( ownerPuppet, targetOfAlly, 0.5 );
					LogSuccess( "HelpAlly success - alerted about target" );
				}
				else
				{
					TargetTrackingExtension.InjectThreat( ownerPuppet, threat );
					LogSuccess( "HelpAlly success - added target threat" );
				}
			}
			return;
		}
		if( !( ScriptedPuppet.IsActive( ally ) ) )
		{
			HelpAllyWithAlert( ownerPuppet, ally, 5.0 );
			LogSuccess( "HelpAlly success - alerted about dead ally" );
			return;
		}
		if( ( ( ( !( dontTrySquad ) && targetOfAlly ) && IsTargetInFront( ally ) ) && IsSquadMateInDanger() ) && AIActionHelper.TryChangingAttitudeToHostile( ownerPuppet, targetOfAlly ) )
		{
			HelpAllyWithAlert( ownerPuppet, targetOfAlly, 5.0 );
			LogSuccess( "HelpAlly success - alerted about target (ally reacting to intruder)" );
			return;
		}
		LogFailure( "HelpAlly failure" );
	}

	private function HelpPlayer( enemy : weak< GameObject > )
	{
		if( !( enemy ) )
		{
			LogFailure( "HelpPlayer failure - no enemy" );
			return;
		}
		if( m_reactionPreset.Type() == gamedataReactionPresetType.NoReaction )
		{
			LogFailure( "HelpPlayer failure - NoReaction reaction preset" );
			return;
		}
		if( AIActionHelper.TryStartCombatWithTarget( GetOwnerPuppet(), enemy ) )
		{
			LogSuccess( "HelpPlayer success - added target threat" );
			return;
		}
		LogFailure( "HelpPlayer failure" );
	}

	private function ReactToSecurityOutput( evt : SecuritySystemOutput )
	{
		var trespasser : weak< GameObject >;
		var deviceNotifier : GameObject;
		if( !( IsEnabled() ) )
		{
			return;
		}
		if( evt.GetOriginalInputEvent().GetNotificationType() == ESecurityNotificationType.DEVICE_DESTROYED && evt.GetCachedSecurityState() != ESecuritySystemState.COMBAT )
		{
			deviceNotifier = ( ( GameObject )( evt.GetOriginalInputEvent().GetNotifierHandle().GetOwnerEntityWeak() ) );
			StimBroadcasterComponent.SendStimDirectly( deviceNotifier, gamedataStimType.ProjectileDistraction, GetOwner() );
			return;
		}
		if( !( evt.GetSecurityStateChanged() ) && ReflectSecSysStateToHLS( evt.GetCachedSecurityState() ) )
		{
			return;
		}
		trespasser = evt.GetOriginalInputEvent().GetWhoBreached();
		switch( evt.GetCachedSecurityState() )
		{
			case ESecuritySystemState.COMBAT:
				if( evt.GetBreachOrigin() == EBreachOrigin.LOCAL && trespasser )
				{
					if( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'LoreAnim' ) )
					{
						m_cacheSecuritySysOutput = evt;
					}
					else
					{
						TriggerCombat( trespasser );
					}
				}
				else if( !( trespasser ) || ( trespasser && trespasser.IsPlayer() ) )
				{
					TriggerAlerted( trespasser );
				}
			break;
			case ESecuritySystemState.ALERTED:
				if( !( trespasser ) || ( trespasser && trespasser.IsPlayer() ) )
				{
					if( evt.GetOriginalInputEvent().GetNotificationType() == ESecurityNotificationType.ALARM )
					{
						if( evt.GetOriginalInputEvent().GetStimTypeTriggeredAlarm() == gamedataStimType.DeadBody )
						{
							m_isAlertedByDeadBody = true;
						}
						else
						{
							m_stolenVehicle = ( ( VehicleObject )( evt.GetOriginalInputEvent().GetNotifierHandle().GetOwnerEntityWeak() ) );
						}
					}
					TriggerAlerted( trespasser );
				}
			break;
			default:
				break;
		}
	}

	private function ReflectSecSysStateToHLS( securityState : ESecuritySystemState ) : Bool
	{
		switch( securityState )
		{
			case ESecuritySystemState.COMBAT:
				if( m_highLevelState == gamedataNPCHighLevelState.Combat )
				{
					return true;
				}
			return false;
			case ESecuritySystemState.ALERTED:
				if( m_highLevelState == gamedataNPCHighLevelState.Alerted )
				{
					return true;
				}
			return false;
			case ESecuritySystemState.SAFE:
				if( m_highLevelState == gamedataNPCHighLevelState.Relaxed )
				{
					return true;
				}
			return false;
			default:
				return false;
		}
	}

	private function TriggerAlerted( optional instigator : weak< GameObject > )
	{
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		if( ( ( !( ownerPuppet.GetSecuritySystem().IsReprimandOngoing() ) && ownerPuppet.GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Relaxed ) && !( ownerPuppet.IsCharacterCivilian() ) ) && !( ownerPuppet.IsCharacterChildren() ) )
		{
			NPCPuppet.ChangeHighLevelState( GetOwner(), gamedataNPCHighLevelState.Alerted );
			if( instigator )
			{
				m_recentAlertObject = instigator;
				m_recentAlertTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + TweakDBInterface.GetFloat( T"AIGeneralSettings.recentAlertValidTimeStamp", 20.0 );
			}
		}
	}

	private function TriggerCombat( trespasser : weak< GameObject > )
	{
		var ownerPuppet : ScriptedPuppet;
		var broadcaster : StimBroadcasterComponent;
		ownerPuppet = GetOwnerPuppet();
		if( ownerPuppet.IsCharacterCivilian() )
		{
			broadcaster = trespasser.GetStimBroadcasterComponent();
			if( broadcaster )
			{
				broadcaster.SendDrirectStimuliToTarget( GetOwner(), gamedataStimType.SecurityBreach, GetOwner() );
			}
		}
		AIActionHelper.TryStartCombatWithTarget( ownerPuppet, trespasser );
	}

	protected event OnReactionBehaviorStatus( evt : ReactionBehaviorStatus )
	{
		switch( evt.status )
		{
			case AIbehaviorUpdateOutcome.IN_PROGRESS:
				if( m_desiredReaction )
				{
					OnReactionStarted( evt.reactionData );
				}
				else
				{
					m_activeReaction = evt.reactionData;
				}
			break;
			default:
				OnReactionEnded();
			break;
		}
	}

	protected event OnEndLookatEvent( evt : EndLookatEvent )
	{
		if( !( m_inReactionSequence ) )
		{
			DeactiveLookAt( evt.repeat );
		}
	}

	protected event OnDisableUndeadAnimFeatureEvent( evt : DisableUndeadAnimFeatureEvent )
	{
		var undeadAnimFeature : AnimFeature_Undead;
		undeadAnimFeature = new AnimFeature_Undead;
		undeadAnimFeature.active = false;
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'Undead', undeadAnimFeature );
	}

	protected event OnTerminateReactionLookatEvent( evt : TerminateReactionLookatEvent )
	{
		DeactiveLookAt();
	}

	protected event OnRepeatLookatEvent( evt : RepeatLookatEvent )
	{
		m_lookatRepeat = false;
		if( m_playerProximity && !( m_inReactionSequence ) )
		{
			TriggerFacialLookAtReaction( false, true );
		}
	}

	protected event OnEventReceived( stimEvent : StimuliEvent )
	{
		if( !( IsStimuliEventValid( stimEvent ) ) )
		{
			return NULL;
		}
		HandleStimEventByTask( stimEvent );
	}

	protected event OnAIEvent( aiEvent : AIEvent )
	{
		if( aiEvent.name == 'ReprimandSuccessful' )
		{
			GetOwnerPuppet().TriggerSecuritySystemNotification( m_activeReaction.stimTarget.GetWorldPosition(), m_activeReaction.stimTarget, ESecurityNotificationType.REPRIMAND_SUCCESSFUL );
		}
		if( aiEvent.name == 'TriggerCombatReaction' )
		{
			TriggerPendingReaction();
			m_inPendingBehavior = false;
		}
		if( aiEvent.name == 'TriggerPendingReaction' )
		{
			TriggerPendingReaction();
			m_inPendingBehavior = false;
		}
	}

	private function Initialiaze()
	{
		var puppetBlackboard : IBlackboard;
		m_delay = TweakDBInterface.GetVector2( T"AIGeneralSettings.reactionDelay", Vector2( 0.30000001, 0.69999999 ) );
		puppetBlackboard = GetOwnerPuppet().GetPuppetStateBlackboard();
		if( puppetBlackboard )
		{
			if( puppetBlackboard.GetBool( GetAllBlackboardDefs().PuppetState.InPendingBehavior ) )
			{
				m_inPendingBehavior = true;
			}
			m_pendingBehaviorCb = puppetBlackboard.RegisterListenerBool( GetAllBlackboardDefs().PuppetState.InPendingBehavior, this, 'OnPendingBehaviorChanged' );
		}
	}

	private function CacheEvent( stimData : StimEventTaskData )
	{
		var stimEvent : StimuliEvent;
		stimEvent = stimData.cachedEvt;
		if( m_stimuliCache.Size() > 0 )
		{
			if( !( IsEventDuplicated( stimEvent ) ) )
			{
				if( m_stimuliCache.Size() > 4 )
				{
					m_stimuliCache.Erase( 0 );
				}
				stimData.id = ( ( Uint32 )( m_stimuliCache.Size() ) );
				m_stimuliCache.PushBack( stimData );
			}
		}
		else
		{
			stimData.id = ( ( Uint32 )( m_stimuliCache.Size() ) );
			m_stimuliCache.PushBack( stimData );
		}
	}

	private function CacheReaction( reactionData : AIReactionData )
	{
		if( m_reactionCache.Size() > 0 )
		{
			if( m_reactionCache.Size() > 4 )
			{
				m_reactionCache.Erase( 0 );
			}
			reactionData.recentReactionTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + TweakDBInterface.GetFloat( T"AIGeneralSettings.recentReactionValidTimeStamp", 120.0 );
			m_reactionCache.PushBack( reactionData );
		}
		else
		{
			reactionData.recentReactionTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + TweakDBInterface.GetFloat( T"AIGeneralSettings.recentReactionValidTimeStamp", 120.0 );
			m_reactionCache.PushBack( reactionData );
		}
	}

	private function IsEventDuplicated( stimEvent : StimuliEvent ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_stimuliCache.Size(); i += 1 )
		{
			if( IsDuplicate( stimEvent, m_stimuliCache[ i ].cachedEvt ) )
			{
				return true;
			}
		}
		return false;
	}

	private function IsDuplicate( stimEvent : StimuliEvent, cacheStim : StimuliEvent ) : Bool
	{
		if( ( stimEvent.sourceObject == cacheStim.sourceObject ) && stimEvent.GetStimType() == cacheStim.GetStimType() )
		{
			return true;
		}
		return false;
	}

	protected event OnStimThresholdEvent( thresholdEvent : StimThresholdEvent )
	{
		var delayEvent : StimThresholdEvent;
		if( thresholdEvent.reset )
		{
			m_currentStimThresholdValue = 0;
			m_timeStampThreshold = 0.0;
		}
		else
		{
			if( ( m_currentStimThresholdValue == 0 ) && ( m_timeStampThreshold == 0.0 ) )
			{
				m_timeStampThreshold = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + thresholdEvent.timeThreshold;
				delayEvent = new StimThresholdEvent;
				delayEvent.reset = true;
				GameInstance.GetDelaySystem( GetOwnerPuppet().GetGame() ).DelayEvent( GetOwner(), delayEvent, thresholdEvent.timeThreshold );
			}
			m_currentStimThresholdValue += 1;
		}
	}

	protected event OnStealthStimThresholdEvent( thresholdEvent : StealthStimThresholdEvent )
	{
		var delayEvent : StealthStimThresholdEvent;
		if( thresholdEvent.reset )
		{
			m_currentStealthStimThresholdValue = 0;
			m_stealthTimeStampThreshold = 0.0;
		}
		else
		{
			if( ( m_currentStealthStimThresholdValue == 0 ) && ( m_stealthTimeStampThreshold == 0.0 ) )
			{
				m_stealthTimeStampThreshold = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + thresholdEvent.timeThreshold;
				delayEvent = new StealthStimThresholdEvent;
				delayEvent.reset = true;
				GameInstance.GetDelaySystem( GetOwnerPuppet().GetGame() ).DelayEvent( GetOwner(), delayEvent, thresholdEvent.timeThreshold );
			}
			m_currentStealthStimThresholdValue += 1;
		}
	}

	public function GetIgnoreList() : array< EntityID >
	{
		return m_ignoreList;
	}

	private function IsStimuliEventValid( stimEvent : StimuliEvent ) : Bool
	{
		var owner : GameObject;
		if( ( stimEvent.sourceObject == NULL ) || Vector4.IsZero( stimEvent.sourcePosition ) )
		{
			return false;
		}
		owner = GetOwner();
		if( stimEvent.sourceObject == owner )
		{
			return false;
		}
		if( stimEvent.GetStimType() == gamedataStimType.Invalid )
		{
			return false;
		}
		if( owner.IsPlayer() )
		{
			return false;
		}
		return true;
	}

	private function ShouldEventBeProcessed( stimEvent : StimuliEvent ) : Bool
	{
		if( StimFilters.IsForTheDead( stimEvent.GetStimType() ) )
		{
			return false;
		}
		if( !( ScriptedPuppet.IsActive( GetOwner() ) ) )
		{
			LogFailure( "owner inactive" );
			return false;
		}
		if( !( stimEvent.stimRecord.IsReactionStim() ) )
		{
			LogFailure( "NOT reaction stim" );
			return false;
		}
		if( m_reactionPreset.Type() == gamedataReactionPresetType.NoReaction )
		{
			LogFailure( "NoReaction reaction preset" );
			return false;
		}
		if( m_puppetReactionBlackboard.GetBool( GetAllBlackboardDefs().PuppetReaction.blockReactionFlag ) )
		{
			LogFailure( "PuppetReaction.blockReactionFlag" );
			return false;
		}
		return true;
	}

	private function ShouldVisualStimBeProcessed( stimEvent : StimuliEvent, reactionData : AIReactionData ) : Bool
	{
		var owner : GameObject;
		if( stimEvent.sourceObject.IsPuppet() )
		{
			if( IsTargetVisible( stimEvent.sourceObject ) || IsTargetVisibleBeyondSenses( stimEvent, reactionData ) )
			{
				return true;
			}
			LogFailure( "stim source not visible" );
			return false;
		}
		if( !( IsTargetInFront( stimEvent.sourceObject ) ) )
		{
			LogFailure( "non-puppet stim source behind" );
			return false;
		}
		if( !( IsTargetClose( stimEvent.sourceObject, m_crowdAimingReactionDistance ) ) )
		{
			LogFailure( "non-puppet stim source too far" );
			return false;
		}
		owner = GetOwner();
		if( GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) )
		{
			if( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Braindance' ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Sleep' ) )
			{
				LogFailure( "owner sleeping or in braindance" );
				return false;
			}
		}
		return true;
	}

	private function ShouldAudioStimBeProcessed( stimEvent : StimuliEvent ) : Bool
	{
		if( IsTargetVisible( stimEvent.sourceObject ) )
		{
			return true;
		}
		if( !( GetOwnerPuppet().GetSensesComponent().IsHearingEnabled() ) )
		{
			LogFailure( "owner hearing disabled" );
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'HearingImpaired' ) && !( CheckHearingDistance( stimEvent ) ) )
		{
			LogFailure( "owner hearing impaired" );
			return false;
		}
		return true;
	}

	private function ShouldFriendlyStimBeProcessed( stimEvent : StimuliEvent, investigateData : stimInvestigateData ) : Bool
	{
		var stimType : gamedataStimType;
		var source : weak< GameObject >;
		var targetOfSource : weak< GameObject >;
		var isSourceGrenade : Bool;
		var sourceNotAlly : Bool;
		var threatenedByNPCGrenade : Bool;
		var threatenedByPlayer : Bool;
		var onlyAlertNoThreat : Bool;
		var allyPuppet : ScriptedPuppet;
		var allyReactionComponent : ReactionManagerComponent;
		var ownerPuppet : ScriptedPuppet;
		var ownerSecuritySystem : SecuritySystemControllerPS;
		var broadcaster : StimBroadcasterComponent;
		stimType = stimEvent.GetStimType();
		onlyAlertNoThreat = false;
		if( stimEvent.IsTagInStimuli( 'IgnoreFriendly' ) && !( stimEvent.sourceObject.IsDevice() ) )
		{
			LogFailure( "IgnoreFriendly" );
			return false;
		}
		if( !( StimFilters.CanTriggerAllyHelp( stimType ) ) )
		{
			return true;
		}
		isSourceGrenade = IsSourceGrenade( stimEvent );
		source = ( ( isSourceGrenade ) ? ( GetGrenadeInstigator( stimEvent ) ) : ( stimEvent.sourceObject ) );
		targetOfSource = ( ( isSourceGrenade ) ? ( GetCombatTarget( source ) ) : ( GetCombatTarget( source, investigateData.attackInstigator ) ) );
		threatenedByNPCGrenade = isSourceGrenade && ShouldReactToNPCGrenade( ( ( BaseGrenade )( stimEvent.sourceObject ) ) );
		sourceNotAlly = ( ( !( IsTargetSquadAlly( source ) ) && !( IsTargetRecentSquadAlly( source ) ) ) && !( IsTargetInSameSecuritySystem( source ) ) ) && !( ShouldHelpTargetFromSameAttitudeGroup( source, targetOfSource ) );
		threatenedByPlayer = ShouldHelpCausePlayerGotTooClose( targetOfSource, onlyAlertNoThreat );
		if( ( sourceNotAlly && !( threatenedByNPCGrenade ) ) && !( threatenedByPlayer ) )
		{
			LogFailure( "stim source is not ally" );
			return false;
		}
		LogInfo( "HelpAlly attempt - friendly stim" );
		HelpAlly( source, targetOfSource, onlyAlertNoThreat );
		allyPuppet = ( ( ScriptedPuppet )( source ) );
		ownerPuppet = GetOwnerPuppet();
		if( allyPuppet && ownerPuppet )
		{
			allyReactionComponent = allyPuppet.GetStimReactionComponent();
			ownerSecuritySystem = ownerPuppet.GetSecuritySystem();
			if( allyReactionComponent && ownerSecuritySystem )
			{
				if( ( ( ( ownerSecuritySystem.IsReprimandOngoing() && ownerSecuritySystem.GetReprimandPerformer() ) || allyReactionComponent.GetReactionPreset().Type() == gamedataReactionPresetType.Civilian_Guard ) || allyReactionComponent.GetReactionPreset().Type() == gamedataReactionPresetType.Civilian_Grabbable ) || allyReactionComponent.GetActiveReactionData().reactionBehaviorName == gamedataOutput.BackOff )
				{
					broadcaster = GetPlayerSystem().GetLocalPlayerControlledGameObject().GetStimBroadcasterComponent();
					if( broadcaster )
					{
						broadcaster.SendDrirectStimuliToTarget( GetOwner(), gamedataStimType.SoundDistraction, GetOwner() );
						LogSuccess( "broadcasted SoundDistraction to owner" );
					}
					return false;
				}
			}
		}
		return StimFilters.CanProcessPastAllyHelp( stimType );
	}

	private function ShouldNeutralStimBeProcessed( stimEvent : StimuliEvent, investigateData : stimInvestigateData ) : Bool
	{
		var stimType : gamedataStimType;
		var source : weak< GameObject >;
		var isSourceGrenade : Bool;
		var threatenedByNPCGrenade : Bool;
		var targetOfSource : weak< GameObject >;
		var onlyAlertNoThreat : Bool;
		stimType = stimEvent.GetStimType();
		if( m_highLevelState == gamedataNPCHighLevelState.Combat || !( StimFilters.CanTriggerNeutralHelp( stimType ) ) )
		{
			return true;
		}
		isSourceGrenade = IsSourceGrenade( stimEvent );
		source = ( ( isSourceGrenade ) ? ( GetGrenadeInstigator( stimEvent ) ) : ( stimEvent.sourceObject ) );
		targetOfSource = ( ( isSourceGrenade ) ? ( GetCombatTarget( source ) ) : ( GetCombatTarget( source, investigateData.attackInstigator ) ) );
		threatenedByNPCGrenade = isSourceGrenade && ShouldReactToNPCGrenade( ( ( BaseGrenade )( stimEvent.sourceObject ) ) );
		if( ShouldHelpCausePlayerGotTooClose( targetOfSource, onlyAlertNoThreat ) || threatenedByNPCGrenade )
		{
			LogInfo( "HelpAlly attempt - neutral stim" );
			HelpAlly( source, targetOfSource, onlyAlertNoThreat, true );
			return true;
		}
		return true;
	}

	private function ShouldHostileStimBeProcessed( stimEvent : StimuliEvent, investigateData : stimInvestigateData ) : Bool
	{
		var stimType : gamedataStimType;
		var source : weak< GameObject >;
		var targetOfSource : weak< GameObject >;
		var isSourceGrenade : Bool;
		var threatenedByNPCGrenade : Bool;
		var onlyAlertNoThreat : Bool;
		stimType = stimEvent.GetStimType();
		if( m_highLevelState == gamedataNPCHighLevelState.Combat || !( StimFilters.CanTriggerEnemyHostility( stimType ) ) )
		{
			return true;
		}
		isSourceGrenade = IsSourceGrenade( stimEvent );
		source = ( ( isSourceGrenade ) ? ( GetGrenadeInstigator( stimEvent ) ) : ( stimEvent.sourceObject ) );
		targetOfSource = ( ( isSourceGrenade ) ? ( GetCombatTarget( source ) ) : ( GetCombatTarget( source, investigateData.attackInstigator ) ) );
		threatenedByNPCGrenade = isSourceGrenade && ShouldReactToNPCGrenade( ( ( BaseGrenade )( stimEvent.sourceObject ) ) );
		if( ShouldHelpCausePlayerGotTooClose( targetOfSource, onlyAlertNoThreat ) || threatenedByNPCGrenade )
		{
			LogInfo( "HelpPlayer attempt - hostile stim" );
			HelpPlayer( source );
			return true;
		}
		return true;
	}

	private function GetRealStimSource( stimEvent : StimuliEvent ) : weak< GameObject >
	{
		var source : weak< GameObject >;
		var investigateData : stimInvestigateData;
		if( IsSourceGrenade( stimEvent ) )
		{
			source = GetGrenadeInstigator( stimEvent );
		}
		else if( StatusEffectSystem.ObjectHasStatusEffect( stimEvent.sourceObject, T"BaseStatusEffect.ImpactedThrownNPC" ) )
		{
			investigateData = stimEvent.stimInvestigateData;
			source = ( ( PlayerPuppet )( investigateData.attackInstigator ) );
		}
		if( !( source ) )
		{
			source = stimEvent.sourceObject;
		}
		return source;
	}

	private function ShouldStimBeProcessed( stimEvent : StimuliEvent, delayed : Bool ) : Bool
	{
		var reactionData : AIReactionData;
		var investigateData : stimInvestigateData;
		var attackInstigator : weak< Entity >;
		var source : weak< GameObject >;
		var investigators : array< EntityID >;
		var reevaluateDetectionOverwriteEvent : ReevaluateDetectionOverwriteEvent;
		var delayStimEvent : DelayStimEvent;
		var device : Device;
		var stimType : gamedataStimType;
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		var delaySystem : DelaySystem;
		var aiComponent : AIHumanComponent;
		var context : ScriptExecutionContext;
		var friendlyStim : Bool;
		var isCooldownActiveOnStim : Bool;
		var canIgnoreOnlyDueToDelay : Bool;
		var canIgnorePlayerCombatStim : Bool;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		stimType = stimEvent.GetStimType();
		if( !( StimFilters.Debug_IsAllowed( stimType ) ) )
		{
			LogFailure( "disabled by debug" );
			return false;
		}
		if( stimType == gamedataStimType.MeleeAttack && WeaponObject.IsFists( GameObject.GetActiveWeapon( stimEvent.sourceObject ).GetItemID() ) )
		{
			LogFailure( "ignored for fists weapon" );
			return false;
		}
		if( stimType == gamedataStimType.WeaponDisplayed && !( CanAskToHolsterWeapon() ) )
		{
			LogFailure( "NOT CanAskToHolsterWeapon" );
			return false;
		}
		if( StimFilters.ShouldBeInPublicZone( stimType ) && !( IsPlayerInZone( gamePSMZones.Public ) ) )
		{
			LogFailure( "player is NOT in public zone" );
			return false;
		}
		if( m_highLevelState == gamedataNPCHighLevelState.Combat && !( StimFilters.AllowFriendlyInCombat( stimType ) ) )
		{
			source = GetRealStimSource( stimEvent );
			friendlyStim = SourceAttitude( source, EAIAttitude.AIA_Friendly );
			if( friendlyStim )
			{
				LogFailure( "NOT AllowFriendlyInCombat" );
				return false;
			}
		}
		reactionData = m_desiredReaction;
		if( !( reactionData ) )
		{
			reactionData = m_activeReaction;
		}
		if( !( stimEvent.IsTagInStimuli( 'Direct' ) ) && !( stimEvent.IsPurelyDirect() ) )
		{
			if( ( stimEvent.IsVisual() && !( ShouldVisualStimBeProcessed( stimEvent, reactionData ) ) ) || ( stimEvent.IsAudio() && !( ShouldAudioStimBeProcessed( stimEvent ) ) ) )
			{
				return false;
			}
		}
		if( StimFilters.IsIgnoredWhileGrappled( stimType ) && ScriptedPuppet.IsBeingGrappled( ownerPuppet ) )
		{
			LogFailure( "ignored while owner is grappled" );
			return false;
		}
		if( ( stimType == gamedataStimType.AimingAt && m_reactionPreset.IsAggressive() ) && IsReactionAvailableInPreset( gamedataStimType.AimingAt ) )
		{
			delaySystem = GameInstance.GetDelaySystem( owner.GetGame() );
			delaySystem.CancelDelay( m_delayDetectionEventID );
			reevaluateDetectionOverwriteEvent = new ReevaluateDetectionOverwriteEvent;
			reevaluateDetectionOverwriteEvent.target = stimEvent.sourceObject;
			m_delayDetectionEventID = delaySystem.DelayEvent( owner, reevaluateDetectionOverwriteEvent, 0.1 );
			delaySystem.CancelDelay( m_delayStimEventID );
			delayStimEvent = new DelayStimEvent;
			delayStimEvent.stimEvent = stimEvent;
			m_delayStimEventID = delaySystem.DelayEvent( owner, delayStimEvent, 0.34999999 );
			LogSuccess( "detection reevaluation and aim reaction delayed" );
			return false;
		}
		if( stimEvent.IsTagInStimuli( 'Stealth' ) && !( ReactOnPlayerStealthStim( owner, stimEvent.sourceObject ) ) )
		{
			LogFailure( "NOT ReactOnPlayerStealthStim" );
			return false;
		}
		if( stimEvent.IsTagInStimuli( 'Security' ) && !( ownerPuppet.IsConnectedToSecuritySystem() ) )
		{
			LogFailure( "NOT IsConnectedToSecuritySystem" );
			return false;
		}
		investigateData = stimEvent.stimInvestigateData;
		attackInstigator = investigateData.attackInstigator;
		if( stimEvent.IsTagInStimuli( 'PlayerOnly' ) && !( stimEvent.sourceObject.IsDevice() ) )
		{
			if( ( !( attackInstigator ) && !( stimEvent.sourceObject.IsPlayer() ) ) || ( attackInstigator && !( ( ( GameObject )( attackInstigator ) ).IsPlayer() ) ) )
			{
				LogFailure( "PlayerOnly constraints failed" );
				return false;
			}
		}
		if( !( source ) )
		{
			source = GetRealStimSource( stimEvent );
			friendlyStim = SourceAttitude( source, EAIAttitude.AIA_Friendly );
		}
		if( !( friendlyStim ) && StimFilters.ShouldBeFriendly( stimType ) )
		{
			LogFailure( "non-friendly stim source" );
			return false;
		}
		if( friendlyStim && !( ShouldFriendlyStimBeProcessed( stimEvent, investigateData ) ) )
		{
			return false;
		}
		if( SourceAttitude( source, EAIAttitude.AIA_Neutral ) && !( ShouldNeutralStimBeProcessed( stimEvent, investigateData ) ) )
		{
			return false;
		}
		if( SourceAttitude( source, EAIAttitude.AIA_Hostile ) && !( ShouldHostileStimBeProcessed( stimEvent, investigateData ) ) )
		{
			return false;
		}
		if( InvestigatingAlready( stimEvent ) )
		{
			LogFailure( "InvestigatingAlready" );
			return false;
		}
		if( ( IsSameStimulus( stimEvent ) && IsSameSourceObject( stimEvent ) ) && StimFilters.IsIgnoredFromSameSource( stimType ) )
		{
			LogFailure( "IsIgnoredFromSameSource" );
			return false;
		}
		if( reactionData && IsLowerPriority( stimEvent, reactionData.stimPriority ) )
		{
			LogFailure( "lower priority compared to current reaction" );
			return false;
		}
		if( m_ignoreList.Contains( stimEvent.sourceObject.GetEntityID() ) )
		{
			LogFailure( "stim source ignored" );
			return false;
		}
		if( GameObject.IsCooldownActive( owner, EnumValueToName( 'gamedataStimType', ( ( Int32 )( stimType ) ) ) ) )
		{
			LogFailure( "stim cooldown active" );
			return false;
		}
		if( ( stimType == gamedataStimType.AreaEffect && !( m_inPendingBehavior ) ) && !( ownerPuppet.IsCharacterCivilian() ) )
		{
			LogFailure( "special AreaEffect check failed" );
			return false;
		}
		isCooldownActiveOnStim = ( m_desiredReaction && m_desiredReaction.stimType == stimType ) && GameObject.IsCooldownActive( owner, StringToName( "ovefloodCooldown" + EnumValueToString( "gamedataStimType", ( ( Int32 )( stimType ) ) ) ) );
		if( isCooldownActiveOnStim )
		{
			LogFailure( "stim overflood cooldown active" );
			return false;
		}
		if( stimType == gamedataStimType.GrenadeLanded && !( ShouldTriggerGrenadeDodgeBehavior( stimEvent ) ) )
		{
			LogFailure( "NOT ShouldTriggerGrenadeDodgeBehavior" );
			return false;
		}
		if( stimType == gamedataStimType.DeviceExplosion && !( CanTriggerPanicInCombat( stimEvent ) ) )
		{
			LogFailure( "NOT CanTriggerPanicInCombat" );
			return false;
		}
		if( ownerPuppet.IsPrevention() && !( ShouldPreventionReact( stimEvent ) ) )
		{
			LogFailure( "NOT ShouldPreventionReact" );
			return false;
		}
		if( StimFilters.ShouldBeDetected( stimType ) && !( IsTargetDetected( stimEvent.sourceObject ) ) )
		{
			LogFailure( "stim source is not detected" );
			return false;
		}
		if( stimType == gamedataStimType.Distract )
		{
			device = ( ( Device )( stimEvent.sourceObject ) );
			investigators = device.GetDevicePS().GetWillingInvestigators();
			if( !( investigators.Contains( owner.GetEntityID() ) ) )
			{
				if( ( IsTargetInFront( stimEvent.sourceObject ) && !( ownerPuppet.IsInvestigating() ) ) && ( investigators.Size() != 0 ) )
				{
					LogSuccess( "lookat activated" );
					ActivateReactionLookAt( stimEvent.sourceObject, true, false, 1.0, true );
				}
				LogFailure( "no further investigation required" );
				return false;
			}
		}
		if( stimType == gamedataStimType.Explosion )
		{
			aiComponent = ownerPuppet.GetAIControllerComponent();
			if( ( aiComponent && ( ( ( weak< weak< GameObject > > )( aiComponent.GetBehaviorArgument( 'StimTarget' ) ) ) == reactionData.stimTarget ) ) && AIHumanComponent.GetScriptContext( ownerPuppet, context ) )
			{
				ScriptExecutionContext.SetArgumentObject( context, 'StimTarget', NULL );
				ScriptExecutionContext.GetTweakActionSystem( context ).ForceDirty( context, T"AIActionTarget.StimTarget" );
			}
		}
		if( ShouldIgnoreCombatStim( stimType, ( ( ScriptedPuppet )( investigateData.attackInstigator ) ), ( ( ScriptedPuppet )( stimEvent.sourceObject ) ), stimEvent.sourcePosition, !( delayed ), canIgnoreOnlyDueToDelay, canIgnorePlayerCombatStim, true ) )
		{
			if( canIgnoreOnlyDueToDelay )
			{
				delaySystem = GameInstance.GetDelaySystem( owner.GetGame() );
				delayStimEvent = new DelayStimEvent;
				delayStimEvent.stimEvent = stimEvent;
				delayStimEvent.fullEventPipeline = true;
				delaySystem.DelayEvent( owner, delayStimEvent, 0.25 );
				LogFailure( "combat stim delayed" );
				return false;
			}
			LogFailure( "combat stim explicitely ignored" );
			return false;
		}
		if( HasCombatTarget() )
		{
			if( CanStimInterruptCombat( stimEvent, canIgnorePlayerCombatStim ) )
			{
				ScriptedPuppet.SendActionSignal( ownerPuppet, 'GracefulCombatInterruption', 2.0 );
				m_inPendingBehavior = true;
				LogInfo( "GracefulCombatInterruption" );
			}
			else
			{
				if( ShouldUpdateThreatPosition( stimEvent ) )
				{
					ownerPuppet.GetTargetTrackerComponent().AddThreat( stimEvent.sourceObject, true, stimEvent.sourceObject.GetWorldPosition(), 1.0, -1.0, false );
					LogInfo( "updating threat position" );
				}
				LogFailure( "NOT CanStimInterruptCombat" );
				return false;
			}
		}
		return true;
	}

	private function ProcessStimParams( stimEvent : StimuliEvent ) : StimParams
	{
		var stimParams : StimParams;
		var reactionOutput : ReactionOutput;
		var rules : array< weak< Rule_Record > >;
		var owner : GameObject;
		owner = GetOwner();
		rules = GetRules();
		reactionOutput = GetReactionOutput( stimEvent.GetStimType(), rules );
		if( GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) )
		{
			reactionOutput.startedInWorkspot = true;
			if( GetOwnerPuppet().IsCharacterCivilian() )
			{
				reactionOutput.workspotReaction = GameInstance.GetWorkspotSystem( owner.GetGame() ).IsReactionAvailable( owner, reactionOutput.workspotReactionType );
			}
		}
		stimParams.stimData = FillStimData( stimEvent );
		stimParams.reactionOutput = reactionOutput;
		return stimParams;
	}

	private virtual function FillStimData( stimEvent : StimuliEvent ) : StimEventData
	{
		var data : StimEventData;
		data.source = stimEvent.sourceObject;
		data.stimType = stimEvent.GetStimType();
		return data;
	}

	private const function IsReactionAvailableInPreset( stimTrigger : gamedataStimType ) : Bool
	{
		var reactionPresetOutput : ReactionOutput;
		reactionPresetOutput = GetReactionOutput( stimTrigger, GetRules() );
		if( reactionPresetOutput.reactionBehavior != gamedataOutput.Ignore && reactionPresetOutput.reactionBehavior != gamedataOutput.Invalid )
		{
			return true;
		}
		return false;
	}

	private function CreateFearThreashold()
	{
		var statSystem : StatsSystem;
		var statMod : gameStatModifierData;
		statSystem = GameInstance.GetStatsSystem( GetOwner().GetGame() );
		statMod = RPGManager.CreateStatModifier( gamedataStatType.PersonalityFear, gameStatModifierType.Additive, m_reactionPreset.FearThreshold() );
		statSystem.AddModifier( GetOwner().GetEntityID(), statMod );
	}

	private function AddReactionValueToStatPool( reactionData : AIReactionData )
	{
		var statPoolSystem : StatPoolsSystem;
		var securitySystem : SecuritySystemControllerPS;
		var ownerPuppet : ScriptedPuppet;
		var ownerId : EntityID;
		ownerPuppet = GetOwnerPuppet();
		statPoolSystem = GameInstance.GetStatPoolsSystem( ownerPuppet.GetGame() );
		if( ( ( reactionData && reactionData.reactionBehaviorName == gamedataOutput.TurnAt ) && reactionData.stimTarget.IsPuppet() ) && IsTargetVisible( reactionData.stimTarget ) )
		{
			return;
		}
		securitySystem = ownerPuppet.GetSecuritySystem();
		if( securitySystem && !( securitySystem.IsReprimandOngoing() ) )
		{
			if( reactionData.stimRecord.Fear() > 0.0 )
			{
				ownerId = ownerPuppet.GetEntityID();
				statPoolSystem.RequestSettingModifierWithRecord( ownerId, gamedataStatPoolType.Fear, gameStatPoolModificationTypes.Decay, T"BaseStatPools.ReactionValueDecay" );
				statPoolSystem.RequestChangingStatPoolValue( ownerId, gamedataStatPoolType.Fear, reactionData.stimRecord.Fear(), NULL, true, false );
			}
		}
	}

	private const function GetRules() : array< weak< Rule_Record > >
	{
		var rules : array< weak< Rule_Record > >;
		m_reactionPreset.Rules( rules );
		return rules;
	}

	private const function GetReactionOutput( stimType : gamedataStimType, const rules : ref< array< weak< Rule_Record > > > ) : ReactionOutput
	{
		var reactionOutput : ReactionOutput;
		var i : Int32;
		for( i = 0; i < rules.Size(); i += 1 )
		{
			if( StimRule( rules[ i ], stimType ) )
			{
				reactionOutput.reactionPriority = rules[ i ].Output().Priority();
				reactionOutput.AIbehaviorPriority = rules[ i ].Output().AIPriority();
				reactionOutput.reactionCooldown = rules[ i ].Cooldown();
				reactionOutput.reactionBehavior = rules[ i ].Output().Type();
				reactionOutput.workspotReactionType = rules[ i ].WorkspotOutput();
				break;
			}
			else
			{
				reactionOutput.reactionBehavior = gamedataOutput.Ignore;
			}
		}
		return reactionOutput;
	}

	private const function StimRule( rule : weak< Rule_Record >, stimType : gamedataStimType ) : Bool
	{
		if( rule && rule.Stimulus().Type() == stimType )
		{
			return true;
		}
		return false;
	}

	private function ProcessReactionOutput( stimData : StimEventTaskData, stimParams : StimParams )
	{
		var reactionData : AIReactionData;
		var grenade : BaseGrenade;
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		var stimEvent : StimuliEvent;
		var vehicle : VehicleObject;
		var driverInstigator : GameObject;
		var instigatorPuppet : ScriptedPuppet;
		var isDuplicateReaction : Bool;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		stimEvent = stimData.cachedEvt;
		instigatorPuppet = ( ( ScriptedPuppet )( stimEvent.sourceObject ) );
		reactionData = m_desiredReaction;
		if( !( reactionData ) )
		{
			reactionData = m_activeReaction;
		}
		grenade = ( ( BaseGrenade )( stimEvent.sourceObject ) );
		if( ( grenade && HasCombatTarget() ) && stimEvent.GetStimType() == gamedataStimType.ProjectileDistraction )
		{
			stimEvent.SetStimType( gamedataStimType.GrenadeLanded );
			stimParams = ProcessStimParams( stimEvent );
		}
		if( stimParams.reactionOutput.workspotReaction && GameInstance.GetWorkspotSystem( owner.GetGame() ).SendReactionSignal( owner, stimParams.reactionOutput.workspotReactionType ) )
		{
			LogSuccess( "sent workspot reaction " + NameToString( stimParams.reactionOutput.workspotReactionType ) );
			return;
		}
		vehicle = ( ( VehicleObject )( stimEvent.sourceObject ) );
		if( vehicle )
		{
			driverInstigator = VehicleComponent.GetDriver( owner.GetGame(), vehicle, vehicle.GetEntityID() );
			instigatorPuppet = ( ( ScriptedPuppet )( driverInstigator ) );
		}
		if( ( ( ownerPuppet.IsPrevention() && StimFilters.IsGunshot( stimEvent.GetStimType() ) ) && stimParams.reactionOutput.reactionBehavior == gamedataOutput.Intruder ) && !( instigatorPuppet.IsPlayer() ) )
		{
			stimParams.reactionOutput.reactionBehavior = gamedataOutput.Investigate;
			stimParams.reactionOutput.reactionPriority = 8;
			stimParams.reactionOutput.AIbehaviorPriority = 4.69999981;
			LogInfo( "prevention Intruder reaction decreased to Investigate for non-player gunshot" );
		}
		if( stimParams.reactionOutput.reactionBehavior == gamedataOutput.Panic && ownerPuppet.IsBoss() )
		{
			if( stimEvent.GetStimType() == gamedataStimType.Explosion )
			{
				stimParams.reactionOutput.reactionBehavior = gamedataOutput.Intruder;
				stimParams.reactionOutput.reactionPriority = 10;
				stimParams.reactionOutput.AIbehaviorPriority = 4.9000001;
				LogInfo( "boss Panic reaction decreased to Intruder" );
			}
			else if( stimEvent.GetStimType() == gamedataStimType.GrenadeLanded )
			{
				TriggerAlerted();
				LogSuccess( "triggered alerted for boss" );
				return;
			}
			else
			{
				if( stimEvent.GetStimType() == gamedataStimType.Hacked || stimEvent.GetStimType() == gamedataStimType.DeviceExplosion )
				{
					TriggerAlerted();
				}
				stimParams.reactionOutput.reactionBehavior = gamedataOutput.Investigate;
				stimParams.reactionOutput.reactionPriority = 8;
				stimParams.reactionOutput.AIbehaviorPriority = 4.69999981;
				LogInfo( "boss Panic reaction decreased to Investigate" );
			}
		}
		LogInfo( "processing reaction " + EnumValueToString( "gamedataOutput", ( ( Int32 )( stimParams.reactionOutput.reactionBehavior ) ) ) );
		if( stimParams.reactionOutput.reactionBehavior == gamedataOutput.Ignore )
		{
			LogFailure( "reaction behavior is Ignore" );
			return;
		}
		if( stimParams.reactionOutput.reactionBehavior == gamedataOutput.LookAt )
		{
			if( IsTargetInFront( stimEvent.sourceObject ) || stimEvent.GetStimType() == gamedataStimType.Attention )
			{
				GameObject.PlayVoiceOver( owner, 'stlh_curious_grunt', 'Scripts:ProcessReactionOutput' );
				ActivateReactionLookAt( stimEvent.sourceObject, true );
				LogSuccess( "activated lookat" );
				return;
			}
			stimParams.reactionOutput.reactionBehavior = gamedataOutput.TurnAt;
			TriggerBehaviorReaction( stimParams.reactionOutput, stimData, stimParams.stimData );
		}
		if( stimEvent.GetStimType() == gamedataStimType.OpeningDoor )
		{
			if( IsTargetInFront( stimEvent.sourceObject ) )
			{
				GameObject.PlayVoiceOver( owner, 'stlh_curious_grunt', 'Scripts:ProcessReactionOutput' );
				ActivateReactionLookAt( stimEvent.sourceObject, true );
				LogSuccess( "activated lookat for door" );
				return;
			}
		}
		if( ( stimParams.reactionOutput.reactionBehavior == gamedataOutput.Intruder || stimParams.reactionOutput.reactionBehavior == gamedataOutput.ProjectileInvestigate ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'LoreAnim' ) ) )
		{
			if( driverInstigator && TryTriggerCombatOrAlertedFromHostileStim( stimEvent, stimData, reactionData, driverInstigator ) )
			{
				return;
			}
			if( GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) && TryTriggerCombatOrAlertedFromHostileStim( stimEvent, stimData, reactionData, stimEvent.sourceObject ) )
			{
				return;
			}
			if( IsSquadMateInDanger() && AIActionHelper.TryChangingAttitudeToHostile( ownerPuppet, stimEvent.sourceObject ) )
			{
				HelpAllyWithAlert( ownerPuppet, stimEvent.sourceObject, 5.0 );
				LogSuccess( "alerted about target (ally reacting to intruder)" );
				return;
			}
		}
		if( reactionData && ( stimParams.reactionOutput.reactionPriority < reactionData.reactionPriority ) )
		{
			LogFailure( "lower reaction priority to current" );
			return;
		}
		if( m_pendingReaction && ( stimParams.reactionOutput.reactionPriority < m_pendingReaction.reactionPriority ) )
		{
			LogFailure( "lower reaction priority to pending" );
			return;
		}
		if( ( m_activeReaction && m_activeReaction.reactionBehaviorName == stimParams.reactionOutput.reactionBehavior ) && m_activeReaction.reactionBehaviorName != gamedataOutput.DeviceInvestigate )
		{
			UpdateActiveReaction( stimParams.reactionOutput, stimEvent, stimParams.stimData, m_updateByActive );
			return;
		}
		isDuplicateReaction = ( ( reactionData && m_desiredReaction ) && reactionData.reactionBehaviorName == stimParams.reactionOutput.reactionBehavior ) && GameObject.IsCooldownActive( owner, StringToName( "ovefloodCooldown" + EnumValueToString( "gamedataOutput", ( ( Int32 )( m_desiredReaction.reactionBehaviorName ) ) ) ) );
		if( isDuplicateReaction )
		{
			LogFailure( "reaction overflow cooldown" );
			return;
		}
		TriggerBehaviorReaction( stimParams.reactionOutput, stimData, stimParams.stimData );
	}

	private function UpdateActiveReaction( reaction : ReactionOutput, stimEvent : StimuliEvent, stimData : StimEventData, updateByActive : Bool )
	{
		var resetDesiredReactionData : ResetReactionEvent;
		var owner : GameObject;
		var stimName : CName;
		owner = GetOwner();
		GameInstance.GetDelaySystem( owner.GetGame() ).CancelDelay( m_resetReactionDataID );
		if( updateByActive )
		{
			m_desiredReaction = m_activeReaction;
		}
		else
		{
			m_desiredReaction = new AIReactionData;
			m_desiredReaction.reactionBehaviorName = reaction.reactionBehavior;
			m_desiredReaction.stimPriority = stimEvent.stimRecord.Priority().Type();
			m_desiredReaction.stimTarget = stimEvent.sourceObject;
			m_desiredReaction.stimSource = GetStimSource( stimEvent );
			m_desiredReaction.stimType = stimEvent.GetStimType();
			m_desiredReaction.stimRecord = stimEvent.stimRecord;
			m_desiredReaction.stimEventData = stimData;
			m_desiredReaction.stimPropagation = stimEvent.stimPropagation;
			m_desiredReaction.reactionPriority = reaction.reactionPriority;
			m_desiredReaction.stimInvestigateData = stimEvent.stimInvestigateData;
			m_desiredReaction.reactionBehaviorAIPriority = reaction.AIbehaviorPriority;
			m_desiredReaction.reactionCooldown = reaction.reactionCooldown;
			m_desiredReaction.initAnimInWorkspot = reaction.startedInWorkspot;
		}
		if( m_desiredReaction.reactionBehaviorName == gamedataOutput.DeviceInvestigate && !( IsInList( m_investigationList, stimData ) ) )
		{
			m_investigationList.PushBack( stimData );
			LogInfo( "added stim to investigation list" );
		}
		UpdateStimSource();
		stimName = EnumValueToName( 'gamedataStimType', ( ( Int32 )( m_desiredReaction.stimType ) ) );
		if( m_desiredReaction.reactionCooldown != 0.0 )
		{
			GameObject.StartCooldown( owner, stimName, m_desiredReaction.reactionCooldown );
		}
		if( !( GameObject.IsCooldownActive( owner, 'ActiveReactionValueCooldown-' + stimName ) ) && !( GetOwnerPuppet().GetSecuritySystem().IsReprimandOngoing() ) )
		{
			AddReactionValueToStatPool( m_desiredReaction );
			GameObject.StartCooldown( owner, 'ActiveReactionValueCooldown-' + stimName, 1.0 );
		}
		resetDesiredReactionData = new ResetReactionEvent;
		resetDesiredReactionData.data = m_desiredReaction;
		m_resetReactionDataID = GameInstance.GetDelaySystem( owner.GetGame() ).DelayEvent( owner, resetDesiredReactionData, 1.0 );
		LogSuccess( "updated active reaction " + EnumValueToString( "gamedataOutput", ( ( Int32 )( m_desiredReaction.reactionBehaviorName ) ) ) );
	}

	private function UpdateStimSource()
	{
		var updateStimSourceAIEvent : StimuliEvent;
		updateStimSourceAIEvent = new StimuliEvent;
		updateStimSourceAIEvent.name = 'updateSource';
		GetOwner().QueueEvent( updateStimSourceAIEvent );
	}

	private virtual function TriggerBehaviorReaction( reaction : ReactionOutput, stimTaskData : StimEventTaskData, stimData : StimEventData )
	{
		var triggerAIEvent : StimuliEvent;
		var exitEvent : AIEvent;
		var owner : GameObject;
		var game : GameInstance;
		var stimEvent : StimuliEvent;
		var reactionBehaviorOk : Bool;
		owner = GetOwner();
		DeactiveLookAt();
		game = owner.GetGame();
		stimEvent = stimTaskData.cachedEvt;
		GameInstance.GetDelaySystem( game ).CancelDelay( m_resetReactionDataID );
		m_desiredReaction = new AIReactionData;
		m_desiredReaction.reactionBehaviorName = reaction.reactionBehavior;
		m_desiredReaction.stimPriority = stimEvent.stimRecord.Priority().Type();
		m_desiredReaction.stimTarget = stimEvent.sourceObject;
		m_desiredReaction.stimType = stimEvent.GetStimType();
		m_desiredReaction.stimSource = GetStimSource( stimEvent );
		m_desiredReaction.stimRecord = stimEvent.stimRecord;
		m_desiredReaction.reactionPriority = reaction.reactionPriority;
		m_desiredReaction.stimInvestigateData = stimEvent.stimInvestigateData;
		m_desiredReaction.stimEventData = stimData;
		m_desiredReaction.reactionBehaviorAIPriority = reaction.AIbehaviorPriority;
		m_desiredReaction.reactionCooldown = reaction.reactionCooldown;
		m_desiredReaction.initAnimInWorkspot = reaction.startedInWorkspot;
		GameObject.StartCooldown( owner, StringToName( "ovefloodCooldown" + EnumValueToString( "gamedataStimType", ( ( Int32 )( m_desiredReaction.stimType ) ) ) ), m_ovefloodCooldown );
		GameObject.StartCooldown( owner, StringToName( "ovefloodCooldown" + EnumValueToString( "gamedataOutput", ( ( Int32 )( m_desiredReaction.reactionBehaviorName ) ) ) ), 0.5 );
		triggerAIEvent = new StimuliEvent;
		triggerAIEvent.SetStimType( gamedataStimType.Invalid );
		if( IsInPendingBehavior() )
		{
			m_pendingReaction = m_desiredReaction;
			m_pendingReaction.validTillTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( game ) ) + TweakDBInterface.GetFloat( T"AIGeneralSettings.pendingReactionValidTimeStamp", 10.0 );
			m_desiredReaction = NULL;
			LogInfo( "set reaction to pending" );
			if( VehicleComponent.IsMountedToVehicle( game, owner ) )
			{
				if( ( owner.IsPrevention() && !( PreventionSystem.IsChasingPlayer( GetOwner().GetGame() ) ) ) && stimEvent.sourceObject.IsPlayer() )
				{
					reactionBehaviorOk = reaction.reactionBehavior == gamedataOutput.Reprimand || reaction.reactionBehavior == gamedataOutput.Investigate;
				}
				else
				{
					reactionBehaviorOk = reaction.reactionBehavior == gamedataOutput.Reprimand;
				}
				if( reactionBehaviorOk )
				{
					exitEvent = new AIEvent;
					exitEvent.name = 'ExitVehicle';
					owner.QueueEvent( exitEvent );
					LogInfo( "queued ExitVehicle event" );
				}
			}
		}
		else
		{
			triggerAIEvent.name = 'triggerReaction';
			reactionBehaviorOk = ( ( reaction.reactionBehavior == gamedataOutput.Flee || reaction.reactionBehavior == gamedataOutput.Surrender ) || reaction.reactionBehavior == gamedataOutput.WalkAway ) || reaction.reactionBehavior == gamedataOutput.CallGuard;
			if( ( m_activeReaction && m_activeReaction.reactionBehaviorName == gamedataOutput.BodyInvestigate ) && AISquadHelper.IsSignalActive( GetOwnerPuppet(), 'BodyInvestigationTicketReceived' ) )
			{
				OnBodyInvestigated( new BodyInvestigatedEvent );
				LogInfo( "triggered body investigated" );
			}
			if( FirstSquadMemberReaction() && !( DelayReaction( stimEvent.GetStimType() ) ) )
			{
				owner.QueueEvent( triggerAIEvent );
				LogInfo( "triggered reaction with NO delay" );
			}
			else
			{
				m_delayReactionEventID = GameInstance.GetDelaySystem( game ).DelayEvent( owner, triggerAIEvent, RandRangeF( 0.2, 0.69999999 ) );
				LogInfo( "triggered reaction with delay" );
			}
			if( reactionBehaviorOk )
			{
				NPCPuppet.ChangeHighLevelState( owner, gamedataNPCHighLevelState.Fear );
				AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, GetFearAnimWrapper( GetFearReactionPhase( stimEvent ) ), 1.0 );
				if( !( m_fearLocomotionWrapper ) )
				{
					AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, GetRandomFearLocomotionAnimWrapper( GetFearReactionPhase( stimEvent ) ), 1.0 );
				}
				LogSuccess( "started fear" );
			}
		}
		if( m_createThreshold )
		{
			CreateFearThreashold();
			m_createThreshold = false;
		}
		CacheEvent( stimTaskData );
		LogSuccess( "triggered behavior reaction " + EnumValueToString( "gamedataOutput", ( ( Int32 )( reaction.reactionBehavior ) ) ) );
	}

	private function GetStimSource( stimEvent : StimuliEvent ) : Vector4
	{
		var investigationSources : array< Vector4 >;
		var closestPosition : Vector4;
		var closestDistanceSquared : Float;
		var distanceSquared : Float;
		var navigationSystem : AINavigationSystem;
		var query : AINavigationSystemQuery;
		var queryId : Uint32;
		var result : AINavigationSystemResult;
		var sourceWorldPosition : WorldPosition;
		var path : NavigationPath;
		var owner : GameObject;
		var ownerPos : Vector4;
		var i : Int32;
		owner = GetOwner();
		ownerPos = owner.GetWorldPosition();
		investigationSources = stimEvent.movePositions;
		for( i = 0; i < investigationSources.Size(); i += 1 )
		{
			distanceSquared = Vector4.DistanceSquared( investigationSources[ i ], ownerPos );
			path = GameInstance.GetAINavigationSystem( owner.GetGame() ).CalculatePathForCharacter( ownerPos, investigationSources[ i ], 0.0, owner );
			if( !( path ) )
			{
				continue;
			}
			if( ( distanceSquared < closestDistanceSquared ) || ( closestDistanceSquared == 0.0 ) )
			{
				closestDistanceSquared = distanceSquared;
				closestPosition = investigationSources[ i ];
			}
		}
		if( !( Vector4.IsZero( closestPosition ) ) )
		{
			return closestPosition;
		}
		if( stimEvent.IsTagInStimuli( 'NavReach' ) )
		{
			navigationSystem = GameInstance.GetAINavigationSystem( owner.GetGame() );
			AIPositionSpec.SetEntity( query.source, GetEntity() );
			WorldPosition.SetVector4( sourceWorldPosition, stimEvent.sourcePosition );
			AIPositionSpec.SetWorldPosition( query.target, sourceWorldPosition );
			queryId = navigationSystem.StartPathfinding( query );
			navigationSystem.GetResult( queryId, result );
			navigationSystem.StopPathfinding( queryId );
			if( result.hasClosestReachablePoint )
			{
				return WorldPosition.ToVector4( result.closestReachablePoint );
			}
		}
		return stimEvent.sourcePosition;
	}

	private function TryTriggerCombatOrAlertedFromHostileStim( stimEvent : StimuliEvent, stimData : StimEventTaskData, reactionData : AIReactionData, instigator : GameObject ) : Bool
	{
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		if( CanTriggerCombatFromHostileStim( stimEvent, reactionData ) && AIActionHelper.TryChangingAttitudeToHostile( ownerPuppet, instigator ) )
		{
			if( !( SenseComponent.ShouldIgnoreIfPlayerCompanion( owner, instigator ) ) )
			{
				TargetTrackingExtension.InjectThreat( ownerPuppet, instigator );
				LogSuccess( "triggered combat from hostile stim" );
			}
			else
			{
				LogFailure( "could not trigger combat against player companion" );
			}
			return true;
		}
		if( CanTriggerAlertedFromHostileStim( stimEvent ) )
		{
			TriggerAlerted( instigator );
			CacheEvent( stimData );
			LogSuccess( "triggered alerted from hostile stim" );
			return true;
		}
		LogInfo( "NOT TryTriggerCombatOrAlertedFromHostileStim" );
		return false;
	}

	private function CanTriggerCombatFromHostileStim( stimEvent : StimuliEvent, reactionData : AIReactionData ) : Bool
	{
		var stimType : gamedataStimType;
		stimType = stimEvent.GetStimType();
		if( GetOwnerPuppet().IsPrevention() )
		{
			return StimFilters.CanTriggerPreventionCombatDirectly( stimType ) && ( StimFilters.CanTriggerPreventionCombatDirectlyEvenInvisible( stimType ) || IsTargetVisible( stimEvent.sourceObject ) );
		}
		return StimFilters.CanTriggerCombatDirectly( stimType );
	}

	private function CanTriggerAlertedFromHostileStim( stimEvent : StimuliEvent ) : Bool
	{
		if( GetOwnerPuppet().IsPrevention() )
		{
			return false;
		}
		return StimFilters.CanTriggerAlertedDirectly( stimEvent.GetStimType() );
	}

	private static function InGunshotCone( shooter : weak< GameObject >, target : weak< GameObject > ) : Bool
	{
		return IsTargetInFrontOfSource( shooter, target, 15.0, true );
	}

	public function ShouldIgnoreCombatStim( stimType : gamedataStimType, instigator : weak< ScriptedPuppet >, source : weak< ScriptedPuppet >, sourcePos : Vector4, optional log : Bool ) : Bool
	{
		var canIgnoreOnlyDueToDelay : Bool;
		var canIgnorePlayerCombatStim : Bool;
		return ShouldIgnoreCombatStim( stimType, instigator, source, sourcePos, false, canIgnoreOnlyDueToDelay, canIgnorePlayerCombatStim, log );
	}

	public function ShouldIgnoreCombatStim( stimType : gamedataStimType, instigator : weak< ScriptedPuppet >, source : weak< ScriptedPuppet >, sourcePos : Vector4, canDelay : Bool, out canIgnoreOnlyDueToDelay : Bool, out canIgnorePlayerCombatStim : Bool, log : Bool ) : Bool
	{
		var puppet : ScriptedPuppet;
		var otherPuppet : ScriptedPuppet;
		var isNPCInCombat : Bool;
		var isPlayerInCombat : Bool;
		var inDangerRange : Bool;
		var squadMates : array< weak< Entity > >;
		var i : Int32;
		if( !( source ) && !( instigator ) )
		{
			return false;
		}
		if( !( source ) )
		{
			source = instigator;
		}
		if( !( source ) || !( source.IsPlayer() ) )
		{
			return false;
		}
		if( !( StimFilters.CanBeIgnoredInCombat( stimType ) ) )
		{
			return false;
		}
		isNPCInCombat = HasCombatTarget();
		isPlayerInCombat = ( ( PlayerPuppet )( source ) ).IsInCombat();
		if( ( !( isNPCInCombat ) && !( isPlayerInCombat ) ) && CombatGracePeriodPassed( ( ( PlayerPuppet )( source ) ) ) )
		{
			if( canDelay )
			{
				canIgnoreOnlyDueToDelay = true;
			}
			else
			{
				return false;
			}
		}
		puppet = GetOwnerPuppet();
		if( NPCPuppet.IsInCombatWithTarget( puppet, source ) )
		{
			return false;
		}
		canIgnorePlayerCombatStim = true;
		if( StimFilters.IsProjectile( stimType ) && IsTargetPositionClose( sourcePos, 4.0 ) )
		{
			if( log )
			{
				LogInfo( "can't be ignored - projectile hit nearby" );
			}
			return false;
		}
		inDangerRange = IsTargetPositionClose( sourcePos, 12.0 );
		if( stimType == gamedataStimType.Explosion && inDangerRange )
		{
			if( log )
			{
				LogInfo( "can't be ignored - explosion nearby" );
			}
			return false;
		}
		if( StimFilters.IsGunshot( stimType ) )
		{
			if( inDangerRange )
			{
				if( log )
				{
					LogInfo( "can't be ignored - gunshot nearby" );
				}
				return false;
			}
			if( InGunshotCone( source, puppet ) )
			{
				if( log )
				{
					LogInfo( "can't be ignored - gunshot at owner" );
				}
				return false;
			}
		}
		if( ( StimFilters.IsIllegal( stimType ) && inDangerRange ) && InGunshotCone( source, puppet ) )
		{
			if( log )
			{
				LogInfo( "can't be ignored - nearby illegal action directed at owner" );
			}
			return false;
		}
		if( IsTargetVeryClose( source ) )
		{
			if( log )
			{
				LogInfo( "can't be ignored - player very close to owner" );
			}
			return false;
		}
		if( puppet.IsConnectedToSecuritySystem() )
		{
			if( puppet.IsTargetTresspassingMyZone( source ) )
			{
				if( log )
				{
					LogInfo( "can't be ignored - player trespassing security zone" );
				}
				return false;
			}
		}
		AISquadHelper.GetSquadmates( puppet, squadMates );
		for( i = 0; i < squadMates.Size(); i += 1 )
		{
			otherPuppet = ( ( ScriptedPuppet )( squadMates[ i ] ) );
			if( otherPuppet && NPCPuppet.IsInCombatWithTarget( otherPuppet, source ) )
			{
				if( log )
				{
					LogInfo( "can't be ignored - squadmate in combat with player" );
				}
				return false;
			}
		}
		return true;
	}

	private function IsSquadMateInDanger() : Bool
	{
		var ownerPuppet : ScriptedPuppet;
		var smi : SquadScriptInterface;
		var squadMembers : array< weak< Entity > >;
		var i : Int32;
		var member : ScriptedPuppet;
		var memberStimReactionComp : ReactionManagerComponent;
		var memberDesiredReaction : AIReactionData;
		var investigateData : stimInvestigateData;
		ownerPuppet = GetOwnerPuppet();
		if( !( AISquadHelper.GetSquadMemberInterface( ownerPuppet, smi ) ) )
		{
			return false;
		}
		squadMembers = smi.ListMembersWeak();
		if( squadMembers.Size() <= 1 )
		{
			return false;
		}
		for( i = 0; i < squadMembers.Size(); i += 1 )
		{
			member = ( ( ScriptedPuppet )( squadMembers[ i ] ) );
			if( !( ScriptedPuppet.IsActive( member ) ) )
			{
				continue;
			}
			if( member == ownerPuppet )
			{
				continue;
			}
			memberStimReactionComp = member.GetStimReactionComponent();
			memberDesiredReaction = memberStimReactionComp.GetDesiredReactionData();
			if( !( memberDesiredReaction ) || memberDesiredReaction.reactionBehaviorName != gamedataOutput.Intruder )
			{
				continue;
			}
			if( !( IsTargetVisible( member ) ) )
			{
				continue;
			}
			investigateData = memberDesiredReaction.stimInvestigateData;
			if( memberStimReactionComp.ShouldIgnoreCombatStim( memberDesiredReaction.stimType, ( ( ScriptedPuppet )( investigateData.attackInstigator ) ), ( ( ScriptedPuppet )( memberDesiredReaction.stimEventData.source ) ), memberDesiredReaction.stimSource ) )
			{
				continue;
			}
			return true;
		}
		return false;
	}

	private function FirstSquadMemberReaction() : Bool
	{
		var smi : SquadScriptInterface;
		var squadMembers : array< weak< Entity > >;
		var member : ScriptedPuppet;
		var i : Int32;
		var stimDistance, distance : Float;
		var suitableSquadmate : ScriptedPuppet;
		var memberDesiredReactionData : AIReactionData;
		if( !( AISquadHelper.GetSquadMemberInterface( GetOwnerPuppet(), smi ) ) )
		{
			return true;
		}
		squadMembers = smi.ListMembersWeak();
		if( squadMembers.Size() <= 1 )
		{
			return true;
		}
		for( i = 0; i < squadMembers.Size(); i += 1 )
		{
			member = ( ( ScriptedPuppet )( squadMembers[ i ] ) );
			if( !( ScriptedPuppet.IsActive( member ) ) )
			{
				continue;
			}
			memberDesiredReactionData = member.GetStimReactionComponent().GetDesiredReactionData();
			if( ( ( m_desiredReaction && member.GetStimReactionComponent().GetReceivedStimType() != m_desiredReaction.stimType ) && memberDesiredReactionData ) && memberDesiredReactionData.stimType != m_desiredReaction.stimType )
			{
				continue;
			}
			stimDistance = Vector4.Distance( member.GetWorldPosition(), m_desiredReaction.stimSource );
			if( ( stimDistance < distance ) || ( distance == 0.0 ) )
			{
				suitableSquadmate = member;
				distance = stimDistance;
			}
		}
		if( suitableSquadmate )
		{
			return GetOwnerPuppet() == suitableSquadmate;
		}
		return true;
	}

	public function ClearPendingReaction()
	{
		m_pendingReaction = NULL;
	}

	private virtual function TriggerPendingReaction()
	{
		var triggerAIEvent : StimuliEvent;
		var crowdMemberComponent : CrowdMemberBaseComponent;
		var owner : GameObject;
		if( !( m_pendingReaction ) )
		{
			return;
		}
		owner = GetOwner();
		if( ( m_pendingReaction.validTillTimeStamp > 0.0 ) && ( m_pendingReaction.validTillTimeStamp < EngineTime.ToFloat( GameInstance.GetSimTime( owner.GetGame() ) ) ) )
		{
			ClearPendingReaction();
			return;
		}
		DeactiveLookAt();
		GameInstance.GetDelaySystem( owner.GetGame() ).CancelDelay( m_resetReactionDataID );
		m_desiredReaction = new AIReactionData;
		m_desiredReaction.reactionBehaviorName = m_pendingReaction.reactionBehaviorName;
		m_desiredReaction.stimPriority = m_pendingReaction.stimPriority;
		m_desiredReaction.stimTarget = m_pendingReaction.stimTarget;
		m_desiredReaction.stimType = m_pendingReaction.stimType;
		m_desiredReaction.stimSource = m_pendingReaction.stimSource;
		m_desiredReaction.stimRecord = m_pendingReaction.stimRecord;
		m_desiredReaction.reactionPriority = m_pendingReaction.reactionPriority;
		m_desiredReaction.stimInvestigateData = m_pendingReaction.stimInvestigateData;
		m_desiredReaction.stimEventData = m_pendingReaction.stimEventData;
		m_desiredReaction.reactionBehaviorAIPriority = m_pendingReaction.reactionBehaviorAIPriority;
		m_desiredReaction.reactionCooldown = m_pendingReaction.reactionCooldown;
		if( IsInitAnimShock( m_pendingReaction.reactionBehaviorName ) )
		{
			m_desiredReaction.initAnimInWorkspot = true;
			if( m_previousFearPhase == 2 )
			{
				m_previousFearPhase = 0;
			}
		}
		triggerAIEvent = new StimuliEvent;
		triggerAIEvent.SetStimType( gamedataStimType.Invalid );
		triggerAIEvent.name = 'triggerReaction';
		if( m_pendingReaction.reactionBehaviorName == gamedataOutput.Flee )
		{
			NPCPuppet.ChangeHighLevelState( owner, gamedataNPCHighLevelState.Fear );
			DeactiveLookAt();
			ResetFacial( 0.0 );
			TriggerFearFacial( 3 );
			AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, GetRandomFearLocomotionAnimWrapper( 3 ), 1.0 );
			if( m_inCrowd )
			{
				crowdMemberComponent = GetOwnerPuppet().GetCrowdMemberComponent();
				crowdMemberComponent.SetThreatLastKnownPosition( GetPlayerSystem().GetLocalPlayerMainGameObject().GetWorldPosition() );
				crowdMemberComponent.AllowWorkspotsUsage( false );
			}
		}
		owner.QueueEvent( triggerAIEvent );
		ClearPendingReaction();
	}

	private function TriggerReactionBehaviorForCrowd( stimEvent : StimuliEvent, reaction : gamedataOutput, dontPlayStartUpAnimation : Bool, optional skipInitialAnimation : Bool )
	{
		var triggerAIEvent : StimuliEvent;
		var crowdMemberComponent : CrowdMemberBaseComponent;
		var stimData : stimInvestigateData;
		stimData = stimEvent.stimInvestigateData;
		m_desiredReaction = new AIReactionData;
		m_desiredReaction.reactionBehaviorName = reaction;
		m_desiredReaction.stimPriority = stimEvent.stimRecord.Priority().Type();
		m_desiredReaction.stimTarget = stimEvent.sourceObject;
		m_desiredReaction.stimType = stimEvent.GetStimType();
		m_desiredReaction.stimSource = GetStimSource( stimEvent );
		m_desiredReaction.stimRecord = stimEvent.stimRecord;
		m_desiredReaction.stimInvestigateData = stimData;
		m_desiredReaction.reactionBehaviorAIPriority = GetOutputPriority( reaction );
		m_desiredReaction.initAnimInWorkspot = dontPlayStartUpAnimation;
		if( skipInitialAnimation )
		{
			m_desiredReaction.skipInitialAnimation = skipInitialAnimation;
		}
		else
		{
			m_desiredReaction.skipInitialAnimation = stimData.skipInitialAnimation;
		}
		triggerAIEvent = new StimuliEvent;
		triggerAIEvent.SetStimType( gamedataStimType.Invalid );
		triggerAIEvent.name = 'triggerReaction';
		if( IsInPendingBehavior() && ( reaction != gamedataOutput.Flee || stimEvent.GetStimType() == gamedataStimType.HijackVehicle ) )
		{
			m_pendingReaction = m_desiredReaction;
			m_pendingReaction.validTillTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + TweakDBInterface.GetFloat( T"AIGeneralSettings.pendingReactionValidTimeStamp", 10.0 );
			m_desiredFearPhase = -1;
			m_desiredReaction = NULL;
		}
		else
		{
			GetOwner().QueueEvent( triggerAIEvent );
			if( m_inCrowd )
			{
				crowdMemberComponent = GetOwnerPuppet().GetCrowdMemberComponent();
				crowdMemberComponent.SetThreatLastKnownPosition( GetPlayerSystem().GetLocalPlayerMainGameObject().GetWorldPosition() );
				crowdMemberComponent.AllowWorkspotsUsage( false );
			}
		}
	}

	private function TriggerReactionBehaviorForCrowd( target : GameObject, reaction : gamedataOutput, initAnimInWorkspot : Bool, optional sourcePosition : Vector4 )
	{
		var triggerAIEvent : StimuliEvent;
		var crowdMemberComponent : CrowdMemberBaseComponent;
		m_desiredReaction = new AIReactionData;
		m_desiredReaction.reactionBehaviorName = reaction;
		m_desiredReaction.stimTarget = target;
		if( Vector4.IsZero( sourcePosition ) )
		{
			m_desiredReaction.stimSource = target.GetWorldPosition();
		}
		else
		{
			m_desiredReaction.stimSource = sourcePosition;
		}
		m_desiredReaction.reactionBehaviorAIPriority = GetOutputPriority( reaction );
		m_desiredReaction.initAnimInWorkspot = initAnimInWorkspot;
		triggerAIEvent = new StimuliEvent;
		triggerAIEvent.SetStimType( gamedataStimType.Invalid );
		triggerAIEvent.name = 'triggerReaction';
		if( IsInPendingBehavior() )
		{
			m_pendingReaction = m_desiredReaction;
			m_pendingReaction.validTillTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) ) + TweakDBInterface.GetFloat( T"AIGeneralSettings.pendingReactionValidTimeStamp", 10.0 );
			m_desiredReaction = NULL;
		}
		else
		{
			GetOwner().QueueEvent( triggerAIEvent );
			if( ( reaction == gamedataOutput.Bump && m_activeReaction ) && ( m_activeReaction.reactionBehaviorName == gamedataOutput.Bump || m_activeReaction.reactionBehaviorName == gamedataOutput.BackOff ) )
			{
				m_desiredReaction.escalateProvoke = true;
			}
			if( m_inCrowd )
			{
				if( m_crowdFearStage != gameFearStage.Relaxed )
				{
					crowdMemberComponent = GetOwnerPuppet().GetCrowdMemberComponent();
					crowdMemberComponent.SetThreatLastKnownPosition( GetPlayerSystem().GetLocalPlayerMainGameObject().GetWorldPosition() );
					crowdMemberComponent.AllowWorkspotsUsage( false );
				}
				else if( reaction == gamedataOutput.CallPolice )
				{
					crowdMemberComponent.TryStopTrafficMovement();
				}
			}
		}
	}

	protected event OnDelayStimEvent( evt : DelayStimEvent )
	{
		if( evt.fullEventPipeline )
		{
			HandleStimEventByTask( evt.stimEvent, true );
			return true;
		}
		if( ( ( ( evt.stimEvent.GetStimType() == gamedataStimType.AimingAt && IsPlayerAiming() ) && evt.stimEvent.sourceObject.IsPlayer() ) && IsTargetDetected( evt.stimEvent.sourceObject ) ) && IsReactionAvailableInPreset( gamedataStimType.AimingAt ) )
		{
			AIActionHelper.TryStartCombatWithTarget( GetOwnerPuppet(), evt.stimEvent.sourceObject );
		}
	}

	private function ProcessEnvironmentalHazard( stimEvent : StimuliEvent )
	{
		var i : Int32;
		var radius : Float;
		if( m_inCrowd )
		{
			return;
		}
		if( !( stimEvent.sourceObject ) )
		{
			return;
		}
		if( stimEvent.stimPropagation == gamedataStimPropagation.Visual && !( IsTargetInFront( stimEvent.sourceObject ) ) )
		{
			return;
		}
		radius = ( ( stimEvent.radius > 0.0 ) ? ( stimEvent.radius ) : ( m_crowdAimingReactionDistance ) );
		if( !( IsTargetClose( stimEvent.sourceObject, radius ) ) )
		{
			return;
		}
		for( i = 0; i < m_environmentalHazards.Size(); i += 1 )
		{
			if( m_environmentalHazards[ i ].sourceObject == stimEvent.sourceObject )
			{
				GameInstance.GetDelaySystem( GetOwner().GetGame() ).CancelDelay( m_environmentalHazardsDelayIDs[ i ] );
				m_environmentalHazards[ i ] = stimEvent;
				m_environmentalHazardsDelayIDs[ i ] = DelayEnvironmentalHazardEvent( stimEvent );
				return;
			}
		}
		m_environmentalHazards.PushBack( stimEvent );
		m_environmentalHazardsDelayIDs.PushBack( DelayEnvironmentalHazardEvent( stimEvent ) );
	}

	private function DelayEnvironmentalHazardEvent( stimEvent : StimuliEvent ) : DelayID
	{
		var cleanEnvironmentalHazardEvent : CleanEnvironmentalHazardEvent;
		cleanEnvironmentalHazardEvent = new CleanEnvironmentalHazardEvent;
		cleanEnvironmentalHazardEvent.stimEvent = stimEvent;
		return GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), cleanEnvironmentalHazardEvent, 2.0 );
	}

	protected event OnCleanEnvironmentalHazardEvent( cleanEnvironmentalHazardEvent : CleanEnvironmentalHazardEvent )
	{
		var i : Int32;
		for( i = 0; i < m_environmentalHazards.Size(); i += 1 )
		{
			if( m_environmentalHazards[ i ] == cleanEnvironmentalHazardEvent.stimEvent )
			{
				m_environmentalHazards.Erase( i );
				m_environmentalHazardsDelayIDs.Erase( i );
			}
		}
	}

	private function ProcessStimForTheDead( stimEvent : StimuliEvent )
	{
		if( ScriptedPuppet.IsAlive( GetOwner() ) )
		{
			LogFailure( "owner alive" );
			return;
		}
		if( !( GetOwnerPuppet().IsHumanoid() ) )
		{
			LogFailure( "owner not humanoid" );
			return;
		}
		if( stimEvent.GetStimType() != gamedataStimType.UndeadCall )
		{
			LogFailure( "unknown stim for the dead" );
			return;
		}
		LogInfo( "processing stim for the dead" );
		if( ActivateUndeadLookAt( stimEvent.sourceObject ) )
		{
			LogSuccess( "activated lookat" );
		}
	}

	private function InitCrowd()
	{
		m_exitWorkspotAim = TweakDBInterface.GetVector2( T"AIGeneralSettings.workspotReactionExitDelay", Vector2( 3.0, 5.0 ) );
		m_fearToIdleDistance = TweakDBInterface.GetVector2( T"AIGeneralSettings.fearToIdleDistance", Vector2( 15.0, 5.0 ) );
		m_crowdAimingReactionDistance = TweakDBInterface.GetFloat( T"AIGeneralSettings.crowdAimingReactionDistance", 20.0 );
		m_fearInPlaceAroundDistance = TweakDBInterface.GetFloat( T"AIGeneralSettings.fearInPlaceAroundDistance", 20.0 );
	}

	private function ShouldStimBeProcessedByCrowd( stimEvent : StimuliEvent ) : Bool
	{
		var vehicle : VehicleObject;
		var stimType : gamedataStimType;
		var owner : GameObject;
		var fearReactionPhase : Int32;
		var reactionSystem : ReactionSystem;
		var sceneSystemInt : SceneSystemInterface;
		var puppetRef : NPCPuppet;
		owner = GetOwner();
		stimType = stimEvent.GetStimType();
		puppetRef = ( ( NPCPuppet )( owner ) );
		if( puppetRef && puppetRef.IsPrevention() )
		{
			LogFailure( "owner is from prevention system" );
			return false;
		}
		if( !( m_initCrowd ) )
		{
			InitCrowd();
			m_initCrowd = true;
		}
		if( stimType == gamedataStimType.HijackVehicle )
		{
			return true;
		}
		if( SourceAttitude( stimEvent.sourceObject, EAIAttitude.AIA_Friendly ) )
		{
			if( !( m_inCrowd ) )
			{
				LogFailure( "friendly stim source and owner not in crowd" );
				return false;
			}
			if( stimType == gamedataStimType.AimingAt )
			{
				LogFailure( "friendly stim source aiming at owner" );
				return false;
			}
		}
		if( stimType == gamedataStimType.MeleeAttack && StatusEffectSystem.ObjectHasStatusEffect( stimEvent.sourceObject, T"GameplayRestriction.FistFight" ) )
		{
			LogFailure( "stim source in a fist fight" );
			return false;
		}
		if( ( ( NPCPuppet )( owner ) ).IsRagdolling() )
		{
			LogFailure( "owner ragdolling" );
			return false;
		}
		if( !( CanReactInVehicle( stimEvent ) ) )
		{
			return false;
		}
		if( stimEvent.IsVisual() && !( StimFilters.IsGunshot( stimType ) ) )
		{
			if( !( IsTargetInFront( stimEvent.sourceObject ) ) )
			{
				LogFailure( "visual source behind" );
				return false;
			}
			if( !( IsTargetClose( stimEvent.sourceObject, m_crowdAimingReactionDistance ) ) )
			{
				LogFailure( "visual source too far" );
				return false;
			}
			if( !( TargetVerticalCheck( stimEvent.sourceObject ) ) )
			{
				LogFailure( "visual source too high/low" );
				return false;
			}
			if( GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) )
			{
				if( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Braindance' ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Sleep' ) )
				{
					LogFailure( "owner sleeping or in braindance" );
					return false;
				}
			}
		}
		if( ( ( ( m_desiredFearPhase > -1 ) || m_activeReaction ) || m_crowdFearStage != gameFearStage.Relaxed ) || ( m_inReactionSequence && GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) ) )
		{
			if( stimType == gamedataStimType.CombatHit )
			{
				if( ( ( Bool )( GetOwnerPuppet().GetAIControllerComponent().GetBehaviorArgument( 'InFearInPlace' ) ) ) )
				{
					GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:ShouldStimBeProcessedByCrowd', , , true );
				}
				else if( PlayerPuppet.GetCurrentCombatState( ( ( PlayerPuppet )( stimEvent.sourceObject ) ) ) == gamePSMCombat.InCombat || !( m_activeReaction ) )
				{
					return true;
				}
			}
			fearReactionPhase = GetFearReactionPhase( stimEvent );
			if( m_desiredFearPhase >= fearReactionPhase )
			{
				LogFailure( "later phase of fear" );
				return false;
			}
			if( !( ShouldInterruptCurrentFearStage( fearReactionPhase ) ) )
			{
				LogFailure( "NOT ShouldInterruptCurrentFearStage" );
				return false;
			}
		}
		if( !( stimEvent.IsTagInStimuli( 'CrowdReaction' ) ) && !( stimEvent.IsTagInStimuli( 'ChildDanger' ) ) )
		{
			LogFailure( "stim doesnt have CrowdReaction or ChildDanger tags" );
			return false;
		}
		if( m_reactionPreset.Type() != gamedataReactionPresetType.Civilian_Neutral && !( IsReactionAvailableInPreset( stimEvent.GetStimType() ) ) )
		{
			LogFailure( "NOT Civilian_Neutral reaction preset and unavailable reaction" );
			return false;
		}
		if( stimType == gamedataStimType.CrowdIllegalAction && StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'LoreVictimSaved' ) )
		{
			LogFailure( "no LoreVictimSaved tag" );
			return false;
		}
		if( stimType == gamedataStimType.Driving && ( ( VehicleObject )( stimEvent.sourceObject ) ) )
		{
			if( !( ( ( VehicleObject )( stimEvent.sourceObject ) ).IsOnPavement() ) )
			{
				LogFailure( "driving outside of pavement ignored" );
				return false;
			}
			if( ( !( IsTargetInFront( stimEvent.sourceObject, 30.0, true ) ) && !( IsTargetBehind( stimEvent.sourceObject, 160.0, true ) ) ) && !( IsTargetClose( stimEvent.sourceObject, 10.0 ) ) )
			{
				LogFailure( "driving distance/angle checks failed" );
				return false;
			}
			vehicle = ( ( VehicleObject )( stimEvent.sourceObject ) );
			if( vehicle )
			{
				if( vehicle.GetCurrentSpeed() < 24.0 )
				{
					LogFailure( "vehicle speed too low" );
					return false;
				}
			}
		}
		if( GameInstance.GetGameFeatureManager( owner.GetGame() ).AggressiveCrowdsEnabled() )
		{
			reactionSystem = GameInstance.GetReactionSystem( owner.GetGame() );
			if( reactionSystem.IsRegisteredAsAggressive( owner.GetEntityID() ) )
			{
				if( !( ShouldFearWhileAggressiveCrowdNPCCombat( stimEvent ) ) )
				{
					LogFailure( "NOT ShouldFearWhileAggressiveCrowdNPCCombat" );
					return false;
				}
			}
			else if( stimEvent.IsTagInStimuli( 'AggressiveCrowd' ) )
			{
				LogFailure( "non-agressive NPC, but stim for AggressiveCrowd" );
				return false;
			}
		}
		else if( stimEvent.IsTagInStimuli( 'AggressiveCrowd' ) )
		{
			LogFailure( "agressive NPCs disabled, but stim for AggressiveCrowd" );
			return false;
		}
		sceneSystemInt = GameInstance.GetSceneSystem( owner.GetGame() ).GetScriptInterface();
		if( sceneSystemInt.IsEntityInScene( owner.GetEntityID() ) && sceneSystemInt.IsEntityInScene( stimEvent.sourceObject.GetEntityID() ) )
		{
			LogFailure( "both owner and stim source in scene" );
			return false;
		}
		if( GameInstance.GetSafeAreaManager( owner.GetGame() ).IsPointInSafeArea( owner.GetWorldPosition() ) && IsEntityInInteriorArea( EntityGameInterface.GetEntity( owner.GetEntity() ) ) )
		{
			LogFailure( "owner in safe interior area" );
			return false;
		}
		return true;
	}

	private function HandleCrowdReaction( stimEvent : StimuliEvent )
	{
		var owner : GameObject;
		var game : GameInstance;
		var reactionSystem : ReactionSystem;
		var mounted : Bool;
		var delayedCrowdReaction : DelayedCrowdReactionEvent;
		var stimAttackData : stimInvestigateData;
		var mountInfo : MountingInfo;
		var vehicleReactionEvent : HandleReactionEvent;
		owner = GetOwner();
		game = owner.GetGame();
		reactionSystem = GameInstance.GetReactionSystem( game );
		mounted = VehicleComponent.IsMountedToVehicle( game, owner );
		if( ( ( ( m_inCrowd && GameInstance.GetGameFeatureManager( game ).AggressiveCrowdsEnabled() ) && ( !( mounted ) || VehicleComponent.IsDriver( game, owner.GetEntityID() ) ) ) && ShouldTriggerAggressiveCrowdNPCCombat( stimEvent ) ) && ( owner.IsPrevention() || reactionSystem.TryRegisteringAggressiveNPC( owner, !( mounted ) ) ) )
		{
			if( mounted )
			{
				m_driverIsAggressive = true;
			}
			else
			{
				TriggerAggressiveCrowdBehavior( owner, stimEvent.sourceObject );
			}
		}
		else if( m_reactionPreset.Type() == gamedataReactionPresetType.Child && stimEvent.IsTagInStimuli( 'ChildDanger' ) )
		{
			m_desiredFearPhase = 3;
			DeactiveLookAt();
			stimAttackData = stimEvent.stimInvestigateData;
			if( stimAttackData.attackInstigator )
			{
				stimEvent.sourceObject = ( ( GameObject )( stimAttackData.attackInstigator ) );
				stimEvent.sourcePosition = stimEvent.sourceObject.GetWorldPosition();
			}
			ActivateReactionLookAt( stimEvent.sourceObject, false );
			delayedCrowdReaction = new DelayedCrowdReactionEvent;
			delayedCrowdReaction.stimEvent = stimEvent;
			owner.QueueEvent( delayedCrowdReaction );
		}
		else if( ( VehicleComponent.IsMountedToVehicle( game, owner ) && stimEvent.GetStimType() != gamedataStimType.HijackVehicle ) && stimEvent.GetStimType() != gamedataStimType.Dying )
		{
			NPCPuppet.ChangeHighLevelState( owner, gamedataNPCHighLevelState.Fear );
			mountInfo = GameInstance.GetMountingFacility( game ).GetMountingInfoSingleWithObjects( owner );
			m_previousFearPhase = ConvertFearStageToFearPhase( m_crowdFearStage );
			vehicleReactionEvent = new HandleReactionEvent;
			vehicleReactionEvent.fearPhase = GetFearReactionPhase( stimEvent );
			vehicleReactionEvent.stimEvent = stimEvent;
			GameInstance.FindEntityByID( game, mountInfo.parentId ).QueueEvent( vehicleReactionEvent );
		}
		else if( m_driverIsAggressive && stimEvent.GetStimType() == gamedataStimType.EnvironmentalHazard )
		{
			TriggerReactionBehaviorForCrowd( stimEvent, gamedataOutput.Panic, true );
		}
		else
		{
			m_previousFearPhase = ConvertFearStageToFearPhase( m_crowdFearStage );
			m_desiredFearPhase = GetFearReactionPhase( stimEvent );
			if( VehicleComponent.IsMountedToVehicle( game, owner ) && stimEvent.GetStimType() == gamedataStimType.HijackVehicle )
			{
				GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:HandleCrowdReaction', , , true );
			}
			DeactiveLookAt();
			stimAttackData = stimEvent.stimInvestigateData;
			if( stimAttackData.attackInstigator )
			{
				stimEvent.sourceObject = ( ( GameObject )( stimAttackData.attackInstigator ) );
				stimEvent.sourcePosition = stimEvent.sourceObject.GetWorldPosition();
			}
			ActivateReactionLookAt( stimEvent.sourceObject, false );
			delayedCrowdReaction = new DelayedCrowdReactionEvent;
			delayedCrowdReaction.stimEvent = stimEvent;
			if( m_desiredFearPhase == -1 )
			{
				return;
			}
			CreateFearArea( stimEvent, m_desiredFearPhase );
			if( !( stimAttackData.skipReactionDelay ) )
			{
				m_delayReactionEventID = GameInstance.GetDelaySystem( game ).DelayEvent( owner, delayedCrowdReaction, RandRangeF( m_delay.X, m_delay.Y ) );
			}
			else
			{
				owner.QueueEvent( delayedCrowdReaction );
			}
		}
	}

	private function TriggerAggressiveCrowdBehavior( owner : GameObject, target : GameObject )
	{
		if( AIActionHelper.TryChangingAttitudeToHostile( ( ( ScriptedPuppet )( owner ) ), target ) )
		{
			owner.GetSensesComponent().IgnoreLODChange( true );
			owner.GetSensesComponent().Toggle( true );
			owner.GetTargetTrackerComponent().Toggle( true );
			TargetTrackingExtension.InjectThreat( ( ( ScriptedPuppet )( owner ) ), target );
			( ( NPCPuppet )( owner ) ).SetWasAggressiveCrowd( true );
			ResetAllFearAnimWrappers();
		}
	}

	protected event OnCrowdReaction( reactionDelayEvent : DelayedCrowdReactionEvent )
	{
		var workspotSystem : WorkspotGameSystem;
		var blackboardSystem : BlackboardSystem;
		var blackboard : IBlackboard;
		var exitWorkspot : ExitWorkspotSequenceEvent;
		var fearPhase : Int32;
		var pointResults : NavigationFindPointResult;
		var crowdMemberComponent : CrowdMemberComponent;
		var broadcaster : StimBroadcasterComponent;
		var owner : GameObject;
		var game : GameInstance;
		var stimType : gamedataStimType;
		var reactionSystem : ReactionSystem;
		var stimData : stimInvestigateData;
		var exitFearInVehicle : DeescalateFearInVehicle;
		owner = GetOwner();
		stimData = reactionDelayEvent.stimEvent.stimInvestigateData;
		if( !( ScriptedPuppet.IsActive( owner ) ) )
		{
			return false;
		}
		stimType = reactionDelayEvent.stimEvent.GetStimType();
		if( m_beignHijacked && stimType != gamedataStimType.HijackVehicle )
		{
			return false;
		}
		game = owner.GetGame();
		reactionSystem = GameInstance.GetReactionSystem( game );
		crowdMemberComponent = GetOwnerPuppet().GetCrowdMemberComponent();
		crowdMemberComponent.OnCrowdReaction( stimType );
		if( stimType == gamedataStimType.AimingAt && ( reactionDelayEvent.vehicleFearPhase != 3 ) )
		{
			blackboardSystem = GameInstance.GetBlackboardSystem( game );
			blackboard = blackboardSystem.GetLocalInstanced( GetPlayerSystem().GetLocalPlayerMainGameObject().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
			if( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody ) == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) ) )
			{
				blackboard = GameInstance.GetBlackboardSystem( game ).Get( GetAllBlackboardDefs().UI_TargetingInfo );
				if( ( blackboard.GetEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget ) != owner.GetEntityID() ) && m_reactionPreset.Type() != gamedataReactionPresetType.InVehicle_Civilian )
				{
					DeactiveLookAt();
					m_desiredFearPhase = -1;
					return NULL;
				}
			}
			else
			{
				DeactiveLookAt();
				m_desiredFearPhase = -1;
				return NULL;
			}
		}
		GameInstance.GetDelaySystem( game ).CancelDelay( m_callingPoliceID );
		fearPhase = GetFearReactionPhase( reactionDelayEvent.stimEvent );
		if( reactionDelayEvent.vehicleFearPhase > 0 )
		{
			fearPhase = reactionDelayEvent.vehicleFearPhase;
		}
		workspotSystem = GameInstance.GetWorkspotSystem( game );
		crowdMemberComponent.SetThreatLastKnownPosition( GetPlayerSystem().GetLocalPlayerMainGameObject().GetWorldPosition() );
		crowdMemberComponent.AllowWorkspotsUsage( false );
		NPCPuppet.ChangeHighLevelState( owner, gamedataNPCHighLevelState.Fear );
		if( !( VehicleComponent.IsMountedToVehicle( game, owner ) ) || stimType == gamedataStimType.HijackVehicle )
		{
			reactionSystem.MarkDespawnCandidate( owner.GetEntityID() );
		}
		DeactiveLookAt();
		ResetFacial( 0.0 );
		TriggerFearFacial( fearPhase );
		if( m_previousFearPhase != 2 )
		{
			AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, GetFearAnimWrapper( fearPhase ), 1.0 );
			if( ( fearPhase != 0 ) && ( !( m_fearLocomotionWrapper ) || ( fearPhase == 3 ) ) )
			{
				AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, GetRandomFearLocomotionAnimWrapper( fearPhase, stimType ), 1.0 );
			}
			m_fearLocomotionWrapper = true;
		}
		switch( fearPhase )
		{
			case 1:
				m_crowdFearStage = gameFearStage.Stressed;
			break;
			case 2:
				m_crowdFearStage = gameFearStage.Alarmed;
			break;
			case 3:
				m_crowdFearStage = gameFearStage.Panic;
			crowdMemberComponent.SetThreatLastKnownPosition( GetPlayerSystem().GetLocalPlayerMainGameObject().GetWorldPosition() );
			break;
			default:
				m_crowdFearStage = gameFearStage.Relaxed;
			break;
		}
		crowdMemberComponent.ChangeFearStage( m_crowdFearStage, !( stimData.skipInitialAnimation ) );
		if( workspotSystem.IsActorInWorkspot( owner ) )
		{
			if( reactionSystem.IsInImmovableWorkspot( owner.GetEntityID() ) )
			{
				if( ( fearPhase > 1 ) && workspotSystem.IsReactionAvailable( owner, 'Fear' ) )
				{
					ActivateReactionLookAt( reactionDelayEvent.stimEvent.sourceObject, false, false, , true );
					workspotSystem.SendReactionSignal( owner, 'Fear' );
					m_inReactionSequence = true;
					GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:OnDelayedReaction' );
				}
				else
				{
					TriggerFacialLookAtReaction( true, true );
				}
			}
			else
			{
				switch( fearPhase )
				{
					case 0:
						TriggerFacialLookAtReaction( true, true );
					break;
					case 1:
						pointResults = GameInstance.GetAINavigationSystem( game ).FindPointInSphereForCharacter( owner.GetWorldPosition(), 0.5, owner );
					if( ( ( IsTargetClose( reactionDelayEvent.stimEvent.sourceObject, 3.0 ) && IsTargetInFront( reactionDelayEvent.stimEvent.sourceObject ) ) && workspotSystem.IsReactionAvailable( owner, 'Fear' ) ) && stimType != gamedataStimType.Bump )
					{
						ActivateReactionLookAt( reactionDelayEvent.stimEvent.sourceObject, false, false, , true );
						workspotSystem.SendReactionSignal( owner, 'Fear' );
						m_inReactionSequence = true;
						GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:OnDelayedReaction' );
						exitWorkspot = new ExitWorkspotSequenceEvent;
						m_exitWorkspotSequenceEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, exitWorkspot, 3.0 );
					}
					else if( pointResults.status == worldNavigationRequestStatus.OK && workspotSystem.HasExitNodes( owner, true, false, true ) )
					{
						TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.WalkAway, false );
					}
					else
					{
						if( workspotSystem.IsReactionAvailable( owner, 'Fear' ) && stimType != gamedataStimType.Bump )
						{
							ActivateReactionLookAt( reactionDelayEvent.stimEvent.sourceObject, false, false, , true );
							workspotSystem.SendReactionSignal( owner, 'Fear' );
							m_inReactionSequence = true;
							GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:OnDelayedReaction' );
							exitWorkspot = new ExitWorkspotSequenceEvent;
							m_exitWorkspotSequenceEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, exitWorkspot, 3.0 );
						}
						else
						{
							TriggerFacialLookAtReaction( true, true );
						}
					}
					break;
					case 2:
						if( workspotSystem.IsReactionAvailable( owner, 'Fear' ) )
						{
							ActivateReactionLookAt( reactionDelayEvent.stimEvent.sourceObject, false, false, , true );
							workspotSystem.SendReactionSignal( owner, 'Fear' );
							m_inReactionSequence = true;
							GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:OnDelayedReaction' );
							exitWorkspot = new ExitWorkspotSequenceEvent;
							m_exitWorkspotSequenceEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, exitWorkspot, 3.0 );
						}
						else if( VehicleComponent.IsMountedToVehicle( game, owner ) )
						{
							ActivateReactionLookAt( reactionDelayEvent.stimEvent.sourceObject, false, false, , true, true );
							GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:OnCrowdReaction', , , true );
							exitFearInVehicle = new DeescalateFearInVehicle;
							m_exitFearInVehicleEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, exitFearInVehicle, 3.0 );
						}
						else if( !( m_inReactionSequence ) )
						{
							if( fearPhase == 3 )
							{
								SetCrowdRunningAwayAnimFeature( stimType );
								TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Flee, false );
							}
							else
							{
								TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Surrender, false );
							}
						}
					break;
					case 3:
						if( ( workspotSystem.IsWorkspotEnabled( owner ) && !( workspotSystem.HasExitNodes( owner, true, false, true ) ) ) && ( workspotSystem.IsReactionAvailable( owner, 'Fear' ) || m_inReactionSequence ) )
						{
							ActivateReactionLookAt( reactionDelayEvent.stimEvent.sourceObject, false, false, , true );
							workspotSystem.SendReactionSignal( owner, 'Fear' );
							m_inReactionSequence = true;
							GameObject.PlayVoiceOver( owner, 'fear_beg', 'Scripts:OnDelayedReaction' );
							exitWorkspot = new ExitWorkspotSequenceEvent;
							m_exitWorkspotSequenceEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, exitWorkspot, 3.0 );
						}
						else if( ( m_previousFearPhase > 1 ) || workspotSystem.HasExitNodes( owner, true, false ) )
						{
							SetCrowdRunningAwayAnimFeature( stimType );
							TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Flee, true );
						}
						else
						{
							SetCrowdRunningAwayAnimFeature( stimType );
							TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Flee, false );
						}
					break;
				}
			}
		}
		else if( m_inTrafficLane )
		{
			GetOwnerPuppet().GetAIControllerComponent().SetBehaviorArgument( 'StimTarget', reactionDelayEvent.stimEvent.sourceObject );
			switch( fearPhase )
			{
				case 0:
					TriggerFacialLookAtReaction( true, true );
				break;
				case 1:
					TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.WalkAway, false, true );
				break;
				case 2:
					crowdMemberComponent.TryStopTrafficMovement();
				TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Surrender, false, true );
				break;
				case 3:
					SetCrowdRunningAwayAnimFeature( stimType );
				TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Flee, false, true );
				break;
			}
		}
		else
		{
			switch( fearPhase )
			{
				case 0:
					TriggerFacialLookAtReaction( true, true );
				break;
				case 1:
					pointResults = GameInstance.GetAINavigationSystem( game ).FindPointInSphereForCharacter( owner.GetWorldPosition(), 0.5, owner );
				if( pointResults.status == worldNavigationRequestStatus.OK )
				{
					TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.WalkAway, false );
				}
				else
				{
					TriggerFacialLookAtReaction( true, true );
				}
				break;
				case 2:
					TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Surrender, true );
				break;
				case 3:
					if( m_previousFearPhase > 1 )
					{
						SetCrowdRunningAwayAnimFeature( stimType );
						TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Flee, false, true );
					}
					else
					{
						SetCrowdRunningAwayAnimFeature( stimType );
						TriggerReactionBehaviorForCrowd( reactionDelayEvent.stimEvent, gamedataOutput.Flee, false );
					}
				break;
			}
		}
		if( ( m_reactionPreset.Type() != gamedataReactionPresetType.Child && ( reactionDelayEvent.stimEvent.sourceObject == GetPlayerSystem().GetLocalPlayerMainGameObject() ) ) && !( reactionDelayEvent.stimEvent.IsTagInStimuli( 'NoFearSpread' ) ) )
		{
			SpreadFear( fearPhase );
		}
		else if( stimType == gamedataStimType.HijackVehicle )
		{
			broadcaster = reactionDelayEvent.stimEvent.sourceObject.GetStimBroadcasterComponent();
			if( broadcaster )
			{
				broadcaster.TriggerSingleBroadcast( owner, gamedataStimType.CrimeWitness );
			}
		}
		m_desiredFearPhase = -1;
	}

	private function SpreadFear( phase : Int32 )
	{
		var broadcaster : StimBroadcasterComponent;
		var stimData : stimInvestigateData;
		var owner : GameObject;
		owner = GetOwner();
		if( !( GameObject.IsCooldownActive( owner, 'spreadFearCooldown' ) ) )
		{
			GameObject.StartCooldown( owner, 'spreadFearCooldown', 3.0 );
			broadcaster = owner.GetStimBroadcasterComponent();
			if( broadcaster )
			{
				if( phase > 1 )
				{
					stimData.fearPhase = 3;
					broadcaster.TriggerSingleBroadcast( owner, gamedataStimType.SpreadFear, 10.0, stimData );
				}
				else
				{
					stimData.fearPhase = 1;
				}
				broadcaster.AddActiveStimuli( owner, gamedataStimType.SpreadFear, 3.0, 15.0, stimData, true );
			}
		}
	}

	private function CreateFearArea( stimEvent : StimuliEvent, fearPhase : Int32 )
	{
		var owner : GameObject;
		var game : GameInstance;
		var debugInfo : String;
		var radius : Float;
		owner = GetOwner();
		radius = stimEvent.radius;
		if( fearPhase <= 1 )
		{
			return;
		}
		game = owner.GetGame();
		radius = radius / SqrtF( 2.0 );
		if( IsFinal() )
		{
			GameInstance.GetReactionSystem( game ).AddFearSource( ( ( Vector3 )( stimEvent.sourcePosition ) ), radius );
		}
		else
		{
			debugInfo = ( ( "[" + EntityID.ToDebugStringDecimal( stimEvent.sourceObject.GetEntityID() ) ) + "] " ) + EnumValueToString( "gamedataStimType", ( ( Int32 )( stimEvent.GetStimType() ) ) );
			debugInfo += ( ( "(R=" + radius ) + ")" );
			GameInstance.GetReactionSystem( game ).AddFearSource( ( ( Vector3 )( stimEvent.sourcePosition ) ), radius, StringToName( debugInfo ) );
		}
	}

	private function ShouldFearInPlace( stimEvent : StimuliEvent ) : Bool
	{
		var weapon : WeaponObject;
		var player : PlayerPuppet;
		var owner : GameObject;
		var game : GameInstance;
		owner = GetOwner();
		game = owner.GetGame();
		if( VehicleComponent.IsMountedToVehicle( game, owner ) )
		{
			return false;
		}
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( game ).GetLocalPlayerMainGameObject() ) );
		if( VehicleComponent.IsMountedToVehicle( game, player ) )
		{
			return false;
		}
		if( stimEvent.IsTagInStimuli( 'TryRunAway' ) )
		{
			return false;
		}
		weapon = GameObject.GetActiveWeapon( player );
		if( weapon.IsMelee() )
		{
			return false;
		}
		if( PlayerPuppet.GetCurrentCombatState( player ) == gamePSMCombat.InCombat )
		{
			if( stimEvent.GetStimType() == gamedataStimType.CombatHit )
			{
				return true;
			}
			if( GetThreatDistanceSquared( stimEvent.sourceObject ) < ( m_fearInPlaceAroundDistance * m_fearInPlaceAroundDistance ) )
			{
				return true;
			}
		}
		return false;
	}

	private function TriggerFacialLookAtReaction( optional forceFear : Bool, optional playVO : Bool )
	{
		var facialReactionAnimFeature : AnimFeature_FacialReaction;
		var lookAtData : LookAtData;
		var vo : CName;
		var target : GameObject;
		facialReactionAnimFeature = new AnimFeature_FacialReaction;
		target = GetPlayerSystem().GetLocalPlayerControlledGameObject();
		if( !( m_inCrowd ) )
		{
			playVO = true;
			facialReactionAnimFeature.category = 3;
			facialReactionAnimFeature.idle = 1;
			m_facialCooldown = 2.0;
			vo = 'greeting';
			ActivateReactionLookAt( target, false );
		}
		else if( forceFear || IsTargetArmed( target ) )
		{
			facialReactionAnimFeature.category = 3;
			facialReactionAnimFeature.idle = 10;
			ActivateReactionLookAt( target, false );
			m_facialCooldown = 5.0;
		}
		else if( ( ( PlayerPuppet )( target ) ).IsNaked() )
		{
			facialReactionAnimFeature.category = 3;
			facialReactionAnimFeature.idle = 7;
			vo = 'fear_foll';
			ActivateReactionLookAt( target, true );
			m_facialCooldown = 5.0;
		}
		else
		{
			SelectFacialEmotion( lookAtData );
			vo = 'greeting';
			ActivateReactionLookAt( target, true, true );
			facialReactionAnimFeature.category = lookAtData.category;
			facialReactionAnimFeature.idle = lookAtData.idle;
			m_facialCooldown = 2.0;
		}
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'FacialReaction', facialReactionAnimFeature );
		if( !( GetOwnerPuppet().IsVendor() ) && playVO )
		{
			GameObject.PlayVoiceOver( GetOwner(), vo, 'Scripts:TriggerLookAtReaction' );
		}
	}

	private function TriggerFearFacial( phase : Int32 )
	{
		var facialReactionAnimFeature : AnimFeature_FacialReaction;
		facialReactionAnimFeature = new AnimFeature_FacialReaction;
		facialReactionAnimFeature.category = 3;
		switch( phase )
		{
			case 1:
				facialReactionAnimFeature.idle = 10;
			break;
			case 2:
				facialReactionAnimFeature.idle = 11;
			break;
			case 3:
				facialReactionAnimFeature.idle = 9;
			break;
			default:
				facialReactionAnimFeature.idle = 10;
			break;
		}
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'FacialReaction', facialReactionAnimFeature );
	}

	private function ResetFacial( cooldown : Float )
	{
		var facialReactionAnimFeature : AnimFeature_FacialReaction;
		var facialResetEvent : ResetFacialEvent;
		if( cooldown > 0.0 )
		{
			GameInstance.GetDelaySystem( GetOwner().GetGame() ).CancelDelay( m_resetFacialEventId );
			facialResetEvent = new ResetFacialEvent;
			m_resetFacialEventId = GetDelaySystem().DelayEvent( GetOwner(), facialResetEvent, cooldown );
		}
		else
		{
			facialReactionAnimFeature = new AnimFeature_FacialReaction;
			AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'FacialReaction', facialReactionAnimFeature );
		}
	}

	protected event OnResetLookatReactionEvent( evt : ResetLookatReactionEvent )
	{
		DeactiveLookAt();
		ResetFacial( m_facialCooldown );
	}

	protected event OnResetFacialEvent( evt : ResetFacialEvent )
	{
		var facialReactionAnimFeature : AnimFeature_FacialReaction;
		m_facialCooldown = 0.0;
		facialReactionAnimFeature = new AnimFeature_FacialReaction;
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'FacialReaction', facialReactionAnimFeature );
	}

	private function CanTriggerExpressionLookAt() : Bool
	{
		var sceneSystem : SceneSystemInterface;
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		if( !( ScriptedPuppet.IsActive( owner ) ) )
		{
			return false;
		}
		if( m_highLevelState == gamedataNPCHighLevelState.Combat || m_highLevelState == gamedataNPCHighLevelState.Alerted )
		{
			return false;
		}
		if( ( ( NPCPuppet )( ownerPuppet ) ).IsRagdolling() )
		{
			return false;
		}
		if( ScriptedPuppet.IsBlinded( ownerPuppet ) )
		{
			return false;
		}
		if( m_reactionPreset.Type() == gamedataReactionPresetType.NoReaction || m_reactionPreset.Type() == gamedataReactionPresetType.Follower )
		{
			return false;
		}
		if( ownerPuppet.IsBoss() )
		{
			return false;
		}
		if( ( m_inReactionSequence || m_crowdFearStage == gameFearStage.Alarmed ) || m_crowdFearStage == gameFearStage.Panic )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'LoreAnim' ) )
		{
			return false;
		}
		if( GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) )
		{
			if( ( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Braindance' ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Sleep' ) ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'Busy' ) )
			{
				return false;
			}
		}
		sceneSystem = GameInstance.GetSceneSystem( owner.GetGame() ).GetScriptInterface();
		if( sceneSystem && ( sceneSystem.IsEntityInDialogue( owner.GetEntityID() ) || sceneSystem.IsEntityInScene( owner.GetEntityID() ) ) )
		{
			return false;
		}
		if( !( ownerPuppet.IsOnAutonomousAI() ) )
		{
			return false;
		}
		return true;
	}

	private function ShouldFearWhileAggressiveCrowdNPCCombat( stimEvent : StimuliEvent ) : Bool
	{
		var playerPuppet : PlayerPuppet;
		var threats : array< TrackedLocation >;
		threats = GetPlayerSystem().GetLocalPlayerControlledGameObject().GetTargetTrackerComponent().GetHostileThreats( false );
		if( threats.Size() > 1 )
		{
			return true;
		}
		if( stimEvent.GetStimType() == gamedataStimType.EnvironmentalHazard )
		{
			return true;
		}
		playerPuppet = ( ( PlayerPuppet )( stimEvent.sourceObject ) );
		if( WeaponObject.IsMelee( GameObject.GetActiveWeapon( GetOwner() ).GetItemID() ) )
		{
			if( stimEvent.GetStimType() == gamedataStimType.WeaponDisplayed && WeaponObject.IsRanged( GameObject.GetActiveWeapon( playerPuppet ).GetItemID() ) )
			{
				return true;
			}
			if( StimFilters.IsGunshot( stimEvent.GetStimType() ) )
			{
				return true;
			}
		}
		return false;
	}

	private function ShouldTriggerAggressiveCrowdNPCCombat( stimEvent : StimuliEvent ) : Bool
	{
		var playerPuppet : PlayerPuppet;
		var owner : GameObject;
		var distanceToPlayer : Float;
		var maxDistanceToTriggerCombat : Float;
		var preventionSystem : PreventionSystem;
		var navigationSystem : AINavigationSystem;
		var isMountedToVehicle : Bool;
		playerPuppet = ( ( PlayerPuppet )( stimEvent.sourceObject ) );
		owner = GetOwner();
		isMountedToVehicle = VehicleComponent.IsMountedToVehicle( owner.GetGame(), owner );
		if( !( playerPuppet ) )
		{
			return false;
		}
		if( playerPuppet.IsInCombat() )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( playerPuppet, 'NoCombat' ) )
		{
			return false;
		}
		if( playerPuppet.GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.Zones ) == ( ( Int32 )( gamePSMZones.Safe ) ) )
		{
			return false;
		}
		if( GameInstance.GetSafeAreaManager( owner.GetGame() ).IsPointInSafeArea( playerPuppet.GetWorldPosition() ) )
		{
			return false;
		}
		preventionSystem = ( ( PreventionSystem )( GameInstance.GetScriptableSystemsContainer( owner.GetGame() ).Get( 'PreventionSystem' ) ) );
		if( ( ( Int32 )( preventionSystem.GetHeatStage() ) ) > 0 )
		{
			return false;
		}
		if( stimEvent.IsTagInStimuli( 'NonAggressiveCrowd' ) )
		{
			return false;
		}
		if( !( isMountedToVehicle ) && !( IsTargetInFront( playerPuppet, 120.0 ) ) )
		{
			return false;
		}
		if( !( isMountedToVehicle ) && !( IsTargetInFront( playerPuppet, 100.0, true ) ) )
		{
			return false;
		}
		if( WeaponObject.IsRanged( GameObject.GetActiveWeapon( playerPuppet ).GetItemID() ) )
		{
			distanceToPlayer = Vector4.DistanceSquared( owner.GetWorldPosition(), playerPuppet.GetWorldPosition() );
			maxDistanceToTriggerCombat = TweakDBInterface.GetFloat( T"AIGeneralSettings.distanceToTriggerAggressiveCrowdRanged", 15.0 );
			if( distanceToPlayer >= ( maxDistanceToTriggerCombat * maxDistanceToTriggerCombat ) )
			{
				return false;
			}
		}
		else
		{
			distanceToPlayer = Vector4.DistanceSquared( owner.GetWorldPosition(), playerPuppet.GetWorldPosition() );
			maxDistanceToTriggerCombat = TweakDBInterface.GetFloat( T"AIGeneralSettings.distanceToTriggerAggressiveCrowdMelee", 5.0 );
			if( distanceToPlayer >= ( maxDistanceToTriggerCombat * maxDistanceToTriggerCombat ) )
			{
				return false;
			}
		}
		if( ( !( m_driverAllowedToGetAggressive ) && stimEvent.GetStimType() == gamedataStimType.HijackVehicle ) && isMountedToVehicle )
		{
			return false;
		}
		if( !( isMountedToVehicle ) )
		{
			navigationSystem = GameInstance.GetAINavigationSystem( owner.GetGame() );
			if( navigationSystem && !( navigationSystem.IsPointOnNavmesh( owner, owner.GetWorldPosition(), 0.1 ) ) )
			{
				return false;
			}
		}
		return true;
	}

	private function IsPlayerFearThreat() : Bool
	{
		if( IsTargetArmed( GetPlayerSystem().GetLocalPlayerControlledGameObject() ) )
		{
			return true;
		}
		if( IsPlayerCarryingBody( ( ( PlayerPuppet )( GetPlayerSystem().GetLocalPlayerControlledGameObject() ) ) ) )
		{
			return true;
		}
		return false;
	}

	private const function IsPlayerCarryingBody( playerPuppet : weak< PlayerPuppet > ) : Bool
	{
		var playerStateMachineBlackboard : IBlackboard;
		if( !( playerPuppet ) )
		{
			return false;
		}
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( playerPuppet.GetGame() ).GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		return playerStateMachineBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.Carrying );
	}

	private function SetCrowdRunningAwayAnimFeature( stimType : gamedataStimType )
	{
		var crowdRunningAwayAnimFeature : AnimFeature_CrowdRunningAway;
		crowdRunningAwayAnimFeature = new AnimFeature_CrowdRunningAway;
		if( stimType == gamedataStimType.Driving )
		{
			crowdRunningAwayAnimFeature.isRunningAwayFromPlayersCar = true;
		}
		else
		{
			crowdRunningAwayAnimFeature.isRunningAwayFromPlayersCar = false;
		}
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'CrowdRunningAway', crowdRunningAwayAnimFeature );
	}

	private function SafeToExitFear() : Bool
	{
		var distanceToPlayer : Float;
		if( IsPlayerFearThreat() )
		{
			distanceToPlayer = Vector4.DistanceSquared( GetOwner().GetWorldPosition(), GetPlayerSystem().GetLocalPlayerControlledGameObject().GetWorldPosition() );
			if( IsTargetInFront( GetPlayerSystem().GetLocalPlayerControlledGameObject(), 120.0 ) )
			{
				if( distanceToPlayer < ( m_fearToIdleDistance.X * m_fearToIdleDistance.X ) )
				{
					return false;
				}
			}
			else
			{
				if( distanceToPlayer < ( m_fearToIdleDistance.Y * m_fearToIdleDistance.Y ) )
				{
					return false;
				}
			}
		}
		return true;
	}

	private function SafeToExitPanicFear() : Bool
	{
		var distanceToPlayer : Float;
		var simTime : Float;
		distanceToPlayer = Vector4.DistanceSquared( GetOwner().GetWorldPosition(), GetPlayerSystem().GetLocalPlayerControlledGameObject().GetWorldPosition() );
		if( GameInstance.GetCameraSystem( GetOwner().GetGame() ).IsInCameraFrustum( GetOwner(), 2.0, 0.75 ) )
		{
			if( distanceToPlayer > ( 35.0 * 35.0 ) )
			{
				simTime = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) );
				if( m_successfulFearDeescalation == 0.0 )
				{
					m_successfulFearDeescalation = simTime + 1.5;
				}
				if( m_successfulFearDeescalation <= simTime )
				{
					return true;
				}
				else
				{
					return false;
				}
			}
		}
		else if( distanceToPlayer > ( 22.5 * 22.5 ) )
		{
			simTime = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) );
			if( m_successfulFearDeescalation == 0.0 )
			{
				m_successfulFearDeescalation = simTime + 0.5;
			}
			if( m_successfulFearDeescalation >= simTime )
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		m_successfulFearDeescalation = 0.0;
		return false;
	}

	private function SurrenderToLeave() : Bool
	{
		var direction : Vector4;
		var angleToTarget : Float;
		var target : GameObject;
		var targetPos : Vector4;
		var ownerPos : Vector4;
		target = GetPlayerSystem().GetLocalPlayerControlledGameObject();
		targetPos = target.GetWorldPosition();
		ownerPos = GetOwner().GetWorldPosition();
		direction = targetPos - ownerPos;
		angleToTarget = Vector4.GetAngleDegAroundAxis( direction, GetOwner().GetWorldForward(), GetOwner().GetWorldUp() );
		if( AbsF( angleToTarget ) > 120.0 )
		{
			return true;
		}
		direction = ownerPos - targetPos;
		angleToTarget = Vector4.GetAngleDegAroundAxis( direction, target.GetWorldForward(), target.GetWorldUp() );
		if( AbsF( angleToTarget ) > 10.0 )
		{
			return true;
		}
		return false;
	}

	private function CanTriggerReprimandOrder() : Bool
	{
		var reprimandAbility : GameplayAbility_Record;
		var record : IPrereq_Record;
		var prereqCount : Int32;
		var i : Int32;
		var owner : GameObject;
		var game : GameInstance;
		owner = GetOwner();
		game = owner.GetGame();
		reprimandAbility = TweakDBInterface.GetGameplayAbilityRecord( T"Ability.CanAskToFollowOrder" );
		prereqCount = reprimandAbility.GetPrereqsForUseCount();
		for( i = 0; i < prereqCount; i += 1 )
		{
			record = reprimandAbility.GetPrereqsForUseItem( i );
			if( IPrereq.CreatePrereq( record.GetID() ).IsFulfilled( game, owner ) )
			{
				return true;
			}
		}
		return false;
	}

	public const function CanAskToHolsterWeapon() : Bool
	{
		var reprimandAbility : GameplayAbility_Record;
		var record : IPrereq_Record;
		var prereqCount : Int32;
		var i : Int32;
		var owner : GameObject;
		var game : GameInstance;
		owner = GetOwner();
		game = owner.GetGame();
		reprimandAbility = TweakDBInterface.GetGameplayAbilityRecord( T"Ability.CanAskToHolsterWeapon" );
		prereqCount = reprimandAbility.GetPrereqsForUseCount();
		for( i = 0; i < prereqCount; i += 1 )
		{
			record = reprimandAbility.GetPrereqsForUseItem( i );
			if( IPrereq.CreatePrereq( record.GetID() ).IsFulfilled( game, owner ) )
			{
				return true;
			}
		}
		return false;
	}

	private function NotifySecuritySystem( stimType : gamedataStimType, stimObject : GameObject )
	{
		if( stimType == gamedataStimType.DeadBody )
		{
			GetOwnerPuppet().TriggerSecuritySystemNotification( stimObject.GetWorldPosition(), stimObject, ESecurityNotificationType.ALARM, stimType );
		}
	}

	private function SetWarningMessage( const lockey : ref< String > )
	{
		var simpleScreenMessage : SimpleScreenMessage;
		simpleScreenMessage.isShown = true;
		simpleScreenMessage.duration = 5.0;
		simpleScreenMessage.message = lockey;
		simpleScreenMessage.isInstant = true;
		GameInstance.GetBlackboardSystem( GetPlayerSystem().GetLocalPlayerControlledGameObject().GetGame() ).Get( GetAllBlackboardDefs().UI_Notifications ).SetVariant( GetAllBlackboardDefs().UI_Notifications.WarningMessage, simpleScreenMessage, true );
	}

	protected event OnTriggerDelayedReactionEvent( evt : TriggerDelayedReactionEvent )
	{
		TriggerReactionBehaviorForCrowd( evt.stimEvent, evt.behavior, evt.initAnim );
	}

	protected event OnExitWorkspotSequenceEvent( evt : ExitWorkspotSequenceEvent )
	{
		var owner : GameObject;
		var game : GameInstance;
		owner = GetOwner();
		game = owner.GetGame();
		if( m_inReactionSequence )
		{
			if( m_crowdFearStage == gameFearStage.Panic && VehicleComponent.IsMountedToVehicle( game, owner ) )
			{
				if( SafeToExitPanicFear() )
				{
					m_successfulFearDeescalation = 0.0;
					GameInstance.GetWorkspotSystem( game ).HardResetPlaybackToStart( owner );
				}
				else
				{
					GameInstance.GetDelaySystem( game ).CancelDelay( m_exitWorkspotSequenceEventId );
					m_exitWorkspotSequenceEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, evt, 1.0 );
				}
			}
			else if( m_crowdFearStage != gameFearStage.Panic )
			{
				if( SafeToExitFear() )
				{
					if( VehicleComponent.IsMountedToVehicle( game, owner ) )
					{
						GameInstance.GetWorkspotSystem( game ).HardResetPlaybackToStart( owner );
					}
					else
					{
						GameInstance.GetWorkspotSystem( game ).ResetPlaybackToStart( owner );
					}
				}
				else
				{
					GameInstance.GetDelaySystem( game ).CancelDelay( m_exitWorkspotSequenceEventId );
					m_exitWorkspotSequenceEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, evt, 1.0 );
				}
			}
		}
	}

	protected event OnDeescalateFearInVehicle( evt : DeescalateFearInVehicle )
	{
		var owner : GameObject;
		var game : GameInstance;
		var crowdSettingsEvent : CrowdSettingsEvent;
		var mountInfo : MountingInfo;
		owner = GetOwner();
		game = owner.GetGame();
		if( VehicleComponent.IsMountedToVehicle( game, owner ) )
		{
			if( SafeToExitPanicFear() )
			{
				m_crowdFearStage = gameFearStage.Relaxed;
				GetOwnerPuppet().GetCrowdMemberComponent().ChangeFearStage( m_crowdFearStage );
				m_successfulFearDeescalation = 0.0;
				AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'fear', 0.0 );
				crowdSettingsEvent = new CrowdSettingsEvent;
				crowdSettingsEvent.movementType = 'normal';
				mountInfo = GameInstance.GetMountingFacility( game ).GetMountingInfoSingleWithObjects( owner );
				GameInstance.FindEntityByID( game, mountInfo.parentId ).QueueEvent( crowdSettingsEvent );
			}
			else
			{
				GameInstance.GetDelaySystem( game ).CancelDelay( m_exitFearInVehicleEventId );
				m_exitFearInVehicleEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, evt, 1.0 );
			}
		}
	}

	protected event OnCrowdSettingsEvent( evt : CrowdSettingsEvent )
	{
		GetOwnerPuppet().GetCrowdMemberComponent().ChangeMoveType( evt.movementType );
	}

	private function GetFearReactionPhase( stimEvent : StimuliEvent ) : Int32
	{
		var stimData : stimInvestigateData;
		if( StimFilters.IsIllegalAction( stimEvent.GetStimType() ) )
		{
			stimData = stimEvent.stimInvestigateData;
			if( stimData.fearPhase != 0 )
			{
				return 0;
			}
		}
		if( stimEvent.IsTagInStimuli( 'Uncomfortable' ) )
		{
			if( m_isInCrosswalk )
			{
				return 1;
			}
			return 0;
		}
		if( stimEvent.IsTagInStimuli( 'PotentialFear' ) )
		{
			return 1;
		}
		if( stimEvent.IsTagInStimuli( 'DirectThreat' ) )
		{
			if( IsTargetClose( stimEvent.sourceObject, 10.0 ) )
			{
				return 2;
			}
			else
			{
				return 1;
			}
		}
		if( stimEvent.IsTagInStimuli( 'PanicFear' ) )
		{
			return 3;
		}
		if( stimEvent.GetStimType() == gamedataStimType.SpreadFear )
		{
			stimData = stimEvent.stimInvestigateData;
			if( stimData.fearPhase == 0 )
			{
				return 1;
			}
			else
			{
				return stimData.fearPhase;
			}
		}
		if( stimEvent.GetStimType() == gamedataStimType.Driving )
		{
			return 3;
		}
		return -1;
	}

	private function ShouldInterruptCurrentFearStage( fearPhase : Int32 ) : Bool
	{
		if( m_highLevelState == gamedataNPCHighLevelState.Relaxed )
		{
			return true;
		}
		if( fearPhase > ConvertFearStageToFearPhase( m_crowdFearStage ) )
		{
			return true;
		}
		return false;
	}

	private function ConvertFearStageToFearPhase( fearStage : gameFearStage ) : Int32
	{
		switch( fearStage )
		{
			case gameFearStage.Relaxed:
				return 0;
			case gameFearStage.Stressed:
				return 1;
			case gameFearStage.Alarmed:
				return 2;
			default:
				return 3;
		}
	}

	private function GetSpreadFearPhase( stimEvent : StimuliEvent ) : Int32
	{
		if( stimEvent.GetStimType() == gamedataStimType.CombatHit )
		{
			return 1;
		}
		if( ( m_desiredFearPhase > 0 ) && ( m_desiredFearPhase != 3 ) )
		{
			return 1;
		}
		return m_desiredFearPhase;
	}

	private function CanReactInVehicle( stimEvent : StimuliEvent ) : Bool
	{
		var stimType : gamedataStimType;
		var mountingInfos : array< MountingInfo >;
		var carMountInfo : MountingInfo;
		var count, i : Int32;
		var owner : GameObject;
		var game : GameInstance;
		var randDraw : Int32;
		stimType = stimEvent.GetStimType();
		owner = GetOwner();
		game = owner.GetGame();
		if( !( VehicleComponent.IsMountedToVehicle( game, owner ) ) )
		{
			return true;
		}
		if( stimType == gamedataStimType.VehicleHit )
		{
			randDraw = RandRange( 0, 100 );
			if( randDraw > 30 )
			{
				LogFailure( "randomly ignoring vehicle hits" );
				return false;
			}
		}
		if( StimFilters.IsIgnoredInVehicle( stimType ) )
		{
			LogFailure( "IsIgnoredInVehicle" );
			return false;
		}
		if( ( stimEvent.IsTagInStimuli( 'TrafficPlayerOnly' ) && !( stimEvent.sourceObject.IsPlayer() ) ) && PlayerPuppet.GetCurrentCombatState( ( ( PlayerPuppet )( GetPlayerSystem().GetLocalPlayerMainGameObject() ) ) ) != gamePSMCombat.InCombat )
		{
			LogFailure( "TrafficPlayerOnly" );
			return false;
		}
		if( stimType == gamedataStimType.Dying )
		{
			carMountInfo = GameInstance.GetMountingFacility( game ).GetMountingInfoSingleWithObjects( owner );
			mountingInfos = GameInstance.GetMountingFacility( game ).GetMountingInfoMultipleWithIds( , carMountInfo.parentId );
			count = mountingInfos.Size();
			for( i = 0; i < count; i += 1 )
			{
				if( ( ( GameObject )( GameInstance.FindEntityByID( game, mountingInfos[ i ].childId ) ) ) == stimEvent.sourceObject )
				{
					return true;
				}
			}
			LogFailure( "ignoring dying in vehicle" );
			return false;
		}
		return true;
	}

	protected event OnCrowdCallingPoliceEvent( evt : CrowdCallingPoliceEvent )
	{
		var reactionSystem : ScriptedReactionSystem;
		var request : UnregisterPoliceCaller;
		m_willingToCallPolice = false;
		reactionSystem = ( ( ScriptedReactionSystem )( GameInstance.GetScriptableSystemsContainer( GetOwner().GetGame() ).Get( 'ScriptedReactionSystem' ) ) );
		if( reactionSystem && reactionSystem.IsCaller( GetEntity() ) )
		{
			TriggerReactionBehaviorForCrowd( GetPlayerSystem().GetLocalPlayerMainGameObject(), gamedataOutput.CallPolice, false );
			request = new UnregisterPoliceCaller;
			reactionSystem.QueueRequest( request );
		}
	}

	private function CheckStalk( target : GameObject, timeout : Float )
	{
		var stalkEvent : StalkEvent;
		if( m_playerProximity && ( m_reactionPreset.Type() != gamedataReactionPresetType.Civilian_Passive || !( SourceAttitude( target, EAIAttitude.AIA_Friendly ) ) ) )
		{
			stalkEvent = new StalkEvent;
			GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), stalkEvent, timeout );
		}
	}

	private function CheckComfortZone()
	{
		var distrurbComfortZoneEvent : DisturbingComfortZone;
		var checkComfortZoneEvent : CheckComfortZoneEvent;
		var broadcaster : StimBroadcasterComponent;
		var owner : GameObject;
		var player : GameObject;
		var game : GameInstance;
		var simTime : Float;
		owner = GetOwner();
		player = GetPlayerSystem().GetLocalPlayerControlledGameObject();
		game = owner.GetGame();
		simTime = EngineTime.ToFloat( GameInstance.GetSimTime( game ) );
		if( ( m_playerProximity && IsTargetInFront( player ) ) && IsTargetInFront( player, 45.0, true ) )
		{
			if( m_disturbingComfortZoneInProgress )
			{
				if( m_comfortZoneTimestamp >= simTime )
				{
					m_entereProximityRecently += 1;
					if( m_entereProximityRecently >= 3 )
					{
						broadcaster = player.GetStimBroadcasterComponent();
						if( broadcaster )
						{
							broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.CrowdIllegalAction, owner );
						}
					}
				}
				else
				{
					m_entereProximityRecently = 0;
					m_disturbingComfortZoneInProgress = false;
				}
			}
			else
			{
				m_disturbingComfortZoneInProgress = true;
				m_entereProximityRecently += 1;
				m_comfortZoneTimestamp = simTime + 10.0;
			}
			distrurbComfortZoneEvent = new DisturbingComfortZone;
			m_disturbComfortZoneEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, distrurbComfortZoneEvent, 10.0 );
		}
		else if( m_playerProximity )
		{
			checkComfortZoneEvent = new CheckComfortZoneEvent;
			m_checkComfortZoneEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, checkComfortZoneEvent, 1.0 );
		}
	}

	protected event OnDisturbingComfortZoneEvent( evt : DisturbingComfortZone )
	{
		var disturbEvent : DisturbingComfortZone;
		var broadcaster : StimBroadcasterComponent;
		var player : GameObject;
		player = GetPlayerSystem().GetLocalPlayerControlledGameObject();
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).CancelDelay( m_disturbComfortZoneEventId );
		if( ( ( ( m_playerProximity && IsTargetInFront( player ) ) && !( GetOwnerPuppet().IsVendor() ) ) && IsTargetClose( player, 1.0 ) ) && IsTargetInFront( player, 45.0, true ) )
		{
			TriggerFacialLookAtReaction( true, true );
			broadcaster = player.GetStimBroadcasterComponent();
			if( broadcaster )
			{
				broadcaster.SendDrirectStimuliToTarget( GetOwner(), gamedataStimType.CrowdIllegalAction, GetOwner() );
			}
		}
		else if( m_playerProximity )
		{
			disturbEvent = new DisturbingComfortZone;
			m_disturbComfortZoneEventId = GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), disturbEvent, 1.0 );
		}
	}

	protected event OnCheckComfortZoneEvent( evt : CheckComfortZoneEvent )
	{
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).CancelDelay( m_checkComfortZoneEventId );
		CheckComfortZone();
	}

	private function GetOutputPriority( output : gamedataOutput ) : Float
	{
		var outputRecord : Output_Record;
		outputRecord = TweakDBInterface.GetOutputRecord( TDBID.Create( "ReactionOutput." + EnumValueToString( "gamedataOutput", ( ( Int32 )( output ) ) ) ) );
		if( outputRecord )
		{
			return outputRecord.AIPriority();
		}
		return 4.0;
	}

	private function DelayReaction( stimType : gamedataStimType ) : Bool
	{
		if( stimType == gamedataStimType.AimingAt )
		{
			return true;
		}
		return false;
	}

	private function SelectFacialEmotion( out lookAtData : LookAtData )
	{
		var personalities : array< gamedataStatType >;
		personalities.PushBack( gamedataStatType.PersonalityAggressive );
		personalities.PushBack( gamedataStatType.PersonalityCuriosity );
		personalities.PushBack( gamedataStatType.PersonalityDisgust );
		personalities.PushBack( gamedataStatType.PersonalityFear );
		personalities.PushBack( gamedataStatType.PersonalityFunny );
		personalities.PushBack( gamedataStatType.PersonalityJoy );
		personalities.PushBack( gamedataStatType.PersonalitySad );
		personalities.PushBack( gamedataStatType.PersonalityShock );
		personalities.PushBack( gamedataStatType.PersonalitySurprise );
		lookAtData.personality = personalities[ RandRange( 0, 9 ) ];
		lookAtData.category = 3;
		switch( lookAtData.personality )
		{
			case gamedataStatType.PersonalityAggressive:
				lookAtData.idle = 1;
			break;
			case gamedataStatType.PersonalityCuriosity:
				lookAtData.category = 1;
			lookAtData.idle = 3;
			break;
			case gamedataStatType.PersonalityDisgust:
				lookAtData.idle = 7;
			break;
			case gamedataStatType.PersonalityFear:
				lookAtData.idle = 10;
			break;
			case gamedataStatType.PersonalityFunny:
				lookAtData.idle = 5;
			break;
			case gamedataStatType.PersonalityJoy:
				lookAtData.idle = 5;
			break;
			case gamedataStatType.PersonalitySad:
				lookAtData.idle = 3;
			break;
			case gamedataStatType.PersonalityShock:
				lookAtData.idle = 8;
			break;
			case gamedataStatType.PersonalitySurprise:
				lookAtData.idle = 8;
			break;
			default:
				lookAtData.idle = 2;
			lookAtData.category = 2;
			break;
		}
	}

	private function MapLookAtVO( lookAtData : LookAtData, out vo : CName )
	{
		switch( lookAtData.personality )
		{
			case gamedataStatType.PersonalityAggressive:
				vo = 'fear_foll';
			break;
			case gamedataStatType.PersonalityCuriosity:
				vo = 'greeting';
			break;
			case gamedataStatType.PersonalityDisgust:
				vo = 'fear_foll';
			break;
			case gamedataStatType.PersonalityFear:
				vo = 'fear_foll';
			break;
			case gamedataStatType.PersonalityFunny:
				vo = 'greeting';
			break;
			case gamedataStatType.PersonalityJoy:
				vo = 'greeting';
			break;
			case gamedataStatType.PersonalitySad:
				vo = '';
			break;
			case gamedataStatType.PersonalityShock:
				vo = 'fear_foll';
			break;
			case gamedataStatType.PersonalitySurprise:
				vo = 'greeting';
			break;
			default:
				vo = '';
			break;
			break;
		}
	}

	private virtual function ActivateReactionLookAt( targetEntity : Entity, optional end : Bool, optional repeat : Bool, optional duration : Float, optional upperBody : Bool, optional inVehicle : Bool ) : Bool
	{
		var lookAtEvent : LookAtAddEvent;
		var endLookat : EndLookatEvent;
		var lookAtPartRequest : LookAtPartRequest;
		var lookAtParts : array< LookAtPartRequest >;
		var owner : GameObject;
		if( !( targetEntity ) )
		{
			return false;
		}
		if( m_highLevelState == gamedataNPCHighLevelState.Combat )
		{
			return false;
		}
		owner = GetOwner();
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'LoreAnim' ) )
		{
			return false;
		}
		if( m_lookatEvent )
		{
			DeactiveLookAt();
		}
		lookAtEvent = new LookAtAddEvent;
		lookAtEvent.SetEntityTarget( targetEntity, 'pla_default_tgt', Vector4.EmptyVector() );
		lookAtEvent.SetStyle( animLookAtStyle.Normal );
		lookAtEvent.request.limits.softLimitDegrees = 360.0;
		lookAtEvent.request.limits.hardLimitDegrees = 270.0;
		lookAtEvent.request.limits.backLimitDegrees = 210.0;
		lookAtEvent.request.limits.hardLimitDistance = GetLookAtLimitDistanceValue( animLookAtLimitDistanceType.None );
		lookAtEvent.request.calculatePositionInParentSpace = true;
		lookAtEvent.bodyPart = 'Eyes';
		lookAtPartRequest.partName = 'Head';
		lookAtPartRequest.weight = 0.1;
		lookAtPartRequest.suppress = 1.0;
		lookAtPartRequest.mode = 0;
		lookAtParts.PushBack( lookAtPartRequest );
		if( !( inVehicle ) )
		{
			lookAtPartRequest.partName = 'Chest';
			lookAtPartRequest.weight = 2.0;
			lookAtPartRequest.suppress = 0.0;
			lookAtPartRequest.mode = 0;
			if( upperBody )
			{
				lookAtPartRequest.weight = 0.2;
			}
			lookAtParts.PushBack( lookAtPartRequest );
		}
		if( !( IsFinal() ) )
		{
			lookAtEvent.SetDebugInfo( "ScriptReactionComponent" );
		}
		lookAtEvent.SetAdditionalPartsArray( lookAtParts );
		owner.QueueEvent( lookAtEvent );
		if( end )
		{
			endLookat = new EndLookatEvent;
			endLookat.repeat = repeat;
			if( duration != 0.0 )
			{
				GetDelaySystem().DelayEvent( owner, endLookat, TweakDBInterface.GetFloat( T"AIGeneralSettings.reactionLookAtDuration", duration ) );
			}
			else
			{
				GetDelaySystem().DelayEvent( owner, endLookat, TweakDBInterface.GetFloat( T"AIGeneralSettings.reactionLookAtDuration", 5.0 ) );
			}
		}
		m_lookatEvent = lookAtEvent;
		return true;
	}

	private function ActivateUndeadLookAt( targetEntity : Entity ) : Bool
	{
		var owner : GameObject;
		var lookAtEvent : LookAtAddEvent;
		var lookAtPartRequest : LookAtPartRequest;
		var lookAtParts : array< LookAtPartRequest >;
		var undeadAnimFeature : AnimFeature_Undead;
		var endLookat : EndLookatEvent;
		var disableUndead : DisableUndeadAnimFeatureEvent;
		if( !( targetEntity ) )
		{
			LogFailure( "can not activate lookat: no target entity" );
			return false;
		}
		owner = GetOwner();
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( owner, 'LoreAnim' ) )
		{
			LogFailure( "can not activate lookat: LoreAnim" );
			return false;
		}
		if( m_lookatEvent )
		{
			DeactiveLookAt();
		}
		lookAtEvent = new LookAtAddEvent;
		lookAtEvent.SetEntityTarget( targetEntity, 'pla_default_tgt', Vector4.EmptyVector() );
		lookAtEvent.SetStyle( animLookAtStyle.Normal );
		lookAtEvent.request.transitionSpeed = 66.59999847;
		lookAtEvent.request.limits.softLimitDegrees = 360.0;
		lookAtEvent.request.limits.hardLimitDegrees = 360.0;
		lookAtEvent.request.limits.backLimitDegrees = 360.0;
		lookAtEvent.request.limits.hardLimitDistance = GetLookAtLimitDistanceValue( animLookAtLimitDistanceType.None );
		lookAtEvent.request.calculatePositionInParentSpace = true;
		lookAtEvent.bodyPart = 'Eyes';
		lookAtPartRequest.partName = 'Head';
		lookAtPartRequest.weight = 0.001;
		lookAtPartRequest.suppress = 1.0;
		lookAtPartRequest.mode = 0;
		lookAtParts.PushBack( lookAtPartRequest );
		lookAtPartRequest.partName = 'Chest';
		lookAtPartRequest.weight = 1.5;
		lookAtPartRequest.suppress = 0.1;
		lookAtPartRequest.mode = 0;
		lookAtParts.PushBack( lookAtPartRequest );
		if( !( IsFinal() ) )
		{
			lookAtEvent.SetDebugInfo( "ScriptReactionComponent.ActivateUndeadLookAt" );
		}
		lookAtEvent.SetAdditionalPartsArray( lookAtParts );
		owner.QueueEvent( lookAtEvent );
		m_lookatEvent = lookAtEvent;
		undeadAnimFeature = new AnimFeature_Undead;
		undeadAnimFeature.active = true;
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'Undead', undeadAnimFeature );
		endLookat = new EndLookatEvent;
		endLookat.repeat = false;
		GetDelaySystem().DelayEvent( owner, endLookat, 15.0 );
		disableUndead = new DisableUndeadAnimFeatureEvent;
		GetDelaySystem().DelayEvent( owner, disableUndead, 20.0 );
		return true;
	}

	private virtual function DeactiveLookAt( optional repeat : Bool ) : Bool
	{
		var repeatLookat : RepeatLookatEvent;
		var owner : GameObject;
		if( !( m_lookatEvent ) )
		{
			return false;
		}
		owner = GetOwner();
		LookAtRemoveEvent.QueueRemoveLookatEvent( owner, m_lookatEvent );
		m_lookatEvent = NULL;
		if( ( ( repeat && m_playerProximity ) && !( ScriptedPuppet.IsDefeated( owner ) ) ) && !( m_lookatRepeat ) )
		{
			repeatLookat = new RepeatLookatEvent;
			repeatLookat.target = GetPlayerSystem().GetLocalPlayerControlledGameObject();
			GetDelaySystem().DelayEvent( owner, repeatLookat, TweakDBInterface.GetFloat( T"AIGeneralSettings.repeatReactionLookAtDelay", 2.5 ) );
			m_lookatRepeat = true;
		}
		return true;
	}

	private function OnReactionStarted( reactionData : AIReactionData )
	{
		var owner : GameObject;
		var game : GameInstance;
		var ownerPuppet : ScriptedPuppet;
		var player : GameObject;
		var stimTypeName : CName;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		game = owner.GetGame();
		m_activeReaction = reactionData;
		if( m_desiredReaction == reactionData )
		{
			m_desiredReaction = NULL;
		}
		m_workspotReactionPlayed = false;
		if( ownerPuppet && ownerPuppet.GetPuppetStateBlackboard() )
		{
			ownerPuppet.GetPuppetStateBlackboard().SetInt( GetAllBlackboardDefs().PuppetState.ReactionBehavior, ( ( Int32 )( m_activeReaction.reactionBehaviorName ) ) );
			ownerPuppet.GetPuppetStateBlackboard().FireCallbacks();
		}
		if( reactionData.reactionCooldown != 0.0 )
		{
			GameObject.StartCooldown( owner, EnumValueToName( 'gamedataStimType', ( ( Int32 )( reactionData.stimType ) ) ), reactionData.reactionCooldown );
		}
		if( reactionData.reactionBehaviorName == gamedataOutput.DeviceInvestigate )
		{
			m_investigationList.PushBack( reactionData.stimEventData );
		}
		if( reactionData.reactionBehaviorName == gamedataOutput.Panic )
		{
			player = GameInstance.GetPlayerSystem( game ).GetLocalPlayerControlledGameObject();
			if( GameInstance.GetStatsSystem( game ).GetStatValue( player.GetEntityID(), gamedataStatType.CausingPanicReducesUltimateHacksCost ) == 1.0 )
			{
				StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.ReduceUltimateHackCostBy2" );
			}
		}
		stimTypeName = EnumValueToName( 'gamedataStimType', ( ( Int32 )( reactionData.stimType ) ) );
		if( !( GameObject.IsCooldownActive( owner, 'ActiveReactionValueCooldown-' + stimTypeName ) ) )
		{
			AddReactionValueToStatPool( reactionData );
			GameObject.StartCooldown( owner, 'ActiveReactionValueCooldown-' + stimTypeName, 1.0 );
		}
		CacheReaction( reactionData );
	}

	public function GetCurrentStimTimeStamp() : Float
	{
		return m_timeStampThreshold;
	}

	public function GetCurrentStimThresholdValue() : Int32
	{
		return m_currentStimThresholdValue;
	}

	public function GetCurrentStealthStimTimeStamp() : Float
	{
		return m_stealthTimeStampThreshold;
	}

	public function GetCurrentStealthStimThresholdValue() : Int32
	{
		return m_currentStealthStimThresholdValue;
	}

	private function AddInvestigatedBody( bodyID : EntityID )
	{
		var body : Entity;
		var bodyInvestigatedEvent : AddInvestigatorEvent;
		bodyInvestigatedEvent = new AddInvestigatorEvent;
		bodyInvestigatedEvent.investigator = GetOwner().GetEntityID();
		body = GameInstance.FindEntityByID( GetOwner().GetGame(), bodyID );
		body.QueueEvent( bodyInvestigatedEvent );
	}

	protected event OnAddInvestigatedBodyEvent( evt : AddInvestigatorEvent )
	{
		m_deadBodyInvestigators.PushBack( evt.investigator );
	}

	public function InformInvestigators()
	{
		var i : Int32;
		var bodyInvestigator : ScriptedPuppet;
		var removeIgnoreListEvent : IgnoreListEvent;
		removeIgnoreListEvent = new IgnoreListEvent;
		removeIgnoreListEvent.bodyID = GetOwner().GetEntityID();
		removeIgnoreListEvent.removeEvent = true;
		for( i = 0; i < m_deadBodyInvestigators.Size(); i += 1 )
		{
			bodyInvestigator = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( GetOwner().GetGame(), m_deadBodyInvestigators[ i ] ) ) );
			if( ScriptedPuppet.IsAlive( bodyInvestigator ) )
			{
				bodyInvestigator.QueueEvent( removeIgnoreListEvent );
			}
		}
	}

	protected event OnBodyPickedUp( evt : SetBodyPositionEvent )
	{
		var droppedPosition : Vector4;
		var acceptableDistance : Float;
		acceptableDistance = 5.0;
		if( evt.pickedUp )
		{
			m_deadBodyStartingPosition = evt.bodyPosition;
			StatusEffectHelper.ApplyStatusEffect( GetOwner(), T"BaseStatusEffect.BeingCarried" );
		}
		else
		{
			droppedPosition = evt.bodyPosition;
			StatusEffectHelper.RemoveStatusEffect( GetOwner(), T"BaseStatusEffect.BeingCarried" );
			if( Vector4.IsZero( m_deadBodyStartingPosition ) )
			{
				return false;
			}
			if( Vector4.Distance( m_deadBodyStartingPosition, droppedPosition ) > acceptableDistance )
			{
				InformInvestigators();
			}
		}
	}

	private function OnReactionEnded()
	{
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		m_activeReaction = NULL;
		if( ownerPuppet && ownerPuppet.GetPuppetStateBlackboard() )
		{
			ownerPuppet.GetPuppetStateBlackboard().SetInt( GetAllBlackboardDefs().PuppetState.ReactionBehavior, ( ( Int32 )( gamedataOutput.Ignore ) ) );
			GetPuppetReactionBlackboard().SetBool( GetAllBlackboardDefs().PuppetReaction.exitReactionFlag, false );
		}
		m_investigationList.Clear();
	}

	protected event OnResetReactionEvent( evt : ResetReactionEvent )
	{
		if( m_desiredReaction == evt.data )
		{
			m_desiredReaction = NULL;
		}
	}

	public static function BodyInvestigated( owner : weak< ScriptedPuppet > )
	{
		if( !( owner ) )
		{
			return;
		}
		owner.QueueEvent( new BodyInvestigatedEvent );
	}

	protected event OnBodyInvestigated( evt : BodyInvestigatedEvent )
	{
		var ignoreListEvent : IgnoreListEvent;
		if( ShouldAddToIgnoreList( m_activeReaction.stimType ) )
		{
			ignoreListEvent = new IgnoreListEvent;
			ignoreListEvent.bodyID = m_activeReaction.stimEventData.source.GetEntityID();
			m_ignoreList.PushBack( ignoreListEvent.bodyID );
			SendIgnoreEventToSquad( ignoreListEvent );
		}
		if( GetOwnerPuppet().GetHighLevelStateFromBlackboard() != gamedataNPCHighLevelState.Combat )
		{
			SetWarningMessage( "LocKey#53240" );
			if( GetOwnerPuppet().IsConnectedToSecuritySystem() )
			{
				NotifySecuritySystem( m_activeReaction.stimType, m_activeReaction.stimTarget );
			}
		}
	}

	private function InvestigatingAlready( stimEvent : StimuliEvent ) : Bool
	{
		return IsInList( m_investigationList, FillStimData( stimEvent ) );
	}

	private const function GetOwnerPuppet() : ScriptedPuppet
	{
		return ( ( ScriptedPuppet )( GetOwner() ) );
	}

	private function HasCombatTarget() : Bool
	{
		var combatTarget : weak< GameObject >;
		if( !( GetOwnerPuppet() ) )
		{
			return false;
		}
		combatTarget = ( ( weak< weak< GameObject > > )( GetOwnerPuppet().GetAIControllerComponent().GetBehaviorArgument( 'CombatTarget' ) ) );
		if( combatTarget )
		{
			return true;
		}
		return false;
	}

	private function PickCloserTarget( newStimEvent : StimuliEvent, out updateByActive : Bool )
	{
		var activeDistanceSquared : Float;
		var ownerPos : Vector4;
		ownerPos = GetOwner().GetWorldPosition();
		activeDistanceSquared = Vector4.DistanceSquared( m_activeReaction.stimSource, ownerPos );
		if( activeDistanceSquared < Vector4.DistanceSquared( newStimEvent.sourcePosition, ownerPos ) )
		{
			updateByActive = true;
		}
	}

	private const function DidTargetMakeMeAlerted( target : GameObject ) : Bool
	{
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		var game : GameInstance;
		var simTime : Float;
		var recentAlertObject : GameObject;
		var reactionData : AIReactionData;
		var stimData : stimInvestigateData;
		var mountInfo : MountingInfo;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		if( ( m_reactionCache.Size() == 0 ) && !( m_recentAlertObject ) )
		{
			return false;
		}
		if( ownerPuppet.GetHighLevelStateFromBlackboard() != gamedataNPCHighLevelState.Alerted )
		{
			return false;
		}
		if( ownerPuppet.GetSecuritySystem().IsReprimandOngoing() )
		{
			return false;
		}
		game = owner.GetGame();
		simTime = EngineTime.ToFloat( GameInstance.GetSimTime( game ) );
		recentAlertObject = ( ( GameObject )( m_recentAlertObject ) );
		if( ( recentAlertObject && ( recentAlertObject == target ) ) && ( m_recentAlertTimeStamp >= simTime ) )
		{
			return true;
		}
		if( m_reactionCache.Size() == 0 )
		{
			return false;
		}
		reactionData = m_reactionCache[ m_reactionCache.Size() - 1 ];
		stimData = reactionData.stimInvestigateData;
		if( ( ( ( reactionData.stimTarget == target ) || ( stimData.attackInstigator == ( ( Entity )( target ) ) ) ) && ( reactionData.recentReactionTimeStamp >= simTime ) ) && reactionData.reactionBehaviorName != gamedataOutput.Reprimand )
		{
			return true;
		}
		if( ( EngineTime.ToFloat( ( ( NPCPuppet )( owner ) ).GetLastSEAppliedByPlayer().GetLastApplicationSimTimestamp() ) + 10.0 ) >= simTime )
		{
			return true;
		}
		if( IsTargetInterestingForRecentSquadMates( target, reactionData.stimTarget ) )
		{
			return true;
		}
		if( m_stolenVehicle && VehicleComponent.IsMountedToVehicle( game, target ) )
		{
			mountInfo = GameInstance.GetMountingFacility( game ).GetMountingInfoSingleWithObjects( target );
			if( m_stolenVehicle == ( ( VehicleObject )( GameInstance.FindEntityByID( game, mountInfo.parentId ) ) ) )
			{
				return true;
			}
		}
		return false;
	}

	private const function IsTargetInterestingForRecentSquadMates( target : GameObject, ally : GameObject ) : Bool
	{
		var stimData : stimInvestigateData;
		var reactionCache : array< AIReactionData >;
		var reactionData : AIReactionData;
		var simTime : Float;
		if( IsTargetRecentSquadAlly( ally ) )
		{
			reactionCache = ( ( ScriptedPuppet )( ally ) ).GetStimReactionComponent().GetReactionCache();
			reactionData = reactionCache[ reactionCache.Size() - 1 ];
			stimData = reactionData.stimInvestigateData;
			simTime = EngineTime.ToFloat( GameInstance.GetSimTime( GetOwner().GetGame() ) );
			if( ( stimData.attackInstigator == ( ( Entity )( target ) ) ) && ( reactionData.recentReactionTimeStamp >= simTime ) )
			{
				return true;
			}
			if( ( EngineTime.ToFloat( ( ( NPCPuppet )( ally ) ).GetLastSEAppliedByPlayer().GetLastApplicationSimTimestamp() ) + 10.0 ) >= simTime )
			{
				return true;
			}
		}
		return false;
	}

	private const function IsPlayerAiming() : Bool
	{
		var weapon : WeaponObject;
		var player : GameObject;
		var blackboard : IBlackboard;
		player = GameInstance.GetPlayerSystem( GetOwner().GetGame() ).GetLocalPlayerControlledGameObject();
		weapon = GameObject.GetActiveWeapon( player );
		blackboard = ( ( PlayerPuppet )( player ) ).GetPlayerStateMachineBlackboard();
		if( ( ( ( ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody ) == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) ) ) && weapon.IsRanged() ) || ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon ) == ( ( Int32 )( gamePSMMeleeWeapon.ChargedHold ) ) ) ) || ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon ) == ( ( Int32 )( gamePSMMeleeWeapon.Targeting ) ) ) ) || ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware ) == ( ( Int32 )( gamePSMLeftHandCyberware.Charge ) ) ) )
		{
			if( GameInstance.GetBlackboardSystem( GetOwner().GetGame() ).Get( GetAllBlackboardDefs().UI_TargetingInfo ).GetEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget ) == GetOwner().GetEntityID() )
			{
				return true;
			}
		}
		if( ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.CombatGadget ) == ( ( Int32 )( gamePSMCombatGadget.Charging ) ) ) && IsTargetInFront( player, 60.0, true ) )
		{
			return true;
		}
		return false;
	}

	public const function GetDesiredReactionData() : AIReactionData
	{
		return m_desiredReaction;
	}

	public const function GetActiveReactionData() : AIReactionData
	{
		return m_activeReaction;
	}

	public const function GetActiveOrDesiredReactionData() : AIReactionData
	{
		if( m_activeReaction )
		{
			return m_activeReaction;
		}
		return m_desiredReaction;
	}

	public const function GetDesiredReactionName() : gamedataOutput
	{
		if( !( m_desiredReaction ) )
		{
			return gamedataOutput.Ignore;
		}
		return m_desiredReaction.reactionBehaviorName;
	}

	public const function GetPendingReactionName() : gamedataOutput
	{
		if( !( m_pendingReaction ) )
		{
			return gamedataOutput.Ignore;
		}
		return m_pendingReaction.reactionBehaviorName;
	}

	public const function GetReactionBehaviorName() : gamedataOutput
	{
		if( !( m_activeReaction ) )
		{
			return gamedataOutput.Ignore;
		}
		return m_activeReaction.reactionBehaviorName;
	}

	public const function GetReactionCache() : array< AIReactionData >
	{
		return m_reactionCache;
	}

	public const function GetStimuliCache() : array< StimEventTaskData >
	{
		return m_stimuliCache;
	}

	public const function GetReceivedStimType() : gamedataStimType
	{
		return m_receivedStimType;
	}

	public const function GetReceivedStimPropagation() : gamedataStimPropagation
	{
		return m_receivedStimPropagation;
	}

	public const function GetWorkSpotReactionFlag() : Bool
	{
		return m_workspotReactionPlayed;
	}

	public const function IsTargetInterestingForPerception( target : GameObject ) : Bool
	{
		var reactionData : AIReactionData;
		if( !( target ) )
		{
			return false;
		}
		if( DidTargetMakeMeAlerted( target ) )
		{
			return true;
		}
		if( IsPlayerCarryingBody( ( ( PlayerPuppet )( target ) ) ) && IsReactionAvailableInPreset( gamedataStimType.CarryBody ) )
		{
			return true;
		}
		if( ( ( PlayerPuppet )( target ) ).GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.Takedown ) > 0 )
		{
			return true;
		}
		if( IsPlayerAiming() && IsReactionAvailableInPreset( gamedataStimType.AimingAt ) )
		{
			return true;
		}
		if( ( ( StatusEffectSystem.ObjectHasStatusEffect( target, T"PreventionStatusEffect.PerformingIllegalAction" ) && IsReactionAvailableInPreset( gamedataStimType.IllegalAction ) ) && !( SourceAttitude( target, EAIAttitude.AIA_Friendly ) ) ) && m_reactionPreset.IsAggressive() )
		{
			return true;
		}
		if( ( !( WeaponObject.IsFists( GameObject.GetActiveWeapon( target ).GetItemID() ) ) && GameObject.GetActiveWeapon( target ) ) && CanAskToHolsterWeapon() )
		{
			return true;
		}
		reactionData = GetActiveReactionData();
		if( reactionData )
		{
			if( reactionData.reactionBehaviorName == gamedataOutput.BodyInvestigate )
			{
				return true;
			}
			if( reactionData.stimTarget == target )
			{
				if( reactionData.reactionBehaviorName == gamedataOutput.AskToFollowOrder && reactionData.reactionBehaviorName != gamedataOutput.TurnAt )
				{
					return false;
				}
				if( reactionData.reactionBehaviorName == gamedataOutput.PlayerCall )
				{
					return false;
				}
				return true;
			}
		}
		return false;
	}

	public const function GetPuppetReactionBlackboard() : IBlackboard
	{
		return m_puppetReactionBlackboard;
	}

	private function IsInitAnimShock( behavior : gamedataOutput ) : Bool
	{
		if( GetOwnerPuppet().GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Combat )
		{
			return false;
		}
		return ( behavior == gamedataOutput.Investigate || behavior == gamedataOutput.Intruder ) || behavior == gamedataOutput.Panic;
	}

	public const function IsInitAnimCall( stim : gamedataStimType ) : Bool
	{
		if( stim == gamedataStimType.Call )
		{
			return true;
		}
		return false;
	}

	public const function GetInPendingBehavior() : Bool
	{
		return m_inPendingBehavior;
	}

	public const function GetReactionPreset() : weak< ReactionPreset_Record >
	{
		return m_reactionPreset;
	}

	private function IsInPendingBehavior() : Bool
	{
		if( m_inPendingBehavior )
		{
			return true;
		}
		if( m_stanceState == gamedataNPCStanceState.Vehicle || m_stanceState == gamedataNPCStanceState.VehicleWindow )
		{
			return true;
		}
		if( ( ( NPCPuppet )( GetOwnerPuppet() ) ).IsRagdolling() )
		{
			return true;
		}
		return false;
	}

	public const function IsAlertedByDeadBody() : Bool
	{
		return m_isAlertedByDeadBody;
	}

	public function GetPreviousFearPhase() : Int32
	{
		return m_previousFearPhase;
	}

	public function GetEnvironmentalHazards() : array< StimuliEvent >
	{
		return m_environmentalHazards;
	}

	private const function GetActiveStimPriority() : gamedataStimPriority
	{
		if( !( m_activeReaction ) )
		{
			return gamedataStimPriority.Invalid;
		}
		return m_activeReaction.stimPriority;
	}

	private const function GetActiveStimTarget() : GameObject
	{
		if( !( m_activeReaction ) )
		{
			return NULL;
		}
		return m_activeReaction.stimTarget;
	}

	private const function GetActiveStimSource() : Vector4
	{
		if( !( m_activeReaction ) )
		{
			return Vector4.EmptyVector();
		}
		return m_activeReaction.stimSource;
	}

	private function ShouldUpdateThreatPosition( stimEvent : StimuliEvent ) : Bool
	{
		if( stimEvent.IsTagInStimuli( 'Direct' ) )
		{
			return false;
		}
		if( stimEvent.sourceObject != ( ( weak< weak< GameObject > > )( GetOwnerPuppet().GetAIControllerComponent().GetBehaviorArgument( 'CombatTarget' ) ) ) )
		{
			return false;
		}
		if( IsTargetVisible( stimEvent.sourceObject ) )
		{
			return false;
		}
		if( stimEvent.IsVisual() && !( StimFilters.IsGunshot( stimEvent.GetStimType() ) ) )
		{
			return false;
		}
		if( SenseComponent.ShouldIgnoreIfPlayerCompanion( GetOwnerPuppet(), stimEvent.sourceObject ) )
		{
			return false;
		}
		return true;
	}

	private function ShouldTriggerGrenadeDodgeBehavior( stimEvent : StimuliEvent ) : Bool
	{
		var stimDistanceSquared : Float;
		var innerRadius : Float;
		var ownerPuppet : ScriptedPuppet;
		var grenade : BaseGrenade;
		ownerPuppet = GetOwnerPuppet();
		if( ownerPuppet.IsCharacterCivilian() || ownerPuppet.GetNPCType() == gamedataNPCType.Drone )
		{
			return true;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( ownerPuppet, gamedataStatusEffectType.Wounded ) )
		{
			return false;
		}
		innerRadius = TweakDBInterface.GetFloat( T"AIGeneralSettings.grenadeDodgeInnerRadius", 0.0 );
		stimDistanceSquared = Vector4.DistanceSquared( GetOwner().GetWorldPosition(), stimEvent.sourcePosition );
		grenade = ( ( BaseGrenade )( stimEvent.sourceObject ) );
		if( ( ( stimDistanceSquared <= ( innerRadius * innerRadius ) ) || IsTargetInFront( grenade ) ) || IsTargetInFront( grenade.GetUser() ) )
		{
			return true;
		}
		return false;
	}

	private function CanTriggerPanicInCombat( stimEvent : StimuliEvent ) : Bool
	{
		var stimDistanceSquared : Float;
		var distance : Float;
		if( !( HasCombatTarget() ) )
		{
			return true;
		}
		stimDistanceSquared = Vector4.DistanceSquared( GetOwner().GetWorldPosition(), stimEvent.sourcePosition );
		distance = TweakDBInterface.GetFloat( T"AIGeneralSettings.panicInCombatReactionDistance", 10.0 );
		if( stimDistanceSquared <= ( distance * distance ) )
		{
			return true;
		}
		return false;
	}

	private function CanStimInterruptCombat( stimEvent : StimuliEvent, canIgnorePlayerCombatStim : Bool ) : Bool
	{
		var grenade : BaseGrenade;
		grenade = ( ( BaseGrenade )( stimEvent.sourceObject ) );
		if( ( !( canIgnorePlayerCombatStim ) && !( stimEvent.IsTagInStimuli( 'Combat' ) ) ) && !( ( stimEvent.GetStimType() == gamedataStimType.ProjectileDistraction && grenade ) ) )
		{
			return false;
		}
		if( ScriptedPuppet.IsOnOffMeshLink( GetOwner() ) )
		{
			return false;
		}
		if( grenade && ( grenade.GetUser() == GetOwner() ) )
		{
			return false;
		}
		return true;
	}

	private function ShouldAddToIgnoreList( stimType : gamedataStimType ) : Bool
	{
		if( stimType == gamedataStimType.DeadBody )
		{
			return true;
		}
		return false;
	}

	public function IsTargetVisible( target : GameObject ) : Bool
	{
		var senseComponent : SenseComponent;
		senseComponent = GetOwnerPuppet().GetSensesComponent();
		if( !( senseComponent ) && !( target ) )
		{
			return false;
		}
		if( !( GetOwnerPuppet().IsActive() ) )
		{
			return false;
		}
		return senseComponent.IsAgentVisible( target );
	}

	private function IsTargetDetected( target : GameObject ) : Bool
	{
		var senseComponent : SenseComponent;
		senseComponent = GetOwnerPuppet().GetSensesComponent();
		if( !( senseComponent ) && !( target ) )
		{
			return false;
		}
		if( !( GetOwnerPuppet().IsActive() ) )
		{
			return false;
		}
		return senseComponent.GetDetection( target.GetEntityID() ) >= 100.0;
	}

	private const function SourceAttitude( source : weak< GameObject >, attitude : EAIAttitude ) : Bool
	{
		var attitudeOwner : AttitudeAgent;
		var attitudeTarget : AttitudeAgent;
		attitudeOwner = GetOwner().GetAttitudeAgent();
		attitudeTarget = source.GetAttitudeAgent();
		if( ( attitudeOwner && attitudeTarget ) && source )
		{
			if( attitudeOwner.GetAttitudeTowards( attitudeTarget ) == attitude )
			{
				return true;
			}
		}
		return false;
	}

	private static function IsTargetInFrontOfSource( source : weak< GameObject >, target : weak< GameObject >, optional frontAngle : Float, optional checkFullAngle : Bool ) : Bool
	{
		var cameraTransform : Transform;
		var sourcePos : Vector4;
		var sourceFwd : Vector4;
		var sourceUp : Vector4;
		var targetPos : Vector4;
		var direction : Vector4;
		var angleToTarget : Float;
		if( !( source ) || !( target ) )
		{
			return false;
		}
		if( ( checkFullAngle && source.IsPlayer() ) && GameInstance.GetCameraSystem( source.GetGame() ).GetActiveCameraWorldTransform( cameraTransform ) )
		{
			sourcePos = Transform.GetPosition( cameraTransform );
			sourceFwd = Transform.GetForward( cameraTransform );
		}
		else
		{
			sourcePos = source.GetWorldPosition();
			sourceFwd = source.GetWorldForward();
			sourceUp = source.GetWorldUp();
		}
		targetPos = target.GetWorldPosition();
		direction = targetPos - sourcePos;
		angleToTarget = ( ( checkFullAngle ) ? ( Vector4.GetAngleBetween( direction, sourceFwd ) ) : ( Vector4.GetAngleDegAroundAxis( direction, sourceFwd, sourceUp ) ) );
		if( frontAngle == 0.0 )
		{
			frontAngle = 90.0;
		}
		return AbsF( angleToTarget ) < frontAngle;
	}

	private const function IsTargetInFront( target : weak< GameObject >, optional frontAngle : Float, optional meInFrontOfTarget : Bool, optional checkFullAngle : Bool ) : Bool
	{
		if( meInFrontOfTarget )
		{
			return IsTargetInFrontOfSource( target, GetOwner(), frontAngle, checkFullAngle );
		}
		return IsTargetInFrontOfSource( GetOwner(), target, frontAngle, checkFullAngle );
	}

	private const function IsTargetBehind( target : weak< GameObject >, optional angle : Float, optional meBehindOfTarget : Bool ) : Bool
	{
		var direction : Vector4;
		var angleToTarget : Float;
		var owner : GameObject;
		var ownerPos : Vector4;
		var ownerFwd : Vector4;
		var ownerUp : Vector4;
		var targetPos : Vector4;
		owner = GetOwner();
		ownerPos = owner.GetWorldPosition();
		ownerFwd = owner.GetWorldForward();
		ownerUp = owner.GetWorldUp();
		targetPos = target.GetWorldPosition();
		direction = targetPos - ownerPos;
		angleToTarget = Vector4.GetAngleDegAroundAxis( direction, ownerFwd, ownerUp );
		if( angle == 0.0 )
		{
			angle = 120.0;
		}
		if( meBehindOfTarget )
		{
			direction = ownerPos - targetPos;
			angleToTarget = Vector4.GetAngleDegAroundAxis( direction, target.GetWorldForward(), target.GetWorldUp() );
			if( AbsF( angleToTarget ) > angle )
			{
				return true;
			}
		}
		else
		{
			direction = targetPos - ownerPos;
			angleToTarget = Vector4.GetAngleDegAroundAxis( direction, ownerFwd, ownerUp );
			if( AbsF( angleToTarget ) > angle )
			{
				return true;
			}
		}
		return false;
	}

	public const function IsTargetInMovementDirection( target : weak< GameObject > ) : Bool
	{
		var vecToTarget : Vector4;
		var angleToTarget : Float;
		var movementDirection : Vector4;
		vecToTarget = target.GetWorldPosition() - GetOwner().GetWorldPosition();
		movementDirection = GetOwnerPuppet().GetCrowdMemberComponent().GetMovementDirection();
		if( !( Vector4.IsZero( movementDirection ) ) )
		{
			angleToTarget = Vector4.GetAngleDegAroundAxis( vecToTarget, movementDirection, GetOwner().GetWorldUp() );
			if( AbsF( angleToTarget ) < 90.0 )
			{
				return true;
			}
		}
		return false;
	}

	private const function IsTargetPositionClose( targetPos : Vector4, distance : Float ) : Bool
	{
		var distanceSquared : Float;
		distanceSquared = Vector4.DistanceSquared( targetPos, GetOwner().GetWorldPosition() );
		return distanceSquared < ( distance * distance );
	}

	private const function IsTargetClose( target : weak< GameObject >, distance : Float ) : Bool
	{
		return IsTargetPositionClose( target.GetWorldPosition(), distance );
	}

	private const function IsTargetVeryClose( target : weak< GameObject > ) : Bool
	{
		return IsTargetClose( target, 3.0 ) || ( IsTargetClose( target, 6.0 ) && IsTargetInFront( target, 60.0 ) );
	}

	private const function TargetVerticalCheck( target : weak< GameObject >, optional distance : Float ) : Bool
	{
		var vecToTarget : Vector4;
		vecToTarget = GetOwner().GetWorldPosition() - target.GetWorldPosition();
		if( AbsF( vecToTarget.Z ) > 2.0 )
		{
			return false;
		}
		return true;
	}

	public static function ReactOnPlayerStealthStim( owner : weak< GameObject >, target : weak< GameObject > ) : Bool
	{
		var attitudeOwner : AttitudeAgent;
		var attitudeTarget : AttitudeAgent;
		var attitudeTowardsTarget : EAIAttitude;
		attitudeOwner = owner.GetAttitudeAgent();
		if( attitudeOwner )
		{
			attitudeTarget = target.GetAttitudeAgent();
			if( attitudeTarget && target.IsPlayer() )
			{
				attitudeTowardsTarget = attitudeOwner.GetAttitudeTowards( attitudeTarget );
			}
			else if( GameObject.IsVehicle( target ) )
			{
				target = GameInstance.GetPlayerSystem( owner.GetGame() ).GetLocalPlayerMainGameObject();
				attitudeTarget = target.GetAttitudeAgent();
				attitudeTowardsTarget = attitudeOwner.GetAttitudeTowards( attitudeTarget );
			}
			if( attitudeTarget )
			{
				if( attitudeTowardsTarget == EAIAttitude.AIA_Hostile )
				{
					return true;
				}
				else if( ( attitudeTowardsTarget == EAIAttitude.AIA_Neutral && owner.IsConnectedToSecuritySystem() ) && owner.IsTargetTresspassingMyZone( target ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	private function CheckHearingDistance( stimEvent : StimuliEvent ) : Bool
	{
		var distanceSquared : Float;
		var radius : Float;
		distanceSquared = Vector4.DistanceSquared( GetOwner().GetWorldPosition(), stimEvent.sourcePosition );
		radius = stimEvent.radius * GameInstance.GetStatsSystem( GetOwner().GetGame() ).GetStatValue( GetOwner().GetEntityID(), gamedataStatType.Hearing );
		if( distanceSquared <= ( radius * radius ) )
		{
			return true;
		}
		return false;
	}

	private function IsVisibleRaycast( stimEvent : StimuliEvent, stimOffset : Vector4 ) : Bool
	{
		var raycastTrace : TraceResult;
		var hit : Bool;
		hit = GameInstance.GetSpatialQueriesSystem( GetOwner().GetGame() ).SyncRaycastByQueryPreset( GetOwner().GetWorldPosition() + stimOffset, stimEvent.sourcePosition + stimOffset, 'Sight Blocker', raycastTrace );
		return !( hit );
	}

	private function IsTargetVisibleBeyondSenses( stimEvent : StimuliEvent, reactionData : AIReactionData ) : Bool
	{
		if( !( GetOwnerPuppet().IsActive() ) )
		{
			return false;
		}
		if( !( StimFilters.IsGunshot( stimEvent.GetStimType() ) ) )
		{
			return false;
		}
		if( StimFilters.IsGunshot( reactionData.stimType ) )
		{
			return false;
		}
		if( IsTargetClose( stimEvent.sourceObject, 25.0 ) )
		{
			return false;
		}
		if( !( IsTargetInFront( stimEvent.sourceObject, 60.0 ) ) || !( IsTargetInFront( stimEvent.sourceObject, 50.0, true ) ) )
		{
			return false;
		}
		return IsVisibleRaycast( stimEvent, Vector4( 0.0, 0.0, 0.80000001, 0.0 ) );
	}

	private function IsPlayerInZone( zone : gamePSMZones ) : Bool
	{
		var psmBlackboard : IBlackboard;
		var blackboardSystem : BlackboardSystem;
		blackboardSystem = GameInstance.GetBlackboardSystem( GetOwner().GetGame() );
		psmBlackboard = blackboardSystem.GetLocalInstanced( GetPlayerSystem().GetLocalPlayerMainGameObject().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		if( ( ( gamePSMZones )( psmBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Zones ) ) ) == zone )
		{
			return true;
		}
		return false;
	}

	private function IsSameStimulus( stimEvent : StimuliEvent ) : Bool
	{
		if( m_activeReaction && m_activeReaction.stimType == stimEvent.GetStimType() )
		{
			return true;
		}
		return false;
	}

	private const function IsSameSourceObject( stimEvent : StimuliEvent ) : Bool
	{
		if( m_activeReaction && ( m_activeReaction.stimTarget == stimEvent.sourceObject ) )
		{
			return true;
		}
		return false;
	}

	private function IsInList( const list : ref< array< StimEventData > >, stimData : StimEventData ) : Bool
	{
		var i : Int32;
		for( i = 0; i < list.Size(); i += 1 )
		{
			if( ( list[ i ].source == stimData.source ) && list[ i ].stimType == stimData.stimType )
			{
				return true;
			}
		}
		return false;
	}

	private function IsLowerPriority( stimEvent : StimuliEvent, activePriority : gamedataStimPriority ) : Bool
	{
		return stimEvent.stimRecord.Priority().Type() == gamedataStimPriority.Low && activePriority == gamedataStimPriority.High;
	}

	private function IsTargetSquadAlly( target : weak< GameObject > ) : Bool
	{
		var ownerSquadInterface : SquadScriptInterface;
		var targetSquadInterface : SquadScriptInterface;
		var ownerSquadName : CName;
		var targetSquadName : CName;
		if( !( AISquadHelper.GetSquadMemberInterface( GetOwnerPuppet(), ownerSquadInterface ) ) )
		{
			return false;
		}
		ownerSquadName = ownerSquadInterface.GetName();
		if( !( AISquadHelper.GetSquadMemberInterface( target, targetSquadInterface ) ) )
		{
			return false;
		}
		targetSquadName = targetSquadInterface.GetName();
		return ownerSquadName == targetSquadName;
	}

	private function IsTargetInSameSecuritySystem( target : weak< GameObject > ) : Bool
	{
		var securitySystem : SecuritySystemControllerPS;
		securitySystem = GetOwnerPuppet().GetSecuritySystem();
		if( !( securitySystem ) )
		{
			return false;
		}
		return securitySystem.GetAgentRegistry().IsAgent( target.GetEntityID() );
	}

	private function IsTargetMelee( target : GameObject ) : Bool
	{
		if( GameObject.GetActiveWeapon( target ) && !( GameObject.GetActiveWeapon( target ).IsRanged() ) )
		{
			return true;
		}
		return false;
	}

	private function IsTargetArmed( target : GameObject ) : Bool
	{
		if( GameObject.GetActiveWeapon( target ) && !( WeaponObject.IsFists( GameObject.GetActiveWeapon( target ).GetItemID() ) ) )
		{
			return true;
		}
		return false;
	}

	private const function IsTargetRecentSquadAlly( target : weak< GameObject > ) : Bool
	{
		var smi : SquadScriptInterface;
		var squadCmp : SquadMemberComponent;
		var squadName : CName;
		squadCmp = target.GetSquadMemberComponent();
		if( !( squadCmp ) )
		{
			return false;
		}
		if( !( AISquadHelper.GetSquadMemberInterface( GetOwner(), smi ) ) )
		{
			return false;
		}
		squadName = smi.GetName();
		if( squadName != squadCmp.MySquadNameCurrentOrRecent( AISquadType.Combat ) )
		{
			return false;
		}
		return true;
	}

	private function ShouldHelpTargetFromSameAttitudeGroup( target : weak< GameObject >, targetOfTarget : weak< GameObject > ) : Bool
	{
		var ownerPuppet : ScriptedPuppet;
		var preventionSys : PreventionSystem;
		ownerPuppet = GetOwnerPuppet();
		if( ownerPuppet.GetAttitudeAgent().GetAttitudeGroup() != target.GetAttitudeAgent().GetAttitudeGroup() )
		{
			return false;
		}
		if( targetOfTarget && !( targetOfTarget.IsPlayer() ) )
		{
			return true;
		}
		preventionSys = ownerPuppet.GetPreventionSystem();
		if( ( ( preventionSys.IsChasingPlayer() && target.IsPrevention() ) && ownerPuppet.IsPrevention() ) && preventionSys.ShouldWorkSpotPoliceJoinChase( ownerPuppet ) )
		{
			return true;
		}
		return false;
	}

	private function ShouldHelpCausePlayerGotTooClose( targetOfTarget : weak< GameObject >, onlyAlertNoThreat : ref< Bool > ) : Bool
	{
		if( ( targetOfTarget && targetOfTarget.IsPlayer() ) && IsTargetVeryClose( targetOfTarget ) )
		{
			onlyAlertNoThreat = true;
			return true;
		}
		return false;
	}

	private function ShouldReactToNPCGrenade( grenade : weak< BaseGrenade > ) : Bool
	{
		return !( grenade.GetUser().IsPlayer() ) && IsTargetClose( grenade, 12.0 );
	}

	private const function ShouldPreventionReact( stimEvent : StimuliEvent ) : Bool
	{
		var stimTargetPuppet : ScriptedPuppet;
		stimTargetPuppet = ( ( ScriptedPuppet )( stimEvent.sourceObject ) );
		if( ( stimEvent.GetStimType() == gamedataStimType.DeadBody && stimTargetPuppet ) && stimTargetPuppet.IsCharacterGanger() )
		{
			LogInfo( "Ignoring ganger body" );
			return false;
		}
		if( IsIllegalActionAgainstGanger( stimEvent ) )
		{
			return false;
		}
		if( stimEvent.GetStimType() == gamedataStimType.SpreadFear )
		{
			LogInfo( "Ignoring SpreadFear" );
			return false;
		}
		if( GetOwner().IsTargetTresspassingMyZone( stimEvent.sourceObject ) )
		{
			return true;
		}
		if( PreventionSystem.ShouldReactionBeAgressive( GetOwner().GetGame() ) )
		{
			return true;
		}
		if( stimEvent.GetStimType() == gamedataStimType.CrimeWitness )
		{
			return true;
		}
		return false;
	}

	private const function IsIllegalActionAgainstGanger( stimEvent : StimuliEvent ) : Bool
	{
		var stimTargetPuppet : ScriptedPuppet;
		var attackData : stimInvestigateData;
		attackData = stimEvent.stimInvestigateData;
		if( !( StimFilters.IsIllegalAction( stimEvent.GetStimType() ) ) || !( attackData.victimEntity ) )
		{
			return false;
		}
		stimTargetPuppet = ( ( ScriptedPuppet )( attackData.victimEntity ) );
		if( !( stimTargetPuppet ) || !( stimTargetPuppet.IsCharacterGanger() ) )
		{
			return false;
		}
		LogInfo( "Ignoring IllegalAction against ganger" );
		return true;
	}

	private function SetBaseReactionPreset( optional ignoreSavedPreset : Bool )
	{
		var savedPresetID : TweakDBID;
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		savedPresetID = ownerPuppet.GetReactionPresetID();
		if( TDBID.IsValid( savedPresetID ) && !( ignoreSavedPreset ) )
		{
			m_reactionPreset = TweakDBInterface.GetReactionPresetRecord( savedPresetID );
		}
		else
		{
			m_reactionPreset = TweakDBInterface.GetCharacterRecord( GetOwnerPuppet().GetRecordID() ).ReactionPreset();
		}
		if( m_reactionPreset != NULL )
		{
			m_presetName = m_reactionPreset.EnumName();
			ownerPuppet.SetReactionPresetID( m_reactionPreset.GetID() );
		}
		ownerPuppet.RefreshCachedReactionPresetData();
		ownerPuppet.TryRegisterToPrevention();
	}

	private function SetReactionPreset( reactionPreset : ReactionPreset_Record )
	{
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		if( reactionPreset == TweakDBInterface.GetCharacterRecord( ownerPuppet.GetRecordID() ).ReactionPreset() )
		{
			ReevaluateReactionPreset( true );
		}
		else
		{
			m_reactionPreset = reactionPreset;
			m_presetName = m_reactionPreset.EnumName();
			ownerPuppet.SetReactionPresetID( reactionPreset.GetID() );
		}
		ownerPuppet.RefreshCachedReactionPresetData();
		ownerPuppet.TryRegisterToPrevention();
	}

	private function MapReactionPreset( const mappingName : ref< String > )
	{
		var ownerPuppet : ScriptedPuppet;
		var basePreset : ReactionPreset_Record;
		var newPreset : ReactionPreset_Record;
		var presets : array< weak< PresetMapper_Record > >;
		var i : Int32;
		ownerPuppet = GetOwnerPuppet();
		if( mappingName == "NoReaction" )
		{
			newPreset = TweakDBInterface.GetReactionPresetRecord( T"ReactionPresets.NoReaction" );
		}
		else if( mappingName == "Follower" )
		{
			newPreset = TweakDBInterface.GetReactionPresetRecord( T"ReactionPresets.Follower" );
		}
		else
		{
			basePreset = TweakDBInterface.GetCharacterRecord( ownerPuppet.GetRecordID() ).ReactionPreset();
			basePreset.PresetMapper( presets );
			for( i = 0; i < presets.Size(); i += 1 )
			{
				if( presets[ i ].MappingName() == mappingName )
				{
					newPreset = presets[ i ].Preset();
				}
			}
		}
		if( newPreset == NULL )
		{
			newPreset = basePreset;
		}
		m_reactionPreset = newPreset;
		m_presetName = m_reactionPreset.EnumName();
		ownerPuppet.RefreshCachedReactionPresetData();
		ownerPuppet.TryRegisterToPrevention();
	}

	private function ReevaluateReactionPreset( optional ignoreSavedPreset : Bool )
	{
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'Braindance' ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'Drunk' ) )
		{
			if( ( ( ScriptedPuppet )( GetOwner() ) ).IsCivilian() )
			{
				MapReactionPreset( "Civilian_NoReaction" );
			}
			else
			{
				MapReactionPreset( "NoReaction" );
			}
		}
		else if( m_aiRole == EAIRole.Follower )
		{
			MapReactionPreset( "Follower" );
		}
		else if( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'Sleep' ) )
		{
			MapReactionPreset( "Sleep" );
		}
		else if( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'LoreAnim' ) )
		{
			MapReactionPreset( "Lore" );
		}
		else if( m_stanceState == gamedataNPCStanceState.Vehicle || m_stanceState == gamedataNPCStanceState.VehicleWindow )
		{
			MapReactionPreset( "Vehicle" );
		}
		else
		{
			SetBaseReactionPreset( ignoreSavedPreset );
		}
	}

	private function CheckCrowd()
	{
		m_inCrowd = GetOwnerPuppet().IsCrowd();
	}

	public function SetDownedBodyVisibleComponent( visible : Bool, description : CName )
	{
		var owner : GameObject;
		var puppet : ScriptedPuppet;
		var visibleObject : VisibleObjectComponent;
		var visibleObjectPosition : Vector4;
		var detectMultEvent : VisibleObjectDetectionMultEvent;
		owner = GetOwner();
		puppet = ( ( ScriptedPuppet )( owner ) );
		visibleObject = puppet.GetVisibleObjectComponent();
		visibleObject.Toggle( visible );
		if( !( visible ) )
		{
			return;
		}
		if( !( puppet.m_visibleObjectPositionUpdated ) )
		{
			visibleObjectPosition = visibleObject.GetLocalPosition();
			visibleObjectPosition.Z = visibleObjectPosition.Z + 1.0;
			visibleObject.SetLocalPosition( visibleObjectPosition );
			puppet.m_visibleObjectPositionUpdated = true;
		}
		visibleObject.visibleObject.description = description;
		visibleObject.visibleObject.visibilityDistance = TweakDBInterface.GetFloat( T"stims.DeadBodyStimuli.radius", 10.0 );
		detectMultEvent = new VisibleObjectDetectionMultEvent;
		detectMultEvent.multiplier = 0.89999998;
		owner.QueueEvent( detectMultEvent );
	}

	private function OnIncapacitated( instigator : GameObject )
	{
		var owner : GameObject;
		var attackData : stimInvestigateData;
		var broadcaster : StimBroadcasterComponent;
		var game : GameInstance;
		var mountInfo : MountingInfo;
		owner = GetOwner();
		game = owner.GetGame();
		if( instigator && instigator.IsPlayer() )
		{
			SetDownedBodyVisibleComponent( true, 'Dead_Body' );
			broadcaster = instigator.GetStimBroadcasterComponent();
			if( broadcaster )
			{
				attackData.victimEntity = EntityGameInterface.GetEntity( owner.GetEntity() );
				broadcaster.SetSingleActiveStimuli( owner, gamedataStimType.IllegalAction, 2.5, , attackData );
			}
			broadcaster = owner.GetStimBroadcasterComponent();
			if( broadcaster )
			{
				attackData.attackInstigator = instigator;
				attackData.attackInstigatorPosition = instigator.GetWorldPosition();
				broadcaster.TriggerSingleBroadcast( owner, gamedataStimType.DeadBody, , attackData );
				broadcaster.AddActiveStimuli( owner, gamedataStimType.Dying, 2.0, , attackData );
				broadcaster.TriggerSingleBroadcast( owner, gamedataStimType.Dying, 4.0, attackData, true );
				broadcaster.AddActiveStimuli( owner, gamedataStimType.CrowdIllegalAction, -1.0 );
			}
		}
		if( VehicleComponent.IsMountedToVehicle( game, owner ) && m_inCrowd )
		{
			mountInfo = GameInstance.GetMountingFacility( game ).GetMountingInfoSingleWithObjects( owner );
			VehicleComponent.QueueExitEventToAllNonFriendlyActivePassengers( game, mountInfo.parentId, owner );
		}
	}

	public static function SendVOEventToSquad( owner : weak< GameObject >, voEvent : CName, optional setOwnerAsAnsweringEntity : Bool, optional onlyForMembersInCombat : Bool )
	{
		var smi : SquadScriptInterface;
		var squadMembers : array< weak< Entity > >;
		var member : ScriptedPuppet;
		var ownerPuppet : ScriptedPuppet;
		var i : Int32;
		var answeringEntityId : EntityID;
		var ownerPosition : Vector4;
		var maxDistBattleCry : Float;
		var voiceOverName : CName;
		ownerPuppet = ( ( ScriptedPuppet )( owner ) );
		if( setOwnerAsAnsweringEntity )
		{
			answeringEntityId = ownerPuppet.GetEntityID();
		}
		if( !( AISquadHelper.GetSquadMemberInterface( ownerPuppet, smi ) ) )
		{
			return;
		}
		squadMembers = smi.ListMembersWeak();
		if( squadMembers.Size() <= 1 )
		{
			return;
		}
		ownerPosition = ownerPuppet.GetWorldPosition();
		maxDistBattleCry = TweakDBInterface.GetFloat( T"AIGeneralSettings.maxDistanceBattleCry", 0.0 );
		voiceOverName = 'Scripts:SendVOEventToSquad: ' + voEvent;
		for( i = 0; i < squadMembers.Size(); i += 1 )
		{
			member = ( ( ScriptedPuppet )( squadMembers[ i ] ) );
			if( member == ownerPuppet )
			{
				continue;
			}
			if( !( ScriptedPuppet.IsActive( member ) ) )
			{
				continue;
			}
			if( onlyForMembersInCombat && member.GetHighLevelStateFromBlackboard() != gamedataNPCHighLevelState.Combat )
			{
				continue;
			}
			if( Vector4.Distance( member.GetWorldPosition(), ownerPosition ) < maxDistBattleCry )
			{
				GameObject.PlayVoiceOver( member, voEvent, voiceOverName, 0.0, answeringEntityId );
			}
		}
	}

	private function SendIgnoreEventToSquad( optional ignoreListEvent : IgnoreListEvent )
	{
		var smi : SquadScriptInterface;
		var squadMembers : array< weak< Entity > >;
		var member : ScriptedPuppet;
		var i : Int32;
		var ownerPuppet : ScriptedPuppet;
		ownerPuppet = GetOwnerPuppet();
		if( !( AISquadHelper.GetSquadMemberInterface( ownerPuppet, smi ) ) )
		{
			return;
		}
		squadMembers = smi.ListMembersWeak();
		if( squadMembers.Size() <= 1 )
		{
			return;
		}
		for( i = 0; i < squadMembers.Size(); i += 1 )
		{
			member = ( ( ScriptedPuppet )( squadMembers[ i ] ) );
			if( member == ownerPuppet )
			{
				continue;
			}
			if( !( ScriptedPuppet.IsActive( member ) ) )
			{
				continue;
			}
			if( ignoreListEvent )
			{
				member.QueueEvent( ignoreListEvent );
			}
		}
	}

	private function GetThreatDistanceSquared( threat : GameObject ) : Float
	{
		var distanceSquared : Float;
		distanceSquared = Vector4.DistanceSquared( GetOwner().GetWorldPosition(), threat.GetWorldPosition() );
		return distanceSquared;
	}

	private function GetFearAnimWrapper( fearPhase : Int32 ) : CName
	{
		switch( fearPhase )
		{
			case 1:
				if( m_fastWalk )
				{
					return 'disturbed';
				}
				else
				{
					return 'default';
				}
			case 2:
				return 'fear';
			case 3:
				return 'panic';
			default:
				return 'default';
		}
	}

	public const function GetRandomFearLocomotionAnimWrapper( fearPhase : Int32, optional stimType : gamedataStimType ) : CName
	{
		var rand : Float;
		if( stimType == gamedataStimType.Driving )
		{
			rand = RandF();
			if( rand <= 0.33000001 )
			{
				return 'FearLocomotion1';
			}
			else if( ( rand > 0.33000001 ) && ( rand <= 0.66000003 ) )
			{
				return 'FearLocomotion3';
			}
			else
			{
				return 'FearLocomotion4';
			}
		}
		rand = RandF();
		if( ( rand > 0.25 ) && ( rand <= 0.5 ) )
		{
			return 'FearLocomotion1';
		}
		else if( ( rand > 0.5 ) && ( rand <= 0.75 ) )
		{
			return 'FearLocomotion2';
		}
		else if( rand <= 0.25 )
		{
			return 'FearLocomotion3';
		}
		else
		{
			return 'FearLocomotion4';
		}
	}

	private function ResetAllFearAnimWrappers()
	{
		var owner : GameObject;
		owner = GetOwner();
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'disturbed', 0.0 );
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'fear', 0.0 );
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'panic', 0.0 );
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'FearLocomotion1', 0.0 );
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'FearLocomotion2', 0.0 );
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'FearLocomotion3', 0.0 );
		AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( owner, 'FearLocomotion4', 0.0 );
		m_fearLocomotionWrapper = false;
	}

	public const function IsFearLocomotionWrapperSet() : Bool
	{
		return m_fearLocomotionWrapper;
	}

	private function ReevaluateReaction()
	{
		if( SourceAttitude( m_activeReaction.stimTarget, EAIAttitude.AIA_Friendly ) )
		{
			GetPuppetReactionBlackboard().SetBool( GetAllBlackboardDefs().PuppetReaction.exitReactionFlag, true );
			NPCPuppet.ChangeHighLevelState( GetOwner(), gamedataNPCHighLevelState.Relaxed );
		}
	}

	private function CombatGracePeriodPassed( player : PlayerPuppet ) : Bool
	{
		var currentSimTime : Float;
		var exitCombatSimTime : Float;
		currentSimTime = EngineTime.ToFloat( GameInstance.GetTimeSystem( player.GetGame() ).GetSimTime() );
		exitCombatSimTime = player.GetCombatExitTimestamp();
		return ( currentSimTime - exitCombatSimTime ) > m_gracePeriodDuration;
	}

	protected event OnStanceLevelChanged( evt : StanceStateChangeEvent )
	{
		m_stanceState = evt.state;
		ReevaluateReactionPreset();
		if( m_stanceState == gamedataNPCStanceState.Stand )
		{
			TriggerPendingReaction();
			if( m_driverIsAggressive )
			{
				TriggerAggressiveCrowdBehavior( GetOwner(), GetPlayerSystem().GetLocalPlayerMainGameObject() );
				m_driverAllowedToGetAggressive = false;
			}
		}
		if( ( m_stanceState == gamedataNPCStanceState.Vehicle || m_stanceState == gamedataNPCStanceState.VehicleWindow ) || m_stanceState == gamedataNPCStanceState.Swim )
		{
			GetOwnerPuppet().GetBumpComponent().Toggle( false );
		}
		else
		{
			GetOwnerPuppet().GetBumpComponent().ToggleComponentOn();
		}
	}

	protected event OnHighLevelStateDataEvent( evt : gameHighLevelStateDataEvent )
	{
		m_highLevelState = evt.currentHighLevelState;
		if( m_highLevelState == gamedataNPCHighLevelState.Dead )
		{
			DeactiveLookAt();
			Toggle( false );
		}
		else if( m_highLevelState == gamedataNPCHighLevelState.Relaxed )
		{
			TriggerPendingReaction();
		}
		if( m_highLevelState == gamedataNPCHighLevelState.Combat )
		{
			( ( NPCPuppet )( GetOwner() ) ).GetComfortZoneComponent().Toggle( false );
		}
		if( ( GetOwnerPuppet().IsConnectedToSecuritySystem() && m_highLevelState != gamedataNPCHighLevelState.Alerted ) && m_isAlertedByDeadBody )
		{
			m_isAlertedByDeadBody = false;
		}
		if( ( ( ( GameInstance.GetGameFeatureManager( GetOwner().GetGame() ).AggressiveCrowdsEnabled() && m_inCrowd ) && m_highLevelState != gamedataNPCHighLevelState.Combat ) && ( ( NPCPuppet )( GetOwner() ) ).IsAggressive() ) && !( m_driverIsAggressive ) )
		{
			( ( NPCPuppet )( GetOwner() ) ).CallUnregisterAggressiveNPC();
		}
	}

	protected event OnRagdollEnabledEvent( evt : RagdollNotifyEnabledEvent )
	{
		if( m_inCrowd )
		{
			m_desiredFearPhase = -1;
		}
	}

	private export function OnGameDetach()
	{
		var puppetBlackboard : IBlackboard;
		if( GetOwnerPuppet() )
		{
			puppetBlackboard = GetOwnerPuppet().GetPuppetStateBlackboard();
		}
		if( puppetBlackboard && m_pendingBehaviorCb )
		{
			puppetBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().PuppetState.InPendingBehavior, m_pendingBehaviorCb );
		}
		if( m_ignoreList.Size() != 0 )
		{
			InformInvestigators();
		}
	}

	protected event OnIncapacitatedEvent( evt : IncapacitatedEvent )
	{
		OnIncapacitated( ( ( NPCPuppet )( GetOwner() ) ).GetMyKiller() );
	}

	protected event OnIgnoreListEvent( evt : IgnoreListEvent )
	{
		if( evt.removeEvent )
		{
			if( m_ignoreList.Contains( evt.bodyID ) )
			{
				m_ignoreList.Remove( evt.bodyID );
			}
		}
		else
		{
			m_ignoreList.PushBack( evt.bodyID );
			AddInvestigatedBody( evt.bodyID );
		}
	}

	protected event OnNPCRoleChangeEvent( evt : NPCRoleChangeEvent )
	{
		m_aiRole = evt.m_newRole.GetRoleEnum();
		ReevaluateReactionPreset();
		if( m_aiRole == EAIRole.Follower )
		{
			GetOwnerPuppet().GetBumpComponent().Toggle( false );
		}
	}

	protected event OnWorkspotStartedEvent( evt : WorkspotStartedEvent ) {}

	protected event OnWorkspotFinishedEvent( evt : WorkspotFinishedEvent ) {}

	protected event OnStatusEffectApplied( evt : ApplyStatusEffectEvent )
	{
		var additionalParam : CName;
		additionalParam = evt.staticData.AdditionalParam();
		if( ( evt.staticData.StatusEffectType().Type() == gamedataStatusEffectType.Sleep || evt.staticData.GameplayTagsContains( 'Braindance' ) ) || additionalParam == 'Drunk' )
		{
			ReevaluateReactionPreset();
		}
		if( additionalParam == 'LoreAnim' )
		{
			ReevaluateReactionPreset();
			m_workspotReactionPlayed = true;
		}
		if( additionalParam == 'LoreVictimSaved' )
		{
			AnimationControllerComponent.SetAnimWrapperWeight( GetOwnerPuppet(), 'LoreVictimSaved', 1.0 );
		}
		if( additionalParam == 'Busy' )
		{
			GetOwnerPuppet().EnableInteraction( 'GenericTalk', false );
		}
	}

	protected event OnStatusEffectRemoved( evt : RemoveStatusEffect )
	{
		var startReaction : StimuliEvent;
		var additionalParam : CName;
		var ownerPuppet : ScriptedPuppet;
		additionalParam = evt.staticData.AdditionalParam();
		ownerPuppet = GetOwnerPuppet();
		if( ( evt.staticData.StatusEffectType().Type() == gamedataStatusEffectType.Sleep || evt.staticData.GameplayTagsContains( 'Braindance' ) ) || additionalParam == 'Drunk' )
		{
			ReevaluateReactionPreset();
		}
		if( additionalParam == 'LoreAnim' )
		{
			if( m_activeReaction || ownerPuppet.GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Alerted )
			{
				startReaction = new StimuliEvent;
				startReaction.name = 'loreAnim';
				GetOwner().QueueEvent( startReaction );
			}
			if( m_cacheSecuritySysOutput )
			{
				ReactToSecuritySystemOutputByTask( m_cacheSecuritySysOutput );
			}
			ReevaluateReactionPreset();
		}
		if( additionalParam == 'LoreVictimSaved' )
		{
			AnimationControllerComponent.SetAnimWrapperWeight( ownerPuppet, 'LoreVictimSaved', 0.0 );
		}
		if( additionalParam == 'Busy' )
		{
			ownerPuppet.EnableInteraction( 'GenericTalk', true );
		}
	}

	protected event OnReactionFinishedEvent( evt : ReactionFinishedEvent )
	{
		var crowdSettingsEvent : CrowdSettingsEvent;
		var mountInfo : MountingInfo;
		var owner : GameObject;
		var game : GameInstance;
		owner = GetOwner();
		game = owner.GetGame();
		DeactiveLookAt();
		ResetFacial( m_facialCooldown );
		m_inReactionSequence = false;
		m_crowdFearStage = gameFearStage.Relaxed;
		GetOwnerPuppet().GetCrowdMemberComponent().ChangeFearStage( m_crowdFearStage );
		if( VehicleComponent.IsMountedToVehicle( game, owner ) )
		{
			crowdSettingsEvent = new CrowdSettingsEvent;
			crowdSettingsEvent.movementType = 'panic';
			mountInfo = GameInstance.GetMountingFacility( game ).GetMountingInfoSingleWithObjects( owner );
			GameInstance.FindEntityByID( game, mountInfo.parentId ).QueueEvent( crowdSettingsEvent );
		}
	}

	protected event OnReevaluatePresetEvent( evt : ReevaluatePresetEvent )
	{
		var setAggressiveMaskEvent : SetAggressiveMask;
		ReevaluateReactionPreset();
		CheckCrowd();
		if( m_reactionPreset && m_reactionPreset.IsAggressive() )
		{
			setAggressiveMaskEvent = new SetAggressiveMask;
			GetOwnerPuppet().QueueEvent( setAggressiveMaskEvent );
		}
	}

	protected event OnReactionChangeRequestEvent( evt : ReactionChangeRequestEvent )
	{
		SetReactionPreset( evt.reactionPresetRecord );
	}

	protected event OnPendingBehaviorChanged( value : Bool )
	{
		var triggerAIEvent : AIEvent;
		if( value )
		{
			m_inPendingBehavior = true;
		}
		else
		{
			m_inPendingBehavior = false;
			triggerAIEvent = new AIEvent;
			triggerAIEvent.name = 'TriggerCombatReaction';
			GetOwnerPuppet().QueueEvent( triggerAIEvent );
		}
	}

	protected event OnAttitudeGroupChanged( evt : AttitudeGroupChangedEvent )
	{
		if( m_activeReaction )
		{
			ReevaluateReaction();
		}
	}

	private function CalculateMoveTypeChangeDelay( totalDistance : Float, distanceLeft : Float, minDistance : Float, maxDelay : Float ) : Float
	{
		var delay : Float;
		delay = ( ( 1.0 - ( ( distanceLeft - minDistance ) / ( totalDistance - minDistance ) ) ) * maxDelay ) * 0.5;
		delay += ( ( RandF() * maxDelay ) * 0.5 );
		return delay;
	}

	protected event OnCrosswalkEvent( evt : CrosswalkEvent )
	{
		var ownerPuppet : ScriptedPuppet;
		var crowdMemberComponent : CrowdMemberBaseComponent;
		var changeMoveTypeEvent : CrowdSettingsEvent;
		ownerPuppet = GetOwnerPuppet();
		changeMoveTypeEvent = new CrowdSettingsEvent;
		if( evt.oldTrafficLightColor == worldTrafficLightColor.INVALID )
		{
			m_isInCrosswalk = true;
		}
		if( evt.trafficLightColor == worldTrafficLightColor.INVALID )
		{
			m_isInCrosswalk = false;
		}
		if( evt.oldTrafficLightColor == worldTrafficLightColor.INVALID || m_crowdFearStage != gameFearStage.Relaxed )
		{
			return false;
		}
		crowdMemberComponent = ownerPuppet.GetCrowdMemberComponent();
		if( evt.trafficLightColor == worldTrafficLightColor.RED )
		{
			if( ( evt.totalDistance - evt.distanceLeft ) <= 1.5 )
			{
				crowdMemberComponent.TryChangeMovementDirection();
			}
			else if( evt.distanceLeft > 2.0 )
			{
				changeMoveTypeEvent.movementType = 'jog';
				GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), changeMoveTypeEvent, CalculateMoveTypeChangeDelay( evt.totalDistance, evt.distanceLeft, 2.0, evt.totalDistance / 5.0 ) );
			}
		}
		else if( evt.trafficLightColor == worldTrafficLightColor.YELLOW )
		{
			if( ( evt.totalDistance - evt.distanceLeft ) <= 1.5 )
			{
				crowdMemberComponent.TryChangeMovementDirection();
			}
			else if( evt.distanceLeft > 4.0 )
			{
				changeMoveTypeEvent.movementType = 'jog';
				GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), changeMoveTypeEvent, CalculateMoveTypeChangeDelay( evt.totalDistance, evt.distanceLeft, 4.0, evt.totalDistance / 5.0 ) );
			}
		}
		else if( evt.trafficLightColor == worldTrafficLightColor.GREEN )
		{
			crowdMemberComponent.ChangeMoveType( 'walk' );
		}
		else
		{
			crowdMemberComponent.ChangeMoveType( 'walk' );
		}
	}

	protected event OnBumpEvent( evt : BumpEvent )
	{
		var workspotSystem : WorkspotGameSystem;
		var broadcaster : StimBroadcasterComponent;
		var distanceSquared : Float;
		var triggerDistance : Float;
		var speedModifier : Float;
		var distanceBuffer : Float;
		var vehicle : VehicleObject;
		var blackboard : IBlackboard;
		var player : GameObject;
		var owner : GameObject;
		var ownerPuppet : ScriptedPuppet;
		var game : GameInstance;
		owner = GetOwner();
		ownerPuppet = GetOwnerPuppet();
		game = owner.GetGame();
		if( !( ownerPuppet ) || !( ownerPuppet.IsActive() ) )
		{
			return false;
		}
		if( m_reactionPreset.Type() == gamedataReactionPresetType.NoReaction )
		{
			return false;
		}
		if( !( m_initCrowd ) )
		{
			InitCrowd();
			m_initCrowd = true;
		}
		player = GetPlayerSystem().GetLocalPlayerMainGameObject();
		if( evt.isMounted )
		{
			distanceBuffer = m_bumpTriggerDistanceBufferMounted;
		}
		else
		{
			if( ( GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( owner ) && GameInstance.GetWorkspotSystem( owner.GetGame() ).IsActorInWorkspot( GetPlayerSystem().GetLocalPlayerControlledGameObject() ) ) && StatusEffectSystem.ObjectHasStatusEffect( GetOwner(), T"WorkspotStatus.SyncAnimation" ) )
			{
				return false;
			}
			if( ScriptedPuppet.IsBeingGrappled( ownerPuppet ) )
			{
				return false;
			}
			blackboard = GameInstance.GetBlackboardSystem( game ).GetLocalInstanced( player.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
			if( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Locomotion ) == ( ( Int32 )( gamePSMLocomotionStates.Crouch ) ) )
			{
				distanceBuffer = m_bumpTriggerDistanceBufferCrouched;
			}
		}
		vehicle = ( ( VehicleObject )( GameInstance.FindEntityByID( game, evt.vehicleEntityID ) ) );
		if( evt.sourceSpeed > 15.0 )
		{
			speedModifier = 3.5;
		}
		else if( evt.sourceSpeed > 10.0 )
		{
			speedModifier = 2.5;
		}
		else if( evt.sourceSpeed > 5.0 )
		{
			speedModifier = 1.5;
		}
		else if( evt.sourceSpeed > 1.5 )
		{
			speedModifier = 0.5;
		}
		distanceSquared = evt.sourceSquaredDistance;
		triggerDistance = ( ( evt.sourceRadius + m_NPCRadius ) + distanceBuffer ) + speedModifier;
		if( !( evt.isMounted ) )
		{
			triggerDistance = ( evt.sourceRadius + m_NPCRadius ) + distanceBuffer;
			if( distanceSquared > ( triggerDistance * triggerDistance ) )
			{
				return false;
			}
		}
		if( !( GameObject.IsCooldownActive( ownerPuppet, 'bumpCooldown' ) ) )
		{
			if( evt.isMounted )
			{
				GameObject.StartCooldown( ownerPuppet, 'bumpCooldown', TDB.GetFloat( T"AIGeneralSettings.vehicleBumpCooldown" ) );
				if( !( ( ( NPCPuppet )( ownerPuppet ) ).IsRagdolling() ) )
				{
					if( ownerPuppet.GetHighLevelStateFromBlackboard() != gamedataNPCHighLevelState.Combat || ownerPuppet.IsPrevention() )
					{
						TriggerReactionBehaviorForCrowd( vehicle, gamedataOutput.DodgeToSide, false, evt.sourcePosition );
					}
				}
			}
			else
			{
				GameObject.StartCooldown( ownerPuppet, 'bumpCooldown', 1.0 );
				if( SourceAttitude( player, EAIAttitude.AIA_Friendly ) )
				{
					return false;
				}
				if( !( NPCPuppet.IsInCombat( ownerPuppet ) ) )
				{
					GameObject.PlayVoiceOver( owner, 'bump', 'Scripts:OnBumpEvent' );
				}
				broadcaster = player.GetStimBroadcasterComponent();
				workspotSystem = GameInstance.GetWorkspotSystem( game );
				if( workspotSystem.IsActorInWorkspot( ownerPuppet ) )
				{
				}
				else if( m_inTrafficLane )
				{
				}
				else
				{
					if( m_inCrowd || IsReactionAvailableInPreset( gamedataStimType.Bump ) )
					{
						TriggerReactionBehaviorForCrowd( player, gamedataOutput.Bump, false );
					}
				}
				if( m_bumpTimestamp >= EngineTime.ToFloat( GameInstance.GetSimTime( game ) ) )
				{
					m_bumpedRecently += 1;
					if( m_inCrowd )
					{
						if( m_bumpedRecently > 2 )
						{
							TriggerFacialLookAtReaction( true, true );
							if( broadcaster )
							{
								broadcaster.SendDrirectStimuliToTarget( ownerPuppet, gamedataStimType.Bump, ownerPuppet );
							}
						}
					}
					else if( m_reactionPreset.IsAggressive() && broadcaster )
					{
						if( m_bumpedRecently <= 2 )
						{
							if( workspotSystem.IsActorInWorkspot( ownerPuppet ) && !( ownerPuppet.IsConnectedToSecuritySystem() ) )
							{
								broadcaster.SendDrirectStimuliToTarget( ownerPuppet, gamedataStimType.Provoke, ownerPuppet );
							}
							else if( m_desiredReaction )
							{
								m_desiredReaction.escalateProvoke = true;
							}
						}
						else if( ( !( CanTriggerReprimandOrder() ) && m_activeReaction ) && m_activeReaction.reactionBehaviorName == gamedataOutput.Bump )
						{
							broadcaster.SendDrirectStimuliToTarget( ownerPuppet, gamedataStimType.Combat, ownerPuppet );
						}
					}
				}
				else
				{
					m_bumpedRecently = 1;
					m_bumpTimestamp = EngineTime.ToFloat( GameInstance.GetSimTime( game ) ) + 10.0;
					if( m_inCrowd )
					{
						TriggerFacialLookAtReaction( true );
					}
					else if( m_reactionPreset.IsAggressive() )
					{
						if( ( broadcaster && workspotSystem.IsActorInWorkspot( ownerPuppet ) ) && !( ownerPuppet.IsConnectedToSecuritySystem() ) )
						{
							broadcaster.SendDrirectStimuliToTarget( ownerPuppet, gamedataStimType.Provoke, ownerPuppet );
						}
					}
				}
			}
		}
	}

	protected event OnClearFearOnHitEvent( evt : ClearFearOnHitEvent )
	{
		if( GetOwnerPuppet().IsCharacterCivilian() && !( m_pendingReaction ) )
		{
			m_crowdFearStage = gameFearStage.Relaxed;
			ResetAllFearAnimWrappers();
		}
	}

	private function PlayBumpInWorkspot( side : gameinteractionsBumpSide, direction : Vector4 )
	{
		var workspotSystem : WorkspotGameSystem;
		var actor : weak< GameObject >;
		var isBumpFromFront : Bool;
		var reactionName : CName;
		actor = GetOwner();
		workspotSystem = GameInstance.GetWorkspotSystem( actor.GetGame() );
		isBumpFromFront = Vector4.Dot2D( actor.GetWorldForward(), direction ) < 0.0;
		switch( side )
		{
			case gameinteractionsBumpSide.Left:
				reactionName = ( ( isBumpFromFront ) ? ( 'BumpLeftFront' ) : ( 'BumpLeftBack' ) );
			break;
			case gameinteractionsBumpSide.Right:
				reactionName = ( ( isBumpFromFront ) ? ( 'BumpRightFront' ) : ( 'BumpRightBack' ) );
			break;
			default:
				return;
		}
		if( workspotSystem.IsReactionAvailable( actor, reactionName ) )
		{
			workspotSystem.SendReactionSignal( actor, reactionName );
		}
	}

	protected event OnVehicleHit( evt : gameVehicleHitEvent )
	{
		var instigator : GameObject;
		var velocityDiff : Vector4;
		var magnitude : Float;
		var shouldOmitStims : Bool;
		instigator = evt.attackData.GetInstigator();
		if( !( instigator ) )
		{
			return false;
		}
		velocityDiff = evt.vehicleVelocity - evt.preyVelocity;
		magnitude = Vector4.Length( velocityDiff );
		shouldOmitStims = ( !( GameObject.IsCooldownActive( GetOwner(), 'vehicleSlowHitOnCivilian' ) ) && instigator.IsPlayer() ) && ( magnitude <= 9.72000027 );
		if( instigator.IsPlayer() && !( shouldOmitStims ) )
		{
			StimBroadcasterComponent.BroadcastStim( instigator, gamedataStimType.CrimeWitness );
		}
		if( !( GameObject.IsCooldownActive( GetOwner(), 'vehicleHitCooldown' ) ) )
		{
			GameObject.StartCooldown( GetOwner(), 'vehicleHitCooldown', 1.0 );
			if( !( shouldOmitStims ) )
			{
				StimBroadcasterComponent.BroadcastStim( instigator, gamedataStimType.VehicleHit, TweakDBInterface.GetFloat( T"AIGeneralSettings.vehicleHitFearSpreadRange", 5.0 ) );
			}
		}
	}

	protected event OnPlayerProximityStartEvent( evt : PlayerProximityStartEvent )
	{
		var proximityLookatEvent : ProximityLookatEvent;
		var player : weak< PlayerPuppet >;
		player = ( ( PlayerPuppet )( GetPlayerSystem().GetLocalPlayerControlledGameObject() ) );
		if( !( player ) )
		{
			return false;
		}
		if( evt.profile == 'Crowds' )
		{
			m_playerProximity = true;
			if( m_inCrowd || !( GetOwnerPuppet().IsConnectedToSecuritySystem() ) )
			{
				if( CanTriggerExpressionLookAt() )
				{
					if( IsTargetInFront( player, 45.0, true ) && IsTargetInFront( player ) )
					{
						if( m_inCrowd )
						{
							ActivateReactionLookAt( player, true, true );
						}
						else
						{
							ActivateReactionLookAt( player, false );
						}
					}
					proximityLookatEvent = new ProximityLookatEvent;
					m_proximityLookatEventId = GetDelaySystem().DelayEvent( GetOwner(), proximityLookatEvent, 2.0 );
				}
			}
		}
	}

	protected event OnPlayerProximityStopEvent( evt : PlayerProximityStopEvent )
	{
		var delaySystem : DelaySystem;
		if( m_playerProximity )
		{
			m_playerProximity = false;
			if( !( m_inReactionSequence ) )
			{
				DeactiveLookAt();
				ResetFacial( m_facialCooldown );
			}
		}
		delaySystem = GameInstance.GetDelaySystem( GetOwner().GetGame() );
		delaySystem.CancelDelay( m_disturbComfortZoneEventId );
		delaySystem.CancelDelay( m_checkComfortZoneEventId );
		delaySystem.CancelDelay( m_proximityLookatEventId );
	}

	protected event OnProximityLookatEvent( evt : ProximityLookatEvent )
	{
		var proximityLookatEvent : ProximityLookatEvent;
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).CancelDelay( m_proximityLookatEventId );
		if( CanTriggerExpressionLookAt() )
		{
			if( ( m_playerProximity && IsTargetInFront( GetPlayerSystem().GetLocalPlayerControlledGameObject() ) ) && IsTargetInFront( GetPlayerSystem().GetLocalPlayerControlledGameObject(), 45.0, true ) )
			{
				TriggerFacialLookAtReaction();
			}
			else if( m_playerProximity )
			{
				proximityLookatEvent = new ProximityLookatEvent;
				m_proximityLookatEventId = GetDelaySystem().DelayEvent( GetOwner(), proximityLookatEvent, 1.5 );
			}
		}
	}

	protected event OnSwapped( evt : AIPuppetSwappedEvent )
	{
		if( m_crowdFearStage != gameFearStage.Relaxed )
		{
			GetPuppetReactionBlackboard().SetBool( GetAllBlackboardDefs().PuppetReaction.exitReactionFlag, true );
			ResetAllFearAnimWrappers();
			GameInstance.GetReactionSystem( GetOwner().GetGame() ).UnmarkDespawnCandidate( GetOwner().GetEntityID() );
		}
	}

	protected event OnTeleported( evt : AIPuppetTeleportedEvent )
	{
		var owner : GameObject;
		owner = GetOwner();
		StatusEffectHelper.RemoveStatusEffectsWithTag( owner, 'Ping' );
		GameObject.UntagObject( owner );
	}

	protected event OnInCrowd( evt : InCrowd )
	{
		m_inTrafficLane = true;
	}

	protected event OnOutOfCrowd( evt : OutOfCrowd )
	{
		m_inTrafficLane = false;
	}

	public function IsInTrafficLane() : Bool
	{
		return m_inTrafficLane;
	}

	protected event OnVehicleHijackEvent( evt : VehicleHijackEvent )
	{
		m_beignHijacked = true;
		m_driverAllowedToGetAggressive = evt.driverAllowedToGetAggressive;
	}

	protected event OnResetVehicleHijackEvent( evt : ResetVehicleHijackEvent )
	{
		m_beignHijacked = false;
	}

	protected event OnSwapPreset( evt : SwapPresetEvent )
	{
		if( evt.mappingName == "Base" )
		{
			SetBaseReactionPreset();
		}
		else
		{
			MapReactionPreset( evt.mappingName );
		}
	}

	protected event OnRainEvent( evt : RainEvent ) {}

	protected event OnDistrurbComfortZoneAggressiveEvent( evt : DistrurbComfortZoneAggressiveEvent )
	{
		var broadcaster : StimBroadcasterComponent;
		var owner : GameObject;
		owner = GetOwner();
		m_backOffInProgress = true;
		m_backOffTimestamp = EngineTime.ToFloat( GameInstance.GetSimTime( owner.GetGame() ) ) + 50.0;
		broadcaster = GetPlayerSystem().GetLocalPlayerControlledGameObject().GetStimBroadcasterComponent();
		if( broadcaster )
		{
			broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.Provoke, owner );
		}
	}

	protected event OnAreaEnter( trigger : AreaEnteredEvent )
	{
		var distrurbComfortZoneAggressiveEvent : DistrurbComfortZoneAggressiveEvent;
		var broadcaster : StimBroadcasterComponent;
		var owner : GameObject;
		var game : GameInstance;
		owner = GetOwner();
		game = owner.GetGame();
		GameObject.PlayVoiceOver( owner, 'stlh_curious_grunt', 'Scripts:ProcessReactionOutput' );
		ActivateReactionLookAt( GameInstance.GetPlayerSystem( game ).GetLocalPlayerMainGameObject(), true );
		if( m_backOffInProgress && ( m_backOffTimestamp >= EngineTime.ToFloat( GameInstance.GetSimTime( game ) ) ) )
		{
			broadcaster = GetPlayerSystem().GetLocalPlayerControlledGameObject().GetStimBroadcasterComponent();
			if( broadcaster )
			{
				broadcaster.SendDrirectStimuliToTarget( owner, gamedataStimType.Provoke, owner );
			}
		}
		else
		{
			m_backOffInProgress = false;
			distrurbComfortZoneAggressiveEvent = new DistrurbComfortZoneAggressiveEvent;
			m_disturbComfortZoneAggressiveEventId = GameInstance.GetDelaySystem( game ).DelayEvent( owner, distrurbComfortZoneAggressiveEvent, 2.0 );
		}
	}

	protected event OnAreaExit( trigger : AreaExitedEvent )
	{
		DeactiveLookAt();
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).CancelDelay( m_disturbComfortZoneAggressiveEventId );
	}

	protected event OnExplorationEnteredEvent( evt : ExplorationEnteredEvent )
	{
		GetOwnerPuppet().GetPuppetStateBlackboard().SetBool( GetAllBlackboardDefs().PuppetState.InPendingBehavior, true );
	}

	protected event OnExplorationLeftEvent( evt : ExplorationLeftEvent )
	{
		GetOwnerPuppet().GetPuppetStateBlackboard().SetBool( GetAllBlackboardDefs().PuppetState.InPendingBehavior, false );
	}

}

