class HitReactionBehaviorData extends IScriptable
{
	var m_hitReactionType : animHitReactionType;
	var m_hitReactionActivationTimeStamp : Float;
	var m_hitReactionDuration : Float;

	public const function GetHitReactionDeactivationTimeStamp() : Float
	{
		return m_hitReactionActivationTimeStamp + m_hitReactionDuration;
	}

}

class HitReactionCumulativeDamageUpdate extends Event
{
	var m_prevUpdateTime : Float;
}

class HitReactionRequest extends Event
{
	var hitEvent : gameHitEvent;
}

class HitReactionStopMotionExtraction extends Event
{
}

struct ScriptHitData
{
	var animVariation : Int32;
	var attackDirection : Int32;
	var hitBodyPart : Int32;
}

class NPCHealthListener extends ScriptStatPoolsListener
{
	var npc : weak< NPCPuppet >;

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		npc.GetHitReactionComponent().UpdateOwnerHealthData( newValue );
	}

}

class NPCHitReactionComponentStatsListener extends ScriptStatsListener
{
	var npc : weak< NPCPuppet >;

	public override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		switch( statType )
		{
			case gamedataStatType.HasExtendedHitReactionImmunity:
				npc.GetHitReactionComponent().UpdateOwnerExtendedHitReactionImmunityData( total );
			break;
			case gamedataStatType.HasMeleeHitReactionAndTakedownResistance:
				npc.GetHitReactionComponent().UpdateOwnerMeleeHitReactionResistanceData( total );
			break;
			case gamedataStatType.KnockdownDamageThresholdImpulse:
				npc.GetHitReactionComponent().UpdateOwnerKnockdownDamageThresholdImpulseData( total );
			break;
			case gamedataStatType.ImpactDamageThresholdInCover:
				npc.GetHitReactionComponent().UpdateOwnerImpactDamageThresholdInCoverData( total );
			break;
			case gamedataStatType.StaggerDamageThresholdInCover:
				npc.GetHitReactionComponent().UpdateOwnerStaggerDamageThresholdInCoverData( total );
			break;
			case gamedataStatType.KnockdownDamageThresholdInCover:
				npc.GetHitReactionComponent().UpdateOwnerKnockdownDamageThresholdInCoverData( total );
			break;
			case gamedataStatType.ImpactDamageThreshold:
				npc.GetHitReactionComponent().UpdateOwnerImpactDamageThresholdData( total );
			break;
			case gamedataStatType.StaggerDamageThreshold:
				npc.GetHitReactionComponent().UpdateOwnerStaggerDamageThresholdData( total );
			break;
			case gamedataStatType.KnockdownDamageThreshold:
				npc.GetHitReactionComponent().UpdateOwnerKnockdownDamageThresholdData( total );
			break;
			case gamedataStatType.KnockdownImmunity:
				npc.GetHitReactionComponent().UpdateOwnerKnockdownImmunityData( total );
			break;
			case gamedataStatType.CanDropWeapon:
				npc.GetHitReactionComponent().UpdateOwnerCanDropWeaponData( total );
			break;
			case gamedataStatType.IsInvulnerable:
				npc.GetHitReactionComponent().UpdateOwnerIsInvulnerableData( total );
			break;
			case gamedataStatType.CanBlock:
				npc.GetHitReactionComponent().UpdateOwnerCanBlockData( total );
			break;
			case gamedataStatType.HasKerenzikov:
				npc.GetHitReactionComponent().UpdateOwnerHasKerenzikovData( total );
			break;
			default:
				return;
		}
	}

}

class HitReactionComponent extends AIMandatoryComponents
{
	protected var m_ownerNPC : weak< NPCPuppet >;
	protected var m_ownerPuppet : weak< ScriptedPuppet >;
	protected var m_ownerWeapon : weak< WeaponObject >;
	protected var m_ownerID : EntityID;
	protected var m_statsSystem : StatsSystem;
	protected var m_ownerIsMassive : Bool;
	protected var m_impactDamageDuration : Float;
	default m_impactDamageDuration = 0.2;
	protected var m_staggerDamageDuration : Float;
	default m_staggerDamageDuration = 0.4;
	protected var m_impactDamageDurationMelee : Float;
	default m_impactDamageDurationMelee = 0.25;
	protected var m_staggerDamageDurationMelee : Float;
	default m_staggerDamageDurationMelee = 1.5;
	protected var m_knockdownDamageDuration : Float;
	default m_knockdownDamageDuration = 2.5;
	protected var m_defeatedMinDuration : Float;
	protected var m_previousHitTime : Float;
	protected var m_reactionType : animHitReactionType;
	protected var m_animHitReaction : AnimFeature_HitReactionsData;
	protected var m_lastAnimHitReaction : AnimFeature_HitReactionsData;
	protected var m_hitReactionAction : ActionHitReactionScriptProxy;
	protected var m_previousAnimHitReactionArray : array< ScriptHitData >;
	protected var m_lastHitReactionPlayed : EAILastHitReactionPlayed;
	protected var m_hitShapeData : HitShapeData;
	protected var m_animVariation : Int32;
	protected var m_specificHitTimeout : Float;
	protected var m_quickMeleeCooldown : Float;
	protected var m_dismembermentBodyPartDamageThreshold : array< Float >;
	protected var m_woundedBodyPartDamageThreshold : array< Float >;
	protected var m_defeatedBodyPartDamageThreshold : array< Float >;
	protected var m_defeatedDamageThreshold : Float;
	protected var m_impactDamageThreshold : Float;
	protected var m_staggerDamageThreshold : Float;
	protected var m_knockdownDamageThreshold : Float;
	protected var m_knockdownImpulseThreshold : Float;
	protected var m_immuneToKnockDown : Bool;
	protected var m_hitComboReset : Float;
	default m_hitComboReset = 2.f;
	protected var m_physicalImpulseReset : Float;
	default m_physicalImpulseReset = 0.5f;
	protected var m_guardBreakImpulseReset : Float;
	default m_guardBreakImpulseReset = 5.f;
	protected var m_cumulatedDamages : Float;
	protected var m_bodyPartWoundCumulatedDamages : array< Float >;
	protected var m_bodyPartDismemberCumulatedDamages : array< Float >;
	protected var m_attackerWeaponKnockdownImpulse : Float;
	protected var m_attackerWeaponKnockdownImpulseForEvade : Float;
	protected var m_attackerWeaponKnockdownImpulseForEvadeCumulation : Float;
	protected var m_ownerWeaponKnockdownImpulseForEvade : Float;
	protected var m_cumulatedPhysicalImpulse : Float;
	protected var m_cumulatedGuardBreakImpulse : Float;
	protected var m_cumulatedEvadeBreakImpulse : Float;
	protected var m_ragdollImpulse : Float;
	protected var m_ragdollInfluenceRadius : Float;
	default m_ragdollInfluenceRadius = 5.f;
	protected var m_hitIntensity : EAIHitIntensity;
	protected var m_previousMeleeHitTimeStamp : Float;
	default m_previousMeleeHitTimeStamp = -1.f;
	protected var m_previousRangedHitTimeStamp : Float;
	default m_previousRangedHitTimeStamp = -1.f;
	protected var m_previousBlockTimeStamp : Float;
	default m_previousBlockTimeStamp = -1.f;
	protected var m_previousParryTimeStamp : Float;
	default m_previousParryTimeStamp = -1.f;
	protected var m_previousDodgeTimeStamp : Float;
	protected var m_previousRagdollTimeStamp : Float;
	protected var m_previousHackStaggerTimeStamp : Float;
	default m_previousHackStaggerTimeStamp = -1.f;
	protected var m_previousHackImpactTimeStamp : Float;
	default m_previousHackImpactTimeStamp = -1.f;
	protected var m_blockCount : Int32;
	protected var m_parryCount : Int32;
	protected var m_dodgeCount : Int32;
	var m_hitCount : Uint32;
	protected var m_defeatedHasBeenPlayed : Bool;
	protected var m_defeatedRegisteredTime : Float;
	protected var m_deathHasBeenPlayed : Bool;
	protected var m_deathRegisteredTime : Float;
	protected var m_extendedDeathRegisteredTime : Float;
	protected var m_extendedDeathDelayRegisteredTime : Float;
	protected var m_extendedHitReactionRegisteredTime : Float;
	protected var m_extendedHitReactionDelayRegisteredTime : Float;
	protected var m_scatteredGuts : Bool;
	protected const var m_cumulativeDamageUpdateInterval : Float;
	default m_cumulativeDamageUpdateInterval = 0.25f;
	protected var m_cumulativeDamageUpdateRequested : Bool;
	default m_cumulativeDamageUpdateRequested = false;
	protected var m_currentStimId : Uint32;
	protected var m_attackData : AttackData;
	protected var m_attackDirectionToInt : Int32;
	protected var m_hitPosition : Vector4;
	protected var m_hitDirection : Vector4;
	protected var m_hitDirectionToInt : Int32;
	protected var m_overridenHitDirection : Bool;
	protected var m_lastHitReactionBehaviorData : HitReactionBehaviorData;
	protected var m_lastStimName : CName;
	protected var m_deathStimName : CName;
	protected var m_meleeHitCount : Int32;
	protected var m_strongMeleeHitCount : Int32;
	protected var m_meleeBaseMaxHitChain : Int32;
	protected var m_rangedBaseMaxHitChain : Int32;
	protected var m_maxHitChainForMelee : Int32;
	default m_maxHitChainForMelee = 2;
	protected var m_maxHitChainForRanged : Int32;
	default m_maxHitChainForRanged = 2;
	protected var m_isAlive : Bool;
	protected var m_frameDamageHealthFactor : Float;
	protected var m_hitCountData : Float[ 100 ];
	protected var m_hitCountArrayEnd : Int32;
	default m_hitCountArrayEnd = 100;
	protected var m_hitCountArrayCurrent : Int32;
	protected var m_allowDefeatedOnCompanion : Bool;
	protected var m_baseCumulativeDamagesDecreaser : Float;
	protected var m_blockCountInterval : Float;
	protected var m_dodgeCountInterval : Float;
	protected var m_globalHitTimer : Float;
	protected var m_hasMantisBladesinRecord : Bool;
	private var m_indicatorEnabledBlackboardId : CallbackHandle;
	private var m_hitIndicatorEnabled : Bool;
	private var m_hasBeenWounded : Bool;
	private var m_inWorkspot : Bool;
	private var m_inCover : Bool;
	private var m_healthListener : NPCHealthListener;
	private var m_hitReactionComponentStatsListener : NPCHitReactionComponentStatsListener;
	private var m_currentHealth : Float;
	private var m_totalHealth : Float;
	private var m_totalStamina : Float;
	private var m_currentCanDropWeapon : Float;
	private var m_currentExtendedHitReactionImmunity : Float;
	private var m_currentIsInvulnerable : Float;
	private var m_currentDefeatedDamageThreshold : Float;
	private var m_currentImpactDamageThreshold : Float;
	private var m_currentImpactDamageThresholdInCover : Float;
	private var m_currentKnockdownDamageThreshold : Float;
	private var m_currentKnockdownDamageThresholdImpulse : Float;
	private var m_currentKnockdownDamageThresholdInCover : Float;
	private var m_currentKnockdownImmunity : Float;
	private var m_currentMeleeHitReactionResistance : Float;
	private var m_currentStaggerDamageThreshold : Float;
	private var m_currentStaggerDamageThresholdInCover : Float;
	private var m_currentCanBlock : Float;
	private var m_currentHasKerenzikov : Float;
	private var m_dismemberExecuteHealthRange : Vector2;
	private var m_dismemberExecuteDistanceRange : Vector2;
	private var m_executeDismembered : Bool;
	default m_executeDismembered = false;
	private var m_attackIsValidBodyPerk : Bool;
	default m_attackIsValidBodyPerk = false;
	private var m_invalidForExecuteDismember : Bool;
	default m_invalidForExecuteDismember = false;

	protected function UpdateDBParams( data : ScriptTaskData )
	{
		GetDBParameters();
	}

	protected function GetDBParameters()
	{
		var ownerID : StatsObjectID;
		var primaryWeaponID : ItemID;
		var primaryItemInRecord : NPCEquipmentItem_Record;
		ownerID = GetOwner().GetEntityID();
		m_totalHealth = m_statsSystem.GetStatValue( ownerID, gamedataStatType.Health );
		m_totalStamina = m_statsSystem.GetStatValue( ownerID, gamedataStatType.Stamina );
		UpdateOwnerHealthData( GameInstance.GetStatPoolsSystem( m_ownerNPC.GetGame() ).GetStatPoolValue( ownerID, gamedataStatPoolType.Health ) );
		m_impactDamageDuration = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitTimerAfterImpact );
		m_staggerDamageDuration = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitTimerAfterStagger );
		m_knockdownDamageDuration = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitTimerAfterKnockdown );
		m_impactDamageDurationMelee = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitTimerAfterImpactMelee );
		m_staggerDamageDurationMelee = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitTimerAfterStaggerMelee );
		m_defeatedMinDuration = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitTimerAfterDefeated );
		m_hitComboReset = TDB.GetFloat( T"GlobalStats.ReactionHitChainReset.value", 10.0 );
		m_frameDamageHealthFactor = m_statsSystem.GetStatValue( ownerID, gamedataStatType.HitReactionDamageHealthFactor );
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.Head ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundHeadDamageThreshold ) ) * 0.01;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ChestLeft ) ) ] = m_totalHealth;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundLArmDamageThreshold ) ) * 0.01;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundLArmDamageThreshold ) ) * 0.01;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ChestRight ) ) ] = m_totalHealth;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundRArmDamageThreshold ) ) * 0.01;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundRArmDamageThreshold ) ) * 0.01;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.Abdomen ) ) ] = m_totalHealth;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.LegLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundLLegDamageThreshold ) ) * 0.01;
		m_woundedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.LegRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.WoundRLegDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.Head ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismHeadDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ChestLeft ) ) ] = m_totalHealth;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismLArmDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismLArmDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ChestRight ) ) ] = m_totalHealth;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismRArmDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismRArmDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.Abdomen ) ) ] = m_totalHealth;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.LegLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismLLegDamageThreshold ) ) * 0.01;
		m_dismembermentBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.LegRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DismRLegDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.Head ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedHeadDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ChestLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedLArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedLArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedLArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ChestRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedRArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedRArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedRArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.Abdomen ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedLArmDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.LegLeft ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedLLegDamageThreshold ) ) * 0.01;
		m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( EHitReactionZone.LegRight ) ) ] = ( m_totalHealth * m_statsSystem.GetStatValue( ownerID, gamedataStatType.DefeatedRLegDamageThreshold ) ) * 0.01;
		m_guardBreakImpulseReset = AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "blockResetCooldown" );
		m_dismemberExecuteHealthRange.X = AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "DismemberingExecute.missingHealthStart" );
		m_dismemberExecuteHealthRange.Y = AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "DismemberingExecute.missingHealthEnd" );
		m_dismemberExecuteDistanceRange.X = AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "DismemberingExecute.distanceCloseRange" );
		m_dismemberExecuteDistanceRange.Y = AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "DismemberingExecute.distanceFarRange" );
		if( AIActionHelper.CheckAbility( m_ownerNPC, TweakDBInterface.GetGameplayAbilityRecord( T"Ability.IsTier1Archetype" ) ) )
		{
			m_meleeBaseMaxHitChain = AITweakParams.GetIntFromTweak( T"AIGeneralSettings", "hitChainBeforeDodgeForMeleeTier1" );
			m_rangedBaseMaxHitChain = AITweakParams.GetIntFromTweak( T"AIGeneralSettings", "hitChainBeforeDodgeForRangedTier1" );
		}
		else if( AIActionHelper.CheckAbility( m_ownerNPC, TweakDBInterface.GetGameplayAbilityRecord( T"Ability.IsTier2Archetype" ) ) )
		{
			m_meleeBaseMaxHitChain = AITweakParams.GetIntFromTweak( T"AIGeneralSettings", "hitChainBeforeDodgeForMeleeTier2" );
			m_rangedBaseMaxHitChain = AITweakParams.GetIntFromTweak( T"AIGeneralSettings", "hitChainBeforeDodgeForRangedTier2" );
			m_guardBreakImpulseReset += AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "tier2BlockCooldownModifier" );
		}
		else if( AIActionHelper.CheckAbility( m_ownerNPC, TweakDBInterface.GetGameplayAbilityRecord( T"Ability.IsTier3Archetype" ) ) )
		{
			m_meleeBaseMaxHitChain = AITweakParams.GetIntFromTweak( T"AIGeneralSettings", "hitChainBeforeDodgeForMeleeTier3" );
			m_rangedBaseMaxHitChain = AITweakParams.GetIntFromTweak( T"AIGeneralSettings", "hitChainBeforeDodgeForRangedTier3" );
			m_guardBreakImpulseReset += AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "tier3BlockCooldownModifier" );
		}
		if( m_ownerNPC.IsBoss() || m_ownerNPC.GetNPCRarity() == gamedataNPCRarity.MaxTac )
		{
			m_guardBreakImpulseReset += AITweakParams.GetFloatFromTweak( T"AIGeneralSettings", "bossBlockCooldownModifier" );
		}
		m_allowDefeatedOnCompanion = AITweakParams.GetBoolFromTweak( T"AIGeneralSettings.followers", "allowDefeated" );
		m_currentDefeatedDamageThreshold = AITweakParams.GetFloatFromTweak( T"AIGeneralSettings.followers", "defeatedDamageThreshold" );
		m_baseCumulativeDamagesDecreaser = TDB.GetFloat( T"GlobalStats.CumulativeDmgDecreaser.value", 2.0 );
		m_blockCountInterval = TDB.GetFloat( T"GlobalStats.BlockCountInterval.value", 2.0 );
		m_dodgeCountInterval = TDB.GetFloat( T"GlobalStats.DodgeCountInterval.value", 1.5 );
		m_globalHitTimer = TDB.GetFloat( T"GlobalStats.GlobalHitTimer.value", 1.0 );
		primaryItemInRecord = ( ( NPCEquipmentItem_Record )( TweakDBInterface.GetCharacterRecord( m_ownerNPC.GetRecordID() ).PrimaryEquipment().GetEquipmentItemsItem( 0 ) ) );
		AIActionTransactionSystem.GetItemID( ( ( ScriptedPuppet )( GetOwner() ) ), primaryItemInRecord.Item(), primaryItemInRecord.OnBodySlot().GetID(), primaryWeaponID );
		m_ownerWeaponKnockdownImpulseForEvade = m_statsSystem.GetStatValue( m_ownerWeapon.GetEntityID(), gamedataStatType.BaseKnockdownImpulse );
		m_hasMantisBladesinRecord = TDB.GetWeaponItemRecord( ItemID.GetTDBID( primaryWeaponID ) ).ItemType().Type() == gamedataItemType.Cyb_MantisBlades;
		m_healthListener = new NPCHealthListener;
		m_healthListener.npc = ( ( NPCPuppet )( GetOwner() ) );
		GameInstance.GetStatPoolsSystem( GetOwner().GetGame() ).RequestRegisteringListener( ownerID, gamedataStatPoolType.Health, m_healthListener );
		m_hitReactionComponentStatsListener = new NPCHitReactionComponentStatsListener;
		m_hitReactionComponentStatsListener.npc = ( ( NPCPuppet )( GetOwner() ) );
		m_statsSystem.RegisterListener( ownerID, m_hitReactionComponentStatsListener );
		UpdateOwnerExtendedHitReactionImmunityData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.HasExtendedHitReactionImmunity ) );
		UpdateOwnerMeleeHitReactionResistanceData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.HasMeleeHitReactionAndTakedownResistance ) );
		UpdateOwnerKnockdownDamageThresholdImpulseData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.KnockdownDamageThresholdImpulse ) );
		UpdateOwnerImpactDamageThresholdInCoverData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.ImpactDamageThresholdInCover ) );
		UpdateOwnerStaggerDamageThresholdInCoverData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.StaggerDamageThresholdInCover ) );
		UpdateOwnerKnockdownDamageThresholdInCoverData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.KnockdownDamageThresholdInCover ) );
		UpdateOwnerImpactDamageThresholdData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.ImpactDamageThreshold ) );
		UpdateOwnerStaggerDamageThresholdData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.StaggerDamageThreshold ) );
		UpdateOwnerKnockdownDamageThresholdData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.KnockdownDamageThreshold ) );
		UpdateOwnerKnockdownImmunityData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.KnockdownImmunity ) );
		UpdateOwnerCanDropWeaponData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.CanDropWeapon ) );
		UpdateOwnerIsInvulnerableData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.IsInvulnerable ) );
		UpdateOwnerCanBlockData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.CanBlock ) );
		UpdateOwnerHasKerenzikovData( m_statsSystem.GetStatValue( ownerID, gamedataStatType.HasKerenzikov ) );
	}

	public const final function GetMeleeMaxHitChain() : Int32
	{
		return m_maxHitChainForMelee;
	}

	public const final function GetRangedMaxHitChain() : Int32
	{
		return m_maxHitChainForRanged;
	}

	public const final function GetDeathHasBeenPlayed() : Bool
	{
		return m_deathHasBeenPlayed;
	}

	public const final function GetDefeatedHasBeenPlayed() : Bool
	{
		return m_defeatedHasBeenPlayed;
	}

	public const final function IsExecutedByDismemberment() : Bool
	{
		return m_executeDismembered;
	}

	public const final function GetHitCountInCombo() : Int32
	{
		return m_meleeHitCount;
	}

	public const final function GetStrongHitCountInCombo() : Int32
	{
		return m_strongMeleeHitCount;
	}

	public const final function GetLastStimName() : CName
	{
		return m_lastStimName;
	}

	public const final function GetDeathStimName() : CName
	{
		return m_deathStimName;
	}

	public const final function GetHitReactionType() : Int32
	{
		return m_animHitReaction.hitType;
	}

	public const final function GetAttackTag() : CName
	{
		var attackTag : CName;
		var attackRecord : Attack_GameEffect_Record;
		attackRecord = ( ( Attack_GameEffect_Record )( m_attackData.GetAttackDefinition().GetRecord() ) );
		if( attackRecord )
		{
			attackTag = attackRecord.AttackTag();
		}
		return attackTag;
	}

	public const final function GetAttackType() : gamedataAttackType
	{
		return m_attackData.GetAttackType();
	}

	public const final function GetSubAttackSubType() : gamedataAttackSubtype
	{
		var attackRecord : Attack_Melee_Record;
		var attackSubTypeRecord : AttackSubtype_Record;
		attackRecord = ( ( Attack_Melee_Record )( m_attackData.GetAttackDefinition().GetRecord() ) );
		if( attackRecord )
		{
			attackSubTypeRecord = attackRecord.AttackSubtype();
			if( attackSubTypeRecord )
			{
				return attackSubTypeRecord.Type();
			}
		}
		return gamedataAttackSubtype.Invalid;
	}

	public const final function GetHitReactionData() : AnimFeature_HitReactionsData
	{
		return m_animHitReaction;
	}

	public const final function GetLastHitReactionData() : AnimFeature_HitReactionsData
	{
		return m_lastAnimHitReaction;
	}

	public const final function GetBlockCount() : Int32
	{
		if( EngineTime.ToFloat( GetSimTime() ) > ( m_previousBlockTimeStamp + m_blockCountInterval ) )
		{
			return 0;
		}
		else
		{
			return m_blockCount;
		}
	}

	public const final function GetParryCount() : Int32
	{
		if( EngineTime.ToFloat( GetSimTime() ) > ( m_previousParryTimeStamp + m_blockCountInterval ) )
		{
			return 0;
		}
		else
		{
			return m_parryCount;
		}
	}

	public const final function GetDodgeCount() : Int32
	{
		if( EngineTime.ToFloat( GetSimTime() ) > ( m_previousDodgeTimeStamp + m_dodgeCountInterval ) )
		{
			return 0;
		}
		else
		{
			return m_dodgeCount;
		}
	}

	public const final function GetCumulatedDamage() : Float
	{
		return m_cumulatedDamages;
	}

	public const final function GetLastHitReactionBehaviorData() : HitReactionBehaviorData
	{
		return m_lastHitReactionBehaviorData;
	}

	public const final function GetHitReactionProxyAction() : ActionHitReactionScriptProxy
	{
		return m_hitReactionAction;
	}

	public const final function GetLastStimID() : Uint32
	{
		return m_currentStimId;
	}

	public const final function GetHitSource() : weak< GameObject >
	{
		return m_attackData.GetSource();
	}

	public const final function GetHitInstigator() : weak< GameObject >
	{
		return m_attackData.GetInstigator();
	}

	public const final function GetHitPosition() : Vector4
	{
		return m_hitPosition;
	}

	public const final function GetHitDirection() : Vector4
	{
		return m_hitDirection;
	}

	public const final function GetAttackDirection() : Int32
	{
		return m_attackDirectionToInt;
	}

	public const final function GetCanBlock() : Bool
	{
		return m_currentCanBlock > 0.0;
	}

	public const final function GetHasKerenzikov() : Bool
	{
		return m_currentHasKerenzikov > 0.0;
	}

	public const final function GetShouldEvade() : Bool
	{
		var attackWeaponType : gamedataItemType;
		var result : Bool;
		attackWeaponType = ScriptedPuppet.GetWeaponRight( m_attackData.GetSource() ).GetWeaponRecord().ItemType().Type();
		if( ( m_attackerWeaponKnockdownImpulseForEvade > m_ownerWeaponKnockdownImpulseForEvade ) || ( ( m_ownerWeapon.GetWeaponRecord().ItemType().Type() == gamedataItemType.Wea_Fists && attackWeaponType != gamedataItemType.Wea_Fists ) && attackWeaponType != gamedataItemType.Cyb_StrongArms ) )
		{
			result = true;
		}
		if( m_cumulatedGuardBreakImpulse >= m_totalStamina )
		{
			result = true;
		}
		return result;
	}

	public final function GetOverridenHitDirection() : Vector4
	{
		var hitPosition : Vector4;
		var ownerPosition : Vector4;
		if( m_overridenHitDirection )
		{
			hitPosition = GetHitPosition();
			ownerPosition = m_ownerNPC.GetWorldPosition();
			ownerPosition.Z = hitPosition.Z;
			return Vector4.Normalize( ownerPosition - hitPosition );
		}
		return GetHitDirection();
	}

	public const final function GetHitDirectionToInt() : Int32
	{
		return m_hitDirectionToInt;
	}

	public const final function GetRagdollImpulse() : Float
	{
		return m_ragdollImpulse;
	}

	public final function IsInKnockdown() : Bool
	{
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( GetOwner(), gamedataStatusEffectType.Knockdown ) )
		{
			return true;
		}
		if( ( m_specificHitTimeout > GetCurrentTime() ) && m_lastHitReactionPlayed == EAILastHitReactionPlayed.Knockdown )
		{
			return true;
		}
		return false;
	}

	public function GetHitAnimationInProgress() : Bool
	{
		return m_specificHitTimeout > GetCurrentTime();
	}

	public function UpdateOwnerHealthData( Value : Float )
	{
		m_currentHealth = Value;
	}

	public function UpdateOwnerCanDropWeaponData( Value : Float )
	{
		m_currentCanDropWeapon = Value;
	}

	public function UpdateOwnerExtendedHitReactionImmunityData( Value : Float )
	{
		m_currentExtendedHitReactionImmunity = Value;
	}

	public function UpdateOwnerIsInvulnerableData( Value : Float )
	{
		m_currentIsInvulnerable = Value;
	}

	public function UpdateOwnerImpactDamageThresholdData( Value : Float )
	{
		m_currentImpactDamageThreshold = Value;
	}

	public function UpdateOwnerImpactDamageThresholdInCoverData( Value : Float )
	{
		m_currentImpactDamageThresholdInCover = Value;
	}

	public function UpdateOwnerKnockdownDamageThresholdData( Value : Float )
	{
		m_currentKnockdownDamageThreshold = Value;
	}

	public function UpdateOwnerKnockdownDamageThresholdImpulseData( Value : Float )
	{
		m_currentKnockdownDamageThresholdImpulse = Value;
	}

	public function UpdateOwnerKnockdownDamageThresholdInCoverData( Value : Float )
	{
		m_currentKnockdownDamageThresholdInCover = Value;
	}

	public function UpdateOwnerKnockdownImmunityData( Value : Float )
	{
		m_currentKnockdownImmunity = Value;
	}

	public function UpdateOwnerMeleeHitReactionResistanceData( Value : Float )
	{
		m_currentMeleeHitReactionResistance = Value;
	}

	public function UpdateOwnerStaggerDamageThresholdData( Value : Float )
	{
		m_currentStaggerDamageThreshold = Value;
	}

	public function UpdateOwnerStaggerDamageThresholdInCoverData( Value : Float )
	{
		m_currentStaggerDamageThresholdInCover = Value;
	}

	public function UpdateOwnerCanBlockData( Value : Float )
	{
		m_currentCanBlock = Value;
	}

	public function UpdateOwnerHasKerenzikovData( Value : Float )
	{
		m_currentHasKerenzikov = Value;
	}

	public function UpdateDeathHasBeenPlayed()
	{
		m_deathHasBeenPlayed = true;
	}

	public function UpdateLastStimID() : Uint32
	{
		m_currentStimId += ( ( Uint32 )( 1 ) );
		return m_currentStimId;
	}

	public function ResetHitCount()
	{
		m_meleeHitCount = 0;
		m_strongMeleeHitCount = 0;
	}

	public function SetLastStimName( laststimName : CName )
	{
		m_lastStimName = laststimName;
	}

	public function SetDeathStimName( laststimName : CName )
	{
		m_deathStimName = laststimName;
	}

	public function UpdateBlockCount()
	{
		m_blockCount += 1;
		if( GetCurrentTime() > ( m_previousBlockTimeStamp + m_blockCountInterval ) )
		{
			m_blockCount = 1;
		}
		m_previousBlockTimeStamp = GetCurrentTime();
	}

	public function UpdateParryCount()
	{
		m_parryCount += 1;
		if( GetCurrentTime() > ( m_previousParryTimeStamp + m_blockCountInterval ) )
		{
			m_parryCount = 1;
		}
		m_previousParryTimeStamp = GetCurrentTime();
	}

	public function UpdateDodgeCount()
	{
		m_dodgeCount += 1;
		if( GetCurrentTime() > ( m_previousDodgeTimeStamp + m_dodgeCountInterval ) )
		{
			m_dodgeCount = 1;
		}
		m_previousDodgeTimeStamp = GetCurrentTime();
	}

	private function GetHealthPecentageNormalized() : Float
	{
		return GetOwnerCurrentHealth() * 0.01;
	}

	private function GetFrameDamage() : Float
	{
		var frameDamage : Float;
		var factor : Float;
		factor = 1.0;
		frameDamage = m_ownerNPC.GetTotalFrameDamage();
		if( m_frameDamageHealthFactor > 0.0 )
		{
			factor = 1.0 + ( ( 1.0 - GetHealthPecentageNormalized() ) * m_frameDamageHealthFactor );
		}
		return frameDamage * factor;
	}

	private function GetPhysicalImpulse( attackData : AttackData, hitPosition : Vector4, out frameImpulse : Float ) : Float
	{
		var finalImpulse : Float;
		var attackWeaponID : StatsObjectID;
		var maxValue : Float;
		var attackSpeed : Float;
		var projectilesNum : Float;
		var inAir : Bool;
		var highSpeedHit : Bool;
		if( attackData.GetWeapon() )
		{
			attackWeaponID = attackData.GetWeapon().GetEntityID();
		}
		if( ( m_previousHitTime + m_physicalImpulseReset ) < GetCurrentTime() )
		{
			m_cumulatedPhysicalImpulse = 0.0;
		}
		if( ( m_previousHitTime + m_guardBreakImpulseReset ) < GetCurrentTime() )
		{
			m_cumulatedGuardBreakImpulse = 0.0;
			m_cumulatedEvadeBreakImpulse = 0.0;
		}
		m_attackerWeaponKnockdownImpulseForEvade = m_statsSystem.GetStatValue( attackWeaponID, gamedataStatType.BaseKnockdownImpulse );
		attackSpeed = m_statsSystem.GetStatValue( attackWeaponID, gamedataStatType.AttackSpeed );
		if( attackSpeed == 0.0 )
		{
			attackSpeed = 1.0;
		}
		m_attackerWeaponKnockdownImpulseForEvadeCumulation = m_statsSystem.GetStatValue( attackWeaponID, gamedataStatType.EvadeImpulse );
		m_attackerWeaponKnockdownImpulseForEvadeCumulation *= ( 1.0 / attackSpeed );
		m_attackerWeaponKnockdownImpulse = m_statsSystem.GetStatValue( attackWeaponID, gamedataStatType.KnockdownImpulse );
		frameImpulse = m_attackerWeaponKnockdownImpulse;
		inAir = m_ownerNPC.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().PuppetState.InAirAnimation );
		highSpeedHit = m_attackData.HasFlag( hitFlag.HighSpeedMelee );
		if( ( ( highSpeedHit || inAir ) || AttackData.IsStrongMelee( m_attackData.GetAttackType() ) ) || AttackData.IsReflect( m_attackData.GetAttackType() ) )
		{
			if( highSpeedHit )
			{
				frameImpulse *= 2.67000008;
				maxValue = frameImpulse;
				m_ragdollImpulse = maxValue;
			}
			else if( AttackData.IsQuickMelee( m_attackData.GetAttackType() ) )
			{
				frameImpulse *= 0.5;
				maxValue = frameImpulse;
				m_ragdollImpulse = maxValue;
			}
			else if( AttackData.IsMelee( m_attackData.GetAttackType() ) )
			{
				m_attackerWeaponKnockdownImpulseForEvadeCumulation *= 1.66999996;
				m_ragdollImpulse = frameImpulse;
				frameImpulse *= 1.66999996;
				maxValue = frameImpulse;
			}
			else
			{
				frameImpulse *= 1.66999996;
				maxValue = frameImpulse;
				m_ragdollImpulse = maxValue;
			}
		}
		else
		{
			maxValue = 1.5 * frameImpulse;
			m_ragdollImpulse = frameImpulse;
		}
		if( AttackData.IsRangedOrDirect( attackData.GetAttackType() ) )
		{
			frameImpulse *= DamageSystem.GetEffectiveRangeModifierForWeapon( attackData, hitPosition, m_statsSystem );
			projectilesNum = m_statsSystem.GetStatValue( attackWeaponID, gamedataStatType.ProjectilesPerShot );
			projectilesNum = ClampF( projectilesNum, 1.0, 5.0 );
			frameImpulse /= projectilesNum;
		}
		if( ( frameImpulse + m_cumulatedPhysicalImpulse ) > maxValue )
		{
			finalImpulse = maxValue - m_cumulatedPhysicalImpulse;
		}
		else
		{
			finalImpulse = frameImpulse;
		}
		return ( ( finalImpulse > 0.0 ) ? ( finalImpulse ) : ( 0.0 ) );
	}

	private function GetFrameWoundsDamage() : Float
	{
		return m_ownerNPC.GetTotalFrameWoundsDamage();
	}

	private function GetFrameDismembermentDamage() : Float
	{
		return m_ownerNPC.GetTotalFrameDismembermentDamage();
	}

	private function GetOwnerHPPercentage() : Float
	{
		return GameInstance.GetStatPoolsSystem( m_ownerNPC.GetGame() ).GetStatPoolValue( m_ownerID, gamedataStatPoolType.Health );
	}

	protected function GetHitShapeUserData() : HitShapeUserDataBase
	{
		return ( ( HitShapeUserDataBase )( m_hitShapeData.userData ) );
	}

	private function ResetFrameDamage()
	{
		var evt : ResetFrameDamage;
		evt = new ResetFrameDamage;
		GetOwner().QueueEvent( evt );
	}

	protected function GetCurrentTime() : Float
	{
		return EngineTime.ToFloat( GetSimTime() );
	}

	private final function IsOwnerFacingInstigator() : Bool
	{
		var toTarget : Vector4;
		var angle : Float;
		toTarget = m_attackData.GetInstigator().GetWorldPosition() - GetOwner().GetWorldPosition();
		angle = Vector4.GetAngleBetween( toTarget, GetOwner().GetWorldForward() );
		return angle < 80.0;
	}

	private function NotifyAboutWoundedInstigated( instigator : weak< GameObject >, const bodyPart : EHitReactionZone )
	{
		var evt : WoundedInstigated;
		if( !( instigator ) )
		{
			return;
		}
		evt = new WoundedInstigated;
		evt.bodyPart = bodyPart;
		instigator.QueueEvent( evt );
	}

	private function NotifyAboutDismembermentInstigated( instigator : weak< GameObject >, const bodyPart : EHitReactionZone, const targetPosition : Vector4 )
	{
		var evt : DismembermentInstigated;
		if( !( instigator ) )
		{
			return;
		}
		evt = new DismembermentInstigated;
		evt.targetPosition = targetPosition;
		evt.attackPosition = m_attackData.GetAttackPosition();
		evt.bodyPart = bodyPart;
		evt.attackType = m_attackData.GetAttackType();
		evt.attackSubtype = m_attackData.GetAttackSubtype();
		evt.weaponRecord = m_attackData.GetWeapon().GetWeaponRecord();
		evt.attackIsExplosion = AttackData.IsExplosion( evt.attackType ) || m_attackData.HasFlag( hitFlag.Explosion );
		evt.target = m_ownerNPC;
		evt.targetIsDead = m_deathHasBeenPlayed;
		evt.timeSinceDeath = ( ( m_deathHasBeenPlayed ) ? ( GetCurrentTime() - m_deathRegisteredTime ) : ( 0.0 ) );
		evt.targetIsDefeated = m_defeatedHasBeenPlayed;
		evt.timeSinceDefeat = ( ( m_defeatedHasBeenPlayed ) ? ( GetCurrentTime() - m_defeatedRegisteredTime ) : ( 0.0 ) );
		instigator.QueueEvent( evt );
	}

	private function GetOwnerTotalHealth() : Float
	{
		return m_totalHealth;
	}

	private function GetOwnerCurrentHealth() : Float
	{
		return m_currentHealth;
	}

	private function GetIsOwnerImmuneToExtendedHitReaction() : Float
	{
		return m_currentExtendedHitReactionImmunity;
	}

	private function GetIsOwnerResistantToMeleeHitReaction() : Bool
	{
		return m_currentMeleeHitReactionResistance >= 1.0;
	}

	public export virtual function OnGameAttach()
	{
		var enumSize : Int32;
		var damageInfoBB : IBlackboard;
		m_animHitReaction = new AnimFeature_HitReactionsData;
		m_lastAnimHitReaction = new AnimFeature_HitReactionsData;
		m_hitReactionAction = new ActionHitReactionScriptProxy;
		m_hitReactionAction.Bind( GetOwner() );
		m_ownerNPC = ( ( NPCPuppet )( GetOwner() ) );
		m_ownerPuppet = ( ( ScriptedPuppet )( GetOwner() ) );
		m_ownerID = GetOwner().GetEntityID();
		m_statsSystem = GameInstance.GetStatsSystem( m_ownerPuppet.GetGame() );
		m_ownerWeapon = GameObject.GetActiveWeapon( m_ownerNPC );
		enumSize = ( ( Int32 )( EnumGetMax( 'EHitReactionZone' ) ) ) + 1;
		m_bodyPartWoundCumulatedDamages.Resize( enumSize );
		m_bodyPartDismemberCumulatedDamages.Resize( enumSize );
		m_woundedBodyPartDamageThreshold.Resize( enumSize );
		m_dismembermentBodyPartDamageThreshold.Resize( enumSize );
		m_defeatedBodyPartDamageThreshold.Resize( enumSize );
		damageInfoBB = GameInstance.GetBlackboardSystem( GetOwner().GetGame() ).Get( GetAllBlackboardDefs().UI_DamageInfo );
		m_indicatorEnabledBlackboardId = damageInfoBB.RegisterListenerBool( GetAllBlackboardDefs().UI_DamageInfo.HitIndicatorEnabled, this, 'OnHitIndicatorEnabledChanged' );
		m_hitIndicatorEnabled = damageInfoBB.GetBool( GetAllBlackboardDefs().UI_DamageInfo.HitIndicatorEnabled );
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).QueueTask( this, NULL, 'UpdateDBParams', gameScriptTaskExecutionStage.Any );
		OnGameAttached();
	}

	public export virtual function OnGameAttached() {}

	private export function OnGameDetach()
	{
		var damageInfoBB : IBlackboard;
		m_hitReactionAction = NULL;
		damageInfoBB = GameInstance.GetBlackboardSystem( GetOwner().GetGame() ).Get( GetAllBlackboardDefs().UI_DamageInfo );
		if( m_indicatorEnabledBlackboardId )
		{
			damageInfoBB.UnregisterListenerBool( GetAllBlackboardDefs().UI_DamageInfo.HitIndicatorEnabled, m_indicatorEnabledBlackboardId );
		}
		GameInstance.GetStatPoolsSystem( GetOwner().GetGame() ).RequestUnregisteringListener( GetOwner().GetEntityID(), gamedataStatPoolType.Health, m_healthListener );
		m_statsSystem.UnregisterListener( GetOwner().GetEntityID(), m_hitReactionComponentStatsListener );
	}

	protected event OnItemAddedToSlot( evt : ItemAddedToSlot )
	{
		var weaponID : EntityID;
		if( ( ( WeaponItem_Record )( TweakDBInterface.GetItemRecord( ItemID.GetTDBID( evt.GetItemID() ) ) ) ) )
		{
			weaponID = GameObject.GetActiveWeapon( m_ownerNPC ).GetEntityID();
			m_ownerWeaponKnockdownImpulseForEvade = m_statsSystem.GetStatValue( weaponID, gamedataStatType.BaseKnockdownImpulse );
		}
		m_ownerWeapon = GameObject.GetActiveWeapon( m_ownerNPC );
	}

	protected event OnDefeated( evt : DefeatedEvent )
	{
		if( ( !( m_invalidForExecuteDismember ) && m_attackIsValidBodyPerk ) && !( m_executeDismembered ) )
		{
			IncrementBodyPerkDismembermentChance( m_attackData.GetInstigator() );
		}
	}

	protected event OnDeath( evt : gameDeathEvent )
	{
		if( ( !( m_invalidForExecuteDismember ) && m_attackIsValidBodyPerk ) && !( m_executeDismembered ) )
		{
			IncrementBodyPerkDismembermentChance( m_attackData.GetInstigator() );
		}
		if( m_isAlive )
		{
			m_reactionType = animHitReactionType.Death;
			m_isAlive = false;
			SendDataToAIBehavior( m_reactionType );
		}
	}

	protected event OnHitIndicatorEnabledChanged( value : Bool )
	{
		m_hitIndicatorEnabled = value;
	}

	protected event OnResurrect( evt : ResurrectEvent )
	{
		var i : Int32;
		for( i = 0; i < m_bodyPartDismemberCumulatedDamages.Size(); i += 1 )
		{
			m_bodyPartDismemberCumulatedDamages[ i ] = 0.0;
		}
		m_cumulatedDamages = 0.0;
		m_defeatedHasBeenPlayed = false;
		m_defeatedRegisteredTime = 0.0;
	}

	protected event OnHitReactionCumulativeDamageUpdate( evt : HitReactionCumulativeDamageUpdate )
	{
		var deltaTime : Float;
		m_cumulativeDamageUpdateRequested = false;
		deltaTime = GetCurrentTime() - evt.m_prevUpdateTime;
		if( UpdateCumulatedDamages( deltaTime ) )
		{
			RequestCumulativeDamageUpdate();
		}
	}

	private function RequestCumulativeDamageUpdate()
	{
		var evt : HitReactionCumulativeDamageUpdate;
		if( m_cumulativeDamageUpdateRequested )
		{
			return;
		}
		evt = new HitReactionCumulativeDamageUpdate;
		evt.m_prevUpdateTime = GetCurrentTime();
		GameInstance.GetDelaySystem( GetOwner().GetGame() ).DelayEvent( GetOwner(), evt, m_cumulativeDamageUpdateInterval );
		m_cumulativeDamageUpdateRequested = true;
	}

	protected event OnHitReactionStopMotionExtraction( evt : HitReactionStopMotionExtraction )
	{
		if( m_hitReactionAction )
		{
			m_hitReactionAction.Stop();
		}
	}

	protected event OnRequestHitReaction( evt : HitReactionRequest )
	{
		EvaluateHit( evt.hitEvent );
	}

	protected event OnForcedHitReaction( forcedHitReaction : ForcedHitReactionEvent )
	{
		if( forcedHitReaction.hitIntensity != ( ( Int32 )( EAIHitIntensity.None ) ) )
		{
			m_animHitReaction.hitIntensity = forcedHitReaction.hitIntensity;
		}
		if( forcedHitReaction.hitSource != ( ( Int32 )( EAIHitSource.None ) ) )
		{
			SetHitReactionSource( ( ( EAIHitSource )( forcedHitReaction.hitSource ) ) );
		}
		if( forcedHitReaction.hitBodyPart != ( ( Int32 )( EAIHitBodyPart.None ) ) )
		{
			m_animHitReaction.hitBodyPart = forcedHitReaction.hitBodyPart;
		}
		if( forcedHitReaction.hitNpcMovementSpeed != -1 )
		{
			m_animHitReaction.npcMovementSpeed = forcedHitReaction.hitNpcMovementSpeed;
		}
		if( forcedHitReaction.hitDirection != ( ( Int32 )( EAIHitDirection.None ) ) )
		{
			m_animHitReaction.hitDirection = forcedHitReaction.hitDirection;
		}
		if( forcedHitReaction.hitNpcMovementDirection != -1 )
		{
			m_animHitReaction.npcMovementDirection = forcedHitReaction.hitNpcMovementDirection;
		}
		if( forcedHitReaction.hitType != -1 )
		{
			SetHitReactionType( ( ( animHitReactionType )( forcedHitReaction.hitType ) ) );
			SendDataToAIBehavior( ( ( animHitReactionType )( forcedHitReaction.hitType ) ) );
		}
		else
		{
			SetHitReactionType( animHitReactionType.Pain );
			SendDataToAIBehavior( animHitReactionType.Stagger );
		}
	}

	protected event OnForcedDeathEvent( forcedDeath : ForcedDeathEvent )
	{
		if( forcedDeath.hitIntensity != ( ( Int32 )( EAIHitIntensity.None ) ) )
		{
			m_animHitReaction.hitIntensity = forcedDeath.hitIntensity;
		}
		if( forcedDeath.hitSource != ( ( Int32 )( EAIHitSource.None ) ) )
		{
			SetHitReactionSource( ( ( EAIHitSource )( forcedDeath.hitSource ) ) );
		}
		if( forcedDeath.hitBodyPart != ( ( Int32 )( EAIHitBodyPart.None ) ) )
		{
			m_animHitReaction.hitBodyPart = forcedDeath.hitBodyPart;
		}
		if( forcedDeath.hitNpcMovementSpeed != -1 )
		{
			m_animHitReaction.npcMovementSpeed = forcedDeath.hitNpcMovementSpeed;
		}
		if( forcedDeath.hitDirection != ( ( Int32 )( EAIHitDirection.None ) ) )
		{
			m_animHitReaction.hitDirection = forcedDeath.hitDirection;
		}
		if( forcedDeath.hitNpcMovementDirection != -1 )
		{
			m_animHitReaction.npcMovementDirection = forcedDeath.hitNpcMovementDirection;
		}
		SetHitReactionType( animHitReactionType.Death );
		m_ownerPuppet.Kill();
		if( forcedDeath.forceRagdoll && ScriptedPuppet.CanRagdoll( m_ownerPuppet ) )
		{
			SendDataToAIBehavior( animHitReactionType.Ragdoll );
		}
		else
		{
			SendDataToAIBehavior( animHitReactionType.Death );
		}
		m_isAlive = false;
	}

	protected event OnSetLastHitReactionBehaviorData( evt : LastHitDataEvent )
	{
		m_lastHitReactionBehaviorData = evt.hitReactionBehaviorData;
	}

	protected event OnSetNewHitReactionBehaviorData( evt : NewHitDataEvent )
	{
		m_animHitReaction.hitDirection = evt.hitDirection;
		m_animHitReaction.hitIntensity = evt.hitIntensity;
		SetHitReactionType( ( ( animHitReactionType )( evt.hitType ) ) );
		m_animHitReaction.hitBodyPart = evt.hitBodyPart;
		m_animHitReaction.npcMovementSpeed = evt.hitNpcMovementSpeed;
		m_animHitReaction.npcMovementDirection = evt.hitNpcMovementDirection;
		m_animHitReaction.stance = evt.stance;
		m_animHitReaction.animVariation = evt.animVariation;
		SetHitReactionSource( ( ( EAIHitSource )( evt.hitSource ) ) );
	}

	private function IsSoundCriticalHit( hitEvent : gameHitEvent ) : Bool
	{
		return hitEvent.attackData.HasFlag( hitFlag.Headshot ) || hitEvent.attackData.HasFlag( hitFlag.WeakspotHit );
	}

	private function GetKillSoundName( hitEvent : gameHitEvent ) : CName
	{
		var isSoundCritical : Bool;
		isSoundCritical = IsSoundCriticalHit( hitEvent );
		if( GetHitShapeUserData().GetShapeType() == EHitShapeType.Cyberware )
		{
			return ( ( isSoundCritical ) ? ( 'w_feedback_hit_cyber_head_kill' ) : ( 'w_feedback_hit_cyber_body_kill' ) );
		}
		return ( ( isSoundCritical ) ? ( 'w_feedback_kill_npc_head' ) : ( 'w_feedback_kill_npc' ) );
	}

	private function GetHitSoundName( hitEvent : gameHitEvent ) : CName
	{
		var isSoundCritical : Bool;
		isSoundCritical = IsSoundCriticalHit( hitEvent );
		if( GetHitShapeUserData().GetShapeType() == EHitShapeType.Metal )
		{
			return ( ( isSoundCritical ) ? ( 'w_feedback_hit_armor_weakpoint' ) : ( 'w_feedback_hit_armor' ) );
		}
		else if( GetHitShapeUserData().GetShapeType() == EHitShapeType.Cyberware )
		{
			return ( ( isSoundCritical ) ? ( 'w_feedback_hit_cyber_head' ) : ( 'w_feedback_hit_cyber_body' ) );
		}
		return ( ( isSoundCritical ) ? ( 'w_feedback_hit_npc_crit' ) : ( 'w_feedback_hit_npc' ) );
	}

	public virtual function EvaluateHit( newHitEvent : gameHitEvent )
	{
		var defeatedOverride : Bool;
		var parentObject : weak< GameObject >;
		var hitFeedbackSound : CName;
		var wasNPCAliveBeforeProcessingHit : Bool;
		var currentCoverId : Uint64;
		var broadcaster : StimBroadcasterComponent;
		var hitReactionZone : EHitReactionZone;
		var isBulletAttack : Bool;
		var guardBreakImpulse : Float;
		var statsDataSystem : StatsDataSystem;
		var difficulty : gameDifficulty;
		statsDataSystem = GameInstance.GetStatsDataSystem( ( ( GameObject )( GetEntity() ) ).GetGame() );
		difficulty = statsDataSystem.GetDifficulty();
		if( !( m_ownerNPC ) )
		{
			return;
		}
		m_attackIsValidBodyPerk = false;
		IncrementHitCountData();
		CacheVars( newHitEvent );
		if( !( GetBodyPart( newHitEvent ) ) )
		{
			return;
		}
		hitReactionZone = ProcessHitReactionZone( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) );
		if( !( m_deathHasBeenPlayed ) )
		{
			currentCoverId = AICoverHelper.GetCurrentCover( m_ownerNPC );
			m_inCover = currentCoverId > ( ( Uint64 )( 0 ) );
			m_inWorkspot = GameInstance.GetWorkspotSystem( GetOwner().GetGame() ).IsActorInWorkspot( GetOwner() );
			defeatedOverride = ProcessDefeated( m_ownerNPC );
		}
		if( ( m_deathRegisteredTime == 0.0 ) && ( m_defeatedHasBeenPlayed || m_ownerNPC.IsAboutToBeDefeated() ) )
		{
			m_deathRegisteredTime = GetCurrentTime();
			m_extendedDeathDelayRegisteredTime = EngineTime.ToFloat( GetEngineTime() );
		}
		isBulletAttack = AttackData.IsRangedOrDirect( newHitEvent.attackData.GetAttackType() );
		if( isBulletAttack )
		{
			hitFeedbackSound = GetHitSoundName( newHitEvent );
			if( defeatedOverride )
			{
				hitFeedbackSound = GetKillSoundName( newHitEvent );
			}
			else if( ( m_isAlive && m_ownerNPC.IsAboutToBeDefeated() ) && !( ScriptedPuppet.IsDefeated( m_ownerNPC ) ) )
			{
				hitFeedbackSound = 'w_feedback_defeat_npc';
			}
			else if( ScriptedPuppet.IsDefeated( m_ownerNPC ) )
			{
				hitFeedbackSound = GetKillSoundName( newHitEvent );
			}
		}
		if( ScriptedPuppet.IsBeingGrappled( m_ownerNPC ) )
		{
			m_reactionType = animHitReactionType.Twitch;
			m_hitDirectionToInt = GameObject.GetAttackAngleInInt( newHitEvent, m_animHitReaction.hitSource );
			if( newHitEvent )
			{
				StoreHitData( m_hitDirectionToInt, m_hitIntensity, m_reactionType, hitReactionZone, m_animVariation );
			}
			SendTwitchDataToAnimationGraph();
			if( newHitEvent )
			{
				parentObject = ScriptedPuppet.GetGrappleParent( newHitEvent.target );
				if( parentObject.IsPlayer() )
				{
					SendTwitchDataToPlayerAnimationGraph( parentObject );
				}
			}
			GameObject.PlayVoiceOver( m_ownerNPC, 'hit_grapple', 'Scripts:Grapple' );
			return;
		}
		else if( VehicleComponent.IsMountedToVehicle( m_ownerNPC.GetGame(), m_ownerID ) && ( m_ownerNPC.GetNPCType() != gamedataNPCType.Drone || m_isAlive ) )
		{
			m_reactionType = animHitReactionType.Twitch;
			m_hitDirectionToInt = GameObject.GetAttackAngleInInt( newHitEvent, m_animHitReaction.hitSource );
			if( newHitEvent )
			{
				StoreHitData( m_hitDirectionToInt, m_hitIntensity, m_reactionType, hitReactionZone, m_animVariation );
			}
			SendTwitchDataToAnimationGraph();
			return;
		}
		if( !( newHitEvent ) )
		{
			if( !( m_isAlive ) )
			{
				if( m_ownerNPC.ShouldSkipDeathAnimation() )
				{
					SetHitReactionType( animHitReactionType.None );
				}
				else
				{
					m_animHitReaction.hitDirection = 0;
					m_animHitReaction.hitIntensity = ( ( Int32 )( EAIHitIntensity.Medium ) );
					SetHitReactionType( animHitReactionType.Death );
					m_animHitReaction.hitBodyPart = ( ( Int32 )( EAIHitBodyPart.Belly ) );
					m_animHitReaction.npcMovementSpeed = 0;
					m_animHitReaction.npcMovementDirection = 0;
				}
				m_hitReactionAction.Stop();
				m_hitReactionAction.Setup( m_animHitReaction );
				m_hitReactionAction.Launch();
				AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'hit', m_animHitReaction );
			}
			else
			{
				return;
			}
		}
		m_hitPosition = newHitEvent.hitPosition;
		m_hitDirection = newHitEvent.hitDirection;
		if( m_deathHasBeenPlayed )
		{
			if( ( ( ( isBulletAttack && ( GetIsOwnerImmuneToExtendedHitReaction() == 0.0 ) ) && !( defeatedOverride ) ) && ( ( ( Int32 )( m_hitIntensity ) ) <= 1 ) ) && ( m_animVariation < 13 ) )
			{
				SetCumulatedDamagesForDeadNPC();
				m_previousHitTime = GetCurrentTime();
				if( ( ( m_deathRegisteredTime + 0.5 ) >= GetCurrentTime() ) || ( ( m_extendedDeathRegisteredTime + 0.5 ) >= GetCurrentTime() ) )
				{
					m_extendedDeathRegisteredTime = GetCurrentTime();
					if( ( m_extendedDeathDelayRegisteredTime + 0.15000001 ) <= EngineTime.ToFloat( GetEngineTime() ) )
					{
						m_extendedDeathDelayRegisteredTime = EngineTime.ToFloat( GetEngineTime() );
						m_reactionType = animHitReactionType.Death;
						ProcessExtendedDeathAnimData( newHitEvent );
						m_hitDirectionToInt = GameObject.GetAttackAngleInInt( newHitEvent, m_animHitReaction.hitSource );
						StoreHitData( m_hitDirectionToInt, m_hitIntensity, m_reactionType, hitReactionZone, m_animVariation );
						SendDataToAIBehavior( m_reactionType );
					}
				}
				else
				{
					if( ScriptedPuppet.CanRagdoll( m_ownerNPC ) )
					{
						if( ( ( m_deathRegisteredTime + 1.0 ) <= GetCurrentTime() ) && ( ( m_extendedDeathRegisteredTime + 1.0 ) <= GetCurrentTime() ) )
						{
							if( m_previousRagdollTimeStamp != GetCurrentTime() )
							{
								m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'Dead_RecivedHit' ) );
								if( m_ownerNPC.IsRagdolling() )
								{
									GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), ( GetHitDirection() * m_ragdollImpulse ) * 0.25, m_ragdollInfluenceRadius ), 0.1, false );
								}
								else
								{
									GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetHitDirection() * m_ragdollImpulse, m_ragdollInfluenceRadius ), 0.1, false );
								}
								m_previousRagdollTimeStamp = GetCurrentTime();
							}
						}
					}
					SetCumulatedDamagesForDeadNPC();
					ProcessWoundsAndDismemberment();
				}
			}
			else
			{
				if( ScriptedPuppet.CanRagdoll( m_ownerNPC ) )
				{
					if( ( m_deathRegisteredTime + 1.0 ) <= GetCurrentTime() )
					{
						if( m_previousRagdollTimeStamp != GetCurrentTime() )
						{
							m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'Dead_RecivedHit1' ) );
							if( m_ownerNPC.IsRagdolling() )
							{
								GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), ( GetHitDirection() * m_ragdollImpulse ) * 0.25, m_ragdollInfluenceRadius ), 0.1, false );
							}
							else
							{
								GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetHitDirection() * m_ragdollImpulse, m_ragdollInfluenceRadius ), 0.1, false );
							}
							m_previousRagdollTimeStamp = GetCurrentTime();
						}
					}
				}
				SetCumulatedDamagesForDeadNPC();
				ProcessWoundsAndDismemberment();
			}
		}
		else
		{
			if( isBulletAttack )
			{
				if( ( m_isAlive && ( m_previousRangedHitTimeStamp != GetCurrentTime() ) ) && !( GetHitTimerAvailability() ) )
				{
					return;
				}
			}
			if( m_isAlive )
			{
				SetCumulatedDamages( newHitEvent.target, guardBreakImpulse );
			}
			else
			{
				SetCumulatedDamagesForDeadNPC();
				if( m_deathHasBeenPlayed && ScriptedPuppet.CanRagdoll( m_ownerNPC ) )
				{
					if( m_previousRagdollTimeStamp != GetCurrentTime() )
					{
						m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'dead_RecivedHit2' ) );
						GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetHitDirection() * m_ragdollImpulse, m_ragdollInfluenceRadius ), 0.1, false );
						m_previousRagdollTimeStamp = GetCurrentTime();
					}
				}
			}
			SetHitSource( m_attackData.GetAttackType() );
			SetStance();
			SetHitReactionThresholds();
			GetHitIntensity( defeatedOverride );
			SetHitReactionImmunities();
			if( !( defeatedOverride ) )
			{
				m_reactionType = GetReactionType( guardBreakImpulse, newHitEvent );
			}
			ProcessWoundsAndDismemberment();
			ProcessSpecialFX( newHitEvent );
			if( ( GetCurrentTime() <= ( m_previousHitTime + 0.09 ) ) && m_reactionType == animHitReactionType.Twitch )
			{
				return;
			}
			m_hitDirectionToInt = GameObject.GetAttackAngleInInt( newHitEvent, m_animHitReaction.hitSource );
			SetAnimVariation( m_hitDirectionToInt, hitReactionZone );
			StoreHitData( m_hitDirectionToInt, m_hitIntensity, m_reactionType, hitReactionZone, m_animVariation );
			m_previousHitTime = GetCurrentTime();
			if( ( m_ownerNPC.IsPlayerCompanion() && m_allowDefeatedOnCompanion ) && ( ( GetHealthPecentageNormalized() < 0.1 ) || ( ( ( ScriptedPuppet )( m_attackData.GetInstigator() ) ).IsBoss() || ( ( ScriptedPuppet )( m_attackData.GetInstigator() ) ).GetNPCRarity() == gamedataNPCRarity.MaxTac ) ) )
			{
				if( ( GetHitCountInCombo() >= 2 ) || ( ( m_defeatedDamageThreshold > 0.0 ) && ( m_cumulatedDamages >= m_defeatedDamageThreshold ) ) )
				{
					if( difficulty == gameDifficulty.Story )
					{
						StatusEffectHelper.ApplyStatusEffect( m_ownerNPC, T"BaseStatusEffect.FollowerDefeatedStory" );
					}
					else if( difficulty == gameDifficulty.Easy )
					{
						StatusEffectHelper.ApplyStatusEffect( m_ownerNPC, T"BaseStatusEffect.FollowerDefeatedEasy" );
					}
					else if( difficulty == gameDifficulty.Hard )
					{
						StatusEffectHelper.ApplyStatusEffect( m_ownerNPC, T"BaseStatusEffect.FollowerDefeatedHard" );
					}
					else if( difficulty == gameDifficulty.VeryHard )
					{
						StatusEffectHelper.ApplyStatusEffect( m_ownerNPC, T"BaseStatusEffect.FollowerDefeatedVeryHard" );
					}
					m_reactionType = animHitReactionType.None;
				}
			}
			if( ( ScriptedPuppet.CanRagdoll( m_ownerNPC ) && m_ownerNPC.GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Unconscious ) || m_ownerNPC.IsRagdolling() )
			{
				if( m_previousRagdollTimeStamp != GetCurrentTime() )
				{
					m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'Unconscious_RecivedHit' ) );
					GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetHitDirection() * 10.0, 5.0 ), 0.1, false );
					m_previousRagdollTimeStamp = GetCurrentTime();
				}
			}
			else if( ( m_animHitReaction.hitType == ( ( Int32 )( animHitReactionType.Twitch ) ) ) && m_ownerNPC.IsAboutToBeDefeated() )
			{
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.PreventQHStaggerAnimation" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.PreventQHStaggerAnimation" );
				}
				if( m_ownerPuppet.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().AIAction.ownerInTumble ) && ( m_previousRagdollTimeStamp != GetCurrentTime() ) )
				{
					m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'Defeated_WhileInTumble' ) );
					GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetHitDirection() * m_ragdollImpulse, m_ragdollInfluenceRadius ), 0.1, false );
					m_previousRagdollTimeStamp = GetCurrentTime();
				}
				return;
			}
			else if( ( m_reactionType == animHitReactionType.Death && !( m_isAlive ) ) && m_ownerNPC.GetNPCType() == gamedataNPCType.Drone )
			{
				SendDataToAIBehavior( m_reactionType );
			}
			else if( ( ( m_animHitReaction.hitType == ( ( Int32 )( animHitReactionType.Twitch ) ) ) || m_ownerPuppet.GetMovePolicesComponent().IsOnOffMeshLink() ) || ( ( m_inWorkspot && !( m_inCover ) ) && !( CheckBrainMeltDeath() ) ) )
			{
				SendTwitchDataToAnimationGraph();
				if( m_attackData.GetHitType() != gameuiHitType.Miss )
				{
					broadcaster = m_attackData.GetSource().GetStimBroadcasterComponent();
					if( broadcaster )
					{
						broadcaster.SendDrirectStimuliToTarget( GetOwner(), gamedataStimType.CombatHit, GetOwner() );
					}
				}
			}
			else
			{
				SendDataToAIBehavior( m_reactionType );
			}
		}
		if( isBulletAttack )
		{
			wasNPCAliveBeforeProcessingHit = m_isAlive;
			if( !( m_isAlive ) && wasNPCAliveBeforeProcessingHit )
			{
				hitFeedbackSound = GetKillSoundName( newHitEvent );
			}
			if( ( newHitEvent.attackData.GetInstigator().IsPlayer() && !( newHitEvent.attackData.HasFlag( hitFlag.DealNoDamage ) ) ) && m_hitIndicatorEnabled )
			{
				GameInstance.GetAudioSystem( newHitEvent.attackData.GetInstigator().GetGame() ).PlayImpact( hitFeedbackSound, newHitEvent.hitPosition, newHitEvent.attackData.GetInstigator() );
			}
		}
		if( m_isAlive )
		{
			UpdateCoverDamage( m_ownerNPC, currentCoverId );
		}
		m_hitCount += ( ( Uint32 )( 1 ) );
	}

	public virtual function UpdateCoverDamage( npc : NPCPuppet, coverId : Uint64 )
	{
		var context : ScriptExecutionContext;
		if( coverId > ( ( Uint64 )( 0 ) ) )
		{
			if( AIHumanComponent.GetScriptContext( npc, context ) )
			{
				AICoverHelper.NotifyGotDamageInCover( npc, coverId, ScriptExecutionContext.GetAITime( context ), AICoverHelper.GetCoverNPCCurrentlyExposed( npc ) );
			}
		}
	}

	protected function CacheVars( hitEvent : gameHitEvent )
	{
		m_isAlive = ScriptedPuppet.IsAlive( GetOwner() );
		m_ownerIsMassive = m_ownerNPC.IsMassive();
		m_attackData = hitEvent.attackData;
		m_overridenHitDirection = false;
		m_inWorkspot = false;
		m_inCover = false;
		m_attackDirectionToInt = -1;
	}

	protected function IncrementHitCountData()
	{
		m_hitCountData[ m_hitCountArrayCurrent ] = GetCurrentTime();
		m_hitCountArrayCurrent += 1;
		if( m_hitCountArrayCurrent > m_hitCountArrayEnd )
		{
			m_hitCountArrayCurrent = 0;
		}
	}

	public const final function GetHitCountData( index : Int32 ) : Float
	{
		var modifiedIndex : Int32;
		if( ( m_hitCountArrayCurrent - index ) < 0 )
		{
			modifiedIndex = m_hitCountArrayEnd + ( m_hitCountArrayCurrent - index );
		}
		else
		{
			modifiedIndex = m_hitCountArrayCurrent - index;
		}
		return m_hitCountData[ modifiedIndex ];
	}

	public const final function GetHitCountDataArrayCurrent() : Int32
	{
		return m_hitCountArrayCurrent;
	}

	public const final function GetHitCountDataArrayEnd() : Int32
	{
		return m_hitCountArrayEnd;
	}

	protected function SetHitReactionType( hitType : animHitReactionType )
	{
		m_animHitReaction.hitType = ( ( Int32 )( hitType ) );
		m_ownerPuppet.NotifyHitReactionTypeChanged( m_animHitReaction.hitType );
	}

	protected function SetHitReactionSource( hitSource : EAIHitSource )
	{
		m_animHitReaction.hitSource = ( ( Int32 )( hitSource ) );
		m_ownerPuppet.NotifyHitReactionSourceChanged( m_animHitReaction.hitSource );
	}

	protected function SetStance()
	{
		if( AIActionHelper.IsCurrentlyCrouching( m_ownerPuppet ) )
		{
			m_animHitReaction.stance = 1;
		}
		else
		{
			m_animHitReaction.stance = 0;
		}
	}

	protected function SetHitReactionThresholds()
	{
		var currentCoverId : Uint64;
		m_knockdownImpulseThreshold = m_currentKnockdownDamageThresholdImpulse;
		if( m_ownerNPC.IsAboutToDie() )
		{
			m_knockdownImpulseThreshold *= 0.60000002;
		}
		if( m_ownerNPC.IsPlayerCompanion() )
		{
			if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.PanamRestoreDefaultHealth" ) )
			{
				m_defeatedDamageThreshold = ( GetOwnerTotalHealth() * ( m_currentDefeatedDamageThreshold * 0.01 ) ) * 0.04;
			}
			else
			{
				m_defeatedDamageThreshold = GetOwnerTotalHealth() * ( m_currentDefeatedDamageThreshold * 0.01 );
			}
		}
		currentCoverId = AICoverHelper.GetCurrentCoverId( m_ownerPuppet );
		if( ( currentCoverId > ( ( Uint64 )( 0 ) ) ) && GameInstance.GetCoverManager( m_ownerPuppet.GetGame() ).IsCoverRegular( currentCoverId ) )
		{
			m_impactDamageThreshold = GetOwnerTotalHealth() * ( m_currentImpactDamageThresholdInCover * 0.01 );
			m_staggerDamageThreshold = GetOwnerTotalHealth() * ( m_currentStaggerDamageThresholdInCover * 0.01 );
			m_knockdownDamageThreshold = GetOwnerTotalHealth() * ( m_currentKnockdownDamageThresholdInCover * 0.01 );
			return;
		}
		m_impactDamageThreshold = GetOwnerTotalHealth() * ( m_currentImpactDamageThreshold * 0.01 );
		m_staggerDamageThreshold = GetOwnerTotalHealth() * ( m_currentStaggerDamageThreshold * 0.01 );
		m_knockdownDamageThreshold = GetOwnerTotalHealth() * ( m_currentKnockdownDamageThreshold * 0.01 );
	}

	protected function SetHitReactionImmunities()
	{
		if( ( m_currentKnockdownImmunity > 0.0 ) || ScriptedPuppet.IsOnOffMeshLink( m_ownerNPC ) )
		{
			m_immuneToKnockDown = true;
		}
		else if( GameObject.GetAttitudeTowards( GetOwner(), GameInstance.GetPlayerSystem( GetOwner().GetGame() ).GetLocalPlayerControlledGameObject() ) == EAIAttitude.AIA_Friendly || m_ownerNPC.GetNPCType() == gamedataNPCType.Cerberus )
		{
			m_immuneToKnockDown = true;
		}
	}

	protected function GetHitTimerAvailability() : Bool
	{
		var currentTime : Float;
		var hitReactionMinDuration : Float;
		currentTime = GetCurrentTime();
		hitReactionMinDuration = m_globalHitTimer;
		if( ( currentTime != m_previousHitTime ) && ( currentTime < ( m_previousHitTime + hitReactionMinDuration ) ) )
		{
			return false;
		}
		if( currentTime <= ( m_previousHitTime + 0.09 ) )
		{
			return false;
		}
		return true;
	}

	protected virtual function SetCumulatedDamages( target : weak< GameObject >, out guardBreakImpulse : Float )
	{
		var hitReactionZoneIndex : Int32;
		var bodyPartWoundCumulatedDamages : Float;
		var bodyPartDismemberCumulatedDamages : Float;
		if( ( ( m_previousHitTime + m_hitComboReset ) < GetCurrentTime() ) && m_lastHitReactionPlayed != EAILastHitReactionPlayed.Knockdown )
		{
			if( m_meleeBaseMaxHitChain > -1 )
			{
				m_maxHitChainForMelee = RandRange( m_meleeBaseMaxHitChain, m_meleeBaseMaxHitChain + 1 );
			}
			if( m_maxHitChainForRanged > -1 )
			{
				m_maxHitChainForRanged = RandRange( m_rangedBaseMaxHitChain, m_rangedBaseMaxHitChain + 1 );
			}
			m_meleeHitCount = 0;
			m_strongMeleeHitCount = 0;
			m_cumulatedDamages = 0.0;
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.Twitch;
		}
		m_cumulatedPhysicalImpulse += GetPhysicalImpulse( m_attackData, m_hitPosition, guardBreakImpulse );
		m_cumulatedDamages += GetFrameDamage();
		if( HitShapeUserDataBase.IsHitReactionZoneLeftArm( GetHitShapeUserData() ) )
		{
			bodyPartWoundCumulatedDamages = m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] + GetFrameWoundsDamage();
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = bodyPartWoundCumulatedDamages;
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = bodyPartWoundCumulatedDamages;
			bodyPartDismemberCumulatedDamages = m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] + GetFrameDismembermentDamage();
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = bodyPartDismemberCumulatedDamages;
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = bodyPartDismemberCumulatedDamages;
		}
		else if( HitShapeUserDataBase.IsHitReactionZoneRightArm( GetHitShapeUserData() ) )
		{
			bodyPartWoundCumulatedDamages = m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] + GetFrameWoundsDamage();
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = bodyPartWoundCumulatedDamages;
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = bodyPartWoundCumulatedDamages;
			bodyPartDismemberCumulatedDamages = m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] + GetFrameDismembermentDamage();
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = bodyPartDismemberCumulatedDamages;
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = bodyPartDismemberCumulatedDamages;
		}
		else
		{
			hitReactionZoneIndex = ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) );
			bodyPartWoundCumulatedDamages = m_bodyPartWoundCumulatedDamages[ hitReactionZoneIndex ] + GetFrameWoundsDamage();
			m_bodyPartWoundCumulatedDamages[ hitReactionZoneIndex ] = bodyPartWoundCumulatedDamages;
			bodyPartDismemberCumulatedDamages = m_bodyPartDismemberCumulatedDamages[ hitReactionZoneIndex ] + GetFrameDismembermentDamage();
			m_bodyPartDismemberCumulatedDamages[ hitReactionZoneIndex ] = bodyPartDismemberCumulatedDamages;
		}
		ResetFrameDamage();
		RequestCumulativeDamageUpdate();
	}

	protected function SetCumulatedDamagesForDeadNPC()
	{
		var bodyPartWoundCumulatedDamages : Float;
		var bodyPartDismemberCumulatedDamages : Float;
		var guardBreakImpulse : Float;
		var hitReactionZoneIndex : Int32;
		m_cumulatedPhysicalImpulse += GetPhysicalImpulse( m_attackData, m_hitPosition, guardBreakImpulse );
		m_cumulatedDamages += GetFrameDamage();
		if( HitShapeUserDataBase.IsHitReactionZoneLeftArm( GetHitShapeUserData() ) )
		{
			bodyPartWoundCumulatedDamages = m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] + GetFrameWoundsDamage();
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = bodyPartWoundCumulatedDamages;
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = bodyPartWoundCumulatedDamages;
			bodyPartDismemberCumulatedDamages = m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] + GetFrameDismembermentDamage();
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmLeft ) ) ] = bodyPartDismemberCumulatedDamages;
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandLeft ) ) ] = bodyPartDismemberCumulatedDamages;
		}
		else if( HitShapeUserDataBase.IsHitReactionZoneRightArm( GetHitShapeUserData() ) )
		{
			bodyPartWoundCumulatedDamages = m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] + GetFrameWoundsDamage();
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = bodyPartWoundCumulatedDamages;
			m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = bodyPartWoundCumulatedDamages;
			bodyPartDismemberCumulatedDamages = m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] + GetFrameDismembermentDamage();
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.ArmRight ) ) ] = bodyPartDismemberCumulatedDamages;
			m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( EHitReactionZone.HandRight ) ) ] = bodyPartDismemberCumulatedDamages;
		}
		else
		{
			hitReactionZoneIndex = ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) );
			bodyPartWoundCumulatedDamages = m_bodyPartWoundCumulatedDamages[ hitReactionZoneIndex ] + GetFrameWoundsDamage();
			m_bodyPartWoundCumulatedDamages[ hitReactionZoneIndex ] = bodyPartWoundCumulatedDamages;
			bodyPartDismemberCumulatedDamages = m_bodyPartDismemberCumulatedDamages[ hitReactionZoneIndex ] + GetFrameDismembermentDamage();
			m_bodyPartDismemberCumulatedDamages[ hitReactionZoneIndex ] = bodyPartDismemberCumulatedDamages;
		}
		ResetFrameDamage();
		RequestCumulativeDamageUpdate();
	}

	private function UpdateCumulatedDamages( deltaTime : Float ) : Bool
	{
		var requiresUpdate : Bool;
		var valueToDecrese : Float;
		var cumulativeDamagesDecreaser : Float;
		cumulativeDamagesDecreaser = ( GetOwnerTotalHealth() * m_baseCumulativeDamagesDecreaser ) * 0.01;
		if( ( m_previousHitTime + m_hitComboReset ) > GetCurrentTime() )
		{
			return false;
		}
		valueToDecrese = cumulativeDamagesDecreaser * deltaTime;
		if( m_cumulatedDamages > 0.0 )
		{
			m_cumulatedDamages -= valueToDecrese;
			m_cumulatedDamages = MaxF( m_cumulatedDamages, 0.0 );
			if( m_cumulatedDamages > 0.0 )
			{
				requiresUpdate = true;
			}
		}
		return requiresUpdate;
	}

	protected final function GetBodyPart( hitEvent : gameHitEvent ) : Bool
	{
		var empty : HitShapeData;
		m_hitShapeData = empty;
		if( hitEvent.hitRepresentationResult.hitShapes.Size() > 0 )
		{
			m_hitShapeData = ( ( HitShapeData )( hitEvent.hitRepresentationResult.hitShapes[ 0 ] ) );
		}
		return m_hitShapeData != empty;
	}

	protected final function CheckInstantDismembermentOnDeath() : Bool
	{
		if( ( ( m_reactionType != animHitReactionType.Death && m_reactionType != animHitReactionType.Pain ) && m_reactionType != animHitReactionType.Ragdoll ) && m_isAlive )
		{
			return false;
		}
		if( m_attackData.DoesAttackWeaponHaveTag( 'ForceDismember' ) )
		{
			return true;
		}
		if( AttackData.IsDismembermentCause( m_attackData.GetAttackType() ) )
		{
			return true;
		}
		if( m_executeDismembered )
		{
			return true;
		}
		return false;
	}

	protected final function GetDismembermentWoundType() : gameDismWoundType
	{
		var weapon : WeaponObject;
		var weaponType : gamedataItemType;
		weapon = ScriptedPuppet.GetWeaponRight( m_attackData.GetSource() );
		weaponType = weapon.GetWeaponRecord().ItemType().Type();
		if( ( ( ( ( ( ( ( weaponType == gamedataItemType.Cyb_NanoWires || weaponType == gamedataItemType.Cyb_MantisBlades ) || weaponType == gamedataItemType.Wea_Katana ) || weaponType == gamedataItemType.Wea_ShortBlade ) || weaponType == gamedataItemType.Wea_LongBlade ) || weaponType == gamedataItemType.Wea_Axe ) || weaponType == gamedataItemType.Wea_Chainsword ) || weaponType == gamedataItemType.Wea_Machete ) || weaponType == gamedataItemType.Wea_Sword )
		{
			return gameDismWoundType.CLEAN;
		}
		else
		{
			if( weapon.IsMelee() )
			{
				return gameDismWoundType.HOLE;
			}
			if( ( HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ) == gameDismBodyPart.BODY || ( HitShapeUserDataBase.IsHitReactionZoneHead( GetHitShapeUserData() ) && ( m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) ) ] < ( GetOwnerTotalHealth() * 2.0 ) ) ) ) && !( m_ownerPuppet.IsAndroid() ) )
			{
				return gameDismWoundType.HOLE;
			}
			return gameDismWoundType.COARSE;
		}
	}

	protected final function ProcessHitReactionZone( bodyPart : EHitReactionZone ) : EHitReactionZone
	{
		if( m_attackData.GetInstigator().IsPlayer() )
		{
			return bodyPart;
		}
		if( !( AttackData.IsMelee( GetAttackType() ) ) )
		{
			return bodyPart;
		}
		if( ( bodyPart == EHitReactionZone.ChestLeft || bodyPart == EHitReactionZone.ChestRight ) || bodyPart == EHitReactionZone.Abdomen )
		{
			bodyPart = EHitReactionZone.Head;
		}
		return bodyPart;
	}

	protected final function ProcessDefeated( npc : NPCPuppet ) : Bool
	{
		if( ( !( npc.IsAboutToBeDefeated() ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( npc, 'Defeated' ) ) ) && !( npc.IsAboutToDie() ) )
		{
			return false;
		}
		if( !( CanDieCondition() ) )
		{
			return false;
		}
		if( DefeatedRemoveConditions( npc ) )
		{
			GetOwner().Record1DamageInHistory( m_attackData.GetInstigator() );
			npc.Kill( m_attackData.GetInstigator() );
			m_reactionType = animHitReactionType.Death;
			m_isAlive = false;
			return true;
		}
		AnimationControllerComponent.PushEventToReplicate( GetOwner(), 'e3_2019_boss_defeated_face' );
		return false;
	}

	public function UpdateDefeated()
	{
		if( !( m_defeatedHasBeenPlayed ) )
		{
			m_defeatedHasBeenPlayed = true;
			m_defeatedRegisteredTime = GetCurrentTime();
			m_specificHitTimeout = m_defeatedRegisteredTime + m_defeatedMinDuration;
		}
	}

	protected final function DefeatedRemoveConditions( npc : NPCPuppet ) : Bool
	{
		if( npc.IsAboutToDie() )
		{
			return true;
		}
		if( ScriptedPuppet.IsOnOffMeshLink( npc ) )
		{
			return true;
		}
		if( m_attackData.HasFlag( hitFlag.QuickHack ) )
		{
			if( CheckBrainMeltDeath() )
			{
				return true;
			}
		}
		if( m_inWorkspot && !( m_inCover ) )
		{
			return true;
		}
		if( VehicleComponent.IsMountedToVehicle( GetOwner().GetGame(), npc ) )
		{
			return true;
		}
		if( ( m_specificHitTimeout > GetCurrentTime() ) && m_lastHitReactionPlayed == EAILastHitReactionPlayed.Knockdown )
		{
			return true;
		}
		if( m_attackData.HasFlag( hitFlag.VehicleDamage ) )
		{
			return true;
		}
		if( AttackData.IsExplosion( m_attackData.GetAttackType() ) )
		{
			return true;
		}
		if( m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) ) ] < 0.0 )
		{
			return true;
		}
		if( ( ( ( ( m_attackData.DoesAttackWeaponHaveTag( 'ForceDismember' ) || m_attackData.DoesAttackWeaponHaveTag( 'HeavyWeapon' ) ) || ( GetFrameDamage() > m_defeatedBodyPartDamageThreshold[ ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) ) ] ) ) || CheckInstantDismembermentOnDeath() ) && ( !( HitShapeUserDataBase.IsHitReactionZoneLimb( GetHitShapeUserData() ) ) || m_attackData.DoesAttackWeaponHaveTag( 'HeavyWeapon' ) ) ) && !( m_defeatedHasBeenPlayed ) )
		{
			return true;
		}
		if( ( GetCurrentTime() > m_specificHitTimeout ) && m_defeatedHasBeenPlayed )
		{
			return true;
		}
		UpdateDefeated();
		return false;
	}

	private function CheckBrainMeltDeath() : Bool
	{
		var attackRecord : Attack_GameEffect_Record;
		var hitFlags : array< String >;
		attackRecord = ( ( Attack_GameEffect_Record )( m_attackData.GetAttackDefinition().GetRecord() ) );
		hitFlags = attackRecord.HitFlags();
		if( hitFlags.Contains( "BrainMeltSkipDefeated" ) )
		{
			return true;
		}
		return false;
	}

	protected final function ProcessDropWeaponOnHit( owner : GameObject, hitBodyPart : EHitReactionZone, hitReaction : animHitReactionType )
	{
		var slotID : TweakDBID;
		var itemInSlotID : ItemID;
		if( m_currentCanDropWeapon == 0.0 )
		{
			return;
		}
		if( hitReaction == animHitReactionType.Impact || hitReaction == animHitReactionType.Twitch )
		{
			return;
		}
		switch( hitBodyPart )
		{
			case EHitReactionZone.HandRight:
				slotID = T"AttachmentSlots.WeaponRight";
			break;
			case EHitReactionZone.HandLeft:
				slotID = T"AttachmentSlots.WeaponLeft";
			break;
			default:
				return;
		}
		if( TDBID.IsValid( slotID ) )
		{
			itemInSlotID = GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemInSlot( owner, slotID ).GetItemData().GetID();
			if( RPGManager.GetItemType( itemInSlotID ) != gamedataItemType.Wea_Fists )
			{
				ScriptedPuppet.DropWeaponFromSlot( owner, slotID );
			}
		}
	}

	protected final function ProcessExtendedDeathAnimData( hitEvent : gameHitEvent )
	{
		if( HitShapeUserDataBase.IsHitReactionZoneRightLeg( GetHitShapeUserData() ) || HitShapeUserDataBase.IsHitReactionZoneLeftLeg( GetHitShapeUserData() ) )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
			m_hitIntensity = EAIHitIntensity.Heavy;
			NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ), m_ownerNPC.GetWorldPosition() );
		}
		else
		{
			if( m_bodyPartDismemberCumulatedDamages[ ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) ) ] >= ( GetOwnerTotalHealth() * 0.30000001 ) )
			{
				DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
				NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ), m_ownerNPC.GetWorldPosition() );
			}
			m_hitIntensity = EAIHitIntensity.Light;
		}
		if( m_animVariation <= 12 )
		{
			m_animVariation += 1;
		}
		else
		{
			m_animVariation = 13;
		}
	}

	protected final function ProcessExtendedHitReactionAnimData( hitEvent : gameHitEvent )
	{
		if( HitShapeUserDataBase.IsHitReactionZoneRightLeg( GetHitShapeUserData() ) || HitShapeUserDataBase.IsHitReactionZoneLeftLeg( GetHitShapeUserData() ) )
		{
			m_reactionType = animHitReactionType.Stagger;
			m_extendedHitReactionDelayRegisteredTime = GetCurrentTime() + 1.79999995;
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.Stagger;
		}
		else
		{
			m_reactionType = animHitReactionType.Impact;
			m_hitIntensity = EAIHitIntensity.Light;
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.Impact;
		}
		if( m_animVariation < 4 )
		{
			m_animVariation += 1;
		}
		else
		{
			m_animVariation = 0;
		}
	}

	protected final function ProcessSpecialFX( hitevent : gameHitEvent )
	{
		var transform : WorldTransform;
		var position : WorldPosition;
		var effect : FxResource;
		effect = m_ownerNPC.GetFxResourceByKey( 'special_finishers_impact' );
		if( FxResource.IsValid( effect ) && hitevent.attackData.GetInstigator().GetIsInFastFinisher() )
		{
			GetBodyPart( hitevent );
			WorldPosition.SetVector4( position, m_hitShapeData.result.hitPositionEnter );
			WorldTransform.SetWorldPosition( transform, position );
			GameInstance.GetFxSystem( m_ownerNPC.GetGame() ).SpawnEffect( effect, transform );
		}
	}

	protected final function ProcessWoundsAndDismemberment()
	{
		var hitReactionZone : EHitReactionZone;
		var broadcaster : StimBroadcasterComponent;
		var woundType : gameDismWoundType;
		var dismembermentCheck : Bool;
		var woundedBaseConditions : Bool;
		var stopScanningEvent : AIEvent;
		var healthMissingAfterAttack : Float;
		if( !( CanDieCondition() ) )
		{
			return;
		}
		if( m_attackData.HasFlag( hitFlag.FragmentationSplinter ) )
		{
			ProcessFragmentationSplinterReaction( m_hitShapeData.result.hitPositionEnter );
			return;
		}
		if( AttackData.IsExplosion( m_attackData.GetAttackType() ) )
		{
			ProcessExplosionDismembement();
			return;
		}
		if( m_ownerNPC.GetMovePolicesComponent().IsOnOffMeshLink() )
		{
			return;
		}
		if( GameInstance.GetCoverManager( m_ownerNPC.GetGame() ).GetCoverActionType( m_ownerNPC ) == AIUninterruptibleActionType.EnteringCover )
		{
			return;
		}
		if( m_attackData.GetAttackType() == gamedataAttackType.QuickMelee )
		{
			return;
		}
		woundType = GetDismembermentWoundType();
		if( ( woundType != gameDismWoundType.CLEAN && !( HitShapeUserDataBase.IsHitReactionZoneHead( GetHitShapeUserData() ) ) ) && AttackData.IsMelee( m_attackData.GetAttackType() ) )
		{
			return;
		}
		hitReactionZone = HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() );
		healthMissingAfterAttack = GameInstance.GetStatPoolsSystem( m_ownerNPC.GetGame() ).GetStatPoolValue( m_ownerNPC.GetEntityID(), gamedataStatPoolType.Health, false ) - m_ownerNPC.GetTotalFrameDamage();
		healthMissingAfterAttack = 1.0 - ClampF( healthMissingAfterAttack / m_statsSystem.GetStatValue( m_ownerNPC.GetEntityID(), gamedataStatType.Health ), 0.0, 1.0 );
		m_attackIsValidBodyPerk = IsValidBodyPerkDismemberAttack( healthMissingAfterAttack );
		if( m_attackIsValidBodyPerk )
		{
			if( TryTriggerBodyPerkDismembement( healthMissingAfterAttack ) )
			{
				ProcessBodyPerkDismembement();
				return;
			}
		}
		if( HitShapeUserDataBase.IsHitReactionZoneHead( GetHitShapeUserData() ) || HitShapeUserDataBase.IsHitReactionZoneTorso( GetHitShapeUserData() ) )
		{
			dismembermentCheck = DismembermentConditions();
			woundedBaseConditions = WoundedBaseConditions();
			if( WoundedCyberConditions( dismembermentCheck, woundedBaseConditions ) )
			{
				DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), woundType, m_hitShapeData.result.hitPositionEnter );
				NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), hitReactionZone, m_ownerNPC.GetWorldPosition() );
				m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( hitReactionZone ) ) ] = 0.0;
				m_reactionType = animHitReactionType.Stagger;
				StatusEffectHelper.ApplyStatusEffect( GetOwner(), T"BaseStatusEffect.AndroidHeadRemovedBlind" );
				stopScanningEvent = new AIEvent;
				stopScanningEvent.name = 'StopScanning';
				GetOwner().QueueEvent( stopScanningEvent );
			}
			else if( dismembermentCheck )
			{
				if( HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ) == gameDismBodyPart.BODY && !( m_scatteredGuts ) )
				{
					DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), woundType, m_hitShapeData.result.hitPositionEnter, false, "base\characters\common\dismemberment\man_big\cut_parts\gore\ragdolls_hole_abdomen.dismdebris", 0.75 );
					DismembermentComponent.RequestGutsFromLastHit( GetOwner(), "base\characters\common\dismemberment\man_big\cut_parts\gore\ragdolls_explosion.dismdebris", 0.75 );
					m_scatteredGuts = true;
				}
				else
				{
					DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), woundType, m_hitShapeData.result.hitPositionEnter );
				}
				NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), hitReactionZone, m_ownerNPC.GetWorldPosition() );
				if( m_isAlive )
				{
					m_ownerPuppet.Kill( m_attackData.GetInstigator() );
					m_reactionType = animHitReactionType.Death;
					m_isAlive = false;
					if( m_hitIntensity != EAIHitIntensity.Heavy )
					{
						m_hitIntensity = EAIHitIntensity.Medium;
					}
				}
			}
		}
		else
		{
			dismembermentCheck = DismembermentConditions();
			woundedBaseConditions = WoundedBaseConditions();
			if( WoundedFleshConditions( dismembermentCheck, woundedBaseConditions ) )
			{
				broadcaster = GetOwner().GetStimBroadcasterComponent();
				if( broadcaster )
				{
					broadcaster.TriggerSingleBroadcast( GetOwner(), gamedataStimType.Attention );
				}
				m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( hitReactionZone ) ) ] = 0.0;
				StatusEffectHelper.ApplyStatusEffect( GetOwner(), TDBID.Create( "BaseStatusEffect.Crippled" + EnumValueToString( "EHitReactionZone", ( ( Int32 )( hitReactionZone ) ) ) ) );
				m_specificHitTimeout = GetCurrentTime() + m_staggerDamageDuration;
				NotifyAboutWoundedInstigated( m_attackData.GetInstigator(), hitReactionZone );
				GameObject.PlayVoiceOver( m_ownerPuppet, EnumValueToName( 'EBarkList', ( ( Int32 )( ReactionZoneEnumToBarkListEnum( hitReactionZone ) ) ) ), 'Scripts:ProcessWoundsAndDismemberment' );
				m_reactionType = animHitReactionType.Pain;
				m_hasBeenWounded = true;
			}
			else if( WoundedCyberConditions( dismembermentCheck, woundedBaseConditions ) )
			{
				DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), woundType, m_hitShapeData.result.hitPositionEnter );
				NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), hitReactionZone, m_ownerNPC.GetWorldPosition() );
				m_bodyPartWoundCumulatedDamages[ ( ( Int32 )( hitReactionZone ) ) ] = 0.0;
				StatusEffectHelper.ApplyStatusEffect( GetOwner(), TDBID.Create( "BaseStatusEffect.Dismembered" + EnumValueToString( "EHitReactionZone", ( ( Int32 )( hitReactionZone ) ) ) ) );
				m_specificHitTimeout = GetCurrentTime() + m_staggerDamageDuration;
				GameObject.PlayVoiceOver( m_ownerPuppet, EnumValueToName( 'EBarkList', ( ( Int32 )( ReactionZoneEnumToBarkListEnum( hitReactionZone ) ) ) ), 'Scripts:ProcessWoundsAndDismemberment' );
				m_reactionType = animHitReactionType.Pain;
				m_hasBeenWounded = true;
			}
			else if( dismembermentCheck )
			{
				DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), woundType, m_hitShapeData.result.hitPositionEnter );
				NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), hitReactionZone, m_ownerNPC.GetWorldPosition() );
				if( m_isAlive )
				{
					m_ownerPuppet.Kill( m_attackData.GetInstigator() );
					m_reactionType = animHitReactionType.Death;
					m_isAlive = false;
					if( m_hitIntensity != EAIHitIntensity.Heavy )
					{
						m_hitIntensity = EAIHitIntensity.Medium;
					}
				}
			}
		}
	}

	protected final function ReactionZoneEnumToBarkListEnum( reactionZone : EHitReactionZone ) : EBarkList
	{
		switch( reactionZone )
		{
			case EHitReactionZone.Head:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.ChestLeft:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.ArmLeft:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.HandLeft:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.ChestRight:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.ArmRight:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.HandRight:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.Abdomen:
				return EBarkList.vo_enemy_reaction_crippled_arm;
			case EHitReactionZone.LegLeft:
				return EBarkList.vo_enemy_reaction_crippled_leg;
			case EHitReactionZone.LegRight:
				return EBarkList.vo_enemy_reaction_crippled_leg;
			default:
				return EBarkList.vo_enemy_reaction_crippled_arm;
		}
	}

	protected final function ReactionZoneEnumToBodyPartID( reactionZone : EHitReactionZone ) : Int32
	{
		switch( reactionZone )
		{
			case EHitReactionZone.Head:
				return 1;
			case EHitReactionZone.ChestLeft:
				return 2;
			case EHitReactionZone.ArmLeft:
				return 2;
			case EHitReactionZone.HandLeft:
				return 2;
			case EHitReactionZone.ChestRight:
				return 3;
			case EHitReactionZone.ArmRight:
				return 3;
			case EHitReactionZone.HandRight:
				return 3;
			case EHitReactionZone.Abdomen:
				return 4;
			case EHitReactionZone.LegLeft:
				return 5;
			case EHitReactionZone.LegRight:
				return 6;
			default:
				return 0;
		}
	}

	protected final function WoundedBaseConditions() : Bool
	{
		var reactionZoneIndex : Int32;
		if( !( m_isAlive ) )
		{
			return false;
		}
		if( m_ownerIsMassive )
		{
			return false;
		}
		if( m_inWorkspot )
		{
			return false;
		}
		if( m_inCover )
		{
			return false;
		}
		if( m_reactionType == animHitReactionType.Death )
		{
			return false;
		}
		if( m_reactionType == animHitReactionType.Pain )
		{
			return false;
		}
		if( m_reactionType == animHitReactionType.Ragdoll )
		{
			return false;
		}
		if( ScriptedPuppet.IsDefeated( GetOwner() ) )
		{
			return false;
		}
		if( m_ownerNPC.IsAboutToBeDefeated() )
		{
			return false;
		}
		if( AttackData.IsLightMelee( m_attackData.GetAttackType() ) )
		{
			return false;
		}
		if( AttackData.IsStrongMelee( m_attackData.GetAttackType() ) )
		{
			return false;
		}
		if( CheckInstantDismembermentOnDeath() )
		{
			return true;
		}
		if( ( ( ( ( m_knockdownImpulseThreshold > 0.0 ) && ( m_cumulatedPhysicalImpulse >= m_knockdownImpulseThreshold ) ) && ( m_knockdownDamageThreshold > 0.0 ) ) && ( m_cumulatedDamages >= m_knockdownDamageThreshold ) ) && !( CheckInstantDismembermentOnDeath() ) )
		{
			return false;
		}
		reactionZoneIndex = ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) );
		if( m_woundedBodyPartDamageThreshold[ reactionZoneIndex ] <= 0.0 )
		{
			return false;
		}
		if( m_bodyPartWoundCumulatedDamages[ reactionZoneIndex ] < m_woundedBodyPartDamageThreshold[ reactionZoneIndex ] )
		{
			return false;
		}
		return true;
	}

	protected final function WoundedFleshConditions( dismembermentCheck : Bool, woundedBaseConditions : Bool ) : Bool
	{
		if( dismembermentCheck )
		{
			return false;
		}
		if( !( woundedBaseConditions ) )
		{
			return false;
		}
		if( m_hasBeenWounded )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( GetOwner(), gamedataStatusEffectType.Wounded ) )
		{
			return false;
		}
		if( m_ownerPuppet.IsMechanical() )
		{
			return false;
		}
		if( GetHitShapeUserData().GetShapeType() != EHitShapeType.Flesh )
		{
			return false;
		}
		if( m_ownerWeapon.IsMelee() )
		{
			return false;
		}
		return true;
	}

	protected final function WoundedCyberConditions( dismembermentCheck : Bool, woundedBaseConditions : Bool ) : Bool
	{
		if( dismembermentCheck )
		{
			return false;
		}
		if( !( woundedBaseConditions ) )
		{
			return false;
		}
		if( m_hasMantisBladesinRecord )
		{
			return false;
		}
		if( GetHitShapeUserData().GetShapeType() != EHitShapeType.Metal && GetHitShapeUserData().GetShapeType() != EHitShapeType.Cyberware )
		{
			return false;
		}
		if( ScriptedPuppet.GetWeaponRight( m_attackData.GetSource() ).GetWeaponRecord().ItemType().Type() == gamedataItemType.Cyb_MantisBlades )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( GetOwner(), gamedataStatusEffectType.Wounded ) && !( HitShapeUserDataBase.IsHitReactionZoneHead( GetHitShapeUserData() ) ) )
		{
			return false;
		}
		return true;
	}

	protected final function CanDieCondition( optional doNotCheckAttackData : Bool ) : Bool
	{
		if( m_attackData && !( doNotCheckAttackData ) )
		{
			if( m_ownerNPC.IsDefeatMechanicActive() && m_attackData.HasFlag( hitFlag.Nonlethal ) )
			{
				return false;
			}
			if( StatusEffectSystem.ObjectHasStatusEffect( m_attackData.GetInstigator(), T"GameplayRestriction.FistFight" ) )
			{
				return false;
			}
		}
		if( ( m_ownerNPC.IsBoss() || m_ownerNPC.GetNPCRarity() == gamedataNPCRarity.MaxTac ) && !( ScriptedPuppet.IsDefeated( GetOwner() ) ) )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"GameplayRestriction.FistFight" ) )
		{
			return false;
		}
		if( GameInstance.GetGodModeSystem( GetOwner().GetGame() ).HasGodMode( m_ownerID, gameGodModeType.Immortal ) )
		{
			return false;
		}
		if( GameInstance.GetGodModeSystem( GetOwner().GetGame() ).HasGodMode( m_ownerID, gameGodModeType.Invulnerable ) )
		{
			return false;
		}
		if( m_currentIsInvulnerable > 0.0 )
		{
			return false;
		}
		return true;
	}

	protected final function DismembermentConditions() : Bool
	{
		var reactionZoneIndex : Int32;
		var bodyPartDamageThreshold : Float;
		reactionZoneIndex = ( ( Int32 )( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) );
		if( HitShapeUserDataBase.IsHitReactionZoneTorso( GetHitShapeUserData() ) && AttackData.IsMelee( m_attackData.GetAttackType() ) )
		{
			return false;
		}
		if( m_attackData.HasFlag( hitFlag.ForceDismember ) )
		{
			return true;
		}
		if( ( ( m_isAlive && !( m_ownerNPC.IsAboutToDie() ) ) && !( m_ownerNPC.IsAboutToBeDefeated() ) ) && !( ScriptedPuppet.IsDefeated( GetOwner() ) ) )
		{
			return false;
		}
		if( CheckInstantDismembermentOnDeath() )
		{
			return true;
		}
		bodyPartDamageThreshold = m_dismembermentBodyPartDamageThreshold[ reactionZoneIndex ];
		if( bodyPartDamageThreshold <= 0.0 )
		{
			return false;
		}
		if( m_attackData.GetInstigator().IsPlayer() && ( PlayerDevelopmentSystem.GetData( m_attackData.GetInstigator() ).IsNewPerkBought( gamedataNewPerkType.Reflexes_Master_Perk_5 ) > 0 ) )
		{
			if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.Reflexes_Master_Perk_5_Bleeding" ) )
			{
				bodyPartDamageThreshold = bodyPartDamageThreshold * 0.75;
			}
		}
		if( m_bodyPartDismemberCumulatedDamages[ reactionZoneIndex ] >= bodyPartDamageThreshold )
		{
			return true;
		}
		return false;
	}

	protected final function ProcessFragmentationSplinterReaction( hitPosition : Vector4 )
	{
		var currentHPPerc : Float;
		var broadcaster : StimBroadcasterComponent;
		currentHPPerc = GetHealthPecentageNormalized();
		if( currentHPPerc > 30.0 )
		{
			return;
		}
		if( RandF() > 0.40000001 )
		{
			if( m_ownerNPC.WasJustKilledOrDefeated() )
			{
				SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.LEFT_LEG, m_attackData.GetSource().GetWorldPosition(), RandRangeF( 50.0, 75.0 ), m_hitShapeData.result.hitPositionEnter );
			}
			else
			{
				StatusEffectHelper.ApplyStatusEffect( GetOwner(), T"BaseStatusEffect.CrippledLegLeft" );
				m_specificHitTimeout = GetCurrentTime() + m_staggerDamageDuration;
			}
		}
		else if( RandF() > 0.40000001 )
		{
			if( m_ownerNPC.WasJustKilledOrDefeated() )
			{
				SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.RIGHT_LEG, m_attackData.GetSource().GetWorldPosition(), RandRangeF( 50.0, 75.0 ), m_hitShapeData.result.hitPositionEnter );
			}
			else
			{
				StatusEffectHelper.ApplyStatusEffect( GetOwner(), T"BaseStatusEffect.CrippledLegRight" );
				m_specificHitTimeout = GetCurrentTime() + m_staggerDamageDuration;
			}
		}
		GameObject.PlayVoiceOver( m_ownerPuppet, EnumValueToName( 'EBarkList', ( ( Int32 )( ReactionZoneEnumToBarkListEnum( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) ) ) ) ), 'Scripts:ProcessFragmentationSplinterReaction' );
		broadcaster = GetOwner().GetStimBroadcasterComponent();
		if( broadcaster )
		{
			broadcaster.TriggerSingleBroadcast( GetOwner(), gamedataStimType.Scream );
		}
	}

	protected final function IsValidBodyPerkDismemberAttack( healthMissing : Float ) : Bool
	{
		var chanceByHealth : Float;
		var weaponType : gamedataItemType;
		chanceByHealth = 0.0;
		if( m_executeDismembered || m_invalidForExecuteDismember )
		{
			return false;
		}
		if( healthMissing < m_dismemberExecuteHealthRange.X )
		{
			return false;
		}
		if( m_ownerNPC.IsBoss() || m_ownerNPC.GetNPCRarity() == gamedataNPCRarity.MaxTac )
		{
			return false;
		}
		if( ( !( m_attackData ) || m_attackData.GetAttackType() != gamedataAttackType.Ranged ) || m_attackData.HasFlag( hitFlag.Explosion ) )
		{
			return false;
		}
		if( !( m_attackData.GetInstigator().IsPlayer() ) )
		{
			return false;
		}
		if( PlayerDevelopmentSystem.GetData( m_attackData.GetInstigator() ).IsNewPerkBought( gamedataNewPerkType.Body_Left_Milestone_3 ) < 3 )
		{
			return false;
		}
		if( !( m_attackData.GetWeapon() ) )
		{
			return false;
		}
		weaponType = RPGManager.GetItemType( m_attackData.GetWeapon().GetItemID() );
		if( ( ( weaponType != gamedataItemType.Wea_Shotgun && weaponType != gamedataItemType.Wea_ShotgunDual ) && weaponType != gamedataItemType.Wea_LightMachineGun ) && weaponType != gamedataItemType.Wea_HeavyMachineGun )
		{
			return false;
		}
		chanceByHealth = m_statsSystem.GetStatValue( m_attackData.GetInstigator().GetEntityID(), gamedataStatType.ExecuteDismemberByHealthChance );
		if( chanceByHealth <= 0.0001 )
		{
			return false;
		}
		return true;
	}

	protected final function TryTriggerBodyPerkDismembement( remainingHealth : Float ) : Bool
	{
		var attackDistance : Float;
		var executeChance : Float;
		var chanceByHealth : Float;
		var chanceByProximity : Float;
		executeChance = 0.0;
		chanceByHealth = 0.0;
		chanceByProximity = 0.0;
		chanceByHealth = m_statsSystem.GetStatValue( m_attackData.GetInstigator().GetEntityID(), gamedataStatType.ExecuteDismemberByHealthChance );
		executeChance += ProportionalClampF( m_dismemberExecuteHealthRange.X, m_dismemberExecuteHealthRange.Y, remainingHealth, 0.0, chanceByHealth );
		chanceByProximity = m_statsSystem.GetStatValue( m_attackData.GetInstigator().GetEntityID(), gamedataStatType.ExecuteDismemberProximityChance );
		if( chanceByProximity > 0.0 )
		{
			attackDistance = Vector4.Distance( m_ownerPuppet.GetWorldPosition(), m_attackData.GetInstigator().GetWorldPosition() );
			executeChance += ProportionalClampF( m_dismemberExecuteDistanceRange.X, m_dismemberExecuteDistanceRange.Y, attackDistance, chanceByProximity, 0.0 );
		}
		if( executeChance <= 0.0 )
		{
			return false;
		}
		return m_attackData.GetRandRoll() < executeChance;
	}

	protected final function IncrementBodyPerkDismembermentChance( player : weak< GameObject > )
	{
		if( !( player ) || !( player.IsPlayer() ) )
		{
			return;
		}
		GameInstance.GetStatusEffectSystem( player.GetGame() ).ApplyStatusEffect( player.GetEntityID(), T"BaseStatusEffect.Body_DismemberExecuteChance_Stack" );
		m_invalidForExecuteDismember = true;
	}

	protected final function ClearBodyPerkDismembermentChance( player : weak< GameObject > )
	{
		if( !( player ) || !( player.IsPlayer() ) )
		{
			return;
		}
		GameInstance.GetStatusEffectSystem( player.GetGame() ).RemoveStatusEffect( player.GetEntityID(), T"BaseStatusEffect.Body_DismemberExecuteChance_Stack" );
	}

	protected final function ProcessBodyPerkDismembement()
	{
		var secondaryDismemberChance : Float;
		var secondaryProjectileBonus : Float;
		var highProjectileWeapon : Bool;
		var impulseFactor : Float;
		var attackWeaponID : StatsObjectID;
		var isLegHit : Bool;
		var adjustedHitDirection : Vector4;
		secondaryDismemberChance = 0.80000001;
		secondaryProjectileBonus = 0.2;
		highProjectileWeapon = false;
		impulseFactor = 1.0;
		isLegHit = false;
		if( m_attackData.GetWeapon() )
		{
			attackWeaponID = m_attackData.GetWeapon().GetEntityID();
			highProjectileWeapon = m_statsSystem.GetStatValue( attackWeaponID, gamedataStatType.ProjectilesPerShot ) > 3.0;
			if( highProjectileWeapon )
			{
				secondaryDismemberChance += secondaryProjectileBonus;
			}
		}
		if( HitShapeUserDataBase.IsHitReactionZoneHead( GetHitShapeUserData() ) )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
		}
		else if( HitShapeUserDataBase.IsHitReactionZoneTorso( GetHitShapeUserData() ) )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.BODY, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter, true );
		}
		else
		{
			isLegHit = HitShapeUserDataBase.IsHitReactionZoneLeg( GetHitShapeUserData() );
			DismembermentComponent.RequestDismemberment( GetOwner(), HitShapeUserDataBase.GetDismembermentBodyPart( GetHitShapeUserData() ), gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
		}
		if( !( isLegHit ) && ( highProjectileWeapon || ( RandF() < secondaryDismemberChance ) ) )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
		}
		if( highProjectileWeapon || ( RandF() < secondaryDismemberChance ) )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.BODY, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter, true );
		}
		if( RandF() < secondaryDismemberChance )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
		}
		if( RandF() < secondaryDismemberChance )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
		}
		if( RandF() < secondaryDismemberChance )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_LEG, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
		}
		if( RandF() < secondaryDismemberChance )
		{
			DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_LEG, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
		}
		NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ), m_ownerNPC.GetWorldPosition() );
		GetOwner().Record1DamageInHistory( m_attackData.GetInstigator() );
		adjustedHitDirection = GetOverridenHitDirection();
		impulseFactor = ProportionalClampF( 0.0, 0.5, AbsF( adjustedHitDirection.Z ), 1.5, 0.80000001 );
		m_cumulatedPhysicalImpulse = m_knockdownImpulseThreshold + 10.0;
		m_ragdollImpulse = m_cumulatedPhysicalImpulse * impulseFactor;
		m_executeDismembered = true;
		GameObject.PlaySoundEvent( m_attackData.GetInstigator(), 'w_gun_perk_gratuitous_violence' );
		if( ( adjustedHitDirection.Z > 0.0 ) && ( adjustedHitDirection.Z < 0.25 ) )
		{
			GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), ( Vector4( 0.0, 0.0, 1.0, 0.0 ) * m_ragdollImpulse ) * RandRangeF( 0.1, 0.33000001 ), 1.0 ), 0.1, false );
		}
		GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), ( adjustedHitDirection * m_ragdollImpulse ) * 0.1, 1.0 ), 0.1, false );
		m_ownerPuppet.Kill( m_attackData.GetInstigator() );
		m_reactionType = animHitReactionType.Ragdoll;
		m_isAlive = false;
		ClearBodyPerkDismembermentChance( m_attackData.GetInstigator() );
	}

	public final function ProcessOnePunchAttackHitImpact( target : ScriptedPuppet, instigator : GameObject )
	{
		var impulseFactor : Float;
		var upwardsVector : Float;
		var evt : PlayOnePunchVFX;
		var vfxDelay : Float;
		var ragdollDirection : Vector4;
		m_ownerNPC = ( ( NPCPuppet )( target ) );
		vfxDelay = TDB.GetFloat( T"Items.StrongArms.vfxDelay" );
		GetOwner().Record1DamageInHistory( m_attackData.GetInstigator() );
		impulseFactor = TDB.GetFloat( T"Items.StrongArms.impulseOfOnePunch" );
		upwardsVector = TDB.GetFloat( T"Items.StrongArms.upwardsVectorStrength" );
		m_cumulatedPhysicalImpulse = m_knockdownImpulseThreshold + 10.0;
		m_ragdollImpulse = m_cumulatedPhysicalImpulse * impulseFactor;
		m_reactionType = animHitReactionType.Ragdoll;
		m_isAlive = false;
		GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateForceRagdollEvent( 'NPC killed with Gorilla Arms' ), 0.01, false );
		ragdollDirection = ( ( GetOverridenHitDirection() * 0.1 ) + ( Vector4( 0.0, 0.0, upwardsVector, 0.0 ) * 0.05 ) ) * m_ragdollImpulse;
		GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), ragdollDirection, 1.0 ), 0.1, false );
		evt = new PlayOnePunchVFX;
		evt.target = m_ownerNPC;
		evt.instigator = instigator;
		GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, evt, vfxDelay, true );
	}

	protected constexpr static function GetMantisBladesInstantDismembermentSpyBuffStatusEffectID() : TweakDBID
	{
		return T"BaseStatusEffect.MantisBladesInstantDismembermentSpyBuff";
	}

	public final function ProcessGrandFinaleHitImpact( target : ScriptedPuppet, instigator : GameObject )
	{
		var evt : PlayGrandFinaleVFX;
		StatusEffectHelper.ApplyStatusEffect( target, GetMantisBladesInstantDismembermentSpyBuffStatusEffectID(), 0.40000001 );
		evt = new PlayGrandFinaleVFX;
		evt.target = target;
		GameInstance.GetDelaySystem( target.GetGame() ).DelayEvent( target, evt, 0.2, true );
	}

	protected event OnPlayOnePunchVFX( evt : PlayOnePunchVFX )
	{
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'blood_mouth_punch_strong' );
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'blood_nose_punch_strong' );
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'blood_headshot' );
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'finisher_katana_01_head' );
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'finisher_katana_01' );
	}

	protected event OnPlayGrandFinaleVFX( evt : PlayGrandFinaleVFX )
	{
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'finisher_katana_01' );
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'finisher_katana_02' );
		GameObjectEffectHelper.StartEffectEvent( evt.target, 'takedown_aerial_front_blood_ground' );
	}

	protected final function ProcessExplosionDismembement()
	{
		var explosionCentrum : Vector4;
		var strengthMin : Float;
		var strengthMax : Float;
		var randomPreset : Int32;
		strengthMin = 50.0;
		strengthMax = 75.0;
		if( ( m_reactionType == animHitReactionType.Death || m_reactionType == animHitReactionType.Pain ) || m_reactionType == animHitReactionType.Ragdoll )
		{
			explosionCentrum = m_attackData.GetSource().GetWorldPosition();
			if( ( Vector4.Distance( m_ownerPuppet.GetWorldPosition(), explosionCentrum ) <= 1.70000005 ) && !( m_isAlive ) )
			{
				randomPreset = RandRange( 1, 9 );
				switch( randomPreset )
				{
					case 1:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					break;
					case 2:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					break;
					case 3:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					break;
					case 4:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					break;
					case 5:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 6:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 7:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter, true );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 8:
						SendDismembermentCriticalEvent( gameDismWoundType.COARSE, gameDismBodyPart.BODY, explosionCentrum, RandRangeF( strengthMin, strengthMax ), m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					break;
					default:
						return;
				}
			}
			else
			{
				randomPreset = RandRange( 1, 7 );
				switch( randomPreset )
				{
					case 1:
						DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.BODY, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 2:
						DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.BODY, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 3:
						DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.BODY, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 4:
						DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_LEG, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 5:
						DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.HEAD, gameDismWoundType.HOLE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_LEG, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_ARM, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					case 6:
						DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.LEFT_LEG, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					DismembermentComponent.RequestDismemberment( GetOwner(), gameDismBodyPart.RIGHT_LEG, gameDismWoundType.COARSE, m_hitShapeData.result.hitPositionEnter );
					break;
					default:
						return;
				}
			}
			NotifyAboutDismembermentInstigated( m_attackData.GetInstigator(), HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ), m_ownerNPC.GetWorldPosition() );
		}
	}

	protected final function SendDismembermentCriticalEvent( dismembermentType : gameDismWoundType, bodyPart : gameDismBodyPart, explosionEpicentrum : Vector4, strength : Float, hitPosition : Vector4 )
	{
		var evt : RequestDismembermentEvent;
		var evtExplosion : DismembermentExplosionEvent;
		evt = new RequestDismembermentEvent;
		evtExplosion = new DismembermentExplosionEvent;
		evtExplosion.m_epicentrum = explosionEpicentrum;
		evtExplosion.m_strength = strength;
		evt.hitPosition = hitPosition;
		evt.dismembermentType = dismembermentType;
		evt.bodyPart = bodyPart;
		evt.isCritical = true;
		m_ownerPuppet.QueueEvent( evtExplosion );
		m_ownerPuppet.QueueEvent( evt );
	}

	protected final function GetHitIntensity( defeatedOverride : Bool )
	{
		if( IsStrongExplosion( m_attackData ) )
		{
			m_hitIntensity = EAIHitIntensity.Explosion;
			m_animVariation = RandRange( 0, 3 );
		}
		else if( ( ( ( !( m_ownerIsMassive ) && ( m_cumulatedPhysicalImpulse >= m_knockdownImpulseThreshold ) ) && ( ( m_cumulatedDamages >= m_knockdownDamageThreshold ) || !( m_isAlive ) ) ) && ( m_knockdownDamageThreshold > 0.0 ) ) && ( m_knockdownImpulseThreshold > 0.0 ) )
		{
			if( ( m_specificHitTimeout > GetCurrentTime() ) && m_lastHitReactionPlayed == EAILastHitReactionPlayed.Knockdown )
			{
				m_hitIntensity = EAIHitIntensity.Medium;
				return;
			}
			m_hitIntensity = EAIHitIntensity.Heavy;
			if( ( !( defeatedOverride ) && CanDieCondition() ) && m_ownerNPC.IsAboutToBeDefeated() )
			{
				GetOwner().Record1DamageInHistory( m_attackData.GetInstigator() );
				m_ownerPuppet.Kill( m_attackData.GetInstigator() );
				m_reactionType = animHitReactionType.Death;
				m_isAlive = false;
			}
		}
		else
		{
			m_hitIntensity = EAIHitIntensity.Medium;
		}
	}

	private final function IsPowerDifferenceBelow( powerDifferential : gameEPowerDifferential ) : Bool
	{
		return ( ( Int32 )( RPGManager.CalculatePowerDifferential( GetOwner() ) ) ) <= ( ( Int32 )( powerDifferential ) );
	}

	protected final function GetReactionType( guardBreakImpulse : Float, newHitEvent : gameHitEvent ) : animHitReactionType
	{
		var weaponRecord : WeaponItem_Record;
		var attackSubType : gamedataAttackSubtype;
		var attackWeaponType : gamedataItemType;
		var currentTimeStamp : Float;
		var hitReactionMin : Int32;
		var hitReactionMax : Int32;
		var powerDifferenceTooHigh : Bool;
		var meleePlayerExhausted : Bool;
		var meleeHitReactionResist : Bool;
		var shouldEvade : Bool;
		if( !( m_isAlive ) )
		{
			return animHitReactionType.Death;
		}
		if( m_ownerIsMassive || StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'DismemberedLeg' ) )
		{
			return animHitReactionType.Twitch;
		}
		if( !( m_ownerNPC.IsBoss() ) && m_ownerNPC.GetNPCRarity() != gamedataNPCRarity.MaxTac )
		{
			powerDifferenceTooHigh = ( ( Int32 )( RPGManager.CalculatePowerDifferential( GetOwner() ) ) ) <= ( ( Int32 )( gameEPowerDifferential.IMPOSSIBLE ) );
		}
		hitReactionMin = m_attackData.GetHitReactionSeverityMin();
		hitReactionMax = m_attackData.GetHitReactionSeverityMax();
		if( newHitEvent.attackData.HasFlag( hitFlag.ForceKnockdown ) )
		{
			if( ( hitReactionMin > -1 ) && ( hitReactionMin < 4 ) )
			{
				hitReactionMin = 3;
			}
			if( ( hitReactionMax > -1 ) && ( hitReactionMax < 4 ) )
			{
				hitReactionMax = 3;
			}
		}
		weaponRecord = ScriptedPuppet.GetWeaponRight( m_attackData.GetSource() ).GetWeaponRecord();
		if( weaponRecord.ForcedMinHitReaction() > hitReactionMin )
		{
			hitReactionMin = weaponRecord.ForcedMinHitReaction();
		}
		if( ( m_attackData.GetInstigator().IsPlayer() && GameObject.GetAttitudeTowards( m_ownerNPC, m_attackData.GetInstigator() ) == EAIAttitude.AIA_Friendly ) || m_ownerNPC.GetNPCType() == gamedataNPCType.Cerberus )
		{
			hitReactionMax = 0;
		}
		currentTimeStamp = GetCurrentTime();
		attackSubType = GetSubAttackSubType();
		if( ( ( attackSubType == gamedataAttackSubtype.ThrowAttack && m_attackData.GetAttackType() == gamedataAttackType.Thrown ) && ( hitReactionMax == 2 ) ) && StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'NoInterrupt' ) )
		{
			hitReactionMax = 0;
		}
		if( ( attackSubType == gamedataAttackSubtype.ComboAttack && StatusEffectSystem.ObjectHasStatusEffectWithTag( GetOwner(), 'InterruptableWithKnives' ) ) && weaponRecord.ItemType().Type() == gamedataItemType.Wea_Knife )
		{
			hitReactionMin = 1;
		}
		if( ( m_attackData.HasFlag( hitFlag.QuickHack ) && m_attackData.GetAttackType() == gamedataAttackType.Hack ) && ( hitReactionMax == 2 ) )
		{
			RecalculateHitReactionValsForHacks( currentTimeStamp, hitReactionMin, hitReactionMax );
		}
		if( AttackData.IsMelee( m_attackData.GetAttackType() ) )
		{
			m_previousMeleeHitTimeStamp = currentTimeStamp;
			if( GetIsOwnerResistantToMeleeHitReaction() && attackSubType != gamedataAttackSubtype.DeflectAttack )
			{
				meleeHitReactionResist = true;
			}
			if( m_attackData.GetInstigator().IsPlayer() )
			{
				meleePlayerExhausted = StatusEffectSystem.ObjectHasStatusEffect( m_attackData.GetInstigator(), T"BaseStatusEffect.PlayerExhausted" );
			}
			if( attackSubType == gamedataAttackSubtype.DeflectAttack && PlayerDevelopmentSystem.GetData( m_attackData.GetInstigator() ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Reflexes_Right_Perk_2_2 ) )
			{
				if( hitReactionMin < 2 )
				{
					hitReactionMin = 2;
				}
				hitReactionMax = 3;
			}
		}
		else
		{
			m_previousRangedHitTimeStamp = currentTimeStamp;
		}
		if( AttackData.IsLightMelee( m_attackData.GetAttackType() ) )
		{
			NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.HitByLightAttack, 1 );
		}
		else if( AttackData.IsStrongMelee( m_attackData.GetAttackType() ) || attackSubType == gamedataAttackSubtype.FinalAttack )
		{
			NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.HitByStrongAttack, 1 );
		}
		if( attackSubType == gamedataAttackSubtype.BlockAttack )
		{
			NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.HitByBlockAttack, 1 );
		}
		if( attackSubType == gamedataAttackSubtype.FinalAttack )
		{
			NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.HitByFinalComboAttack, 1 );
		}
		if( ( ( ( !( meleeHitReactionResist ) && !( m_immuneToKnockDown ) ) && !( powerDifferenceTooHigh ) ) && !( meleePlayerExhausted ) ) && ( ( IsStrongExplosion( m_attackData ) || m_attackData.HasFlag( hitFlag.HighSpeedMelee ) ) || ( ( ( ( m_knockdownDamageThreshold > 0.0 ) && ( m_knockdownImpulseThreshold > 0.0 ) ) && ( ( hitReactionMax >= 3 ) || ( hitReactionMax == -1 ) ) ) && ( ( hitReactionMin >= 3 ) || ( ( m_cumulatedPhysicalImpulse >= m_knockdownImpulseThreshold ) && ( m_cumulatedDamages >= m_knockdownDamageThreshold ) ) ) ) ) )
		{
			if( ( m_specificHitTimeout > currentTimeStamp ) && m_lastHitReactionPlayed == EAILastHitReactionPlayed.Knockdown )
			{
				return animHitReactionType.Twitch;
			}
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.Knockdown;
			m_specificHitTimeout = currentTimeStamp + m_knockdownDamageDuration;
			return animHitReactionType.Knockdown;
		}
		if( m_attackData.WasDeflected() )
		{
			attackWeaponType = ScriptedPuppet.GetWeaponRight( m_attackData.GetSource() ).GetWeaponRecord().ItemType().Type();
			if( attackWeaponType == gamedataItemType.Cyb_StrongArms )
			{
				NPCPuppet.StartReplicatedEffectEvent( GetOwner(), 'strong_arms_block' );
			}
			if( GetHasKerenzikov() && !( GetCanBlock() ) )
			{
				shouldEvade = true;
			}
			if( ( meleePlayerExhausted || !( CanAttackGuardBreak( attackWeaponType, guardBreakImpulse ) ) ) || shouldEvade )
			{
				m_previousMeleeHitTimeStamp = 0.0;
				NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.DeflectedAttack, 1 );
				return animHitReactionType.Parry;
			}
			m_animVariation = 1;
			NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.WasGuardBreaked, 1 );
			return animHitReactionType.GuardBreak;
		}
		if( !( meleeHitReactionResist ) && m_attackData.WasBlocked() )
		{
			attackWeaponType = ScriptedPuppet.GetWeaponRight( m_attackData.GetSource() ).GetWeaponRecord().ItemType().Type();
			if( attackWeaponType == gamedataItemType.Cyb_StrongArms )
			{
				NPCPuppet.StartReplicatedEffectEvent( GetOwner(), 'strong_arms_block' );
			}
			if( GetHasKerenzikov() && !( GetCanBlock() ) )
			{
				shouldEvade = true;
			}
			if( ( meleePlayerExhausted || !( CanAttackGuardBreak( attackWeaponType, guardBreakImpulse ) ) ) || shouldEvade )
			{
				NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.BlockedAttack, 1 );
				return animHitReactionType.Block;
			}
			m_animVariation = 0;
			NPCPuppet.SendNPCHitDataTrackingRequest( m_ownerNPC, ENPCTelemetryData.WasGuardBreaked, 1 );
			return animHitReactionType.GuardBreak;
		}
		if( AttackData.IsQuickMelee( m_attackData.GetAttackType() ) )
		{
			m_quickMeleeCooldown = currentTimeStamp + ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( m_ownerNPC.GetGame() ).GetLocalPlayerMainGameObject() ) ).GetQuickMeleeCooldown();
			if( meleeHitReactionResist )
			{
				return animHitReactionType.Twitch;
			}
			if( ( ( !( powerDifferenceTooHigh ) && ( m_quickMeleeCooldown <= currentTimeStamp ) ) && RPGManager.HasStatFlag( m_attackData.GetInstigator(), gamedataStatType.CanQuickMeleeStagger ) ) && ( hitReactionMax >= 2 ) )
			{
				m_lastHitReactionPlayed = EAILastHitReactionPlayed.Stagger;
				m_specificHitTimeout = currentTimeStamp + m_staggerDamageDuration;
				m_previousRangedHitTimeStamp = currentTimeStamp;
				return animHitReactionType.Stagger;
			}
			else if( ( hitReactionMax < 2 ) && ( hitReactionMin >= 1 ) )
			{
				return animHitReactionType.Impact;
			}
			else
			{
				return animHitReactionType.Twitch;
			}
		}
		if( newHitEvent.attackData.HasFlag( hitFlag.StunApplied ) )
		{
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.None;
			m_specificHitTimeout = currentTimeStamp + m_staggerDamageDurationMelee;
			return animHitReactionType.None;
		}
		if( ( ( ( ( !( meleeHitReactionResist ) && !( powerDifferenceTooHigh ) ) && !( meleePlayerExhausted ) ) && ( m_staggerDamageThreshold > 0.0 ) ) && ( ( hitReactionMax >= 2 ) || ( hitReactionMax == -1 ) ) ) && ( ( hitReactionMin >= 2 ) || ( m_cumulatedDamages >= m_staggerDamageThreshold ) ) )
		{
			if( ( m_specificHitTimeout > currentTimeStamp ) && ( ( m_lastHitReactionPlayed == EAILastHitReactionPlayed.Stagger || m_lastHitReactionPlayed == EAILastHitReactionPlayed.Knockdown ) || AIActionHelper.IsCurrentlyCrouching( m_ownerPuppet ) ) )
			{
				return animHitReactionType.Twitch;
			}
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.Stagger;
			m_specificHitTimeout = currentTimeStamp + m_staggerDamageDurationMelee;
			return animHitReactionType.Stagger;
		}
		if( ( ( ( !( meleeHitReactionResist ) && ( m_impactDamageThreshold > 0.0 ) ) && ( ( hitReactionMax >= 1 ) || ( hitReactionMax == -1 ) ) ) && ( ( hitReactionMin >= 1 ) || ( m_cumulatedDamages >= m_impactDamageThreshold ) ) ) || ( ( m_ownerNPC.GetNPCType() == gamedataNPCType.Drone && m_attackData.HasFlag( hitFlag.SuccessfulAttack ) ) && ( m_attackData.GetAttackType() == gamedataAttackType.Hack || ( m_attackData.GetAttackType() == gamedataAttackType.Explosion && !( VehicleComponent.IsMountedToVehicle( m_ownerNPC.GetGame(), m_ownerID ) ) ) ) ) )
		{
			if( m_specificHitTimeout > currentTimeStamp )
			{
				return animHitReactionType.Twitch;
			}
			m_lastHitReactionPlayed = EAILastHitReactionPlayed.Impact;
			m_specificHitTimeout = currentTimeStamp + m_impactDamageDurationMelee;
			return animHitReactionType.Impact;
		}
		return animHitReactionType.Twitch;
	}

	private final function CanAttackGuardBreak( weaponType : gamedataItemType, guardBreakImpulse : Float ) : Bool
	{
		m_cumulatedGuardBreakImpulse += guardBreakImpulse;
		m_cumulatedEvadeBreakImpulse += ( 100.0 / m_attackerWeaponKnockdownImpulseForEvadeCumulation );
		if( GetSubAttackSubType() == gamedataAttackSubtype.DeflectAttack )
		{
			return true;
		}
		if( ( m_attackerWeaponKnockdownImpulseForEvade > m_ownerWeaponKnockdownImpulseForEvade ) || ( ( m_ownerWeapon.GetWeaponRecord().ItemType().Type() == gamedataItemType.Wea_Fists && weaponType != gamedataItemType.Wea_Fists ) && weaponType != gamedataItemType.Cyb_StrongArms ) )
		{
			if( GetHasKerenzikov() )
			{
				if( m_cumulatedEvadeBreakImpulse >= ( m_totalStamina / 2.0 ) )
				{
					m_cumulatedGuardBreakImpulse = 0.0;
					m_cumulatedEvadeBreakImpulse = 0.0;
					StartGuardBreakCooldown();
					return true;
				}
				else
				{
					return false;
				}
			}
			else
			{
				m_cumulatedGuardBreakImpulse = 0.0;
				m_cumulatedEvadeBreakImpulse = 0.0;
				StartGuardBreakCooldown();
				return true;
			}
		}
		if( !( GetCanBlock() ) && GetHasKerenzikov() )
		{
			if( m_cumulatedEvadeBreakImpulse >= ( m_totalStamina / 2.0 ) )
			{
				m_cumulatedGuardBreakImpulse = 0.0;
				m_cumulatedEvadeBreakImpulse = 0.0;
				StartGuardBreakCooldown();
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			if( m_cumulatedGuardBreakImpulse >= m_totalStamina )
			{
				if( GetHasKerenzikov() )
				{
					if( m_cumulatedEvadeBreakImpulse >= ( m_totalStamina / 2.0 ) )
					{
						m_cumulatedGuardBreakImpulse = 0.0;
						m_cumulatedEvadeBreakImpulse = 0.0;
						StartGuardBreakCooldown();
						return true;
					}
					else
					{
						return false;
					}
				}
				m_cumulatedGuardBreakImpulse = 0.0;
				m_cumulatedEvadeBreakImpulse = 0.0;
				StartGuardBreakCooldown();
				return true;
			}
			return false;
		}
	}

	private final function StartGuardBreakCooldown()
	{
		var cdRequest : RegisterNewCooldownRequest;
		var cs : ICooldownSystem;
		cs = CSH.GetCooldownSystem( m_ownerNPC );
		cdRequest.cooldownName = 'GuardBreak';
		cdRequest.duration = m_guardBreakImpulseReset;
		cdRequest.owner = m_ownerNPC;
		cs.Register( cdRequest );
		cdRequest.cooldownName = 'DodgeCooldown';
		cs.Register( cdRequest );
		cdRequest.cooldownName = 'DodgeHitCooldown';
		cs.Register( cdRequest );
	}

	private final function IsStrongExplosion( attackData : AttackData ) : Bool
	{
		var explosionCentrum : Vector4;
		var explosionImpulse : Float;
		if( AttackData.IsExplosion( attackData.GetAttackType() ) && !( attackData.HasFlag( hitFlag.WeakExplosion ) ) )
		{
			explosionCentrum = m_attackData.GetSource().GetWorldPosition();
			explosionImpulse = m_cumulatedPhysicalImpulse * ( 3.0 / Vector4.Distance( m_ownerPuppet.GetWorldPosition(), explosionCentrum ) );
			if( Vector4.Distance( m_ownerPuppet.GetWorldPosition(), explosionCentrum ) <= 3.0 )
			{
				return true;
			}
			if( explosionImpulse >= m_knockdownImpulseThreshold )
			{
				return true;
			}
		}
		return false;
	}

	private final function SendDataToAIBehavior( reactionPlayed : animHitReactionType )
	{
		var hitAIEvent : StimuliEvent;
		var canRagdoll : Bool;
		hitAIEvent = new StimuliEvent;
		if( ( reactionPlayed == animHitReactionType.Death || m_ownerNPC.IsAboutToBeDefeated() ) && m_ownerPuppet.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().AIAction.ownerInTumble ) )
		{
			hitAIEvent.name = 'ForcedRagdoll';
		}
		else
		{
			switch( reactionPlayed )
			{
				case animHitReactionType.Impact:
					hitAIEvent.name = 'Impact';
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_Hard" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_Hard" );
				}
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_VeryHard" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_VeryHard" );
				}
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.Cloaked" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.Cloaked" );
				}
				break;
				case animHitReactionType.Pain:
					hitAIEvent.name = 'Pain';
				break;
				case animHitReactionType.Stagger:
					if( ( ( m_previousRagdollTimeStamp != GetCurrentTime() ) && m_ownerNPC.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().PuppetState.InAirAnimation ) ) && ( ScriptedPuppet.CanRagdoll( m_ownerNPC ) || m_ownerNPC.IsRagdolling() ) )
					{
						m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'InAir_RecivedHit' ) );
						GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetOverridenHitDirection() * m_ragdollImpulse, 5.0 ), 0.1, false );
						m_previousRagdollTimeStamp = GetCurrentTime();
					}
				hitAIEvent.name = 'Stagger';
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_Hard" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_Hard" );
				}
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_VeryHard" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_VeryHard" );
				}
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.Cloaked" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.Cloaked" );
				}
				break;
				case animHitReactionType.Knockdown:
					if( ( ( m_previousRagdollTimeStamp != GetCurrentTime() ) && ScriptedPuppet.CanRagdoll( m_ownerNPC ) ) || m_ownerNPC.IsRagdolling() )
					{
						if( m_ownerNPC.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().PuppetState.InAirAnimation ) )
						{
							m_ownerNPC.QueueEvent( CreateForceRagdollEvent( 'InAir_RecivedHit' ) );
							GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetOverridenHitDirection() * m_ragdollImpulse, 5.0 ), 0.1, false );
							m_previousRagdollTimeStamp = GetCurrentTime();
						}
						else if( m_ragdollImpulse > ( 1.5 * m_knockdownImpulseThreshold ) )
						{
							m_ownerNPC.SetIndividualTimeDilation( 'VeryStrongHit', m_ragdollImpulse / m_knockdownImpulseThreshold, 0.5, '', '' );
							GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateForceRagdollEvent( 'VeryStrongHit' ), 0.1, false );
							GameInstance.GetDelaySystem( m_ownerNPC.GetGame() ).DelayEvent( m_ownerNPC, CreateRagdollApplyImpulseEvent( GetHitPosition(), GetOverridenHitDirection() * m_ragdollImpulse, 5.0 ), 0.2, false );
						}
					}
				hitAIEvent.name = 'Knockdown';
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_Hard" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_Hard" );
				}
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_VeryHard" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.SpecialCloaked_VeryHard" );
				}
				if( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.Cloaked" ) )
				{
					StatusEffectHelper.RemoveStatusEffect( m_ownerNPC, T"BaseStatusEffect.Cloaked" );
				}
				break;
				case animHitReactionType.Block:
					hitAIEvent.name = 'Block';
				break;
				case animHitReactionType.GuardBreak:
					hitAIEvent.name = 'GuardBreak';
				break;
				case animHitReactionType.Parry:
					hitAIEvent.name = 'Parry';
				break;
				case animHitReactionType.Death:
					canRagdoll = ScriptedPuppet.CanRagdoll( m_ownerPuppet );
				if( m_ownerNPC.ShouldSkipDeathAnimation() )
				{
					if( canRagdoll )
					{
						hitAIEvent.name = 'ForcedRagdoll';
					}
					break;
				}
				if( !( canRagdoll ) )
				{
					hitAIEvent.name = 'Death';
				}
				else if( StatusEffectSystem.ObjectHasStatusEffectOfType( GetOwner(), gamedataStatusEffectType.Knockdown ) || m_ownerNPC.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().PuppetState.InAirAnimation ) )
				{
					hitAIEvent.name = 'ForcedRagdoll';
				}
				else if( m_ownerPuppet.GetPuppetStateBlackboard().GetBool( GetAllBlackboardDefs().PuppetState.ForceRagdollOnDeath ) )
				{
					if( ( ( ( GetIsOwnerImmuneToExtendedHitReaction() == 0.0 ) && ( ( ( Int32 )( m_hitIntensity ) ) <= 1 ) ) && ( m_animVariation < 13 ) ) && ( ( ( m_deathRegisteredTime + 0.5 ) >= GetCurrentTime() ) || ( ( m_extendedDeathRegisteredTime + 0.5 ) >= GetCurrentTime() ) ) )
					{
						hitAIEvent.name = 'Death';
					}
					else
					{
						hitAIEvent.name = 'ForcedRagdoll';
					}
				}
				else
				{
					hitAIEvent.name = 'Death';
				}
				break;
				default:
					return;
			}
		}
		hitAIEvent.id += ( ( Uint32 )( 1 ) );
		m_currentStimId = hitAIEvent.id;
		m_lastStimName = hitAIEvent.name;
		if( reactionPlayed == animHitReactionType.Death || reactionPlayed == animHitReactionType.Ragdoll )
		{
			m_deathStimName = hitAIEvent.name;
		}
		GetOwner().QueueEvent( hitAIEvent );
	}

	private function RecalculateHitReactionValsForHacks( currentTimeStamp : Float, out hitReactionMin : Int32, out hitReactionMax : Int32 )
	{
		var preventQhStaggerDurationVal : Float;
		var preventQhImpactDurationVal : Float;
		preventQhStaggerDurationVal = TDB.GetFloat( T"BaseStats.PreventQuickhackStaggerDuration.staggerValue" );
		preventQhImpactDurationVal = TDB.GetFloat( T"BaseStats.PreventQuickhackStaggerDuration.impactValue" );
		if( ( preventQhStaggerDurationVal > 0.0 ) && ( ( currentTimeStamp - m_previousHackStaggerTimeStamp ) <= preventQhStaggerDurationVal ) )
		{
			if( !( StatusEffectSystem.ObjectHasStatusEffect( m_ownerNPC, T"BaseStatusEffect.PreventQHStaggerAnimation" ) ) )
			{
				StatusEffectHelper.ApplyStatusEffect( m_ownerNPC, T"BaseStatusEffect.PreventQHStaggerAnimation" );
			}
			hitReactionMax = 1;
			if( ( preventQhImpactDurationVal > 0.0 ) && ( ( currentTimeStamp - m_previousHackImpactTimeStamp ) <= preventQhImpactDurationVal ) )
			{
				hitReactionMax = 0;
			}
			else
			{
				m_previousHackImpactTimeStamp = currentTimeStamp;
			}
		}
		else
		{
			m_previousHackStaggerTimeStamp = currentTimeStamp;
		}
		hitReactionMin = Min( hitReactionMin, hitReactionMax );
	}

	protected final function SendMechDataToAIBehavior( reactionPlayed : animHitReactionType )
	{
		var hitAIEvent : StimuliEvent;
		hitAIEvent = new StimuliEvent;
		switch( reactionPlayed )
		{
			case animHitReactionType.Impact:
				hitAIEvent.name = 'Impact';
			break;
			case animHitReactionType.Stagger:
				hitAIEvent.name = 'Stagger';
			break;
			case animHitReactionType.Death:
				hitAIEvent.name = 'Death';
			break;
			default:
				return;
		}
		hitAIEvent.id += ( ( Uint32 )( 1 ) );
		m_currentStimId = hitAIEvent.id;
		m_lastStimName = hitAIEvent.name;
		if( reactionPlayed == animHitReactionType.Death || reactionPlayed == animHitReactionType.Ragdoll )
		{
			m_deathStimName = hitAIEvent.name;
		}
		GetOwner().QueueEvent( hitAIEvent );
	}

	protected final function SetHitSource( attackType : gamedataAttackType )
	{
		if( ( ( attackType == gamedataAttackType.Direct || attackType == gamedataAttackType.Ranged ) || attackType == gamedataAttackType.Explosion ) || attackType == gamedataAttackType.Hack )
		{
			SetHitReactionSource( EAIHitSource.Ranged );
		}
		else if( attackType == gamedataAttackType.WhipAttack || attackType == gamedataAttackType.ChargedWhipAttack )
		{
			SetHitReactionSource( EAIHitSource.MeleeSharp );
		}
		else if( attackType == gamedataAttackType.Melee || attackType == gamedataAttackType.StrongMelee )
		{
			SetHitReactionSource( EAIHitSource.MeleeBlunt );
		}
		else if( attackType == gamedataAttackType.QuickMelee )
		{
			if( RPGManager.HasStatFlag( m_attackData.GetInstigator(), gamedataStatType.CanQuickMeleeStagger ) && ( m_quickMeleeCooldown <= GetCurrentTime() ) )
			{
				SetHitReactionSource( EAIHitSource.QuickMelee );
			}
			else
			{
				SetHitReactionSource( EAIHitSource.MeleeBlunt );
			}
		}
	}

	private final function SetAnimVariation( out hitDirection : Int32, out hitReactionZone : EHitReactionZone )
	{
		var excludedVariation : Int32;
		var currentBodyPart : Int32;
		var attackDirection : gamedataMeleeAttackDirection;
		var i : Int32;
		if( m_reactionType == animHitReactionType.GuardBreak )
		{
			if( AttackData.IsMelee( m_attackData.GetAttackType() ) )
			{
				attackDirection = ( ( Attack_Melee_Record )( m_attackData.GetAttackDefinition().GetRecord() ) ).AttackDirection().Direction().Type();
				switch( attackDirection )
				{
					case gamedataMeleeAttackDirection.LeftToRight:
						m_reactionType = animHitReactionType.Stagger;
					m_overridenHitDirection = true;
					switch( hitDirection )
					{
						case 1:
							hitDirection = 4;
						break;
						case 2:
							hitDirection = 1;
						break;
						case 3:
							hitDirection = 2;
						break;
						case 4:
							hitDirection = 3;
						break;
						default:
							hitDirection = 3;
						return;
					}
					break;
					case gamedataMeleeAttackDirection.RightToLeft:
						m_reactionType = animHitReactionType.Stagger;
					m_overridenHitDirection = true;
					switch( hitDirection )
					{
						case 1:
							hitDirection = 2;
						break;
						case 2:
							hitDirection = 3;
						break;
						case 3:
							hitDirection = 4;
						break;
						case 4:
							hitDirection = 1;
						break;
						default:
							hitDirection = 1;
						return;
					}
					break;
					default:
						return;
				}
			}
		}
		else if( m_reactionType != animHitReactionType.Parry )
		{
			if( AttackData.IsMelee( m_attackData.GetAttackType() ) && m_reactionType != animHitReactionType.Twitch )
			{
				if( m_reactionType != animHitReactionType.Death )
				{
					excludedVariation = -1;
					currentBodyPart = ReactionZoneEnumToBodyPartID( HitShapeUserDataBase.GetHitReactionZone( GetHitShapeUserData() ) );
					attackDirection = ( ( Attack_Melee_Record )( m_attackData.GetAttackDefinition().GetRecord() ) ).AttackDirection().Direction().Type();
					for( i = 0; i < m_previousAnimHitReactionArray.Size(); i += 1 )
					{
						if( ( m_previousAnimHitReactionArray[ i ].hitBodyPart == currentBodyPart ) && ( m_previousAnimHitReactionArray[ i ].attackDirection == ( ( Int32 )( attackDirection ) ) ) )
						{
							excludedVariation = m_previousAnimHitReactionArray[ i ].animVariation;
						}
					}
					switch( attackDirection )
					{
						case gamedataMeleeAttackDirection.Center:
							m_animVariation = 0 + RandDifferent( excludedVariation, 3 );
						break;
						case gamedataMeleeAttackDirection.DownToUp:
							m_animVariation = 3 + RandDifferent( excludedVariation - 3, 3 );
						if( hitReactionZone == EHitReactionZone.LegLeft || hitReactionZone == EHitReactionZone.LegRight )
						{
							hitReactionZone = EHitReactionZone.Head;
						}
						break;
						case gamedataMeleeAttackDirection.LeftDownToRightUp:
							m_animVariation = 6 + RandDifferent( excludedVariation - 6, 3 );
						m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 4;
							break;
							case 2:
								hitDirection = 1;
							break;
							case 3:
								hitDirection = 2;
							break;
							case 4:
								hitDirection = 3;
							break;
							default:
								hitDirection = 3;
							break;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.LeftToRight:
							m_animVariation = 9 + RandDifferent( excludedVariation - 9, 3 );
						m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 4;
							break;
							case 2:
								hitDirection = 1;
							break;
							case 3:
								hitDirection = 2;
							break;
							case 4:
								hitDirection = 3;
							break;
							default:
								hitDirection = 3;
							break;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitDirection = 4;
								break;
								case 2:
									hitDirection = 1;
								break;
								case 3:
									hitDirection = 2;
								break;
								case 4:
									hitDirection = 3;
								break;
								default:
									hitDirection = 3;
								break;
							}
							;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.LeftUpToRightDown:
							m_animVariation = 12 + RandDifferent( excludedVariation - 12, 3 );
						m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 4;
							break;
							case 2:
								hitDirection = 1;
							break;
							case 3:
								hitDirection = 2;
							break;
							case 4:
								hitDirection = 3;
							break;
							default:
								hitDirection = 3;
							break;
						}
						if( m_reactionType == animHitReactionType.Knockdown )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								default:
									break;
							}
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.RightDownToLeftUp:
							m_animVariation = 15 + RandDifferent( excludedVariation - 15, 3 );
						m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 2;
							break;
							case 2:
								hitDirection = 3;
							break;
							case 3:
								hitDirection = 4;
							break;
							case 4:
								hitDirection = 1;
							break;
							default:
								hitDirection = 1;
							break;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.RightToLeft:
							m_animVariation = 18 + RandDifferent( excludedVariation - 18, 3 );
						m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 2;
							break;
							case 2:
								hitDirection = 3;
							break;
							case 3:
								hitDirection = 4;
							break;
							case 4:
								hitDirection = 1;
							break;
							default:
								hitDirection = 1;
							break;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitDirection = 2;
								break;
								case 2:
									hitDirection = 3;
								break;
								case 3:
									hitDirection = 4;
								break;
								case 4:
									hitDirection = 1;
								break;
								default:
									hitDirection = 1;
								break;
							}
							;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.RightUpToLeftDown:
							m_animVariation = 21 + RandDifferent( excludedVariation - 21, 3 );
						m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 2;
							break;
							case 2:
								hitDirection = 3;
							break;
							case 3:
								hitDirection = 4;
							break;
							case 4:
								hitDirection = 1;
							break;
							default:
								hitDirection = 1;
							break;
						}
						if( m_reactionType == animHitReactionType.Knockdown )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								default:
									break;
							}
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.UpToDown:
							m_animVariation = 24 + RandDifferent( excludedVariation - 24, 3 );
						if( m_reactionType == animHitReactionType.Knockdown )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								default:
									break;
							}
						}
						break;
						default:
							return;
					}
				}
				else
				{
					m_animVariation = RandRange( 0, 3 );
					attackDirection = ( ( Attack_Melee_Record )( m_attackData.GetAttackDefinition().GetRecord() ) ).AttackDirection().Direction().Type();
					switch( attackDirection )
					{
						case gamedataMeleeAttackDirection.UpToDown:
							if( m_reactionType == animHitReactionType.Knockdown || m_hitIntensity == EAIHitIntensity.Heavy )
							{
								switch( hitDirection )
								{
									case 1:
										hitReactionZone = EHitReactionZone.LegLeft;
									break;
									case 2:
										hitReactionZone = EHitReactionZone.LegLeft;
									break;
									case 3:
										hitReactionZone = EHitReactionZone.LegRight;
									break;
									case 4:
										hitReactionZone = EHitReactionZone.LegRight;
									break;
									default:
										break;
								}
							}
						break;
						case gamedataMeleeAttackDirection.LeftDownToRightUp:
							switch( hitDirection )
							{
								case 1:
									hitDirection = 4;
								break;
								case 2:
									hitDirection = 1;
								break;
								case 3:
									hitDirection = 2;
								break;
								case 4:
									hitDirection = 3;
								break;
								default:
									hitDirection = 3;
								break;
							}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.LeftToRight:
							m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 4;
							break;
							case 2:
								hitDirection = 1;
							break;
							case 3:
								hitDirection = 2;
							break;
							case 4:
								hitDirection = 3;
							break;
							default:
								hitDirection = 3;
							break;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitDirection = 4;
								break;
								case 2:
									hitDirection = 1;
								break;
								case 3:
									hitDirection = 2;
								break;
								case 4:
									hitDirection = 3;
								break;
								default:
									hitDirection = 3;
								break;
							}
							;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.LeftUpToRightDown:
							switch( hitDirection )
							{
								case 1:
									hitDirection = 4;
								break;
								case 2:
									hitDirection = 1;
								break;
								case 3:
									hitDirection = 2;
								break;
								case 4:
									hitDirection = 3;
								break;
								default:
									hitDirection = 3;
								break;
							}
						if( m_reactionType == animHitReactionType.Knockdown || m_hitIntensity == EAIHitIntensity.Heavy )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								default:
									break;
							}
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.RightDownToLeftUp:
							switch( hitDirection )
							{
								case 1:
									hitDirection = 2;
								break;
								case 2:
									hitDirection = 3;
								break;
								case 3:
									hitDirection = 4;
								break;
								case 4:
									hitDirection = 1;
								break;
								default:
									hitDirection = 1;
								break;
							}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.RightToLeft:
							m_overridenHitDirection = true;
						switch( hitDirection )
						{
							case 1:
								hitDirection = 2;
							break;
							case 2:
								hitDirection = 3;
							break;
							case 3:
								hitDirection = 4;
							break;
							case 4:
								hitDirection = 1;
							break;
							default:
								hitDirection = 1;
							break;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitDirection = 2;
								break;
								case 2:
									hitDirection = 3;
								break;
								case 3:
									hitDirection = 4;
								break;
								case 4:
									hitDirection = 1;
								break;
								default:
									hitDirection = 1;
								break;
							}
							;
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								default:
									break;
							}
						}
						break;
						case gamedataMeleeAttackDirection.RightUpToLeftDown:
							switch( hitDirection )
							{
								case 1:
									hitDirection = 2;
								break;
								case 2:
									hitDirection = 3;
								break;
								case 3:
									hitDirection = 4;
								break;
								case 4:
									hitDirection = 1;
								break;
								default:
									hitDirection = 1;
								break;
							}
						if( m_reactionType == animHitReactionType.Knockdown || m_hitIntensity == EAIHitIntensity.Heavy )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.LegRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.LegLeft;
								break;
								default:
									break;
							}
						}
						if( hitReactionZone == EHitReactionZone.Abdomen )
						{
							switch( hitDirection )
							{
								case 1:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 2:
									hitReactionZone = EHitReactionZone.ChestRight;
								break;
								case 3:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								case 4:
									hitReactionZone = EHitReactionZone.ChestLeft;
								break;
								default:
									break;
							}
						}
						break;
						default:
							return;
					}
				}
			}
			else
			{
				m_animVariation = RandRange( 0, 3 );
			}
		}
	}

	protected final function StoreHitData( attackAngle : Int32, hitSeverity : EAIHitIntensity, reactionType : animHitReactionType, bodyPart : EHitReactionZone, variation : Int32 )
	{
		var scriptStoredHitData : ScriptHitData;
		if( attackAngle != 0 )
		{
			m_animHitReaction.hitDirection = attackAngle;
		}
		else
		{
			m_animHitReaction.hitDirection = 4;
		}
		if( !( m_attackData.GetInstigator().IsPlayer() ) )
		{
			if( ( m_hitShapeData.hitShapeName == 'ChestLeft' || m_hitShapeData.hitShapeName == 'ChestRight' ) || m_hitShapeData.hitShapeName == 'Abdomen' )
			{
				m_hitShapeData.hitShapeName = 'Head';
			}
		}
		m_animHitReaction.hitIntensity = ( ( Int32 )( hitSeverity ) );
		SetHitReactionType( reactionType );
		m_animHitReaction.animVariation = ( ( Int32 )( variation ) );
		if( IsStrongExplosion( m_attackData ) )
		{
			m_animHitReaction.hitBodyPart = ( ( Int32 )( EAIHitBodyPart.Belly ) );
		}
		else
		{
			m_animHitReaction.hitBodyPart = ReactionZoneEnumToBodyPartID( bodyPart );
		}
		if( ( ( m_deathRegisteredTime == 0.0 ) && reactionType == animHitReactionType.Death ) && !( m_deathHasBeenPlayed ) )
		{
			m_deathRegisteredTime = GetCurrentTime();
			m_extendedDeathDelayRegisteredTime = EngineTime.ToFloat( GetEngineTime() );
		}
		if( reactionType != animHitReactionType.Twitch )
		{
			m_lastAnimHitReaction.hitBodyPart = m_animHitReaction.hitBodyPart;
			m_lastAnimHitReaction.hitDirection = m_animHitReaction.hitDirection;
			m_lastAnimHitReaction.hitType = m_animHitReaction.hitType;
			m_lastAnimHitReaction.stance = m_animHitReaction.stance;
			m_lastAnimHitReaction.hitIntensity = m_animHitReaction.hitIntensity;
			m_lastAnimHitReaction.animVariation = m_animHitReaction.animVariation;
			m_lastAnimHitReaction.hitSource = m_animHitReaction.hitSource;
			m_lastAnimHitReaction.useInitialRotation = m_animHitReaction.useInitialRotation;
			if( AttackData.IsLightMelee( m_attackData.GetAttackType() ) || AttackData.IsStrongMelee( m_attackData.GetAttackType() ) )
			{
				m_meleeHitCount += 1;
				if( AttackData.IsStrongMelee( m_attackData.GetAttackType() ) )
				{
					m_strongMeleeHitCount += 1;
				}
			}
			else
			{
				m_meleeHitCount = 0;
				m_strongMeleeHitCount = 0;
			}
		}
		SetHitStimEvent( m_animHitReaction );
		if( m_previousAnimHitReactionArray.Size() > 3 )
		{
			m_previousAnimHitReactionArray.Erase( 0 );
		}
		scriptStoredHitData.animVariation = m_animHitReaction.animVariation;
		scriptStoredHitData.attackDirection = ( ( Int32 )( ( ( Attack_Melee_Record )( m_attackData.GetAttackDefinition().GetRecord() ) ).AttackDirection().Direction().Type() ) );
		m_attackDirectionToInt = scriptStoredHitData.attackDirection;
		scriptStoredHitData.hitBodyPart = m_animHitReaction.hitBodyPart;
		m_previousAnimHitReactionArray.PushBack( scriptStoredHitData );
	}

	protected final function SendTwitchDataToAnimationGraph()
	{
		m_reactionType = animHitReactionType.Twitch;
		SetHitReactionType( m_reactionType );
		if( m_animHitReaction.animVariation >= 3 )
		{
			m_animHitReaction.animVariation = RandRange( 0, 3 );
		}
		AnimationControllerComponent.ApplyFeatureToReplicate( GetOwner(), 'hit', m_animHitReaction );
		AnimationControllerComponent.PushEventToReplicate( GetOwner(), 'hit' );
	}

	protected final function SendTwitchDataToPlayerAnimationGraph( playerObject : weak< GameObject > )
	{
		SetHitReactionType( animHitReactionType.Twitch );
		AnimationControllerComponent.ApplyFeatureToReplicate( playerObject, 'hit', m_animHitReaction );
		AnimationControllerComponent.PushEventToReplicate( playerObject, 'hit' );
	}


	private var m_hitReactionData : AnimFeature_HitReactionsData;

	private function SetHitStimEvent( hitData : AnimFeature_HitReactionsData )
	{
		m_hitReactionData = hitData;
	}

	public const function GetHitStimEvent() : AnimFeature_HitReactionsData
	{
		return m_hitReactionData;
	}

	public const function GetLastHitTimeStamp() : Float
	{
		if( m_previousMeleeHitTimeStamp > m_previousHitTime )
		{
			return m_previousMeleeHitTimeStamp;
		}
		return m_previousHitTime;
	}

	protected event OnClearHitStimEvent( evt : ClearHitStimEvent )
	{
		m_hitReactionData = NULL;
	}

	public static function ClearHitStim( obj : GameObject )
	{
		var evt : ClearHitStimEvent;
		evt = new ClearHitStimEvent;
		obj.QueueEvent( evt );
	}

}

class ClearHitStimEvent extends Event
{
}

class ClearFearOnHitEvent extends Event
{
}

class WoundedInstigated extends Event
{
	var bodyPart : EHitReactionZone;
}

class DismembermentInstigated extends Event
{
	var target : weak< GameObject >;
	var targetPosition : Vector4;
	var attackPosition : Vector4;
	var bodyPart : EHitReactionZone;
	var weaponRecord : WeaponItem_Record;
	var attackType : gamedataAttackType;
	var attackSubtype : gamedataAttackSubtype;
	var timeSinceDeath : Float;
	var timeSinceDefeat : Float;
	var targetIsDead : Bool;
	var targetIsDefeated : Bool;
	var attackIsExplosion : Bool;
}

class PlayGrandFinaleVFX extends Event
{
	var target : weak< ScriptedPuppet >;
}

class PlayOnePunchVFX extends Event
{
	var target : weak< GameObject >;
	var instigator : weak< GameObject >;
}

