abstract class AIHitReactionTask extends AIbehaviortaskScript
{
	protected var m_activationTimeStamp : Float;
	private var m_reactionDuration : Float;
	private var m_hitReactionAction : ActionHitReactionScriptProxy;
	private var m_hitReactionType : animHitReactionType;

	public export function Dispose()
	{
		m_hitReactionAction = NULL;
	}

	private virtual function OnActivate( context : ScriptExecutionContext ) {}

	private virtual function OnUpdate( context : ScriptExecutionContext, aiTime : Float ) {}

	private virtual function OnDeactivate( context : ScriptExecutionContext ) {}

	private virtual function GetHitReactionType() : animHitReactionType
	{
		return animHitReactionType.None;
	}

	private virtual function GetDesiredHitReactionDuration( context : ScriptExecutionContext ) : Float
	{
		return -1.0;
	}

	private virtual function GetHitReactionDurationWithInterrupt( context : ScriptExecutionContext ) : Float
	{
		var hitReactionType : Int32;
		var hitCountInCombo : Int32;
		var meleeHitChainBeforeBreaking : Int32;
		var rangedHitChainBeforeBreaking : Int32;
		var hitReactionComponent : HitReactionComponent;
		var hitReaction : AnimFeature_HitReactionsData;
		var aiComponent : AIHumanComponent;
		var weapon : weak< WeaponObject >;
		if( GetPuppet( context ).IsCrowd() )
		{
			return GetDesiredHitReactionDuration( context );
		}
		hitReactionComponent = GetHitReactionComponent( context );
		hitReactionType = hitReactionComponent.GetHitReactionType();
		if( hitReactionType == ( ( Int32 )( animHitReactionType.GuardBreak ) ) )
		{
			return GetDesiredHitReactionDuration( context );
		}
		weapon = GameObject.GetActiveWeapon( ScriptExecutionContext.GetOwner( context ) );
		if( !( weapon ) )
		{
			return GetDesiredHitReactionDuration( context );
		}
		if( hitReactionType == ( ( Int32 )( animHitReactionType.Knockdown ) ) )
		{
			if( weapon.IsMelee() )
			{
				hitReaction = hitReactionComponent.GetLastHitReactionData();
				if( ( hitReaction.hitBodyPart == 5 ) || ( hitReaction.hitBodyPart == 6 ) )
				{
					ScriptExecutionContext.GetOwner( context ).SetIndividualTimeDilation( 'MeleeLegKnockdown', 1.60000002, 0.5, '', '' );
				}
			}
			return GetDesiredHitReactionDuration( context );
		}
		hitCountInCombo = hitReactionComponent.GetHitCountInCombo();
		meleeHitChainBeforeBreaking = hitReactionComponent.GetMeleeMaxHitChain();
		if( ( ( meleeHitChainBeforeBreaking > -1 ) && weapon.IsMelee() ) && ( hitCountInCombo < meleeHitChainBeforeBreaking ) )
		{
			return GetDesiredHitReactionDuration( context );
		}
		rangedHitChainBeforeBreaking = hitReactionComponent.GetRangedMaxHitChain();
		if( ( ( rangedHitChainBeforeBreaking > -1 ) && weapon.IsRanged() ) && ( hitCountInCombo < rangedHitChainBeforeBreaking ) )
		{
			return GetDesiredHitReactionDuration( context );
		}
		if( ( ( hitReactionComponent.GetHitReactionData().hitSource != ( ( Int32 )( EAIHitSource.Ranged ) ) ) && ( hitReactionType != ( ( Int32 )( animHitReactionType.Block ) ) ) ) && ( hitReactionType != ( ( Int32 )( animHitReactionType.Parry ) ) ) )
		{
			aiComponent = GetPuppet( context ).GetAIControllerComponent();
			if( aiComponent.GetCombatMode() != AIbehaviorCombatModes.Default && aiComponent.CheckTweakCondition( "DodgeAfterHitReactionCondition" ) )
			{
				hitReactionComponent.ResetHitCount();
				hitReaction = hitReactionComponent.GetLastHitReactionData();
				if( weapon.IsRanged() )
				{
					if( hitReactionType == ( ( Int32 )( animHitReactionType.Stagger ) ) )
					{
						if( !( IsTumbleStagger( hitReaction ) ) )
						{
							return 2.0999999;
						}
						return 2.4000001;
					}
					return 0.30000001;
				}
				else if( ( hitReactionType == ( ( Int32 )( animHitReactionType.Stagger ) ) ) && !( IsTumbleStagger( hitReaction ) ) )
				{
					return 0.44999999;
				}
				else
				{
					return 0.30000001;
				}
			}
		}
		return GetDesiredHitReactionDuration( context );
	}

	protected function IsTumbleStagger( hitReaction : AnimFeature_HitReactionsData ) : Bool
	{
		if( !( hitReaction ) )
		{
			return false;
		}
		if( hitReaction.hitBodyPart == 1 )
		{
			if( hitReaction.hitDirection == 2 )
			{
				return true;
			}
			else
			{
				switch( hitReaction.animVariation )
				{
					case 0:
						return true;
					case 1:
						return true;
					case 2:
						return true;
					case 3:
						return true;
					case 4:
						return true;
					case 5:
						return true;
					case 6:
						return true;
					case 7:
						return true;
					case 8:
						return true;
					case 9:
						return true;
					case 10:
						return true;
					case 11:
						return true;
					case 12:
						return true;
					case 13:
						return true;
					case 14:
						return true;
					case 15:
						return true;
					case 16:
						return true;
					case 20:
						return true;
					case 21:
						return true;
					case 22:
						return true;
					case 24:
						return true;
					case 25:
						return true;
					default:
						return false;
				}
			}
		}
		if( hitReaction.hitBodyPart == 2 )
		{
			switch( hitReaction.animVariation )
			{
				case 1:
					return true;
				case 2:
					return true;
				case 5:
					return true;
				case 10:
					return true;
				case 11:
					return true;
				case 13:
					return true;
				case 14:
					return true;
				case 16:
					return true;
				case 17:
					return true;
				case 19:
					return true;
				case 20:
					return true;
				case 22:
					return true;
				case 23:
					return true;
				case 25:
					return true;
				case 26:
					return true;
				default:
					return false;
			}
		}
		if( hitReaction.hitBodyPart == 3 )
		{
			switch( hitReaction.animVariation )
			{
				case 2:
					return true;
				case 5:
					return true;
				case 8:
					return true;
				case 11:
					return true;
				case 12:
					return true;
				case 13:
					return true;
				case 17:
					return true;
				case 20:
					return true;
				case 21:
					return true;
				case 22:
					return true;
				case 24:
					return true;
				case 25:
					return true;
				case 26:
					return true;
				default:
					return false;
			}
		}
		if( hitReaction.hitBodyPart == 5 )
		{
			return true;
		}
		if( hitReaction.hitBodyPart == 6 )
		{
			return true;
		}
		return false;
	}

	protected override function Activate( context : ScriptExecutionContext )
	{
		var puppet : ScriptedPuppet;
		var clearFearEvent : ClearFearOnHitEvent;
		puppet = GetPuppet( context );
		m_hitReactionAction = GetHitReactionComponent( context ).GetHitReactionProxyAction();
		m_reactionDuration = GetHitReactionDurationWithInterrupt( context );
		NPCPuppet.ChangeUpperBodyState( ScriptExecutionContext.GetOwner( context ), gamedataNPCUpperBodyState.ChargedAttack );
		if( puppet.IsCrowd() )
		{
			puppet.GetCrowdMemberComponent().TryStopTrafficMovement();
			clearFearEvent = new ClearFearOnHitEvent;
			puppet.QueueEvent( clearFearEvent );
		}
		InitialiseReaction( context );
	}

	protected export override function Update( context : ScriptExecutionContext ) : AIbehaviorUpdateOutcome
	{
		var aiTime : Float;
		if( CheckForReevaluation( context ) )
		{
			InitialiseReaction( context );
		}
		aiTime = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
		OnUpdate( context, aiTime );
		if( aiTime < ( m_activationTimeStamp + m_reactionDuration ) )
		{
			return AIbehaviorUpdateOutcome.IN_PROGRESS;
		}
		return AIbehaviorUpdateOutcome.SUCCESS;
	}

	protected export override function Deactivate( context : ScriptExecutionContext )
	{
		OnDeactivate( context );
		NPCPuppet.ChangeUpperBodyState( ScriptExecutionContext.GetOwner( context ), gamedataNPCUpperBodyState.Normal );
		if( m_hitReactionAction )
		{
			m_hitReactionAction.Stop();
		}
	}

	private function CheckForReevaluation( context : ScriptExecutionContext ) : Bool
	{
		if( !( IsThisFrameActivationFrame( context ) ) && ( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent() != NULL ) )
		{
			if( ( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent().hitType == ( ( Int32 )( animHitReactionType.Block ) ) ) || ( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent().hitType == ( ( Int32 )( animHitReactionType.Parry ) ) ) )
			{
				return true;
			}
			if( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent().hitSource == 1 )
			{
				return true;
			}
		}
		return false;
	}

	private function AngleToAttackSource( context : ScriptExecutionContext, hitData : AnimFeature_HitReactionsData ) : Float
	{
		var finalAngleToAttackSource : Float;
		var finalHitDirection : Int32;
		if( hitData.hitSource == 0 )
		{
			switch( ( ( NPCPuppet )( ScriptExecutionContext.GetOwner( context ) ) ).GetHitReactionComponent().GetHitReactionData().hitDirection )
			{
				case 4:
					finalAngleToAttackSource = 180.0;
				break;
				case 1:
					finalAngleToAttackSource = 270.0;
				break;
				case 2:
					finalAngleToAttackSource = 0.0;
				break;
				case 3:
					finalAngleToAttackSource = 90.0;
				break;
				default:
					break;
			}
		}
		else
		{
			finalHitDirection = GameObject.GetTargetAngleInInt( GetPuppet( context ).GetHitReactionComponent().GetHitInstigator(), GetPuppet( context ) );
			if( finalHitDirection == 0 )
			{
				finalHitDirection = 4;
			}
			switch( finalHitDirection )
			{
				case 2:
					finalAngleToAttackSource = 0.0;
				break;
				case 3:
					finalAngleToAttackSource = 90.0;
				break;
				case 4:
					finalAngleToAttackSource = 180.0;
				break;
				case 1:
					finalAngleToAttackSource = 270.0;
				break;
				default:
					break;
			}
		}
		return finalAngleToAttackSource;
	}

	private function IsThisFrameActivationFrame( context : ScriptExecutionContext ) : Bool
	{
		if( m_activationTimeStamp == EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) ) )
		{
			return true;
		}
		return false;
	}

	private function InitialiseReaction( context : ScriptExecutionContext )
	{
		HitReactionComponent.ClearHitStim( ScriptExecutionContext.GetOwner( context ) );
		m_activationTimeStamp = EngineTime.ToFloat( ScriptExecutionContext.GetAITime( context ) );
		SendDataToAnimationGraph( context );
		SendDataToHitReactionComponent( context );
		OnActivate( context );
	}

	private function SendDataToHitReactionComponent( context : ScriptExecutionContext )
	{
		var hitReactionBehaviorData : HitReactionBehaviorData;
		var setLastHitDataEvent : LastHitDataEvent;
		hitReactionBehaviorData = new HitReactionBehaviorData;
		hitReactionBehaviorData.m_hitReactionType = GetHitReactionType();
		hitReactionBehaviorData.m_hitReactionActivationTimeStamp = m_activationTimeStamp;
		hitReactionBehaviorData.m_hitReactionDuration = m_reactionDuration;
		setLastHitDataEvent = new LastHitDataEvent;
		setLastHitDataEvent.hitReactionBehaviorData = hitReactionBehaviorData;
		GetPuppet( context ).QueueEvent( setLastHitDataEvent );
	}

	private function SendDataToAnimationGraph( context : ScriptExecutionContext )
	{
		var hitData : AnimFeature_HitReactionsData;
		var weapon : WeaponObject;
		var victimYaw : Float;
		var instigatorYaw : Float;
		var owner : NPCPuppet;
		owner = ( ( NPCPuppet )( ScriptExecutionContext.GetOwner( context ) ) );
		hitData = new AnimFeature_HitReactionsData;
		hitData = GetHitReactionComponent( context ).GetHitReactionData();
		hitData.angleToAttack = AngleToAttackSource( context, hitData );
		if( hitData.hitSource != 0 )
		{
			instigatorYaw = Vector4.Heading( owner.GetHitReactionComponent().GetHitInstigator().GetWorldForward() );
			victimYaw = Vector4.Heading( owner.GetWorldForward() );
			hitData.hitDirectionWs = Vector4.RotByAngleXY( owner.GetWorldForward(), victimYaw - instigatorYaw );
		}
		else
		{
			hitData.hitDirectionWs = owner.GetLastHitAttackDirection();
		}
		if( owner.GetBoolFromCharacterTweak( "Hit_Initial_Rotation_Disabled" ) || Vector4.IsZero( hitData.hitDirectionWs ) )
		{
			hitData.useInitialRotation = false;
		}
		else
		{
			hitData.useInitialRotation = true;
		}
		hitData.initialRotationDuration = 0.1;
		if( hitData.hitType == ( ( Int32 )( animHitReactionType.Block ) ) )
		{
			AnimationControllerComponent.ApplyFeatureToReplicate( owner, 'hit', hitData );
			AnimationControllerComponent.PushEventToReplicate( owner, 'PlayBlock' );
		}
		else if( hitData.hitType == ( ( Int32 )( animHitReactionType.Parry ) ) )
		{
			AnimationControllerComponent.ApplyFeatureToReplicate( owner, 'hit', hitData );
			AnimationControllerComponent.PushEventToReplicate( owner, 'PlayParry' );
		}
		else if( m_hitReactionAction )
		{
			if( hitData.hitType == ( ( Int32 )( animHitReactionType.Pain ) ) )
			{
				if( ( hitData.hitBodyPart == 2 ) || ( hitData.hitBodyPart == 3 ) )
				{
					if( owner.GetAIControllerComponent().CheckTweakCondition( "WoundedArmHandgunCondition" ) )
					{
						hitData.animVariation = 0;
					}
					else if( owner.GetAIControllerComponent().CheckTweakCondition( "WoundedArmKnifeCondition" ) )
					{
						hitData.animVariation = 1;
					}
					else
					{
						hitData.animVariation = 2;
					}
				}
				else
				{
					weapon = ( ( WeaponObject )( GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemInSlot( owner, T"AttachmentSlots.WeaponRight" ) ) );
					if( !( weapon ) )
					{
						weapon = ( ( WeaponObject )( GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemInSlot( owner, T"AttachmentSlots.WeaponLeft" ) ) );
					}
					if( !( HasDismemberedLeg( context ) ) && ( TDB.GetWeaponItemRecord( ItemID.GetTDBID( weapon.GetItemID() ) ).ItemType().Type() == gamedataItemType.Wea_Handgun || TDB.GetWeaponItemRecord( ItemID.GetTDBID( weapon.GetItemID() ) ).ItemType().Type() == gamedataItemType.Wea_Revolver ) )
					{
						hitData.animVariation = 0;
					}
					else if( !( HasDismemberedLeg( context ) ) && TDB.GetWeaponItemRecord( ItemID.GetTDBID( weapon.GetItemID() ) ).ItemType().Type() == gamedataItemType.Wea_Knife )
					{
						hitData.animVariation = 1;
					}
					else if( ( !( HasDismemberedLeg( context ) ) && ( TDB.GetWeaponItemRecord( ItemID.GetTDBID( weapon.GetItemID() ) ).ItemType().Type() == gamedataItemType.Wea_Fists || TDB.GetWeaponItemRecord( ItemID.GetTDBID( weapon.GetItemID() ) ).ItemType().Type() == gamedataItemType.Cyb_StrongArms ) ) || ( !( HasDismemberedLeg( context ) ) && !( weapon ) ) )
					{
						hitData.animVariation = 2;
					}
					else
					{
						hitData.animVariation = 3;
					}
				}
			}
			m_hitReactionAction.Stop();
			m_hitReactionAction.Setup( hitData );
			m_hitReactionAction.Launch();
			AnimationControllerComponent.ApplyFeatureToReplicate( owner, 'hit', hitData );
		}
	}

	protected function SpawnAttackGameEffect( context : ScriptExecutionContext, gameEffect : EffectRef, startPosition : Vector4, endPosition : Vector4, duration : Float, colliderBoxSize : Vector4, const statusEffect : ref< String > )
	{
		var npcPuppet : NPCPuppet;
		var attackStartPositionWorld : Vector4;
		var attackEndPositionWorld : Vector4;
		var attackDirectionWorld : Vector4;
		var puppetWorldPosition : Vector4;
		var puppetWorldForward : Vector4;
		var puppetWorldTransform : Transform;
		var storedEffect : EffectInstance;
		npcPuppet = ( ( NPCPuppet )( ScriptExecutionContext.GetOwner( context ) ) );
		puppetWorldPosition = npcPuppet.GetWorldPosition();
		puppetWorldPosition.Z += 1.5;
		puppetWorldForward = npcPuppet.GetWorldForward();
		Transform.SetPosition( puppetWorldTransform, puppetWorldPosition );
		Transform.SetOrientationFromDir( puppetWorldTransform, puppetWorldForward );
		attackStartPositionWorld = Transform.TransformPoint( puppetWorldTransform, startPosition );
		attackEndPositionWorld = Transform.TransformPoint( puppetWorldTransform, endPosition );
		attackDirectionWorld = attackEndPositionWorld - attackStartPositionWorld;
		storedEffect = GameInstance.GetGameEffectSystem( ScriptExecutionContext.GetOwner( context ).GetGame() ).CreateEffect( gameEffect, npcPuppet, npcPuppet );
		EffectData.SetVector( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.box, colliderBoxSize );
		EffectData.SetFloat( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.duration, duration );
		EffectData.SetVector( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, attackStartPositionWorld );
		EffectData.SetQuat( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.rotation, Transform.GetOrientation( puppetWorldTransform ) );
		EffectData.SetVector( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.forward, Vector4.Normalize( attackDirectionWorld ) );
		EffectData.SetFloat( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.range, Vector4.Length( attackDirectionWorld ) );
		if( statusEffect != "" )
		{
			EffectData.SetString( storedEffect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.effectName, statusEffect );
		}
		storedEffect.Run();
	}

	private function HasDismemberedLeg( context : ScriptExecutionContext ) : Bool
	{
		if( StatusEffectSystem.ObjectHasStatusEffect( GetPuppet( context ), T"BaseStatusEffect.DismemberedLegLeft" ) || StatusEffectSystem.ObjectHasStatusEffect( GetPuppet( context ), T"BaseStatusEffect.DismemberedLegRight" ) )
		{
			return true;
		}
		return false;
	}

	private function GetBCVOName( context : ScriptExecutionContext ) : CName
	{
		var ownerHealth : Float;
		var damage : Float;
		damage = GetHitReactionComponent( context ).GetCumulatedDamage();
		ownerHealth = GameInstance.GetStatPoolsSystem( ScriptExecutionContext.GetOwner( context ).GetGame() ).GetStatPoolMaxPointValue( ScriptExecutionContext.GetOwner( context ).GetEntityID(), gamedataStatPoolType.Health );
		if( damage > ( ownerHealth * TweakDBInterface.GetFloat( T"AIGeneralSettings.damageThresholdBattleCry", 0.0 ) ) )
		{
			return 'battlecry_curse';
		}
		return 'battlecry_morale';
	}

}

class ImpactReactionTask extends AIHitReactionTask
{
	var m_tweakDBPackage : TweakDBID;

	protected export override function Activate( context : ScriptExecutionContext )
	{
		var hitData : AnimFeature_HitReactionsData;
		var broadcaster : StimBroadcasterComponent;
		hitData = new AnimFeature_HitReactionsData;
		hitData = GetHitReactionComponent( context ).GetHitReactionData();
		if( ( hitData.hitSource != 0 ) && ScriptedPuppet.CanRagdoll( ScriptExecutionContext.GetOwner( context ) ) )
		{
			StatusEffectHelper.ApplyStatusEffect( ScriptExecutionContext.GetOwner( context ), T"BaseStatusEffect.UncontrolledMovement_RagdollOffLedge" );
		}
		GameObject.PlayVoiceOver( ScriptExecutionContext.GetOwner( context ), 'hit_reaction_light', 'Scripts:ImpactReactionTask' );
		broadcaster = ScriptExecutionContext.GetOwner( context ).GetStimBroadcasterComponent();
		if( broadcaster )
		{
			broadcaster.TriggerSingleBroadcast( ScriptExecutionContext.GetOwner( context ), gamedataStimType.Attention );
		}
		super.Activate( context );
	}

	private override function GetHitReactionType() : animHitReactionType
	{
		return animHitReactionType.Impact;
	}

	private override function GetDesiredHitReactionDuration( context : ScriptExecutionContext ) : Float
	{
		var duration : Float;
		m_tweakDBPackage = T"AIGeneralSettings";
		if( ( ( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent().hitSource == 1 ) || ( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent().hitSource == 2 ) ) || ( GetPuppet( context ).GetHitReactionComponent().GetHitStimEvent().hitSource == 3 ) )
		{
			duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "impact_melee_duration" );
		}
		else
		{
			duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "impact_ranged_duration" );
		}
		if( duration > 0.0 )
		{
			return duration;
		}
		return 0.60000002;
	}

}

class StaggerReactionTask extends AIHitReactionTask
{
	var m_tweakDBPackage : TweakDBID;
	var m_tumble : Bool;
	var m_onUpdateCompleted : Bool;

	protected export override function Activate( context : ScriptExecutionContext )
	{
		GameObject.PlayVoiceOver( ScriptExecutionContext.GetOwner( context ), 'hit_reaction_heavy', 'Scripts:StaggerReactionTask' );
		StatusEffectHelper.ApplyStatusEffect( ScriptExecutionContext.GetOwner( context ), T"BaseStatusEffect.HitReactionStagger" );
		if( ScriptedPuppet.IsPlayerCompanion( ScriptExecutionContext.GetOwner( context ) ) )
		{
			GameObject.PlayVoiceOver( ScriptExecutionContext.GetOwner( context ), 'battlecry_curse', 'Scripts:CompanionStaggerReactionTask' );
		}
		if( ScriptedPuppet.CanRagdoll( ScriptExecutionContext.GetOwner( context ) ) )
		{
			StatusEffectHelper.ApplyStatusEffect( ScriptExecutionContext.GetOwner( context ), T"BaseStatusEffect.UncontrolledMovement_RagdollOffLedge" );
		}
		super.Activate( context );
	}

	private override function GetHitReactionType() : animHitReactionType
	{
		return animHitReactionType.Stagger;
	}

	private override function GetDesiredHitReactionDuration( context : ScriptExecutionContext ) : Float
	{
		var duration : Float;
		var valid : Bool;
		var puppet : ScriptedPuppet;
		m_tweakDBPackage = T"AIGeneralSettings";
		puppet = GetPuppet( context );
		valid = puppet.GetHitReactionComponent() && puppet.GetHitReactionComponent().GetHitStimEvent();
		if( valid && ( ( puppet.GetHitReactionComponent().GetHitStimEvent().hitSource == ( ( Int32 )( EAIHitSource.MeleeSharp ) ) ) || ( puppet.GetHitReactionComponent().GetHitStimEvent().hitSource == ( ( Int32 )( EAIHitSource.MeleeBlunt ) ) ) ) )
		{
			duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "stagger_melee_duration" );
		}
		else if( valid && ( ( puppet.GetHitReactionComponent().GetHitStimEvent().hitSource == ( ( Int32 )( EAIHitSource.QuickMelee ) ) ) && ( puppet.GetHitReactionComponent().GetHitStimEvent().hitDirection == 4 ) ) )
		{
			duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "quickMelee_duration" );
		}
		else
		{
			duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "stagger_ranged_duration" );
		}
		if( duration > 0.0 )
		{
			return duration;
		}
		return 1.5;
	}

	private override function OnUpdate( context : ScriptExecutionContext, aiTime : Float )
	{
		if( !( m_onUpdateCompleted ) && IsTumbleStagger( GetHitReactionComponent( context ).GetLastHitReactionData() ) )
		{
			if( !( m_tumble ) && ( aiTime > ( m_activationTimeStamp + 0.40000001 ) ) )
			{
				( ( ScriptedPuppet )( ScriptExecutionContext.GetOwner( context ) ) ).GetPuppetStateBlackboard().SetBool( GetAllBlackboardDefs().AIAction.ownerInTumble, true );
				m_tumble = true;
			}
			else if( m_tumble && ( aiTime > ( m_activationTimeStamp + 2.0999999 ) ) )
			{
				( ( ScriptedPuppet )( ScriptExecutionContext.GetOwner( context ) ) ).GetPuppetStateBlackboard().SetBool( GetAllBlackboardDefs().AIAction.ownerInTumble, false );
				m_onUpdateCompleted = true;
				m_tumble = false;
			}
		}
	}

	private override function OnDeactivate( context : ScriptExecutionContext )
	{
		if( m_tumble )
		{
			( ( ScriptedPuppet )( ScriptExecutionContext.GetOwner( context ) ) ).GetPuppetStateBlackboard().SetBool( GetAllBlackboardDefs().AIAction.ownerInTumble, false );
			m_tumble = false;
		}
	}

}

class KnockdownReactionTask extends AIHitReactionTask
{
	var m_tweakDBPackage : TweakDBID;

	protected export override function Activate( context : ScriptExecutionContext )
	{
		StatusEffectHelper.ApplyStatusEffect( ScriptExecutionContext.GetOwner( context ), T"BaseStatusEffect.HitReactionKnockdown" );
		if( ScriptedPuppet.CanRagdoll( ScriptExecutionContext.GetOwner( context ) ) )
		{
			StatusEffectHelper.ApplyStatusEffect( ScriptExecutionContext.GetOwner( context ), T"BaseStatusEffect.UncontrolledMovement_Default" );
		}
		GameObject.PlayVoiceOver( ScriptExecutionContext.GetOwner( context ), 'hit_reaction_heavy', 'Scripts:KnockdownReactionTask' );
		super.Activate( context );
	}

	private override function GetHitReactionType() : animHitReactionType
	{
		return animHitReactionType.Knockdown;
	}

	private override function GetDesiredHitReactionDuration( context : ScriptExecutionContext ) : Float
	{
		var duration : Float;
		m_tweakDBPackage = T"AIGeneralSettings";
		duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "knockdown_duration" );
		if( duration > 0.0 )
		{
			return duration;
		}
		return 4.0;
	}

}

class PainReactionTask extends AIHitReactionTask
{
	protected var m_weaponOverride : AnimFeature_WeaponOverride;

	protected override function Activate( context : ScriptExecutionContext )
	{
		m_weaponOverride = new AnimFeature_WeaponOverride;
		m_weaponOverride.state = 1;
		AnimationControllerComponent.ApplyFeatureToReplicate( ScriptExecutionContext.GetOwner( context ), 'weaponOverride', m_weaponOverride );
		if( ScriptedPuppet.CanRagdoll( ScriptExecutionContext.GetOwner( context ) ) )
		{
			StatusEffectHelper.ApplyStatusEffect( ScriptExecutionContext.GetOwner( context ), T"BaseStatusEffect.UncontrolledMovement_RagdollOffLedge" );
		}
		super.Activate( context );
	}

	protected override function Deactivate( context : ScriptExecutionContext )
	{
		m_weaponOverride.state = 0;
		AnimationControllerComponent.ApplyFeatureToReplicate( ScriptExecutionContext.GetOwner( context ), 'weaponOverride', m_weaponOverride );
		super.Deactivate( context );
	}

	private override function GetHitReactionType() : animHitReactionType
	{
		return animHitReactionType.Pain;
	}

	private override function GetDesiredHitReactionDuration( context : ScriptExecutionContext ) : Float
	{
		return 2.20000005;
	}

}

class GuardbreakReactionTask extends AIHitReactionTask
{
	var m_tweakDBPackage : TweakDBID;

	private override function GetHitReactionType() : animHitReactionType
	{
		return animHitReactionType.GuardBreak;
	}

	private override function GetDesiredHitReactionDuration( context : ScriptExecutionContext ) : Float
	{
		var duration : Float;
		m_tweakDBPackage = T"AIGeneralSettings";
		duration = AITweakParams.GetFloatFromTweak( m_tweakDBPackage, "guardbreak_duration" );
		if( duration > 0.0 )
		{
			return duration;
		}
		return 1.0;
	}

}

class BlockReactionTask extends AIbehaviortaskScript
{

	protected override function Deactivate( context : ScriptExecutionContext )
	{
		GetPuppet( context ).GetHitReactionComponent().UpdateBlockCount();
	}

}

class ParryReactionTask extends AIbehaviortaskScript
{

	protected override function Deactivate( context : ScriptExecutionContext )
	{
		GetPuppet( context ).GetHitReactionComponent().UpdateParryCount();
	}

}

class DodgeReactionTask extends AIbehaviortaskScript
{

	protected override function Deactivate( context : ScriptExecutionContext )
	{
		GetPuppet( context ).GetHitReactionComponent().UpdateDodgeCount();
	}

}

class BlockReactionFlag extends AIbehaviortaskScript
{
	var target : weak< GameObject >;

	protected export override function Activate( context : ScriptExecutionContext )
	{
		GetPuppet( context ).GetStimReactionComponent().GetPuppetReactionBlackboard().SetBool( GetAllBlackboardDefs().PuppetReaction.blockReactionFlag, true );
	}

	protected export override function Deactivate( context : ScriptExecutionContext )
	{
		GetPuppet( context ).GetStimReactionComponent().GetPuppetReactionBlackboard().SetBool( GetAllBlackboardDefs().PuppetReaction.blockReactionFlag, false );
	}

}

class BroadcastCombatHitStim extends AIbehaviortaskScript
{

	protected override function Activate( context : ScriptExecutionContext )
	{
		var broadcaster : StimBroadcasterComponent;
		var investigateData : stimInvestigateData;
		var target : weak< GameObject >;
		target = GetPuppet( context ).GetHitReactionComponent().GetHitSource();
		broadcaster = target.GetStimBroadcasterComponent();
		if( !( broadcaster ) )
		{
			target = GetPuppet( context ).GetHitReactionComponent().GetHitInstigator();
			broadcaster = target.GetStimBroadcasterComponent();
		}
		if( broadcaster )
		{
			investigateData.skipReactionDelay = true;
			investigateData.skipInitialAnimation = true;
			broadcaster.SendDrirectStimuliToTarget( ScriptExecutionContext.GetOwner( context ), gamedataStimType.CombatHit, GetPuppet( context ), investigateData );
		}
	}

}

