import class CacheData extends IScriptable
{
	var targetImmortalityMode : gameGodModeType;
	var TEMP_ImmortalityCached : Bool;
	var chargeDamageMultiplier : Float;
	var logFlags : Int64;
}

importonly class HitDebugData extends IScriptable
{
	import var sourceHitPosition : Vector4;
	import var targetHitPosition : Vector4;
	import var attackTime : Float;
	import var instigator : weak< GameObject >;
	import var source : weak< GameObject >;
	import var target : weak< GameObject >;
	import var instigatorName : CName;
	import var sourceName : CName;
	import var targetName : CName;
	import var sourceAttackDebugData : AttackDebugData;
	import var sourceWeaponName : CName;
	import var sourceAttackName : CName;
	import var calculatedDamages : array< DamageDebugData >;
	import var appliedDamages : array< DamageDebugData >;
	import var hitType : CName;
	import var hitFlags : CName;
}

importonly class DamageDebugData extends IScriptable
{
	import var statPoolType : gamedataStatPoolType;
	import var damageType : gamedataDamageType;
	import var value : Float;
}

importonly class ServerHitData extends IScriptable
{
	import var damageInfos : array< DamageInfo >;
	import var instigator : weak< GameObject >;
}

importonly class ServerKillData extends IScriptable
{
	import var killInfo : KillInfo;
}

struct previewTargetStruct
{
	var currentlyTrackedTarget : weak< GameObject >;
	var currentBodyPart : EHitReactionZone;
	var currentlyHittingBreach : Bool;
}

import enum gameDamageCallbackType
{
	HitTriggered,
	MissTriggered,
	HitReceived,
	PipelineProcessed,
	COUNT,
	INVALID,
}

import enum gameDamagePipelineStage
{
	PreProcess,
	Process,
	ProcessHitReaction,
	PostProcess,
	COUNT,
	INVALID,
}

import enum DMGPipelineType
{
	None,
	Damage,
	ProjectedDamage,
	All,
}

import operator|( a : DMGPipelineType, b : DMGPipelineType ) : DMGPipelineType;

importonly abstract class IDamageSystem extends IReplicatedGameSystem
{
}

import final class DamageSystem extends IDamageSystem
{
	var m_previewTarget : previewTargetStruct;
	var m_previewLock : Bool;
	var m_previewRWLockTemp : RWLock;

	public import final function QueueHitEvent( evt : gameHitEvent, receiver : weak< GameObject > );
	public import final function QueueMissEvent( evt : gameMissEvent );
	public import final function StartProjectionPipeline( evt : gameProjectedHitEvent );
	public import static function GetDamageModFromCurve( curve : CName, value : Float ) : Float;

	private export function ProcessPipeline( hitEvent : gameHitEvent, cache : CacheData )
	{
		ProcessSyncStageCallbacks( gameDamagePipelineStage.PreProcess, hitEvent, DMGPipelineType.Damage );
		if( PreProcess( hitEvent, cache ) )
		{
			ProcessSyncStageCallbacks( gameDamagePipelineStage.Process, hitEvent, DMGPipelineType.Damage );
			Process( hitEvent, cache );
			ProcessSyncStageCallbacks( gameDamagePipelineStage.ProcessHitReaction, hitEvent, DMGPipelineType.Damage );
			ProcessHitReaction( hitEvent );
			ProcessSyncStageCallbacks( gameDamagePipelineStage.PostProcess, hitEvent, DMGPipelineType.Damage );
			PostProcess( hitEvent );
		}
	}

	private export function ProcessMissPipeline( missEvent : gameMissEvent )
	{
		ProcessSyncStageMissCallbacks( missEvent );
	}

	private function ProcessProjectionPipeline( hitEvent : gameProjectedHitEvent, cache : CacheData )
	{
		if( CheckProjectionPipelineTargetConditions( hitEvent ) )
		{
			hitEvent.projectionPipeline = true;
			ProcessSyncStageCallbacks( gameDamagePipelineStage.PreProcess, hitEvent, DMGPipelineType.ProjectedDamage );
			if( PreProcess( hitEvent, cache ) )
			{
				ProcessSyncStageCallbacks( gameDamagePipelineStage.Process, hitEvent, DMGPipelineType.ProjectedDamage );
				Process( hitEvent, cache );
				ProcessSyncStageCallbacks( gameDamagePipelineStage.PostProcess, hitEvent, DMGPipelineType.ProjectedDamage );
				FillInDamageBlackboard( hitEvent );
			}
		}
	}

	private function CheckProjectionPipelineTargetConditions( hitEvent : gameProjectedHitEvent ) : Bool
	{
		var hitZone : EHitReactionZone;
		var hittingBreach : Bool;
		var previewLockLocal : Bool;
		var previewTargetLocal : previewTargetStruct;
		RWLock.AcquireShared( m_previewRWLockTemp );
		previewLockLocal = m_previewLock;
		previewTargetLocal.currentlyTrackedTarget = m_previewTarget.currentlyTrackedTarget;
		previewTargetLocal.currentBodyPart = m_previewTarget.currentBodyPart;
		previewTargetLocal.currentlyHittingBreach = m_previewTarget.currentlyHittingBreach;
		RWLock.ReleaseShared( m_previewRWLockTemp );
		if( previewLockLocal )
		{
			return false;
		}
		hitZone = GetHitReactionZone( hitEvent );
		hittingBreach = BreachFinderComponent.TryProcessBreachHit( hitEvent, hitZone == EHitReactionZone.Head, true );
		if( ( ( !( previewTargetLocal.currentlyTrackedTarget ) || ( previewTargetLocal.currentlyTrackedTarget != hitEvent.target ) ) || previewTargetLocal.currentlyHittingBreach != hittingBreach ) || ( previewTargetLocal.currentBodyPart != hitZone && ( previewTargetLocal.currentBodyPart == EHitReactionZone.Head || hitZone == EHitReactionZone.Head ) ) )
		{
			SetPreviewTargetStruct( hitEvent.target, hitZone, hittingBreach );
			return true;
		}
		return false;
	}

	private function SetPreviewTargetStruct( trackedTarget : weak< GameObject >, bodyPart : EHitReactionZone, hittingBreach : Bool )
	{
		RWLock.Acquire( m_previewRWLockTemp );
		m_previewTarget.currentlyTrackedTarget = trackedTarget;
		m_previewTarget.currentBodyPart = bodyPart;
		m_previewTarget.currentlyHittingBreach = hittingBreach;
		RWLock.Release( m_previewRWLockTemp );
	}

	public function ClearPreviewTargetStruct()
	{
		SetPreviewTargetStruct( NULL, EHitReactionZone.Special, false );
	}

	public function SetPreviewLock( newState : Bool )
	{
		RWLock.Acquire( m_previewRWLockTemp );
		m_previewLock = newState;
		RWLock.Release( m_previewRWLockTemp );
	}

	private function GetHitReactionZone( hitEvent : gameProjectedHitEvent ) : EHitReactionZone
	{
		var hitShapes : array< HitShapeData >;
		var hitUserData : HitShapeUserDataBase;
		hitShapes = hitEvent.hitRepresentationResult.hitShapes;
		hitUserData = DamageSystemHelper.GetHitShapeUserDataBase( hitShapes[ 0 ] );
		return HitShapeUserDataBase.GetHitReactionZone( hitUserData );
	}

	private function CreateDebugDataName( gameObject : weak< GameObject > ) : CName
	{
		var entityIDString : String;
		var displayName : String;
		var result : CName;
		if( !( gameObject ) )
		{
			return '[Unkown]';
		}
		entityIDString = EntityID.ToDebugStringDecimal( gameObject.GetEntityID() );
		displayName = gameObject.GetDisplayName();
		if( displayName == "" )
		{
			return StringToName( ( NameToString( gameObject.GetClassName() ) + " | " ) + entityIDString );
		}
		else
		{
			result = StringToName( ( GetLocalizedText( displayName ) + " | " ) + entityIDString );
		}
		return result;
	}

	private export function GatherDebugData( hitEvent : gameHitEvent, cache : CacheData, out hitDebugData : HitDebugData )
	{
		var attackData : AttackData;
		var hitFlagEnums : String;
		var i : Int32;
		var flags : array< SHitFlag >;
		var appliedDamages : array< Float >;
		var calculatedDamages : array< Float >;
		var damageType : gamedataDamageType;
		var appliedDamage : DamageDebugData;
		var calculatedDamage : DamageDebugData;
		var entityIDString : String;
		attackData = hitEvent.attackData;
		flags = attackData.GetFlags();
		appliedDamages = hitEvent.attackComputed.GetAttackValues();
		calculatedDamages = hitEvent.attackComputed.GetOriginalAttackValues();
		hitDebugData.attackTime = attackData.GetAttackTime();
		hitDebugData.instigator = attackData.GetInstigator();
		hitDebugData.source = attackData.GetSource();
		hitDebugData.target = hitEvent.target;
		hitDebugData.sourceHitPosition = attackData.GetSource().GetWorldPosition();
		hitDebugData.targetHitPosition = hitEvent.target.GetWorldPosition();
		hitDebugData.instigatorName = CreateDebugDataName( hitDebugData.instigator );
		hitDebugData.sourceName = CreateDebugDataName( hitDebugData.source );
		hitDebugData.targetName = CreateDebugDataName( hitDebugData.target );
		hitDebugData.sourceAttackDebugData = attackData.GetAttackDefinition().GetDebugData();
		if( attackData.GetWeapon() )
		{
			entityIDString = EntityID.ToDebugStringDecimal( attackData.GetWeapon().GetEntityID() );
			hitDebugData.sourceWeaponName = StringToName( ( TDBID.ToStringDEBUG( ItemID.GetTDBID( attackData.GetWeapon().GetItemID() ) ) + " | " ) + entityIDString );
			if( hitDebugData.sourceWeaponName == '' )
			{
				hitDebugData.sourceWeaponName = StringToName( "[Unkown] " + entityIDString );
			}
		}
		hitDebugData.sourceAttackName = StringToName( TDBID.ToStringDEBUG( attackData.GetAttackDefinition().GetRecord().GetID() ) );
		for( i = 0; i < appliedDamages.Size(); i += 1 )
		{
			damageType = ( ( gamedataDamageType )( i ) );
			appliedDamage = new DamageDebugData;
			appliedDamage.statPoolType = gamedataStatPoolType.Health;
			appliedDamage.damageType = damageType;
			appliedDamage.value = appliedDamages[ i ];
			hitDebugData.appliedDamages.PushBack( appliedDamage );
			calculatedDamage = new DamageDebugData;
			calculatedDamage.statPoolType = gamedataStatPoolType.Health;
			calculatedDamage.damageType = damageType;
			calculatedDamage.value = calculatedDamages[ i ];
			hitDebugData.calculatedDamages.PushBack( calculatedDamage );
		}
		hitDebugData.hitType = StringToName( EnumValueToString( "gamedataAttackType", ( ( Int64 )( attackData.GetAttackType() ) ) ) );
		for( i = 0; i < flags.Size(); i += 1 )
		{
			hitFlagEnums += EnumValueToString( "hitFlag", ( ( Int64 )( flags[ i ].flag ) ) );
			hitFlagEnums += ( "," + ( ( String )( flags[ i ].source ) ) );
			hitFlagEnums += "|";
		}
		hitDebugData.hitFlags = StringToName( hitFlagEnums );
	}

	private function FillInDamageBlackboard( hitEvent : gameHitEvent )
	{
		var player : weak< PlayerPuppet >;
		var blackboard : IBlackboard;
		var damage : Int32;
		player = ( ( PlayerPuppet )( hitEvent.attackData.GetInstigator() ) );
		blackboard = GameInstance.GetBlackboardSystem( player.GetGame() ).Get( GetAllBlackboardDefs().UI_NameplateData );
		if( player && blackboard )
		{
			damage = ( ( Int32 )( hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health ) ) );
			if( damage != blackboard.GetInt( GetAllBlackboardDefs().UI_NameplateData.DamageProjection ) )
			{
				blackboard.SetInt( GetAllBlackboardDefs().UI_NameplateData.DamageProjection, damage, true );
			}
		}
	}

	private function GatherServerData( hitEvent : gameHitEvent, cache : CacheData, out serverHitData : ServerHitData )
	{
		if( hitEvent.attackData.HasFlag( hitFlag.DealNoDamage ) )
		{
			return;
		}
		serverHitData.damageInfos = ConvertHitDataToDamageInfo( hitEvent );
		serverHitData.instigator = hitEvent.attackData.GetInstigator();
	}

	public function ConvertHitDataToDamageInfo( hitEvent : gameHitEvent ) : array< DamageInfo >
	{
		var i : Int32;
		var finalDmgValue : Float;
		var attackValues : array< Float >;
		var hitShapes : array< HitShapeData >;
		var dmgPosition : Vector4;
		var dmgInfo : DamageInfo;
		var result : array< DamageInfo >;
		var player : PlayerPuppet;
		var breachFinder : BreachFinderComponent;
		var breachComponent : BreachComponent;
		hitShapes = hitEvent.hitRepresentationResult.hitShapes;
		dmgInfo.userData = new DamageInfoUserData;
		dmgInfo.userData.flags = hitEvent.attackData.GetFlags();
		if( hitShapes.Size() != 0 )
		{
			dmgPosition = hitShapes[ 0 ].result.hitPositionEnter;
			dmgInfo.userData.hitShapeType = DamageSystemHelper.GetHitShapeTypeFromData( hitShapes[ 0 ] );
		}
		else
		{
			dmgPosition = hitEvent.hitPosition;
		}
		if( hitEvent.attackData.HasFlag( hitFlag.BreachHit ) && !( AttackData.IsRangedOrDirectOrThrown( hitEvent.attackData.GetAttackType() ) ) )
		{
			player = ( ( PlayerPuppet )( hitEvent.attackData.GetInstigator() ) );
			if( player )
			{
				breachFinder = player.GetBreachFinderComponent();
				if( breachFinder )
				{
					breachComponent = breachFinder.GetTrackedBreachComponent();
					if( breachComponent )
					{
						dmgPosition = breachComponent.GetPosition();
					}
				}
			}
		}
		dmgInfo.hitPosition = dmgPosition;
		dmgInfo.hitType = hitEvent.attackData.GetHitType();
		if( hitEvent.target )
		{
			if( ( !( IsMultiplayer() ) || hitEvent.target.IsReplicated() ) || EntityID.IsStatic( hitEvent.target.GetEntityID() ) )
			{
				dmgInfo.entityHit = hitEvent.target;
			}
		}
		if( hitEvent.attackData.GetInstigator() )
		{
			if( ( !( IsMultiplayer() ) || hitEvent.attackData.GetInstigator().IsReplicated() ) || EntityID.IsStatic( hitEvent.attackData.GetInstigator().GetEntityID() ) )
			{
				dmgInfo.instigator = hitEvent.attackData.GetInstigator();
			}
		}
		if( !( hitEvent.attackData.HasFlag( hitFlag.DamageNullified ) ) )
		{
			attackValues = hitEvent.attackComputed.GetAttackValues();
			for( i = 0; i < attackValues.Size(); i += 1 )
			{
				finalDmgValue += attackValues[ i ];
			}
		}
		else
		{
			finalDmgValue = 0.0;
		}
		if( AttackData.IsDoT( hitEvent.attackData ) )
		{
			dmgInfo.damageType = hitEvent.attackComputed.GetDominatingDamageType();
		}
		else
		{
			dmgInfo.damageType = gamedataDamageType.Physical;
		}
		dmgInfo.damageValue = finalDmgValue;
		result.PushBack( dmgInfo );
		return result;
	}

	private function ProcessClientHit( serverHitData : ServerHitData )
	{
		if( serverHitData.instigator && serverHitData.instigator.IsControlledByLocalPeer() )
		{
			serverHitData.instigator.DisplayHitUI( serverHitData.damageInfos );
		}
	}

	private function ProcessClientKill( serverKillData : ServerKillData )
	{
		if( serverKillData.killInfo.killerEntity && serverKillData.killInfo.killerEntity.IsControlledByLocalPeer() )
		{
			serverKillData.killInfo.killerEntity.DisplayKillUI( serverKillData.killInfo );
		}
	}

	private function PreProcess( hitEvent : gameHitEvent, cache : CacheData ) : Bool
	{
		ConvertDPSToHitDamage( hitEvent );
		CalculateDamageVariants( hitEvent );
		CacheLocalVars( hitEvent, cache );
		if( GetDamageSystemLogFlags() & damageSystemLogFlags.GENERAL )
		{
		}
		ModifyHitFlagsForPlayer( hitEvent, cache );
		if( CheckForQuickExit( hitEvent, cache ) )
		{
			return false;
		}
		InvulnerabilityCheck( hitEvent, cache );
		ImmortalityCheck( hitEvent, cache );
		DeathCheck( hitEvent );
		ModifyHitData( hitEvent );
		return true;
	}

	private function ConvertDPSToHitDamage( hitEvent : gameHitEvent )
	{
		var projectilesPerShot : Float;
		var statsSystem : StatsSystem;
		var weaponObject : WeaponObject;
		weaponObject = hitEvent.attackData.GetWeapon();
		if( !( weaponObject ) )
		{
			return;
		}
		if( !( hitEvent.attackData.GetInstigator().IsPlayer() ) )
		{
			if( weaponObject.IsRanged() && !( AttackData.IsMelee( hitEvent.attackData.GetAttackType() ) ) )
			{
				statsSystem = GameInstance.GetStatsSystem( hitEvent.attackData.GetSource().GetGame() );
				projectilesPerShot = statsSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.ProjectilesPerShot );
				if( projectilesPerShot > 0.0 )
				{
					hitEvent.attackComputed.MultAttackValue( 1.0 / projectilesPerShot );
				}
			}
		}
	}

	private function CalculateDamageVariants( hitEvent : gameHitEvent )
	{
		var rand : Float;
		if( hitEvent.projectionPipeline )
		{
			return;
		}
		if( hitEvent.attackData.GetInstigator().IsPlayer() && !( hitEvent.target.IsPlayer() ) )
		{
			rand = RandRangeF( 0.89999998, 1.10000002 );
			hitEvent.attackComputed.MultAttackValue( rand );
		}
	}

	private function ModifyHitData( hitEvent : gameHitEvent )
	{
		DamageManager.ModifyHitData( hitEvent );
		if( hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health ) == 0.0 )
		{
			hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'no_valid_damage' );
		}
		ProcessDamageReduction( hitEvent );
		ProcessLocalizedDamage( hitEvent );
		ProcessInstantKill( hitEvent );
		ProcessDodge( hitEvent );
		ProcessEvasion( hitEvent );
		ProcessMitigation( hitEvent );
		ProcessPlayerIncomingDamageMultiplier( hitEvent );
		PreProcessVehicleTarget( hitEvent );
	}

	private function ProcessDamageReduction( hitEvent : gameHitEvent )
	{
		var statsSystem : StatsSystem;
		var target : GameObject;
		var instigatorPuppet : ScriptedPuppet;
		var attackType : gamedataAttackType;
		var instigatorNpcType : gamedataNPCType;
		var resistance : Float;
		var attackValues : array< Float >;
		var i : Int32;
		var damageType : gamedataDamageType;
		target = hitEvent.target;
		instigatorPuppet = ( ( ScriptedPuppet )( hitEvent.attackData.GetInstigator() ) );
		attackType = hitEvent.attackData.GetAttackType();
		statsSystem = GameInstance.GetStatsSystem( target.GetGame() );
		if( hitEvent.attackData.HasFlag( hitFlag.ReduceDamage ) )
		{
			hitEvent.attackComputed.MultAttackValue( 0.1 );
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( instigatorPuppet, gamedataStatusEffectType.Poisoned ) && ( instigatorPuppet != target ) )
		{
			hitEvent.attackComputed.MultAttackValue( 1.0 - statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.PercentDamageReductionFromPoisonedEnemies ) );
		}
		if( AttackData.IsExplosion( attackType ) || hitEvent.attackData.HasFlag( hitFlag.BreachExplosion ) )
		{
			resistance = statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.ExplosionResistance ) * 0.01;
			if( resistance != 0.0 )
			{
				hitEvent.attackComputed.MultAttackValue( 1.0 - resistance );
			}
		}
		if( AttackData.IsMelee( attackType ) )
		{
			resistance = statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.MeleeResistance ) * 0.01;
			if( resistance != 0.0 )
			{
				hitEvent.attackComputed.MultAttackValue( 1.0 - resistance );
			}
		}
		if( AttackData.IsHack( attackType ) )
		{
			resistance = statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.QuickhackResistance ) * 0.01;
			if( resistance != 0.0 )
			{
				hitEvent.attackComputed.MultAttackValue( 1.0 - resistance );
			}
		}
		if( AttackData.IsDoT( hitEvent.attackData ) )
		{
			resistance = statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.DamageOverTimeResistance ) * 0.01;
			if( resistance != 0.0 )
			{
				hitEvent.attackComputed.MultAttackValue( 1.0 - resistance );
			}
		}
		if( instigatorPuppet )
		{
			if( instigatorPuppet.GetNPCRarity() == gamedataNPCRarity.Boss || instigatorPuppet.GetNPCRarity() == gamedataNPCRarity.MaxTac )
			{
				resistance = statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.BossResistance ) * 0.01;
				if( resistance != 0.0 )
				{
					hitEvent.attackComputed.MultAttackValue( 1.0 - resistance );
				}
			}
			instigatorNpcType = instigatorPuppet.GetNPCType();
			if( ( instigatorNpcType == gamedataNPCType.Drone || instigatorNpcType == gamedataNPCType.Android ) || instigatorNpcType == gamedataNPCType.Mech )
			{
				resistance = statsSystem.GetStatValue( target.GetEntityID(), gamedataStatType.MechResistance ) * 0.01;
				if( resistance != 0.0 )
				{
					hitEvent.attackComputed.MultAttackValue( 1.0 - resistance );
				}
			}
		}
		if( AttackData.IsRangedOnly( attackType ) )
		{
			return;
		}
		attackValues = hitEvent.attackComputed.GetAttackValues();
		for( i = 0; i < attackValues.Size(); i += 1 )
		{
			if( attackValues[ i ] > 0.0 )
			{
				damageType = ( ( gamedataDamageType )( i ) );
				if( damageType != gamedataDamageType.Physical )
				{
					resistance = statsSystem.GetStatValue( target.GetEntityID(), RPGManager.GetResistanceTypeFromDamageType( damageType ) ) * 0.01;
					attackValues[ i ] = MaxF( attackValues[ i ] * ( 1.0 - resistance ), 1.0 );
				}
			}
		}
		hitEvent.attackComputed.SetAttackValues( attackValues );
	}

	private function ProcessLocalizedDamage( hitEvent : gameHitEvent )
	{
		var hitShapes : array< HitShapeData >;
		var multValue : Float;
		var hitShapeDamageMod : Float;
		var hitUserData : HitShapeUserDataBase;
		var immunity : Int32;
		hitShapes = hitEvent.hitRepresentationResult.hitShapes;
		if( !( hitEvent.attackData.GetInstigator().IsPlayer() ) )
		{
			return;
		}
		if( AttackData.IsAreaOfEffect( hitEvent.attackData.GetAttackType() ) )
		{
			return;
		}
		if( hitShapes.Size() > 0 )
		{
			hitUserData = DamageSystemHelper.GetHitShapeUserDataBase( hitShapes[ 0 ] );
		}
		if( !( hitUserData ) )
		{
			if( ( ( WeakspotObject )( hitEvent.target ) ) )
			{
				BreachFinderComponent.TryProcessBreachHit( hitEvent, false );
			}
			return;
		}
		if( hitEvent.attackData.HasFlag( hitFlag.DamageNullified ) )
		{
			hitEvent.attackComputed.MultAttackValue( 0.0 );
		}
		if( HitShapeUserDataBase.IsInternalWeakspot( hitUserData ) || ( ( WeakspotObject )( hitEvent.target ) ) )
		{
			hitEvent.attackData.AddFlag( hitFlag.WeakspotHit, 'ProcessLocalizedDamage' );
		}
		if( AttackData.IsRangedOrDirectOrThrown( hitEvent.attackData.GetAttackType() ) && HitShapeUserDataBase.IsHitReactionZoneHead( hitUserData ) )
		{
			GameInstance.GetTelemetrySystem( hitEvent.target.GetGame() ).LogHeadshotGGP( 1 );
			multValue = GetHeadshotDamageModifier( GameInstance.GetStatsSystem( hitEvent.target.GetGame() ), hitEvent.attackData );
			immunity = ( ( Int32 )( GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.HeadshotImmunity ) ) );
			if( !( FloatIsEqual( multValue, 0.0 ) ) && ( immunity == 0 ) )
			{
				hitEvent.attackData.AddFlag( hitFlag.Headshot, 'ProcessLocalizedDamage' );
			}
		}
		BreachFinderComponent.TryProcessBreachHit( hitEvent, hitEvent.attackData.HasFlag( hitFlag.Headshot ) );
		hitShapeDamageMod = HitShapeUserDataBase.GetHitShapeDamageMod( hitUserData );
		if( hitShapeDamageMod != 0.0 )
		{
			hitEvent.attackComputed.MultAttackValue( hitShapeDamageMod );
		}
		if( hitUserData.m_hitShapeType == EHitShapeType.None )
		{
			hitEvent.attackComputed.MultAttackValue( 0.0 );
		}
	}

	private function ProcessInstantKill( hitEvent : gameHitEvent )
	{
		var attackData : AttackData;
		var targetID : StatsObjectID;
		attackData = hitEvent.attackData;
		targetID = hitEvent.target.GetEntityID();
		if( hitEvent.projectionPipeline )
		{
			return;
		}
		if( attackData.HasFlag( hitFlag.Kill ) )
		{
			attackData.AddFlag( hitFlag.DealNoDamage, 'instant_kill' );
			attackData.AddFlag( hitFlag.DontShowDamageFloater, 'instant_kill' );
			GameInstance.GetStatPoolsSystem( GetGameInstance() ).RequestSettingStatPoolMinValue( targetID, gamedataStatPoolType.Health, attackData.GetInstigator() );
		}
	}

	private function ProcessDodge( hitEvent : gameHitEvent )
	{
		if( GameInstance.GetStatsSystem( GetGameInstance() ).GetStatBoolValue( hitEvent.target.GetEntityID(), gamedataStatType.IsDodging ) )
		{
			hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'ProcessDodge' );
			if( hitEvent.target.IsPlayer() )
			{
				SetTutorialFact( 'gmpl_player_dodged_attack' );
			}
		}
	}

	private function ProcessEvasion( hitEvent : gameHitEvent )
	{
		var evasionChance, randomFloat : Float;
		evasionChance = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.Evasion );
		if( ( evasionChance > 0.0 ) && !( hitEvent.attackData.HasFlag( hitFlag.DamageOverTime ) ) )
		{
			randomFloat = RandRangeF( 0.0, 100.0 );
			if( evasionChance > randomFloat )
			{
				hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'ProcessEvasion' );
				hitEvent.attackData.AddFlag( hitFlag.WasEvaded, 'ProcessEvasion' );
			}
		}
	}

	private function ProcessMitigation( hitEvent : gameHitEvent )
	{
		var mitigationChance, mitigationMult, randomFloat : Float;
		mitigationChance = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.MitigationChance );
		if( mitigationChance > 0.0 )
		{
			randomFloat = RandRangeF( 0.0, 100.0 );
			if( mitigationChance > randomFloat )
			{
				mitigationMult = 1.0 - ( GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.MitigationStrength ) * 0.01 );
				hitEvent.attackComputed.MultAttackValue( mitigationMult );
				hitEvent.attackData.AddFlag( hitFlag.WasMitigated, 'ProcessMitigation' );
			}
		}
	}

	private function ProcessPlayerIncomingDamageMultiplier( hitEvent : gameHitEvent )
	{
		var playerIncomingDamageMultiplier : Float;
		playerIncomingDamageMultiplier = hitEvent.attackData.GetAttackDefinition().GetRecord().PlayerIncomingDamageMultiplier() * PreventionSystem.GetDamageToPlayerMultiplier( hitEvent.target.GetGame() );
		if( ( ( PlayerPuppet )( hitEvent.target ) ) || ScriptedPuppet.IsPlayerCompanion( hitEvent.target ) )
		{
			if( playerIncomingDamageMultiplier != 1.0 )
			{
			}
			hitEvent.attackComputed.MultAttackValue( playerIncomingDamageMultiplier );
		}
		else if( ScriptedPuppet.IsPlayerCompanion( hitEvent.attackData.GetInstigator() ) && !( hitEvent.target.IsPlayer() ) )
		{
			hitEvent.attackComputed.MultAttackValue( playerIncomingDamageMultiplier );
		}
	}

	private function InvulnerabilityCheck( hitEvent : gameHitEvent, cache : CacheData )
	{
		if( hitEvent.attackData.HasFlag( hitFlag.IgnoreImmortalityModes ) )
		{
			return;
		}
		if( IsTargetInvulnerable( cache ) )
		{
			hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'invulnerable' );
			if( cache.logFlags & damageSystemLogFlags.GENERAL )
			{
			}
		}
		if( GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.IsInvulnerable ) > 0.0 )
		{
			hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'invulnerable stat flag' );
		}
	}

	private function DEBUG_InvulnerabilityCheckForVehicle( hitEvent : gameHitEvent ) : Bool
	{
		var targetVehicle : VehicleObject;
		targetVehicle = ( ( VehicleObject )( hitEvent.target ) );
		if( targetVehicle )
		{
			return VehicleComponent.PlayerPassengerHasGodModeFromCheatSystem( hitEvent.target.GetGame(), hitEvent.target.GetEntityID(), gameGodModeType.Invulnerable );
		}
		return false;
	}

	private function ImmortalityCheck( hitEvent : gameHitEvent, cache : CacheData )
	{
		if( hitEvent.attackData.HasFlag( hitFlag.IgnoreImmortalityModes ) )
		{
			return;
		}
		if( IsTargetImmortal( cache ) )
		{
			hitEvent.attackData.AddFlag( hitFlag.ImmortalTarget, 'immortal' );
			if( cache.logFlags & damageSystemLogFlags.GENERAL )
			{
			}
		}
	}

	private function DeathCheck( hitEvent : gameHitEvent )
	{
		var deviceTarget : Device;
		var gameObjectTarget : GameObject;
		var puppetTarget : ScriptedPuppet;
		deviceTarget = ( ( Device )( hitEvent.target ) );
		gameObjectTarget = hitEvent.target;
		puppetTarget = ( ( ScriptedPuppet )( hitEvent.target ) );
		if( ( deviceTarget && deviceTarget.GetDevicePS().IsBroken() ) || ( gameObjectTarget && gameObjectTarget.IsDead() ) )
		{
			hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'dead' );
		}
		if( puppetTarget && puppetTarget.IsDeadNoStatPool() )
		{
			hitEvent.attackData.AddFlag( hitFlag.TargetWasAlreadyDeadNoStatPool, 'dead' );
		}
	}

	private function SendVehicleImpactTelemetryIfValid( hitEvent : gameHitEvent )
	{
		var vehicleHitEvent : gameVehicleHitEvent;
		if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Physical ) <= 0.0 )
		{
			return;
		}
		if( !( hitEvent.attackData.GetInstigator().IsPlayer() ) )
		{
			return;
		}
		vehicleHitEvent = ( ( gameVehicleHitEvent )( hitEvent ) );
		if( !( hitEvent.attackData.HasFlag( hitFlag.VehicleImpact ) ) && !( vehicleHitEvent ) )
		{
			return;
		}
		GameInstance.GetTelemetrySystem( hitEvent.attackData.GetInstigator().GetGame() ).LogPlayerVehicleImpact();
	}

	private function Process( hitEvent : gameHitEvent, cache : CacheData )
	{
		if( hitEvent.attackData.HasFlag( hitFlag.DealNoDamage ) )
		{
			return;
		}
		ProcessPlayerFixedPercentageOverride( hitEvent );
		ProcessDeviceExplosionDamageToTierNPC( hitEvent );
		ProcessGrenadeExplosionDamageToPlayer( hitEvent );
		CalculateSourceModifiers( hitEvent, cache );
		CalculateTargetModifiers( hitEvent );
		CalculateSourceVsTargetModifiers( hitEvent );
		CalculateGlobalModifiers( hitEvent, cache );
		ProcessCrowdTarget( hitEvent );
		ProcessVehicleTarget( hitEvent, cache );
		ProcessVehicleHit( hitEvent );
		ProcessRagdollHit( hitEvent );
		ProcessTurretAttack( hitEvent );
		ProcessDeviceTarget( hitEvent );
		ProcessQuickHackModifiers( hitEvent );
		ProcessOneShotProtection( hitEvent );
		if( !( hitEvent.projectionPipeline ) )
		{
			DealDamages( hitEvent );
		}
	}

	private function ProcessHitReaction( hitEvent : gameHitEvent )
	{
		hitEvent.target.ReactToHitProcess( hitEvent );
	}

	private function ProcessRagdollHit( hitEvent : gameHitEvent )
	{
		var ragdollHitEvent : gameRagdollHitEvent;
		var targetPuppet : ScriptedPuppet;
		var targetIsFriendly : Bool;
		var targetMaxHealth : Float;
		var curveDamagePercentage : Float;
		var heightDeltaMultiplier : Float;
		var terminalVelocityReached : Bool;
		var isHighFall : Bool;
		ragdollHitEvent = ( ( gameRagdollHitEvent )( hitEvent ) );
		if( !( ragdollHitEvent ) )
		{
			return;
		}
		targetPuppet = ( ( ScriptedPuppet )( hitEvent.target ) );
		if( !( targetPuppet ) )
		{
			return;
		}
		if( targetPuppet.IsCrowd() )
		{
			if( ( ( NPCPuppet )( targetPuppet ) ).m_shouldBeImmuneToVehicleHit )
			{
				hitEvent.attackComputed.SetAttackValue( 0.0 );
				return;
			}
		}
		targetIsFriendly = GameObject.GetAttitudeTowards( targetPuppet, GameInstance.GetPlayerSystem( targetPuppet.GetGame() ).GetLocalPlayerControlledGameObject() ) == EAIAttitude.AIA_Friendly;
		if( targetIsFriendly )
		{
			hitEvent.attackComputed.SetAttackValue( 0.0 );
			return;
		}
		terminalVelocityReached = ragdollHitEvent.speedDelta >= TweakDBInterface.GetFloat( T"AIGeneralSettings.ragdollImpactKillVelocityThreshold", 11.0 );
		isHighFall = ( ragdollHitEvent.speedDelta >= TweakDBInterface.GetFloat( T"AIGeneralSettings.ragdollHighFallVelocityThreshold", 8.0 ) ) && ( ragdollHitEvent.heightDelta >= TweakDBInterface.GetFloat( T"AIGeneralSettings.ragdollHighFallHeightThreshold", 6.0 ) );
		targetMaxHealth = GameInstance.GetStatsSystem( targetPuppet.GetGame() ).GetStatValue( targetPuppet.GetEntityID(), gamedataStatType.Health );
		if( terminalVelocityReached || isHighFall )
		{
			if( ( ( NPCPuppet )( targetPuppet ) ) )
			{
				( ( NPCPuppet )( targetPuppet ) ).SetMyKiller( hitEvent.attackData.GetInstigator() );
				( ( NPCPuppet )( targetPuppet ) ).MarkForDeath();
			}
			hitEvent.attackComputed.SetAttackValue( targetMaxHealth, gamedataDamageType.Physical );
			hitEvent.attackData.AddFlag( hitFlag.DeterministicDamage, 'ragdoll_collision' );
		}
		else if( RPGManager.CalculatePowerDifferential( targetPuppet ) != gameEPowerDifferential.IMPOSSIBLE )
		{
			curveDamagePercentage = GameInstance.GetStatsDataSystem( targetPuppet.GetGame() ).GetValueFromCurve( 'puppet_ragdoll_force_to_damage', ragdollHitEvent.speedDelta, 'ragdoll_speed_to_damage' );
			heightDeltaMultiplier = GameInstance.GetStatsDataSystem( targetPuppet.GetGame() ).GetValueFromCurve( 'puppet_ragdoll_force_to_damage', ragdollHitEvent.heightDelta, 'ragdoll_altitude_difference_multiplier' );
			hitEvent.attackComputed.SetAttackValue( ( curveDamagePercentage * heightDeltaMultiplier ) * targetMaxHealth, gamedataDamageType.Physical );
		}
	}

	private function ProcessCrowdTarget( hitEvent : gameHitEvent )
	{
		var targetPuppet : NPCPuppet;
		var instigator : weak< GameObject >;
		targetPuppet = ( ( NPCPuppet )( hitEvent.target ) );
		instigator = hitEvent.attackData.GetInstigator();
		if( ( ( !( targetPuppet ) || !( targetPuppet.IsCrowd() ) ) || ( ( gameRagdollHitEvent )( hitEvent ) ) ) || ( ( gameVehicleHitEvent )( hitEvent ) ) )
		{
			return;
		}
		if( hitEvent.projectionPipeline )
		{
			return;
		}
		if( instigator.IsPlayer() )
		{
			NPCPuppet.TutorialAddIllegalActionFact( targetPuppet );
			if( !( NPCPuppet.IsInCombat( targetPuppet ) ) )
			{
				hitEvent.attackData.AddFlag( hitFlag.DontShowDamageFloater, 'target is crowd' );
			}
		}
	}

	private function ProcessTurretAttack( hitEvent : gameHitEvent )
	{
		var instigatorTurret : SecurityTurret;
		var isTurretFriendlyToPlayer : Bool;
		instigatorTurret = ( ( SecurityTurret )( hitEvent.attackData.GetInstigator() ) );
		if( !( instigatorTurret ) )
		{
			return;
		}
		isTurretFriendlyToPlayer = GameObject.GetAttitudeTowards( instigatorTurret, GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerControlledGameObject() ) == EAIAttitude.AIA_Friendly;
		if( isTurretFriendlyToPlayer )
		{
			hitEvent.attackComputed.MultAttackValue( 0.60000002 );
		}
	}

	private function ProcessTurretDamageTakenFromMelee( hitEvent : gameHitEvent )
	{
		var targetTurret : SecurityTurret;
		var attackType : gamedataAttackType;
		targetTurret = ( ( SecurityTurret )( hitEvent.target ) );
		attackType = hitEvent.attackData.GetAttackType();
		if( targetTurret && ( ( AttackData.IsLightMelee( attackType ) || AttackData.IsStrongMelee( attackType ) ) || hitEvent.attackData.HasFlag( hitFlag.BodyPerksMeleeAttack ) ) )
		{
			hitEvent.attackComputed.MultAttackValue( 1.20000005 );
		}
	}

	private function ProcessDeviceTarget( hitEvent : gameHitEvent )
	{
		var targetDevice : Device;
		targetDevice = ( ( Device )( hitEvent.target ) );
		if( targetDevice && !( targetDevice.ShouldShowDamageNumber() ) )
		{
			hitEvent.attackData.AddFlag( hitFlag.DontShowDamageFloater, 'device' );
		}
	}

	private function ProcessOneShotProtection( hitEvent : gameHitEvent )
	{
		var weapon : weak< WeaponObject >;
		var maxHealth : Float;
		var damageCap : Float;
		var reductionProportion : Float;
		var damages : array< Float >;
		var i : Int32;
		var difficulty : gameDifficulty;
		var statsDataSys : StatsDataSystem;
		var maxPercentDamagePerHit : Float;
		weapon = ( ( WeaponObject )( hitEvent.attackData.GetWeapon() ) );
		if( !( weapon ) )
		{
			return;
		}
		if( !( hitEvent.attackData.GetInstigator().IsPlayer() ) && hitEvent.target.IsPlayer() )
		{
			statsDataSys = GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() );
			difficulty = statsDataSys.GetDifficulty();
			switch( difficulty )
			{
				case gameDifficulty.Story:
					maxPercentDamagePerHit = TDB.GetFloat( T"Constants.DamageSystem.maxPercentDamagePerHitStory" );
				break;
				case gameDifficulty.Easy:
					maxPercentDamagePerHit = TDB.GetFloat( T"Constants.DamageSystem.maxPercentDamagePerHitEasy" );
				break;
				case gameDifficulty.Hard:
					maxPercentDamagePerHit = TDB.GetFloat( T"Constants.DamageSystem.maxPercentDamagePerHitHard" );
				break;
				case gameDifficulty.VeryHard:
					maxPercentDamagePerHit = TDB.GetFloat( T"Constants.DamageSystem.maxPercentDamagePerHitVeryHard" );
				break;
			}
			maxHealth = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.Health );
			damageCap = ( maxHealth * maxPercentDamagePerHit ) / 100.0;
			if( hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health ) > damageCap )
			{
				reductionProportion = damageCap / hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
				damages = hitEvent.attackComputed.GetAttackValues();
				for( i = 0; i < damages.Size(); i += 1 )
				{
					damages[ i ] *= reductionProportion;
				}
				hitEvent.attackComputed.SetAttackValues( damages );
			}
		}
	}

	private function ProcessPlayerFixedPercentageOverride( hitEvent : gameHitEvent )
	{
		var player : weak< PlayerPuppet >;
		var attackRecord : Attack_GameEffect_Record;
		var hitFlags : array< String >;
		var totalDamage : Float;
		var playerMaxHealth : Float;
		var numerOfDamageTypes : Float;
		var overridePlayerDamageFixedPercentage : Float;
		var isExplosionReductionPerkBought : Bool;
		var i : Int32;
		var difficulty : gameDifficulty;
		var statsDataSys : StatsDataSystem;
		var instig : weak< GameObject >;
		player = ( ( PlayerPuppet )( hitEvent.target ) );
		if( !( player ) )
		{
			return;
		}
		if( StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.DeviceExplosionInvulnerability" ) )
		{
			return;
		}
		if( !( hitEvent.attackData.GetAttackType() == gamedataAttackType.Explosion || hitEvent.attackData.GetAttackType() == gamedataAttackType.PressureWave ) )
		{
			return;
		}
		attackRecord = ( ( Attack_GameEffect_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
		hitFlags = attackRecord.HitFlags();
		for( i = 0; i < hitFlags.Size(); i += 1 )
		{
			if( hitFlags[ i ] == "OverridePlayerDamageWithFixedPercentage" )
			{
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Physical ) > 0.0 )
				{
					numerOfDamageTypes = numerOfDamageTypes + 1.0;
				}
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Thermal ) > 0.0 )
				{
					numerOfDamageTypes = numerOfDamageTypes + 1.0;
				}
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Electric ) > 0.0 )
				{
					numerOfDamageTypes = numerOfDamageTypes + 1.0;
				}
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Chemical ) > 0.0 )
				{
					numerOfDamageTypes = numerOfDamageTypes + 1.0;
				}
				playerMaxHealth = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.Health );
				overridePlayerDamageFixedPercentage = TweakDBInterface.GetFloat( attackRecord.GetID() + T".overridePlayerDamageFixedPercentage", 0.2 );
				statsDataSys = GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() );
				difficulty = statsDataSys.GetDifficulty();
				switch( difficulty )
				{
					case gameDifficulty.Story:
						overridePlayerDamageFixedPercentage *= 0.5;
					break;
					case gameDifficulty.Easy:
						overridePlayerDamageFixedPercentage *= 0.75;
					break;
					case gameDifficulty.Hard:
						overridePlayerDamageFixedPercentage *= 1.0;
					break;
					case gameDifficulty.VeryHard:
						overridePlayerDamageFixedPercentage *= 1.29999995;
					break;
				}
				isExplosionReductionPerkBought = PlayerDevelopmentSystem.GetData( player ).IsNewPerkBought( gamedataNewPerkType.Tech_Left_Perk_3_01 ) == 1;
				instig = hitEvent.attackData.GetInstigator();
				if( isExplosionReductionPerkBought && instig.IsPlayer() )
				{
					overridePlayerDamageFixedPercentage *= 0.5;
				}
				totalDamage = ( overridePlayerDamageFixedPercentage * playerMaxHealth ) / numerOfDamageTypes;
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Physical ) > 0.0 )
				{
					hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Physical );
				}
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Thermal ) > 0.0 )
				{
					hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Thermal );
				}
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Electric ) > 0.0 )
				{
					hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Electric );
				}
				if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Chemical ) > 0.0 )
				{
					hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Chemical );
				}
			}
		}
		if( hitFlags.Contains( "DeviceExplosionAttack" ) )
		{
			StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.DeviceExplosionInvulnerability", player.GetEntityID() );
		}
	}

	private function ProcessDeviceExplosionDamageToTierNPC( hitEvent : gameHitEvent )
	{
		var isHackedExplosionPerkBought : Bool;
		var npcRarityMultiplier : Float;
		var targetNpcRarity : gamedataNPCRarity;
		var bonusDamageMult : Float;
		var attackRecord : Attack_GameEffect_Record;
		var totalDamage : Float;
		var npcHealth : Float;
		var hitFlags : array< String >;
		var player : GameObject;
		var npc : weak< ScriptedPuppet >;
		var i : Int32;
		npc = ( ( NPCPuppet )( hitEvent.target ) );
		if( !( npc ) )
		{
			return;
		}
		if( !( hitEvent.attackData.GetAttackType() == gamedataAttackType.Explosion ) )
		{
			return;
		}
		attackRecord = ( ( Attack_GameEffect_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
		hitFlags = attackRecord.HitFlags();
		for( i = 0; i < hitFlags.Size(); i += 1 )
		{
			if( hitFlags[ i ] == "OverrideNPCDamageBasedOnTiers" )
			{
				npcHealth = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( npc.GetEntityID(), gamedataStatType.Health );
				targetNpcRarity = ( ( ScriptedPuppet )( hitEvent.target ) ).GetNPCRarity();
				switch( targetNpcRarity )
				{
					case gamedataNPCRarity.Trash:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".trashNPCMaxHPDamage", 0.2 );
						break;
					}
					case gamedataNPCRarity.Weak:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".weakNPCMaxHPDamage", 0.2 );
						break;
					}
					case gamedataNPCRarity.Normal:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".normalNPCMaxHPDamage", 0.2 );
						break;
					}
					case gamedataNPCRarity.Rare:
					case gamedataNPCRarity.Officer:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".rareNPCMaxHPDamage", 0.2 );
						break;
					}
					;
					case gamedataNPCRarity.Elite:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".eliteNPCMaxHPDamage", 0.2 );
						break;
					}
					case gamedataNPCRarity.MaxTac:
					case gamedataNPCRarity.Boss:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".bossNPCMaxHPDamage", 0.02 );
						break;
					}
					;
					default:
					{
						npcRarityMultiplier = TweakDBInterface.GetFloat( attackRecord.GetID() + T".normalNPCMaxHPDamage", 0.02 );
						break;
					}
				}
				totalDamage = npcHealth * npcRarityMultiplier;
				player = GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerControlledGameObject();
				isHackedExplosionPerkBought = PlayerDevelopmentSystem.GetData( player ).IsNewPerkBought( gamedataNewPerkType.Intelligence_Left_Perk_1_1 ) == 1;
				if( isHackedExplosionPerkBought )
				{
					bonusDamageMult = 1.0;
					if( hitEvent.attackData.HasFlag( hitFlag.QuickHack ) )
					{
						bonusDamageMult += 0.40000001;
					}
					if( StatusEffectSystem.ObjectHasStatusEffect( npc, T"BaseStatusEffect.DistractionDuration" ) || StatusEffectSystem.ObjectHasStatusEffect( npc, T"BaseStatusEffect.WasQuickHacked" ) )
					{
						bonusDamageMult += 0.80000001;
					}
					totalDamage *= bonusDamageMult;
				}
				hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Thermal );
			}
		}
	}

	private function ProcessGrenadeExplosionDamageToPlayer( hitEvent : gameHitEvent )
	{
		var tweakid : TweakDBID;
		var player : weak< PlayerPuppet >;
		var instig : weak< GameObject >;
		var hitFlags : array< String >;
		var difficulty : gameDifficulty;
		var totalDamage : Float;
		var attackRecord : Attack_GameEffect_Record;
		var playerMaxHealth : Float;
		var isExplosionReductionPerkBought : Bool;
		var overridePlayerDamageFixedPercentage : Float;
		var grenadeFromNpc : Bool;
		player = ( ( PlayerPuppet )( hitEvent.target ) );
		instig = hitEvent.attackData.GetInstigator();
		if( !( player ) )
		{
			return;
		}
		attackRecord = ( ( Attack_GameEffect_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
		hitFlags = attackRecord.HitFlags();
		if( !( hitFlags.Contains( "PlayerGrenadeDamageOverride" ) ) )
		{
			return;
		}
		grenadeFromNpc = isExplosionReductionPerkBought = false;
		if( instig.IsNPC() )
		{
			grenadeFromNpc = true;
		}
		if( hitFlags.Contains( "PlayerEMPGrenadeDOTOverride" ) || hitFlags.Contains( "EmpGrenade" ) )
		{
			tweakid = T"Items.GrenadeEMPRegular";
		}
		else if( hitFlags.Contains( "PlayerBurnGrenadeDOTOverride" ) )
		{
			tweakid = T"Items.GrenadeIncendiaryRegular";
		}
		else if( hitFlags.Contains( "PlayerBioGrenadeDOTOverride" ) )
		{
			tweakid = T"Items.GrenadeBiohazardRegular";
		}
		else if( hitFlags.Contains( "PlayerFragGrenadeDamageOverride" ) )
		{
			tweakid = T"Items.GrenadeFragRegular";
		}
		if( TDBID.IsValid( tweakid ) )
		{
			difficulty = GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() ).GetDifficulty();
			if( grenadeFromNpc == false )
			{
				switch( difficulty )
				{
					case gameDifficulty.Story:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".storyDifficultySelfDamagePerTick" );
					break;
					case gameDifficulty.Easy:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".normalDifficultySelfDamagePerTick" );
					break;
					case gameDifficulty.Hard:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".hardDifficultySelfDamagePerTick" );
					break;
					case gameDifficulty.VeryHard:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".veryHardDifficultySelfDamagePerTick" );
					break;
				}
				isExplosionReductionPerkBought = PlayerDevelopmentSystem.GetData( player ).IsNewPerkBought( gamedataNewPerkType.Tech_Left_Perk_3_01 ) == 1;
			}
			else if( grenadeFromNpc )
			{
				switch( difficulty )
				{
					case gameDifficulty.Story:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".npc_storyDifficultySelfDamagePerTick" );
					break;
					case gameDifficulty.Easy:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".npc_normalDifficultySelfDamagePerTick" );
					break;
					case gameDifficulty.Hard:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".npc_hardDifficultySelfDamagePerTick" );
					break;
					case gameDifficulty.VeryHard:
						overridePlayerDamageFixedPercentage = TDB.GetFloat( tweakid + T".npc_veryHardDifficultySelfDamagePerTick" );
					break;
				}
			}
		}
		playerMaxHealth = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.Health );
		totalDamage = ( overridePlayerDamageFixedPercentage * 0.01 ) * playerMaxHealth;
		if( isExplosionReductionPerkBought )
		{
			totalDamage *= 0.5;
		}
		if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Physical ) > 0.0 )
		{
			hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Physical );
		}
		if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Thermal ) > 0.0 )
		{
			hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Thermal );
		}
		if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Electric ) > 0.0 )
		{
			hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Electric );
		}
		if( hitEvent.attackComputed.GetAttackValue( gamedataDamageType.Chemical ) > 0.0 )
		{
			hitEvent.attackComputed.SetAttackValue( totalDamage, gamedataDamageType.Chemical );
		}
	}

	private function ProcessQuickHackModifiers( hitEvent : gameHitEvent )
	{
		var player : weak< PlayerPuppet >;
		var targetNpcType : gamedataNPCType;
		var targetNpcRarity : gamedataNPCRarity;
		var attackRecord : Attack_GameEffect_Record;
		var hitFlags : array< String >;
		var i : Int32;
		var j : Int32;
		var damageMultiplier : Float;
		var tempValue : Float;
		var currentMemory : Float;
		var maxMemory : Float;
		var statusEffectSystem : StatusEffectSystem;
		var statusEffects : array< StatusEffect >;
		var statValue : Float;
		var attackType : gamedataAttackType;
		var attackValues : array< Float >;
		statusEffectSystem = GameInstance.GetStatusEffectSystem( GetGameInstance() );
		player = ( ( PlayerPuppet )( hitEvent.attackData.GetInstigator() ) );
		if( !( player ) )
		{
			return;
		}
		if( !( hitEvent.target.IsPuppet() ) )
		{
			return;
		}
		if( hitEvent.attackData.HasFlag( hitFlag.CriticalHit ) )
		{
			attackType = hitEvent.attackData.GetAttackType();
			if( ( ( GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.ShortCircuitOnCriticalHit ) == 1.0 ) && !( statusEffectSystem.HasStatusEffectWithTag( hitEvent.target.GetEntityID(), 'Overload' ) ) ) && ( ( ( ( ( ( attackType == gamedataAttackType.ChargedWhipAttack || attackType == gamedataAttackType.Melee ) || attackType == gamedataAttackType.QuickMelee ) || attackType == gamedataAttackType.Ranged ) || attackType == gamedataAttackType.StrongMelee ) || attackType == gamedataAttackType.Thrown ) || attackType == gamedataAttackType.WhipAttack ) )
			{
				statusEffectSystem.ApplyStatusEffect( hitEvent.target.GetEntityID(), T"BaseStatusEffect.Overload", GameObject.GetTDBID( player ), player.GetEntityID(), 1, hitEvent.hitDirection );
			}
		}
		if( statusEffectSystem.HasStatusEffect( hitEvent.target.GetEntityID(), T"BaseStatusEffect.OverheatLevel4" ) && hitEvent.attackData.GetAttackType() != gamedataAttackType.Hack )
		{
			attackValues = hitEvent.attackComputed.GetAttackValues();
			attackValues[ ( ( Int32 )( gamedataDamageType.Thermal ) ) ] += ( attackValues[ ( ( Int32 )( gamedataDamageType.Physical ) ) ] * TweakDBInterface.GetFloat( T"Items.OverheatLvl4Program.bonusThermalDamageFactor", 0.0 ) );
			hitEvent.attackComputed.SetAttackValues( attackValues );
		}
		if( statusEffectSystem.HasStatusEffect( hitEvent.target.GetEntityID(), T"BaseStatusEffect.OverheatLevel4PlusPlus" ) && hitEvent.attackData.GetAttackType() != gamedataAttackType.Hack )
		{
			attackValues = hitEvent.attackComputed.GetAttackValues();
			attackValues[ ( ( Int32 )( gamedataDamageType.Thermal ) ) ] += ( attackValues[ ( ( Int32 )( gamedataDamageType.Physical ) ) ] * TweakDBInterface.GetFloat( T"Items.OverheatLvl4PlusPlusProgram.bonusThermalDamageFactor", 0.0 ) );
			hitEvent.attackComputed.SetAttackValues( attackValues );
		}
		if( hitEvent.attackData.GetAttackType() != gamedataAttackType.Hack )
		{
			return;
		}
		damageMultiplier = 1.0;
		attackRecord = ( ( Attack_GameEffect_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
		hitFlags = attackRecord.HitFlags();
		targetNpcType = ( ( ScriptedPuppet )( hitEvent.target ) ).GetNPCType();
		targetNpcRarity = ( ( ScriptedPuppet )( hitEvent.target ) ).GetNPCRarity();
		for( i = 0; i < hitFlags.Size(); i += 1 )
		{
			if( hitFlags[ i ] == "FleshDamageBonus" )
			{
				if( targetNpcType == gamedataNPCType.Human )
				{
					damageMultiplier += TweakDBInterface.GetFloat( attackRecord.GetID() + T".fleshDamageBonusMultiplier", 1.0 );
				}
			}
			if( ( hitEvent.attackData.GetInstigator() == player ) && hitFlags[ i ] == "DamageBasedOnMissingMemoryBonus" )
			{
				currentMemory = GameInstance.GetStatPoolsSystem( player.GetGame() ).GetStatPoolValue( player.GetEntityID(), gamedataStatPoolType.Memory, false );
				maxMemory = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.Memory );
				if( currentMemory < maxMemory )
				{
					tempValue = maxMemory - currentMemory;
					if( statusEffectSystem.HasStatusEffect( player.GetEntityID(), T"BaseStatusEffect.Intelligence_Central_Milestone_3_Overclock_Buff" ) )
					{
						tempValue *= ( TDB.GetFloat( attackRecord.GetID() + T".damageBasedOnMissingMemoryBonusMultiplier" ) * 2.0 );
					}
					else
					{
						tempValue *= TDB.GetFloat( attackRecord.GetID() + T".damageBasedOnMissingMemoryBonusMultiplier" );
					}
					damageMultiplier += MinF( tempValue, TDB.GetFloat( attackRecord.GetID() + T".damageBasedOnMissingMemoryBonusMax" ) );
				}
			}
			if( hitFlags[ i ] == "CyberwareMalfunctionDamageBonus" )
			{
				tempValue = 0.0;
				statusEffectSystem.GetAppliedEffectsWithTag( hitEvent.target.GetEntityID(), 'CyberwareMalfunction', statusEffects );
				for( j = 0; j < statusEffects.Size(); j += 1 )
				{
					tempValue += ( ( Float )( statusEffects[ j ].GetStackCount() ) );
				}
				tempValue *= TweakDBInterface.GetFloat( attackRecord.GetID() + T".cyberwareMalfunctionDamageBonus", 0.0 );
				damageMultiplier += tempValue;
			}
			if( hitFlags[ i ] == "NonEliteDamageBonus" )
			{
				if( ( ( ( targetNpcRarity == gamedataNPCRarity.Normal || targetNpcRarity == gamedataNPCRarity.Trash ) || targetNpcRarity == gamedataNPCRarity.Weak ) || targetNpcRarity == gamedataNPCRarity.Rare ) || targetNpcRarity == gamedataNPCRarity.Officer )
				{
					damageMultiplier += TweakDBInterface.GetFloat( attackRecord.GetID() + T".nonEliteDamageBonusMultiplier", 0.0 );
				}
			}
			if( hitFlags[ i ] == "DamageOverTime" )
			{
				damageMultiplier += GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.QuickhackDamageOverTimeBonusMultiplier );
			}
		}
		if( StatusEffectSystem.ObjectHasStatusEffect( hitEvent.target, T"MinigameAction.VulnerabilityMinigame" ) )
		{
			statValue = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.QuickhackExtraDamageMultiplier );
			if( statValue > 0.0 )
			{
				damageMultiplier += statValue;
			}
		}
		if( hitEvent.attackData.GetInstigator().IsPlayer() )
		{
			statValue = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.QuickhackDamageBonusMultiplier );
			if( statValue > 0.0 )
			{
				damageMultiplier += statValue;
			}
		}
		if( damageMultiplier != 1.0 )
		{
			hitEvent.attackComputed.MultAttackValue( damageMultiplier );
		}
	}

	private function PreProcessVehicleTarget( hitEvent : gameHitEvent )
	{
		var hitComponentName : CName;
		var targetVehicle : VehicleObject;
		targetVehicle = ( ( VehicleObject )( hitEvent.target ) );
		if( targetVehicle )
		{
			hitComponentName = hitEvent.hitComponent.GetName();
			if( targetVehicle.ShouldDamageSystemIgnoreHit( hitComponentName ) )
			{
				hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'DamageSystemIgnoreHit' );
			}
			hitEvent.attackData.AddFlag( hitFlag.ForceNoCrit, 'PreProcessVehicleTarget' );
		}
	}

	private function ProcessVehicleTarget( hitEvent : gameHitEvent, cache : CacheData )
	{
		var vehicleDamageQualityDivisor, minimumHealthPercent : Float;
		var targetVehicle : VehicleObject;
		var statsSystem : StatsSystem;
		var weaponType : gamedataItemType;
		var multiplier : Float;
		var godModeSystem : GodModeSystem;
		var weaponObject : WeaponObject;
		var instigator : GameObject;
		multiplier = 1.0;
		if( hitEvent.projectionPipeline )
		{
			return;
		}
		targetVehicle = ( ( VehicleObject )( hitEvent.target ) );
		weaponObject = hitEvent.attackData.GetWeapon();
		if( targetVehicle )
		{
			if( ( targetVehicle.IsPlayerMounted() || targetVehicle.IsCrowdVehicle() ) || !( targetVehicle.HasPassengers() ) )
			{
				hitEvent.attackData.AddFlag( hitFlag.DontShowDamageFloater, 'Target is either the player vehicle, a crowd vehicle or does not have any passengers' );
			}
			if( targetVehicle.GetVehicleComponent().IsVehicleImmuneInDecay() )
			{
				hitEvent.attackComputed.MultAttackValue( 0.0 );
				hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'VehicleInDecayState' );
				return;
			}
			instigator = hitEvent.attackData.GetInstigator();
			if( hitEvent.attackData.HasFlag( hitFlag.PlayerWallImpact ) )
			{
				hitEvent.attackComputed.MultAttackValue( 0.0 );
				hitEvent.attackData.AddFlag( hitFlag.DealNoDamage, 'PlayerWallImpact' );
				return;
			}
			else if( hitEvent.attackData.HasFlag( hitFlag.VehicleImpact ) )
			{
				hitEvent.attackComputed.SetAttackValues( hitEvent.attackComputed.GetOriginalAttackValues() );
				multiplier = hitEvent.attackData.GetVehicleImpactForce();
				if( targetVehicle.IsPlayerDriver() )
				{
					if( PlayerDevelopmentSystem.GetData( VehicleComponent.GetDriver( targetVehicle.GetGame(), targetVehicle, targetVehicle.GetEntityID() ) ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Body_Right_Milestone_1 ) )
					{
						multiplier *= 0.5;
					}
				}
				else if( instigator.IsPlayer() && hitEvent.attackData.HasFlag( hitFlag.VehicleImpactWithPlayer ) )
				{
					if( PlayerDevelopmentSystem.GetData( instigator ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Body_Right_Milestone_1 ) )
					{
						multiplier *= 1.5;
					}
				}
			}
			else
			{
				weaponType = RPGManager.GetItemRecord( weaponObject.GetItemID() ).ItemType().Type();
				statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
				if( !( IsOverridenExplosionVsVehicleHit( hitEvent ) ) )
				{
					if( !( targetVehicle.IsPlayerMounted() ) && instigator.IsPlayer() )
					{
						multiplier *= ( 1.0 + statsSystem.GetStatValue( instigator.GetEntityID(), gamedataStatType.VehicleDamagePercentBonus ) );
						multiplier *= ( 1.0 + statsSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.WeaponVehicleDamagePercentBonus ) );
						vehicleDamageQualityDivisor = statsSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.VehicleDamageQualityDivisor );
						if( vehicleDamageQualityDivisor > 0.0 )
						{
							multiplier /= vehicleDamageQualityDivisor;
						}
						multiplier *= GetVehiclePerksDamageMultiplier( hitEvent.attackData );
					}
					else
					{
						hitEvent.attackComputed.MultAttackValue( 0.0 );
						if( targetVehicle.IsPlayerMounted() )
						{
							if( targetVehicle.TrySetHitCooldown() )
							{
								hitEvent.attackComputed.SetAttackValue( 15.0, gamedataDamageType.Physical );
							}
							else
							{
								hitEvent.attackComputed.SetAttackValue( 1.0, gamedataDamageType.Physical );
							}
						}
						else
						{
							hitEvent.attackComputed.SetAttackValue( 4.0, gamedataDamageType.Physical );
						}
					}
					switch( weaponType )
					{
						case gamedataItemType.Wea_HeavyMachineGun:
							multiplier *= 1.29999995;
						break;
						default:
							break;
					}
				}
			}
			hitEvent.attackComputed.MultAttackValue( multiplier );
			minimumHealthPercent = statsSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.VehicleMinHealthPercentWhenDamaged );
			if( hitEvent.attackData.GetAttackType() == gamedataAttackType.QuickMelee )
			{
				minimumHealthPercent = MaxF( minimumHealthPercent, TDB.GetFloat( T"player.quickMelee.quickMeleeVehicleMinHealthPercentWhenDamaged", 20.0 ) );
			}
			godModeSystem = GameInstance.GetGodModeSystem( hitEvent.target.GetGame() );
			if( godModeSystem.HasGodMode( hitEvent.target.GetEntityID(), gameGodModeType.Immortal ) )
			{
				minimumHealthPercent = MaxF( minimumHealthPercent, targetVehicle.GetVehicleComponent().GetVehicleDecayThreshold() + 1.0 );
			}
			if( minimumHealthPercent > 0.0 )
			{
				hitEvent.attackData.SetMinimumHealthPercent( minimumHealthPercent );
			}
		}
	}

	private function CalculateVehicleTargetRangedDamage( weaponObject : WeaponObject, chargeDamageMultiplier : Float, statSystem : StatsSystem ) : Float
	{
		var multiplier : Float;
		var weaponMultiplier : Float;
		var desiredWeaponDPS : Float;
		multiplier = 1.0;
		desiredWeaponDPS = 90.0;
		weaponMultiplier = statSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.CycleTimeBase );
		weaponMultiplier /= statSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.ProjectilesPerShot );
		multiplier *= ( desiredWeaponDPS * weaponMultiplier );
		if( chargeDamageMultiplier > 0.0 )
		{
			multiplier *= chargeDamageMultiplier;
		}
		return multiplier;
	}

	private function CalculateVehicleTargetMeleeDamage( weaponObject : WeaponObject, attackType : gamedataAttackType, isBodySlam : Bool, statSystem : StatsSystem ) : Float
	{
		var multiplier : Float;
		var weaponMultiplier : Float;
		var desiredWeaponDPS : Float;
		var strongMeleeMultiplier : Float;
		var bodySlamMultiplier : Float;
		var physicalImpulse : Float;
		multiplier = 1.0;
		physicalImpulse = 1.0;
		desiredWeaponDPS = 24.0;
		strongMeleeMultiplier = 2.5;
		bodySlamMultiplier = 0.30000001;
		if( attackType == gamedataAttackType.QuickMelee )
		{
			multiplier = desiredWeaponDPS;
			physicalImpulse = 10.0;
		}
		else
		{
			if( isBodySlam )
			{
				multiplier = desiredWeaponDPS * bodySlamMultiplier;
			}
			else
			{
				weaponMultiplier = statSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.AttacksPerSecondBase );
				multiplier *= ( desiredWeaponDPS / weaponMultiplier );
				if( attackType == gamedataAttackType.StrongMelee )
				{
					multiplier *= strongMeleeMultiplier;
				}
			}
			physicalImpulse = statSystem.GetStatValue( weaponObject.GetEntityID(), gamedataStatType.PhysicalImpulse );
		}
		multiplier *= ( PowF( physicalImpulse, 0.30000001 ) - 0.80000001 );
		return multiplier;
	}

	private function ProcessVehicleHit( hitEvent : gameHitEvent )
	{
		var vehicleHitEvent : gameVehicleHitEvent;
		var targetPuppet : ScriptedPuppet;
		var targetIsFriendly : Bool;
		var preyVelocityComponent : Vector4;
		var resultantVelocity : Vector4;
		var magnitude : Float;
		var curveDamagePercentage : Float;
		var targetMaxHealth : Float;
		var isCrowdOrCivilian : Bool;
		var isMechanical : Bool;
		var instaKilledByVehicleCollision : Bool;
		vehicleHitEvent = ( ( gameVehicleHitEvent )( hitEvent ) );
		if( !( vehicleHitEvent ) )
		{
			return;
		}
		targetPuppet = ( ( ScriptedPuppet )( hitEvent.target ) );
		if( !( targetPuppet ) )
		{
			return;
		}
		if( targetPuppet.IsCrowd() )
		{
			if( ( ( NPCPuppet )( targetPuppet ) ).m_shouldBeImmuneToVehicleHit )
			{
				hitEvent.attackComputed.SetAttackValue( 0.0 );
				return;
			}
		}
		targetIsFriendly = GameObject.GetAttitudeTowards( targetPuppet, GameInstance.GetPlayerSystem( targetPuppet.GetGame() ).GetLocalPlayerControlledGameObject() ) == EAIAttitude.AIA_Friendly;
		if( targetIsFriendly )
		{
			hitEvent.attackComputed.SetAttackValue( 0.0 );
			return;
		}
		preyVelocityComponent = Vector4.Normalize( vehicleHitEvent.vehicleVelocity ) * ( Vector4.Dot( vehicleHitEvent.preyVelocity, vehicleHitEvent.vehicleVelocity ) / Vector4.Length( vehicleHitEvent.vehicleVelocity ) );
		resultantVelocity = vehicleHitEvent.vehicleVelocity + preyVelocityComponent;
		magnitude = Vector4.Length( resultantVelocity );
		targetMaxHealth = GameInstance.GetStatsSystem( targetPuppet.GetGame() ).GetStatValue( targetPuppet.GetEntityID(), gamedataStatType.Health );
		isCrowdOrCivilian = targetPuppet.IsCrowd() || targetPuppet.IsCivilian();
		isMechanical = targetPuppet.HasMechanicalImpactComponent();
		instaKilledByVehicleCollision = ( magnitude >= TweakDBInterface.GetFloat( T"AIGeneralSettings.vehicleHitKillThreshold", 25.0 ) ) || ( isCrowdOrCivilian && ( magnitude >= TweakDBInterface.GetFloat( T"AIGeneralSettings.vehicleHitCrowdKillThreshold", 20.0 ) ) );
		if( !( isMechanical ) && instaKilledByVehicleCollision )
		{
			if( ( ( NPCPuppet )( targetPuppet ) ) )
			{
				( ( NPCPuppet )( targetPuppet ) ).SetMyKiller( hitEvent.attackData.GetInstigator() );
				( ( NPCPuppet )( targetPuppet ) ).MarkForDeath();
			}
			hitEvent.attackComputed.SetAttackValue( targetMaxHealth, gamedataDamageType.Physical );
			hitEvent.attackData.AddFlag( hitFlag.DeterministicDamage, 'vehicle_collision' );
		}
		else
		{
			if( isCrowdOrCivilian )
			{
				if( hitEvent.attackData.GetInstigator().IsPlayer() )
				{
					curveDamagePercentage = GameInstance.GetStatsDataSystem( targetPuppet.GetGame() ).GetValueFromCurve( 'vehicle_collision_damage', magnitude, 'crowd_hit_damage' );
					if( curveDamagePercentage == 0.0 )
					{
						if( GameObject.IsCooldownActive( targetPuppet, 'vehicleSlowHitOnCivilian' ) )
						{
							curveDamagePercentage = GameInstance.GetStatsDataSystem( targetPuppet.GetGame() ).GetValueFromCurve( 'vehicle_collision_damage', magnitude, 'crowd_hit_high_damage' );
							GameObject.StartCooldown( targetPuppet, 'vehicleSlowHitOnCivilian', 0.0 );
						}
						else
						{
							hitEvent.attackData.AddFlag( hitFlag.DontShowDamageFloater, 'dealing 0 damage' );
						}
						GameObject.StartCooldown( targetPuppet, 'vehicleSlowHitOnCivilian', 3.0 );
					}
				}
				else
				{
					curveDamagePercentage = GameInstance.GetStatsDataSystem( targetPuppet.GetGame() ).GetValueFromCurve( 'vehicle_collision_damage', magnitude, 'crowd_hit_high_damage' );
				}
			}
			else
			{
				curveDamagePercentage = GameInstance.GetStatsDataSystem( targetPuppet.GetGame() ).GetValueFromCurve( 'vehicle_collision_damage', magnitude, 'npc_hit_damage' );
			}
			if( isMechanical )
			{
				curveDamagePercentage = MinF( curveDamagePercentage, 0.25 );
			}
			hitEvent.attackComputed.SetAttackValue( curveDamagePercentage * targetMaxHealth, gamedataDamageType.Physical );
			if( RPGManager.CalculatePowerDifferential( targetPuppet ) == gameEPowerDifferential.IMPOSSIBLE )
			{
				hitEvent.attackComputed.MultAttackValue( 0.5 );
			}
		}
		GameInstance.GetTelemetrySystem( targetPuppet.GetGame() ).LogDamageByVehicle( hitEvent );
	}

	private function DealDamages( hitEvent : gameHitEvent )
	{
		var resourcesLost : array< SDamageDealt >;
		var forReal : Bool;
		forReal = !( GameInstance.GetRuntimeInfo( GetGameInstance() ).IsClient() );
		StatPoolsManager.ApplyDamage( hitEvent, forReal, resourcesLost );
		SendDamageEvents( hitEvent, resourcesLost );
	}

	private function SendDamageEvents( hitEvent : gameHitEvent, const resourcesLost : ref< array< SDamageDealt > > )
	{
		var damageDealtEvent : gameTargetDamageEvent;
		var damageReceivedEvent : gameDamageReceivedEvent;
		var totalDamage : Float;
		var i : Int32;
		totalDamage = 0.0;
		for( i = 0; i < resourcesLost.Size(); i += 1 )
		{
			totalDamage += resourcesLost[ i ].value;
		}
		damageDealtEvent = new gameTargetDamageEvent;
		damageDealtEvent.target = hitEvent.target;
		damageDealtEvent.attackData = hitEvent.attackData;
		damageDealtEvent.hitPosition = hitEvent.hitPosition;
		damageDealtEvent.hitDirection = hitEvent.hitDirection;
		damageDealtEvent.hitRepresentationResult = hitEvent.hitRepresentationResult;
		damageDealtEvent.damage = totalDamage;
		hitEvent.attackData.GetInstigator().QueueEvent( damageDealtEvent );
		if( totalDamage > 0.0 )
		{
			damageReceivedEvent = new gameDamageReceivedEvent;
			damageReceivedEvent.totalDamageReceived = totalDamage;
			damageReceivedEvent.hitEvent = hitEvent;
			hitEvent.target.QueueEvent( damageReceivedEvent );
		}
	}

	private function PostProcess( hitEvent : gameHitEvent )
	{
		var poiseDamage : Float;
		ProcessStatusEffects( hitEvent );
		ProcessReturnedDamage( hitEvent );
		DamageManager.PostProcess( hitEvent );
		if( ( ( hitEvent.attackData.GetInstigator().IsPlayer() && hitEvent.attackData.GetInstigator().HasFinisherAvailable() ) && hitEvent.target.CanReceivePoiseDamage() ) && !( hitEvent.attackData.GetInstigator().GetIsInFastFinisher() ) )
		{
			poiseDamage = hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
			switch( hitEvent.attackData.GetAttackType() )
			{
				case gamedataAttackType.QuickMelee:
				{
					poiseDamage *= TweakDBInterface.GetFloat( T"NewPerks.Reflexes_Right_Milestone_3.poiseQuickMeleeMultiplier", 1.0 );
					break;
				}
				case gamedataAttackType.StrongMelee:
				{
					poiseDamage *= TweakDBInterface.GetFloat( T"NewPerks.Reflexes_Right_Milestone_3.poiseStrongMeleeMultiplier", 1.0 );
					break;
				}
			}
			if( GameObject.TargetHasDebuff( hitEvent.target ) )
			{
				poiseDamage *= TweakDBInterface.GetFloat( T"NewPerks.Reflexes_Right_Milestone_3.poiseDebufMultiplier", 1.0 );
			}
			if( hitEvent.target.IsInFinisherHealthThreshold( hitEvent.attackData.GetInstigator() ) )
			{
				poiseDamage *= TweakDBInterface.GetFloat( T"NewPerks.Reflexes_Right_Milestone_3.poiseBossLowHealthMultiplier", 1.0 );
			}
			GameInstance.GetStatPoolsSystem( hitEvent.target.GetGame() ).RequestChangingStatPoolValue( hitEvent.target.GetEntityID(), gamedataStatPoolType.Poise, -( poiseDamage ), hitEvent.attackData.GetInstigator(), false, false );
		}
		if( !( hitEvent.attackData.HasFlag( hitFlag.TargetWasAlreadyDeadNoStatPool ) ) && hitEvent.attackData.GetInstigator().IsPlayer() )
		{
			SendDamageRequestToPreventionSystem( hitEvent );
		}
	}

	private function SendDamageRequestToPreventionSystem( hitEvent : gameHitEvent )
	{
		var npcMaxHealth : Float;
		var damage : Float;
		var damageDealtPercent : Float;
		npcMaxHealth = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.Health );
		damage = hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
		damageDealtPercent = damage / npcMaxHealth;
		if( damageDealtPercent > PreventionSystem.GetDamageReactionThreshold() )
		{
			PreventionSystem.CreateNewPreventionDamageRequest( GetGameInstance(), hitEvent.target, hitEvent.attackData.GetAttackTime(), hitEvent.attackData.GetAttackType(), damageDealtPercent, false );
		}
	}

	protected static function HasGrandFinaleStatFlag( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return RPGManager.HasStatFlag( scriptInterface.executionOwner, gamedataStatType.CanDoGrandFinaleWithMantisBlades );
	}

	private function ProcessStatusEffects( hitEvent : gameHitEvent )
	{
		var attackData : AttackData;
		var i : Int32;
		var instantEffects : array< SHitStatusEffect >;
		var effectDamages : array< weak< StatusEffectAttackData_Record > >;
		var target : GameObject;
		var targetId : EntityID;
		var targetPuppet : ScriptedPuppet;
		var statusEffectID : TweakDBID;
		var instantApply : Bool;
		var statusEffectSystem : StatusEffectSystem;
		var instigator : GameObject;
		var hasPerkPurchased : Bool;
		var playerJustLeaped : Bool;
		var isMechanical : Bool;
		var isExo : Bool;
		attackData = hitEvent.attackData;
		target = hitEvent.target;
		targetId = target.GetEntityID();
		targetPuppet = ( ( ScriptedPuppet )( target ) );
		statusEffectSystem = GameInstance.GetStatusEffectSystem( GetGameInstance() );
		instigator = attackData.GetInstigator();
		hasPerkPurchased = RPGManager.HasStatFlag( instigator, gamedataStatType.CanDoGrandFinaleWithMantisBlades );
		playerJustLeaped = StatusEffectSystem.ObjectHasStatusEffectWithTag( instigator, 'JustLeaped' );
		isMechanical = targetPuppet.IsMechanical();
		isExo = AIActionHelper.CheckAbility( targetPuppet, TweakDBInterface.GetGameplayAbilityRecord( T"Ability.IsExo" ) );
		if( ( attackData.WasBlocked() || ( !( instigator.IsPlayer() ) && attackData.WasDeflectedAny() ) ) || attackData.HasFlag( hitFlag.FriendlyFireIgnored ) )
		{
			return;
		}
		if( GameObject.IsVehicle( hitEvent.target ) && !( ShouldProcessStatusEffectsOnVehicleDriver( hitEvent, targetId ) ) )
		{
			return;
		}
		if( ( !( target.IsPlayer() ) && instigator.IsPlayer() ) && GameObject.GetAttitudeTowards( target, instigator ) == EAIAttitude.AIA_Friendly )
		{
			return;
		}
		instantEffects = hitEvent.attackData.GetStatusEffects();
		for( i = 0; i < instantEffects.Size(); i += 1 )
		{
			statusEffectID = instantEffects[ i ].id;
			if( !( IsImmune( target, statusEffectID, attackData ) ) )
			{
				statusEffectSystem.ApplyStatusEffect( targetId, statusEffectID, GameObject.GetTDBID( instigator ), instigator.GetEntityID(), ( ( Uint32 )( instantEffects[ i ].stacks ) ), hitEvent.hitDirection );
			}
		}
		attackData.GetAttackDefinition().GetRecord().StatusEffects( effectDamages );
		for( i = 0; i < effectDamages.Size(); i += 1 )
		{
			statusEffectID = effectDamages[ i ].StatusEffect().GetID();
			if( !( IsImmune( target, statusEffectID, attackData ) ) )
			{
				instantApply = effectDamages[ i ].ApplyImmediately();
				if( instantApply )
				{
					statusEffectSystem.ApplyStatusEffect( targetId, statusEffectID, GameObject.GetTDBID( instigator ), instigator.GetEntityID(), 1, hitEvent.hitDirection );
				}
				else
				{
					StatPoolsManager.ApplyStatusEffectDamage( hitEvent, effectDamages[ i ].ResistPool(), statusEffectID );
				}
			}
		}
		if( instigator.IsPlayer() && statusEffectSystem.HasStatusEffectWithTag( targetId, 'GagOpportunity' ) )
		{
			statusEffectSystem.RemoveStatusEffect( targetId, T"BaseStatusEffect.GagOpportunity" );
			statusEffectSystem.ApplyStatusEffect( targetId, T"BaseStatusEffect.Gag" );
		}
		if( ( ( ( ( !( target.IsPlayer() ) && instigator.IsPlayer() ) && hasPerkPurchased ) && playerJustLeaped ) && !( isMechanical ) ) && !( isExo ) )
		{
			StatusEffectHelper.ApplyStatusEffect( target, T"BaseStatusEffect.MantisBladesRelicDismemberment" );
		}
		ProcessStatusEffectApplicationStats( hitEvent );
	}

	private static function GetMantisBladesCripplingRandStatusEffectID() : TweakDBID
	{
		var statusEffectNames : array< String >;
		var statusEffectsIDs : array< TweakDBID >;
		var appliedStatusEffect : TweakDBID;
		var i, size : Int32;
		statusEffectNames = TDB.GetStringArray( T"BaseStatusEffect.CripplingStatusEffects" );
		size = statusEffectNames.Size();
		for( i = 0; i < size; i += 1 )
		{
			statusEffectsIDs.PushBack( TDBID.Create( statusEffectNames[ i ] ) );
		}
		i = RandRange( 0, statusEffectsIDs.Size() );
		appliedStatusEffect = statusEffectsIDs[ i ];
		return appliedStatusEffect;
	}

	private function ProcessStatusEffectApplicationStats( hitEvent : gameHitEvent )
	{
		var attackType : gamedataAttackType;
		var attackSubType : gamedataAttackSubtype;
		var bleedingID : TweakDBID;
		var burningID : TweakDBID;
		var poisonedID : TweakDBID;
		var electrocutedID : TweakDBID;
		var stunnedID : TweakDBID;
		var isTargetPlayer : Bool;
		var isTargetPuppet : Bool;
		var transactionSystem : TransactionSystem;
		attackType = hitEvent.attackData.GetAttackType();
		attackSubType = hitEvent.attackData.GetAttackSubtype();
		isTargetPlayer = hitEvent.target.IsPlayer();
		isTargetPuppet = hitEvent.target.IsPuppet();
		transactionSystem = GameInstance.GetTransactionSystem( hitEvent.target.GetGame() );
		if( ( isTargetPuppet && ( ( AttackData.IsLightMelee( attackType ) || AttackData.IsStrongMelee( attackType ) ) || AttackData.IsRangedOrDirectOrThrown( attackType ) ) ) && attackSubType != gamedataAttackSubtype.BodySlamAttack )
		{
			if( isTargetPlayer )
			{
				bleedingID = T"BaseStatusEffect.PlayerBleeding";
				burningID = T"BaseStatusEffect.PlayerBurning";
				poisonedID = T"BaseStatusEffect.PlayerPoisoned";
				electrocutedID = T"BaseStatusEffect.PlayerElectrocuted";
			}
			else
			{
				if( transactionSystem.HasTag( hitEvent.attackData.GetInstigator(), 'Saburo_Tanto_Bleed', hitEvent.attackData.GetWeapon().GetItemID() ) )
				{
					bleedingID = T"BaseStatusEffect.Tanto_Saburo_Bleeding";
				}
				else if( transactionSystem.HasTag( hitEvent.attackData.GetInstigator(), 'Saburo_Katana_Bleed', hitEvent.attackData.GetWeapon().GetItemID() ) )
				{
					bleedingID = T"BaseStatusEffect.Katana_Saburo_Bleeding";
				}
				else if( TDB.GetWeaponItemRecord( ItemID.GetTDBID( hitEvent.attackData.GetWeapon().GetItemID() ) ).Evolution().Type() == gamedataWeaponEvolution.Blade )
				{
					bleedingID = T"BaseStatusEffect.KenjutsuBleeding";
				}
				else
				{
					bleedingID = T"BaseStatusEffect.Bleeding";
				}
				if( transactionSystem.HasTag( hitEvent.attackData.GetInstigator(), 'Padre_Burn', hitEvent.attackData.GetWeapon().GetItemID() ) )
				{
					burningID = T"BaseStatusEffect.Liberty_Padre_Burning";
				}
				else if( transactionSystem.HasTag( hitEvent.attackData.GetInstigator(), 'AirDrop_Burn', hitEvent.attackData.GetWeapon().GetItemID() ) )
				{
					burningID = T"BaseStatusEffect.AirDrop_Burning";
				}
				else
				{
					burningID = T"BaseStatusEffect.Burning";
				}
				if( TDB.GetWeaponItemRecord( ItemID.GetTDBID( hitEvent.attackData.GetWeapon().GetItemID() ) ).ItemType().Type() == gamedataItemType.Wea_Knife )
				{
					poisonedID = T"BaseStatusEffect.KnifePoison";
				}
				else if( transactionSystem.HasTag( hitEvent.attackData.GetInstigator(), 'Hercules_Poison', hitEvent.attackData.GetWeapon().GetItemID() ) )
				{
					poisonedID = T"BaseStatusEffect.Hercules_Poison";
				}
				else
				{
					poisonedID = T"BaseStatusEffect.Poisoned";
				}
				electrocutedID = T"BaseStatusEffect.Electrocuted";
				stunnedID = T"BaseStatusEffect.Stun";
			}
			ApplyStatusEffectByApplicationRate( hitEvent, gamedataStatType.BleedingApplicationRate, bleedingID );
			ApplyStatusEffectByApplicationRate( hitEvent, gamedataStatType.BurningApplicationRate, burningID );
			ApplyStatusEffectByApplicationRate( hitEvent, gamedataStatType.PoisonedApplicationRate, poisonedID );
			ApplyStatusEffectByApplicationRate( hitEvent, gamedataStatType.ElectrocutedApplicationRate, electrocutedID );
			ApplyStatusEffectByApplicationRate( hitEvent, gamedataStatType.StunApplicationRate, stunnedID );
		}
	}

	private function ApplyStatusEffectByApplicationRate( hitEvent : gameHitEvent, statType : gamedataStatType, effect : TweakDBID )
	{
		var value : Float;
		var rand : Float;
		var ses : StatusEffectSystem;
		var ss : StatsSystem;
		var weapon : weak< WeaponObject >;
		ss = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
		ses = GameInstance.GetStatusEffectSystem( hitEvent.target.GetGame() );
		weapon = hitEvent.attackData.GetWeapon();
		value = ss.GetStatValue( weapon.GetEntityID(), statType ) / 100.0;
		if( hitEvent.target.IsPlayer() )
		{
			return;
		}
		if( !( FloatIsEqual( value, 0.0 ) ) )
		{
			rand = RandRangeF( 0.0, 1.0 );
			if( rand <= value )
			{
				if( !( IsImmune( hitEvent.target, effect, hitEvent.attackData ) ) )
				{
					ses.ApplyStatusEffect( hitEvent.target.GetEntityID(), effect, , hitEvent.attackData.GetInstigator().GetEntityID(),  );
					hitEvent.attackData.AddFlag( ( ( statType == gamedataStatType.StunApplicationRate ) ? ( hitFlag.StunApplied ) : ( hitFlag.DotApplied ) ), 'SETriggered' );
				}
			}
		}
	}

	private function ShouldProcessStatusEffectsOnVehicleDriver( hitEvent : gameHitEvent, out driverEntityID : EntityID ) : Bool
	{
		var rec : Attack_GameEffect_Record;
		var effectTag : CName;
		var vehicle : VehicleObject;
		vehicle = ( ( VehicleObject )( hitEvent.target ) );
		if( !( vehicle ) || !( vehicle.IsPlayerDriver() ) )
		{
			return false;
		}
		rec = ( ( Attack_GameEffect_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
		effectTag = rec.EffectTag();
		if( effectTag == 'flashbang_explosion' )
		{
			driverEntityID = GameInstance.GetPlayerSystem( hitEvent.target.GetGame() ).GetLocalPlayerControlledGameObject().GetEntityID();
			return true;
		}
		return false;
	}

	private function IsImmune( target : GameObject, statusEffectID : TweakDBID, attackData : AttackData ) : Bool
	{
		var immunityStats : array< weak< Stat_Record > >;
		var statusEffect : weak< StatusEffect_Record >;
		var i : Int32;
		var statsSystem : StatsSystem;
		var tags : array< CName >;
		var player : PlayerPuppet;
		statusEffect = TweakDBInterface.GetStatusEffectRecord( statusEffectID );
		if( !( statusEffect ) )
		{
			return true;
		}
		tags = statusEffect.GameplayTags();
		if( target.IsPlayer() )
		{
			if( tags.Contains( 'DoNotApplyOnPlayer' ) )
			{
				return true;
			}
			if( statusEffect.StatusEffectType().Type() == gamedataStatusEffectType.Defeated )
			{
				return true;
			}
			if( statusEffect.StatusEffectType().Type() == gamedataStatusEffectType.UncontrolledMovement )
			{
				return true;
			}
		}
		else if( target.IsPuppet() && statusEffect.StatusEffectType().Type() == gamedataStatusEffectType.UncontrolledMovement )
		{
			if( !( ScriptedPuppet.CanRagdoll( target ) ) )
			{
				return true;
			}
		}
		else if( statusEffect.StatusEffectType().Type() == gamedataStatusEffectType.Poisoned )
		{
			player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( target.GetGame() ).GetLocalPlayerControlledGameObject() ) );
			if( ( ( ( attackData.GetInstigator() && attackData.GetInstigator().IsPlayer() ) && attackData.GetWeapon() ) && attackData.GetWeapon().IsThrowable() ) && PlayerDevelopmentSystem.GetData( player ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Cool_Right_Perk_2_1 ) )
			{
				return false;
			}
		}
		if( GameInstance.GetGodModeSystem( target.GetGame() ).HasGodMode( target.GetEntityID(), gameGodModeType.Invulnerable ) )
		{
			if( tags.Contains( 'Debuff' ) )
			{
				return true;
			}
		}
		statusEffect.ImmunityStats( immunityStats );
		statsSystem = GameInstance.GetStatsSystem( target.GetGame() );
		for( i = 0; i < immunityStats.Size(); i += 1 )
		{
			if( statsSystem.GetStatValue( target.GetEntityID(), immunityStats[ i ].StatType() ) > 0.0 )
			{
				return true;
			}
		}
		return false;
	}

	private function ProcessReturnedDamage( hitEvent : gameHitEvent )
	{
		if( hitEvent.attackData.HasFlag( hitFlag.CannotReturnDamage ) )
		{
			return;
		}
	}

	private function CalculateGlobalModifiers( hitEvent : gameHitEvent, cache : CacheData )
	{
		var attackData : AttackData;
		var factVal : Int32;
		var targetID : StatsObjectID;
		attackData = hitEvent.attackData;
		targetID = hitEvent.target.GetEntityID();
		factVal = GetFact( GetGameInstance(), 'cheat_weak' );
		if( factVal > 0 )
		{
			attackData.ClearDamage();
			hitEvent.attackComputed.AddAttackValue( 0.01, gamedataDamageType.Physical );
			attackData.AddFlag( hitFlag.CannotModifyDamage, 'cheat_weak' );
			if( cache.logFlags & damageSystemLogFlags.GENERAL )
			{
			}
		}
		if( attackData.GetInstigator().IsPlayer() )
		{
			factVal = GetFact( GetGameInstance(), 'cheat_op' );
			if( factVal > 0 )
			{
				hitEvent.attackComputed.SetAttackValue( GameInstance.GetStatPoolsSystem( GetGameInstance() ).GetStatPoolMaxPointValue( targetID, gamedataStatPoolType.Health ) * 0.60000002, gamedataDamageType.Physical );
				attackData.ClearDamage();
				attackData.AddFlag( hitFlag.CannotModifyDamage, 'cheat_op' );
				if( cache.logFlags & damageSystemLogFlags.GENERAL )
				{
				}
			}
		}
		DamageManager.CalculateGlobalModifiers( hitEvent );
	}

	private function CalculateTargetModifiers( hitEvent : gameHitEvent )
	{
		DamageManager.CalculateTargetModifiers( hitEvent );
		ProcessArmor( hitEvent );
		ProcessOnVehicleMitigation( hitEvent );
	}

	private function CalculateSourceModifiers( hitEvent : gameHitEvent, cache : CacheData )
	{
		DamageManager.CalculateSourceModifiers( hitEvent );
		ProcessChargeAttack( hitEvent, cache );
		ProcessPierceAttack( hitEvent );
		ProcessRicochet( hitEvent );
		ProcessCriticalHit( hitEvent );
		ProcessDamageMultipliers( hitEvent );
		ProcessCyberwareModifiers( hitEvent );
		ProcessStealthAttack( hitEvent );
		ProcessNPCPassengerVehicleCollision( hitEvent );
		ProcessSpreadingMultiplier( hitEvent );
	}

	private function ProcessPierceAttack( hitEvent : gameHitEvent )
	{
		var attackData : AttackData;
		var statsSystem : StatsSystem;
		var weaponObject : WeaponObject;
		var damageFactor : Float;
		var statsOwner : EntityID;
		attackData = hitEvent.attackData;
		statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
		weaponObject = attackData.GetWeapon();
		if( !( weaponObject ) )
		{
			return;
		}
		if( hitEvent.hasPiercedTechSurface )
		{
			statsOwner = weaponObject.GetEntityID();
			weaponObject = attackData.GetWeapon();
			damageFactor = 1.0 + statsSystem.GetStatValue( statsOwner, gamedataStatType.TechPierceDamageFactor );
			if( damageFactor > 0.0 )
			{
				hitEvent.attackComputed.MultAttackValue( damageFactor );
			}
		}
	}

	private function ProcessChargeAttack( hitEvent : gameHitEvent, cache : CacheData )
	{
		var attackData : AttackData;
		var statsSystem : StatsSystem;
		var chargeNormalized : Float;
		var chargeDamageMult : Float;
		var maxChargeThreshold : Float;
		var maxChargeModifier : Float;
		var statsOwner : EntityID;
		var weaponObject : WeaponObject;
		attackData = hitEvent.attackData;
		statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
		weaponObject = attackData.GetWeapon();
		if( !( weaponObject ) )
		{
			return;
		}
		if( attackData.GetInstigator().IsPlayer() )
		{
			chargeNormalized = attackData.GetWeaponCharge();
			if( chargeNormalized > 0.0 )
			{
				statsOwner = weaponObject.GetEntityID();
				maxChargeThreshold = weaponObject.GetMaxChargeTreshold();
				if( maxChargeThreshold == WeaponObject.GetOverchargeThreshold( weaponObject ) )
				{
					maxChargeModifier = 1.65999997;
				}
				else if( maxChargeThreshold == WeaponObject.GetFullyChargedThreshold( weaponObject ) )
				{
					maxChargeModifier = 1.33000004;
				}
				else
				{
					maxChargeModifier = 1.0;
				}
				if( chargeNormalized >= 1.0 )
				{
					chargeDamageMult = 1.0 + ( statsSystem.GetStatValue( statsOwner, gamedataStatType.ChargeFullMultiplier ) * maxChargeModifier );
				}
				else
				{
					chargeDamageMult = 1.0 + ( ( statsSystem.GetStatValue( statsOwner, gamedataStatType.ChargeMultiplier ) * chargeNormalized ) * maxChargeModifier );
				}
				cache.chargeDamageMultiplier = chargeDamageMult;
				hitEvent.attackComputed.MultAttackValue( chargeDamageMult );
			}
		}
	}

	private function ProcessRicochet( hitEvent : gameHitEvent )
	{
		var baseRicochetDamage : Float;
		var bonusDmg : Float;
		var bonusCritChance : Float;
		if( hitEvent.attackData.GetInstigator().IsPlayer() )
		{
			if( hitEvent.attackData.GetNumRicochetBounces() > 0 )
			{
				baseRicochetDamage = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.attackData.GetInstigator().GetEntityID(), gamedataStatType.BaseRicochetDamageModifier );
				hitEvent.attackComputed.MultAttackValue( baseRicochetDamage );
				bonusDmg = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.attackData.GetInstigator().GetEntityID(), gamedataStatType.BonusRicochetDamage );
				if( !( FloatIsEqual( bonusDmg, 0.0 ) ) )
				{
					hitEvent.attackComputed.MultAttackValue( 1.0 + ( bonusDmg / 100.0 ) );
				}
				bonusCritChance = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( ScriptedPuppet.GetActiveWeapon( hitEvent.attackData.GetInstigator() ).GetEntityID(), gamedataStatType.BonusRicochetCritChance );
				if( !( FloatIsEqual( bonusCritChance, 0.0 ) ) )
				{
					hitEvent.attackData.SetAdditionalCritChance( bonusCritChance );
				}
			}
		}
	}

	private function ProcessSpreadingMultiplier( hitEvent : gameHitEvent )
	{
		var multiplier : Float;
		if( hitEvent.attackData.GetInstigator().IsPlayer() )
		{
			if( hitEvent.attackData.GetNumAttackSpread() > 0 )
			{
				multiplier = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( ScriptedPuppet.GetActiveWeapon( hitEvent.attackData.GetInstigator() ).GetEntityID(), gamedataStatType.SpreadingAttackDamageMultiplier );
				hitEvent.attackComputed.MultAttackValue( multiplier );
			}
		}
	}

	private function ProcessStealthAttack( hitEvent : gameHitEvent )
	{
		var stealthHitDamageBonus, stealthHitMult : Float;
		var canStealthHit : Bool;
		var hitNotQuickMelee : Bool;
		var player : weak< PlayerPuppet >;
		var powerDifferential : gameEPowerDifferential;
		player = ( ( PlayerPuppet )( hitEvent.attackData.GetInstigator() ) );
		if( player && ( ( ScriptedPuppet )( hitEvent.target ) ) )
		{
			if( hitEvent.attackData.GetWeapon() )
			{
				if( !( AttackData.IsPlayerInCombat( hitEvent.attackData ) ) || StatusEffectHelper.HasStatusEffectWithTagConst( player, 'ExtendedStealth' ) )
				{
					canStealthHit = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.attackData.GetWeapon().GetEntityID(), gamedataStatType.CanSilentKill ) > 0.0;
					hitNotQuickMelee = hitEvent.attackData.GetAttackType() != gamedataAttackType.QuickMelee;
					if( canStealthHit && hitNotQuickMelee )
					{
						powerDifferential = RPGManager.CalculatePowerDifferential( hitEvent.target );
						if( powerDifferential != gameEPowerDifferential.IMPOSSIBLE )
						{
							stealthHitDamageBonus = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.attackData.GetInstigator().GetEntityID(), gamedataStatType.StealthHitDamageBonus );
							stealthHitMult = GameInstance.GetStatsSystem( GetGameInstance() ).GetStatValue( hitEvent.attackData.GetInstigator().GetEntityID(), gamedataStatType.StealthHitDamageMultiplier );
							hitEvent.attackComputed.AddAttackValue( stealthHitDamageBonus, gamedataDamageType.Physical );
							if( stealthHitMult > 1.0 )
							{
								hitEvent.attackComputed.MultAttackValue( stealthHitMult );
							}
						}
					}
				}
			}
		}
	}

	private function ProcessNPCPassengerVehicleCollision( hitEvent : gameHitEvent )
	{
		if( hitEvent.attackData.GetInstigator().IsPlayer() )
		{
			if( hitEvent.attackData.HasFlag( hitFlag.NPCPassengerVehicleCollision ) )
			{
				if( PlayerDevelopmentSystem.GetData( hitEvent.attackData.GetInstigator() ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Body_Right_Milestone_1 ) )
				{
					hitEvent.attackComputed.MultAttackValue( 1.5 );
				}
			}
		}
	}

	private function CalculateSourceVsTargetModifiers( hitEvent : gameHitEvent )
	{
		ProcessVehicleVsExplosion( hitEvent );
		ProcessBikeMelee( hitEvent );
		ProcessEffectiveRange( hitEvent );
		ProcessBlockAndDeflect( hitEvent );
		if( GetFact( GetGameInstance(), 'story_mode' ) )
		{
			ScalePlayerDamage( hitEvent );
		}
	}

	private function CacheLocalVars( hitEvent : gameHitEvent, cache : CacheData )
	{
		var target : GameObject;
		target = hitEvent.target;
		cache.logFlags = GetDamageSystemLogFlags();
		if( target )
		{
			cache.TEMP_ImmortalityCached = GetImmortality( target, cache.targetImmortalityMode );
		}
	}

	private function ModifyHitFlagsForPlayer( hitEvent : gameHitEvent, cache : CacheData )
	{
		var attackData : AttackData;
		attackData = hitEvent.attackData;
		if( !( attackData.GetInstigator().IsPlayer() ) )
		{
			return;
		}
		attackData.RemoveFlag( hitFlag.FriendlyFire, 'PreAttack' );
	}

	private function CheckForQuickExit( hitEvent : gameHitEvent, cache : CacheData ) : Bool
	{
		var attackData : AttackData;
		var mountingInfo : MountingInfo;
		var targetAttitudeOwner : weak< GameObject >;
		var vehicle : weak< VehicleObject >;
		attackData = hitEvent.attackData;
		if( !( attackData ) )
		{
			if( cache.logFlags & damageSystemLogFlags.ASSERT )
			{
			}
			return true;
		}
		if( !( hitEvent.target ) )
		{
			if( cache.logFlags & damageSystemLogFlags.ASSERT )
			{
			}
			return true;
		}
		if( !( attackData.GetSource() ) )
		{
			if( cache.logFlags & damageSystemLogFlags.ASSERT )
			{
			}
			return true;
		}
		if( !( GameInstance.IsValid( GetGameInstance() ) ) )
		{
			if( cache.logFlags & damageSystemLogFlags.ASSERT )
			{
			}
			return true;
		}
		if( ( ( VehicleObject )( hitEvent.target ) ) && VehicleComponent.GetVehicle( GetGameInstance(), attackData.GetSource().GetEntityID(), vehicle ) )
		{
			if( vehicle == hitEvent.target )
			{
				if( cache.logFlags & damageSystemLogFlags.ASSERT )
				{
				}
				return true;
			}
		}
		if( ( ( VehicleObject )( attackData.GetInstigator() ) ) && VehicleComponent.GetVehicle( GetGameInstance(), hitEvent.target, vehicle ) )
		{
			if( vehicle == attackData.GetInstigator() )
			{
				if( cache.logFlags & damageSystemLogFlags.ASSERT )
				{
				}
				return true;
			}
		}
		if( hitEvent.target == attackData.GetInstigator() )
		{
			if( !( attackData.HasFlag( hitFlag.CanDamageSelf ) ) )
			{
				attackData.AddFlag( hitFlag.DealNoDamage, 'SelfDamageIgnored' );
				if( cache.logFlags & damageSystemLogFlags.ASSERT )
				{
				}
				return true;
			}
		}
		else
		{
			if( !( attackData.HasFlag( hitFlag.FriendlyFire ) ) )
			{
				mountingInfo = GameInstance.GetMountingFacility( hitEvent.target.GetGame() ).GetMountingInfoSingleWithObjects( hitEvent.target );
				if( EntityID.IsDefined( mountingInfo.parentId ) )
				{
					targetAttitudeOwner = ( ( GameObject )( GameInstance.FindEntityByID( hitEvent.target.GetGame(), mountingInfo.parentId ) ) );
				}
				if( ( ( ScriptedPuppet )( targetAttitudeOwner ) ) == NULL )
				{
					targetAttitudeOwner = hitEvent.target;
				}
				if( GameObject.GetAttitudeBetween( targetAttitudeOwner, attackData.GetInstigator() ) == EAIAttitude.AIA_Friendly && !( StatusEffectSystem.ObjectHasStatusEffect( attackData.GetInstigator(), T"BaseStatusEffect.DoNotBlockShootingOnFriendlyFire" ) ) )
				{
					attackData.AddFlag( hitFlag.DealNoDamage, 'FriendlyFireIgnored' );
					attackData.AddFlag( hitFlag.DontShowDamageFloater, 'FriendlyFireIgnored' );
					attackData.AddFlag( hitFlag.FriendlyFireIgnored, 'FriendlyFireIgnored' );
				}
			}
		}
		if( AttackData.IsDoT( hitEvent.attackData ) && StatusEffectSystem.ObjectHasStatusEffectWithTag( hitEvent.target, 'Defeated' ) )
		{
			return true;
		}
		return false;
	}

	private function IsTargetImmortal( cache : CacheData ) : Bool
	{
		if( !( cache.TEMP_ImmortalityCached ) )
		{
			return false;
		}
		return cache.targetImmortalityMode == gameGodModeType.Immortal;
	}

	private function IsTargetInvulnerable( cache : CacheData ) : Bool
	{
		if( !( cache.TEMP_ImmortalityCached ) )
		{
			return false;
		}
		return cache.targetImmortalityMode == gameGodModeType.Invulnerable;
	}

	public function IsOverridenExplosionVsVehicleHit( hitEvent : gameHitEvent ) : Bool
	{
		return hitEvent.attackData.HasFlag( hitFlag.ExplosionOverride );
	}

	public function ProcessVehicleVsExplosion( hitEvent : gameHitEvent )
	{
		var explosionDamageVSVehicles : Float;
		if( !( hitEvent.target.IsVehicle() ) )
		{
			return;
		}
		if( !( AttackData.IsExplosion( hitEvent.attackData.GetAttackType() ) ) )
		{
			return;
		}
		explosionDamageVSVehicles = hitEvent.attackData.GetAttackDefinition().GetRecord().ExplosionDamageVSVehicles();
		if( explosionDamageVSVehicles <= 0.0 )
		{
			return;
		}
		explosionDamageVSVehicles *= GetVehiclePerksDamageMultiplier( hitEvent.attackData );
		hitEvent.attackComputed.SetAttackValue( explosionDamageVSVehicles, gamedataDamageType.Physical );
		hitEvent.attackData.AddFlag( hitFlag.ExplosionOverride, 'vs_vehicle_explosion_tuning' );
	}

	public function ProcessBikeMelee( hitEvent : gameHitEvent )
	{
		var attackData : AttackData;
		var playerPuppet : PlayerPuppet;
		var mountedBike : BikeObject;
		var bikeVel : Vector4;
		var bikeDmgScale : Float;
		var targetVehicle : VehicleObject;
		var targetPuppet : gamePuppet;
		var targetVel : Vector4;
		var velDelta : Vector4;
		attackData = hitEvent.attackData;
		targetVehicle = ( ( VehicleObject )( hitEvent.target ) );
		targetPuppet = ( ( gamePuppet )( hitEvent.target ) );
		if( !( attackData.GetInstigator().IsPlayer() ) )
		{
			return;
		}
		if( !( AttackData.IsMelee( attackData.GetAttackType() ) || AttackData.IsThrown( attackData.GetAttackType() ) ) )
		{
			return;
		}
		playerPuppet = ( ( PlayerPuppet )( attackData.GetInstigator() ) );
		mountedBike = ( ( BikeObject )( playerPuppet.GetMountedVehicle() ) );
		if( !( mountedBike ) )
		{
			return;
		}
		bikeVel = mountedBike.GetLinearVelocity();
		if( targetVehicle )
		{
			targetVel = targetVehicle.GetLinearVelocity();
		}
		else if( targetPuppet )
		{
			targetVel = targetPuppet.GetVelocity();
		}
		velDelta = bikeVel - targetVel;
		bikeDmgScale = 1.0 + ProportionalClampF( 8.0, 30.0, Vector4.Length( velDelta ), 0.0, 4.0 );
		hitEvent.attackComputed.MultAttackValue( bikeDmgScale, gamedataDamageType.Physical );
		if( Vector4.Length( velDelta ) > 8.0 )
		{
			attackData.AddFlag( hitFlag.HighSpeedMelee, 'BikeStrike' );
			attackData.SetHitReactionSeverityMax( 4 );
		}
	}

	public function ProcessEffectiveRange( hitEvent : gameHitEvent )
	{
		var attackData : AttackData;
		var attackDistance : Float;
		var damageMod : Float;
		var effectiveRange : Float;
		var attackWeapon : WeaponObject;
		var baseGrenade : BaseGrenade;
		var percentOfRange : Float;
		attackData = hitEvent.attackData;
		if( AttackData.IsExplosion( attackData.GetAttackType() ) )
		{
			baseGrenade = ( ( BaseGrenade )( attackData.GetSource() ) );
			if( baseGrenade )
			{
				effectiveRange = baseGrenade.GetAttackRadius();
			}
			else
			{
				effectiveRange = attackData.GetAttackDefinition().GetRecord().Range();
			}
			attackDistance = Vector4.Length( attackData.GetAttackPosition() - hitEvent.hitPosition );
			percentOfRange = ClampF( attackDistance / effectiveRange, 0.0, 1.0 );
			damageMod = GameInstance.GetStatsDataSystem( GetGameInstance() ).GetValueFromCurve( 'explosions', percentOfRange, 'distance_to_damage_reduction' );
			hitEvent.attackComputed.MultAttackValue( damageMod );
			return;
		}
		attackWeapon = attackData.GetWeapon();
		if( !( attackWeapon ) )
		{
			return;
		}
		damageMod = GetEffectiveRangeModifierForWeapon( attackData, hitEvent.hitPosition, GameInstance.GetStatsSystem( GetGameInstance() ) );
		if( damageMod != 1.0 )
		{
			hitEvent.attackComputed.MultAttackValue( damageMod );
		}
	}

	public static function GetEffectiveRangeModifierForWeapon( attackData : AttackData, hitPosition : Vector4, statsSystem : StatsSystem ) : Float
	{
		var effectiveRange : Float;
		var result : Float;
		var itemRecord : WeaponItem_Record;
		var attackDistance : Float;
		result = 1.0;
		if( attackData.GetInstigator().IsPlayer() )
		{
			effectiveRange = statsSystem.GetStatValue( attackData.GetWeapon().GetEntityID(), gamedataStatType.EffectiveRange );
			itemRecord = TweakDBInterface.GetWeaponItemRecord( ItemID.GetTDBID( attackData.GetWeapon().GetItemID() ) );
			attackDistance = Vector4.Length( attackData.GetAttackPosition() - hitPosition );
			if( attackDistance < effectiveRange )
			{
				if( IsNameValid( itemRecord.EffectiveRangeCurve() ) )
				{
					result = GetDamageModFromCurve( itemRecord.EffectiveRangeCurve(), attackDistance );
				}
			}
			else if( IsNameValid( itemRecord.EffectiveRangeFalloffCurve() ) )
			{
				if( attackData.GetWeapon() && ( statsSystem.GetStatValue( attackData.GetWeapon().GetEntityID(), gamedataStatType.DamageFalloffDisabled ) <= 0.0 ) )
				{
					attackDistance = attackDistance - effectiveRange;
					result = GetDamageModFromCurve( itemRecord.EffectiveRangeFalloffCurve(), attackDistance );
				}
			}
		}
		return result;
	}

	public function ProcessArmor( hitEvent : gameHitEvent )
	{
		var statsSystem : StatsSystem;
		var statusEffectSystem : StatusEffectSystem;
		var weapon : weak< WeaponObject >;
		var hitShapes : array< HitShapeData >;
		var hitUserData : HitShapeUserDataBase;
		var attackValues : array< Float >;
		var effectiveHealthPerArmorPoint : Float;
		var armorPenetration : Float;
		var armorPoints : Float;
		var armorMeltMaxPercent : Float;
		var hitShapeArmorPoints : Float;
		var reducedValue : Float;
		var damageMultiplier : Float;
		var i : Int32;
		var targetIsPlayer : Bool;
		var appliedStatusEffects : array< StatusEffect >;
		hitShapes = hitEvent.hitRepresentationResult.hitShapes;
		if( AttackData.IsDoT( hitEvent.attackData ) )
		{
			return;
		}
		weapon = hitEvent.attackData.GetWeapon();
		if( !( weapon ) )
		{
			return;
		}
		armorPenetration = GetArmorPenetrationValue( weapon, hitEvent );
		if( armorPenetration >= 1.0 )
		{
			return;
		}
		statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
		statusEffectSystem = GameInstance.GetStatusEffectSystem( hitEvent.target.GetGame() );
		targetIsPlayer = hitEvent.target.IsPlayer();
		armorPoints = statsSystem.GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.Armor );
		if( hitEvent.attackData.GetInstigator().IsPlayer() && ( hitShapes.Size() > 0 ) )
		{
			hitUserData = DamageSystemHelper.GetHitShapeUserDataBase( hitShapes[ 0 ] );
			if( hitUserData && DamageSystemHelper.IsHitShapeArmored( hitUserData.m_hitShapeType ) )
			{
				hitShapeArmorPoints = statsSystem.GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.HitShapeArmor );
				if( hitShapeArmorPoints > armorPoints )
				{
					armorPoints = hitShapeArmorPoints;
				}
			}
		}
		statusEffectSystem.GetAppliedEffectsWithTag( hitEvent.target.GetEntityID(), 'OverheatArmorMelt', appliedStatusEffects );
		if( appliedStatusEffects.Size() > 0 )
		{
			armorMeltMaxPercent = TweakDBInterface.GetFloat( appliedStatusEffects[ 0 ].GetRecord().GetID() + T".armorMeltMaxPercent", 0.0 );
			armorPoints *= ( 1.0 - ( ( 1.0 - ( ClampF( appliedStatusEffects[ 0 ].GetRemainingDuration(), 0.0, 5.0 ) / 5.0 ) ) * armorMeltMaxPercent ) );
		}
		if( armorPenetration != 0.0 )
		{
			armorPoints *= ( 1.0 - armorPenetration );
		}
		effectiveHealthPerArmorPoint = GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() ).GetArmorEffectivenessValue( targetIsPlayer );
		if( targetIsPlayer )
		{
			effectiveHealthPerArmorPoint *= statsSystem.GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.ArmorEffectivenessMultiplier );
		}
		if( armorPoints >= 0.0 )
		{
			damageMultiplier = 1.0 / ( 1.0 + ( armorPoints * effectiveHealthPerArmorPoint ) );
			if( ( ( armorPoints > 0.0 ) && ( armorPenetration <= 0.0 ) ) && !( hitEvent.attackData.HasFlag( hitFlag.CriticalHit ) ) )
			{
				hitEvent.attackData.SetHitType( gameuiHitType.Glance );
			}
		}
		else
		{
			damageMultiplier = 1.0 - ( armorPoints * effectiveHealthPerArmorPoint );
		}
		attackValues = hitEvent.attackComputed.GetAttackValues();
		for( i = 0; i < attackValues.Size(); i += 1 )
		{
			if( attackValues[ i ] > 0.0 )
			{
				reducedValue = attackValues[ i ] * damageMultiplier;
				if( reducedValue < 1.0 )
				{
					reducedValue = 1.0;
				}
				attackValues[ i ] = reducedValue;
			}
		}
		hitEvent.attackComputed.SetAttackValues( attackValues );
	}

	public function ProcessOnVehicleMitigation( hitEvent : gameHitEvent )
	{
		var statsSystem : StatsSystem;
		var attackType : gamedataAttackType;
		var mountedBike : BikeObject;
		var mountedVehicle : VehicleObject;
		var playerPuppet : PlayerPuppet;
		var playerBikeVel : Vector4;
		var bikeDamageTaken : Float;
		var isRanged : Bool;
		var isAOE : Bool;
		var mountedExplosionFactor : Float;
		attackType = hitEvent.attackData.GetAttackType();
		playerPuppet = ( ( PlayerPuppet )( hitEvent.target ) );
		mountedExplosionFactor = 1.0;
		if( !( playerPuppet ) || hitEvent.attackData.HasFlag( hitFlag.CannotModifyDamage ) )
		{
			return;
		}
		mountedVehicle = playerPuppet.GetMountedVehicle();
		if( mountedVehicle == NULL )
		{
			return;
		}
		mountedBike = ( ( BikeObject )( mountedVehicle ) );
		if( mountedBike && AttackData.IsRangedOnly( attackType ) )
		{
			statsSystem = GameInstance.GetStatsSystem( playerPuppet.GetGame() );
			bikeDamageTaken = statsSystem.GetStatValue( playerPuppet.GetEntityID(), gamedataStatType.OnBikeDamageTakenPercent );
			if( ( bikeDamageTaken >= 1.0 ) && ( bikeDamageTaken < 0.0 ) )
			{
				return;
			}
			playerBikeVel = mountedBike.GetLinearVelocity();
			bikeDamageTaken = ProportionalClampF( statsSystem.GetStatValue( playerPuppet.GetEntityID(), gamedataStatType.OnBikeDamageTakenPercentMinSpeed ), statsSystem.GetStatValue( playerPuppet.GetEntityID(), gamedataStatType.OnBikeDamageTakenPercentMaxSpeed ), Vector4.Length( playerBikeVel ), 1.0, bikeDamageTaken );
			hitEvent.attackComputed.MultAttackValue( bikeDamageTaken );
		}
		else
		{
			isRanged = hitEvent.attackData.GetWeapon().IsRanged();
			isAOE = AttackData.IsExplosion( attackType );
			if( ( !( isRanged ) && isAOE ) && ( hitEvent.attackData.GetSource() != mountedVehicle ) )
			{
				if( mountedVehicle.GetRecord().IsArmoredVehicle() )
				{
					mountedExplosionFactor = 0.33000001;
				}
				else
				{
					mountedExplosionFactor = ( ( mountedBike ) ? ( 0.75 ) : ( 0.5 ) );
				}
				hitEvent.attackComputed.MultAttackValue( mountedExplosionFactor );
			}
		}
	}

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

	public function ProcessCriticalHit( hitEvent : gameHitEvent )
	{
		var isQuickHack : Bool;
		var attackType : gamedataAttackType;
		var attackData : AttackData;
		var criticalHit : Bool;
		var statsSystem : StatsSystem;
		var playerCritChance : Float;
		var weaponCritChance : Float;
		var accumulatedCritChance : Float;
		var accumulatedCritDamage : Float;
		attackType = hitEvent.attackData.GetAttackType();
		attackData = hitEvent.attackData;
		criticalHit = attackData.HasFlag( hitFlag.CriticalHit );
		statsSystem = GameInstance.GetStatsSystem( GetGameInstance() );
		if( ( ( ( attackData.HasFlag( hitFlag.CannotModifyDamage ) || attackData.HasFlag( hitFlag.DeterministicDamage ) ) || ( attackData.HasFlag( hitFlag.ForceNoCrit ) && !( attackData.HasFlag( hitFlag.BreachHit ) ) ) ) || hitEvent.target.IsPlayer() ) || !( attackData.GetInstigator().IsPlayer() ) )
		{
			return;
		}
		if( !( criticalHit ) )
		{
			if( attackType == gamedataAttackType.Hack || attackData.HasFlag( hitFlag.QuickHack ) )
			{
				if( statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.CanQuickHackCriticallyHit ) <= 0.0 )
				{
					return;
				}
				isQuickHack = true;
			}
			else if( AttackData.IsEffect( attackType ) && !( AttackData.CanEffectCriticallyHit( attackData, statsSystem ) ) )
			{
				return;
			}
			else if( ( ( WeaponGrenade )( hitEvent.attackData.GetSource() ) ) && !( AttackData.CanGrenadeCriticallyHit( attackData, statsSystem ) ) )
			{
				return;
			}
			if( GetSubAttackSubType( attackData ) == gamedataAttackSubtype.DeflectAttack && PlayerDevelopmentSystem.GetData( attackData.GetInstigator() ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Reflexes_Right_Perk_2_2 ) )
			{
				criticalHit = true;
			}
			if( !( criticalHit ) )
			{
				if( isQuickHack )
				{
					accumulatedCritChance = statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.QuickHackCritChance ) / 100.0;
				}
				else
				{
					if( attackData.GetInstigator() )
					{
						playerCritChance = statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.CritChance ) / 100.0;
					}
					if( attackData.GetWeapon() )
					{
						if( AllowWeaponCrit( attackData ) )
						{
							weaponCritChance = statsSystem.GetStatValue( attackData.GetWeapon().GetEntityID(), gamedataStatType.CritChance ) / 100.0;
						}
					}
					accumulatedCritChance = ( playerCritChance + weaponCritChance ) + hitEvent.attackData.GetAdditionalCritChance();
				}
				if( accumulatedCritChance >= 1.0 )
				{
					criticalHit = true;
				}
				else if( hitEvent.projectionPipeline )
				{
					return;
				}
				else
				{
					criticalHit = RandF() <= accumulatedCritChance;
				}
			}
		}
		if( criticalHit && !( attackData.HasFlag( hitFlag.CriticalHitNoDamageModifier ) ) )
		{
			accumulatedCritDamage = GetCritDamageModifier( statsSystem, attackData );
			if( accumulatedCritDamage > 0.0 )
			{
				attackData.AddFlag( hitFlag.CriticalHit, 'critical_hit' );
			}
			else
			{
				attackData.RemoveFlag( hitFlag.CriticalHit );
			}
		}
		if( !( hitEvent.attackData.HasFlag( hitFlag.Special ) ) )
		{
			hitEvent.attackData.SetHitType( ( ( attackData.GetHitType() == gameuiHitType.Glance ) ? ( gameuiHitType.Glance ) : ( gameuiHitType.Hit ) ) );
		}
		if( attackData.HasFlag( hitFlag.CriticalHit ) )
		{
			hitEvent.attackData.SetHitType( gameuiHitType.CriticalHit );
		}
	}

	public function ProcessDamageMultipliers( hitEvent : gameHitEvent )
	{
		var attackData : AttackData;
		var statsSystem : StatsSystem;
		var player : PlayerPuppet;
		var breachFinder : BreachFinderComponent;
		var headshotDamage : Float;
		var critDamage : Float;
		var weakspotDamage : Float;
		var breachDamage : Float;
		attackData = hitEvent.attackData;
		if( attackData.HasFlag( hitFlag.ReflexesMasterPerk1 ) )
		{
			return;
		}
		statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
		if( attackData.HasFlag( hitFlag.Headshot ) )
		{
			headshotDamage = GetHeadshotDamageModifier( statsSystem, attackData );
		}
		if( attackData.HasFlag( hitFlag.CriticalHit ) && !( attackData.HasFlag( hitFlag.CriticalHitNoDamageModifier ) ) )
		{
			critDamage = GetCritDamageModifier( statsSystem, attackData );
		}
		if( attackData.HasFlag( hitFlag.WeakspotHit ) )
		{
			weakspotDamage = GetWeakspotDamageModifier( statsSystem, attackData );
		}
		if( attackData.HasFlag( hitFlag.BreachHit ) )
		{
			player = ( ( PlayerPuppet )( attackData.GetInstigator() ) );
			if( player )
			{
				breachFinder = player.GetBreachFinderComponent();
				if( breachFinder )
				{
					breachDamage = 0.05 * breachFinder.GetBreachStreak();
				}
			}
		}
		hitEvent.attackComputed.MultAttackValue( ( ( ( 1.0 + headshotDamage ) + critDamage ) + weakspotDamage ) + breachDamage );
		if( ( ( VehicleObject )( hitEvent.target ) ) == NULL )
		{
			hitEvent.attackComputed.MultAttackValue( GetVehiclePerksDamageMultiplier( hitEvent.attackData ) );
		}
	}

	public function ProcessCyberwareModifiers( hitEvent : gameHitEvent )
	{
		var statSystem : StatsSystem;
		var target : GameObject;
		var instigator : GameObject;
		var instigatorID : EntityID;
		var attackType : gamedataAttackType;
		var damageType : gamedataDamageType;
		var attackValues : array< Float >;
		var tempDamage : Float;
		var health : Float;
		var maxHealth : Float;
		var i : Int32;
		var weapon : WeaponObject;
		statSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
		target = hitEvent.target;
		instigator = hitEvent.attackData.GetInstigator();
		instigatorID = instigator.GetEntityID();
		attackType = hitEvent.attackData.GetAttackType();
		tempDamage = 0.0;
		if( target == instigator )
		{
			return;
		}
		health = GameInstance.GetStatPoolsSystem( target.GetGame() ).GetStatPoolValue( target.GetEntityID(), gamedataStatPoolType.Health, false );
		maxHealth = GameInstance.GetStatPoolsSystem( target.GetGame() ).GetStatPoolMaxPointValue( target.GetEntityID(), gamedataStatPoolType.Health );
		tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.AllDamageDonePercentBonus );
		if( AttackData.IsExplosion( attackType ) )
		{
			tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.ExplosionDamagePercentBonus );
		}
		if( AttackData.IsMelee( attackType ) )
		{
			tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.MeleeDamagePercentBonus );
		}
		if( FloatIsEqual( health, maxHealth ) )
		{
			tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.BonusPercentDamageToEnemiesAtFullHealth );
		}
		if( health <= ( maxHealth / 2.0 ) )
		{
			tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.BonusPercentDamageToEnemiesBelowHalfHealth );
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( target, gamedataStatusEffectType.Burning ) )
		{
			tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.BonusPercentDamageVsBurningEnemies );
		}
		if( AttackData.IsDoT( hitEvent.attackData ) )
		{
			tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.DamageOverTimePercentBonus );
		}
		weapon = hitEvent.attackData.GetWeapon();
		if( weapon )
		{
			if( weapon.WeaponHasTag( 'SmartWeapon' ) )
			{
				tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.SmartWeaponDamagePercentBonus );
			}
			if( weapon.WeaponHasTag( 'TechWeapon' ) )
			{
				tempDamage += statSystem.GetStatValue( instigatorID, gamedataStatType.TechWeaponDamagePercentBonus );
			}
		}
		attackValues = hitEvent.attackComputed.GetAttackValues();
		for( i = 0; i < attackValues.Size(); i += 1 )
		{
			if( attackValues[ i ] > 0.0 )
			{
				damageType = ( ( gamedataDamageType )( i ) );
				switch( damageType )
				{
					case gamedataDamageType.Thermal:
						attackValues[ i ] *= ( ( 1.0 + tempDamage ) + statSystem.GetStatValue( instigatorID, gamedataStatType.ThermalDamagePercentBonus ) );
					break;
					case gamedataDamageType.Electric:
						attackValues[ i ] *= ( ( 1.0 + tempDamage ) + statSystem.GetStatValue( instigatorID, gamedataStatType.ElectricDamagePercentBonus ) );
					break;
					case gamedataDamageType.Chemical:
						attackValues[ i ] *= ( ( 1.0 + tempDamage ) + statSystem.GetStatValue( instigatorID, gamedataStatType.ChemicalDamagePercentBonus ) );
					break;
					default:
						attackValues[ i ] *= ( 1.0 + tempDamage );
				}
			}
		}
		hitEvent.attackComputed.SetAttackValues( attackValues );
	}

	protected function GetArmorPenetrationValue( weapon : WeaponObject, hitEvent : gameHitEvent ) : Float
	{
		var armorPenetration : Float;
		if( !( weapon ) )
		{
			return 0.0;
		}
		armorPenetration = WeaponObject.CanIgnoreArmor( weapon );
		if( hitEvent.attackData.HasFlag( hitFlag.BreachHit ) )
		{
			armorPenetration += 0.25;
		}
		if( ( GameInstance.GetStatsSystem( weapon.GetGame() ).GetStatValue( weapon.GetEntityID(), gamedataStatType.TechPierceEnabled ) > 0.0 ) && ( WeaponObject.GetWeaponChargeNormalized( weapon ) >= WeaponObject.TechPierceChargeLevel( weapon ) ) )
		{
			armorPenetration += 0.25;
		}
		return armorPenetration;
	}

	protected function GetHeadshotDamageModifier( statSystem : StatsSystem, attackData : AttackData ) : Float
	{
		if( attackData.HasFlag( hitFlag.ForceHeadshotMult25 ) )
		{
			return 0.25;
		}
		if( attackData.HasFlag( hitFlag.ForceHeadshotMult10 ) )
		{
			return 0.1;
		}
		return statSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.HeadshotDamageMultiplier );
	}

	protected function GetWeakspotDamageModifier( statSystem : StatsSystem, attackData : AttackData ) : Float
	{
		if( attackData.HasFlag( hitFlag.ForceWeakspotMult25 ) )
		{
			return 0.25;
		}
		if( attackData.HasFlag( hitFlag.ForceWeakspotMult10 ) )
		{
			return 0.1;
		}
		return statSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.WeakspotDamageMultiplier );
	}

	protected function GetCritDamageModifier( statSystem : StatsSystem, attackData : AttackData ) : Float
	{
		var playerCritDamage : Float;
		var weaponCritDamage : Float;
		if( attackData.GetInstigator() )
		{
			playerCritDamage = statSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.CritDamage ) / 100.0;
		}
		if( AllowWeaponCrit( attackData ) )
		{
			weaponCritDamage = statSystem.GetStatValue( attackData.GetWeapon().GetEntityID(), gamedataStatType.CritDamage ) / 100.0;
		}
		return playerCritDamage + weaponCritDamage;
	}

	protected function GetVehiclePerksDamageMultiplier( attackData : AttackData ) : Float
	{
		var statsSystem : StatsSystem;
		var instigator : GameObject;
		var weaponObject : WeaponObject;
		var weaponType : gamedataItemType;
		var damageMultiplier : Float;
		damageMultiplier = 1.0;
		instigator = attackData.GetInstigator();
		if( !( instigator.IsPlayer() ) )
		{
			return damageMultiplier;
		}
		if( !( VehicleComponent.IsMountedToVehicle( instigator.GetGame(), instigator.GetEntityID() ) ) )
		{
			return damageMultiplier;
		}
		weaponObject = attackData.GetWeapon();
		if( !( weaponObject ) )
		{
			return damageMultiplier;
		}
		statsSystem = GameInstance.GetStatsSystem( instigator.GetGame() );
		weaponType = RPGManager.GetItemRecord( weaponObject.GetItemID() ).ItemType().Type();
		if( weaponType == gamedataItemType.Wea_VehiclePowerWeapon || weaponType == gamedataItemType.Wea_VehicleMissileLauncher )
		{
			damageMultiplier = statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.GearheadDamageMultiplier );
		}
		else if( weaponObject.IsRanged() || weaponObject.IsMelee() )
		{
			if( PlayerDevelopmentSystem.GetData( instigator ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Cool_Left_Milestone_1 ) && StatusEffectSystem.ObjectHasStatusEffect( instigator, T"BaseStatusEffect.DriverCombatVehicleManeuvers" ) )
			{
				damageMultiplier = statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.RoadWarriorDamageMultiplier );
			}
		}
		return damageMultiplier;
	}

	protected function AllowWeaponCrit( attackData : AttackData ) : Bool
	{
		return !( AttackData.IsDoT( attackData ) ) && ( attackData.GetWeapon().IsRanged() || attackData.GetWeapon().IsMelee() );
	}

	private function ProcessBlockAndDeflect( hitEvent : gameHitEvent )
	{
		var blockingItem : weak< ItemObject >;
		var attackingItem : weak< ItemObject >;
		var statsSystem : StatsSystem;
		var statPoolsSystem : StatPoolsSystem;
		var currentStamina : Float;
		var meleeCostToBlock : Float;
		var newStamina : Float;
		var staminaReduction : Float;
		var blockFactor : Float;
		var targetID : EntityID;
		var playerTarget : PlayerPuppet;
		var blockBreakTDBID : TweakDBID;
		var meleeAttackRecord : Attack_Melee_Record;
		var computedDamageFactor : Float;
		blockBreakTDBID = T"BaseStatusEffect.BlockBroken";
		computedDamageFactor = 1.0;
		if( AttackData.IsMelee( hitEvent.attackData.GetAttackType() ) )
		{
			if( !( hitEvent.attackData.WasBlocked() ) && !( hitEvent.attackData.WasDeflected() ) )
			{
				return;
			}
			blockingItem = GameInstance.GetTransactionSystem( hitEvent.target.GetGame() ).GetItemInSlot( hitEvent.target, T"AttachmentSlots.WeaponRight" );
			attackingItem = hitEvent.attackData.GetWeapon();
			if( !( blockingItem ) || !( attackingItem ) )
			{
				return;
			}
			statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
			statPoolsSystem = GameInstance.GetStatPoolsSystem( hitEvent.target.GetGame() );
			if( !( RPGManager.GetItemRecord( blockingItem.GetItemID() ).ItemType().Type() == gamedataItemType.Wea_Fists && RPGManager.GetItemRecord( attackingItem.GetItemID() ).ItemType().Type() != gamedataItemType.Wea_Fists ) )
			{
				computedDamageFactor = 0.0;
			}
			if( hitEvent.attackData.WasDeflected() )
			{
				RPGManager.AwardExperienceFromDeflect( hitEvent );
			}
			if( hitEvent.attackData.WasBlocked() )
			{
				targetID = hitEvent.target.GetEntityID();
				currentStamina = statPoolsSystem.GetStatPoolValue( hitEvent.target.GetEntityID(), gamedataStatPoolType.Stamina, false );
				playerTarget = ( ( PlayerPuppet )( hitEvent.target ) );
				blockFactor = statsSystem.GetStatValue( targetID, gamedataStatType.BlockFactor );
				if( playerTarget )
				{
					if( StatusEffectSystem.ObjectHasStatusEffect( hitEvent.target, PlayerStaminaHelpers.GetExhaustedStatusEffectID() ) )
					{
						StatusEffectHelper.ApplyStatusEffect( hitEvent.target, blockBreakTDBID );
						hitEvent.attackData.RemoveFlag( hitFlag.WasBlocked, 'BlockBreak' );
						computedDamageFactor = TDB.GetFloat( T"Constants.DamageSystem.blockBreakPlayerDamageFactor", 0.5 );
					}
					else
					{
						meleeAttackRecord = ( ( Attack_Melee_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
						meleeCostToBlock = statsSystem.GetStatValue( attackingItem.GetEntityID(), gamedataStatType.StaminaCostToBlock );
						if( meleeAttackRecord )
						{
							meleeCostToBlock = meleeCostToBlock * meleeAttackRecord.BlockCostFactor();
						}
						staminaReduction = meleeCostToBlock / blockFactor;
						newStamina = MaxF( currentStamina - staminaReduction, 0.0 );
						if( newStamina <= 0.0 )
						{
							StatusEffectHelper.ApplyStatusEffect( hitEvent.target, blockBreakTDBID );
							hitEvent.attackData.RemoveFlag( hitFlag.WasBlocked, 'BlockBreak' );
							computedDamageFactor = TDB.GetFloat( T"Constants.DamageSystem.blockBreakPlayerDamageFactor", 0.5 );
						}
						PlayerStaminaHelpers.ModifyStamina( playerTarget, -( staminaReduction ) );
						PlayerStaminaHelpers.OnPlayerBlock( playerTarget );
					}
					SetTutorialFact( 'gmpl_player_blocked_attack' );
				}
				else
				{
					staminaReduction = statsSystem.GetStatValue( targetID, gamedataStatType.Stamina ) / blockFactor;
					newStamina = MaxF( currentStamina - staminaReduction, 0.0 );
					if( newStamina <= 0.0 )
					{
						StatusEffectHelper.ApplyStatusEffect( hitEvent.target, blockBreakTDBID );
						newStamina = 0.0;
					}
					statPoolsSystem.RequestSettingStatPoolValue( targetID, gamedataStatPoolType.Stamina, newStamina, hitEvent.attackData.GetInstigator(), false );
				}
			}
			if( computedDamageFactor != 1.0 )
			{
				hitEvent.attackComputed.MultAttackValue( computedDamageFactor );
			}
		}
		else if( AttackData.IsRangedOnly( hitEvent.attackData.GetAttackType() ) )
		{
			ProcessBulletBlockAndDeflect( hitEvent );
		}
	}

	private function ProcessBulletBlockAndDeflect( hitEvent : gameHitEvent )
	{
		var blockingItem : weak< ItemObject >;
		var attackingItem : weak< ItemObject >;
		var statsSystem : StatsSystem;
		var statPoolsSystem : StatPoolsSystem;
		var currentStamina : Float;
		var newStamina : Float;
		var staminaReduction : Float;
		var targetID : EntityID;
		var playerTarget : PlayerPuppet;
		var computedDamageFactor : Float;
		var meleeCostToBlock : Float;
		var isBulletTimeActive : Bool;
		var isDeflect : Bool;
		var playerDevelopmentData : PlayerDevelopmentData;
		var perkLevel : Int32;
		var i : Int32;
		var originalDamages : array< Float >;
		var totalOriginalDamage : Float;
		var playerMaxHealth : Float;
		var damagePerc : Float;
		var maxStaminaDamagePerc : Float;
		computedDamageFactor = 1.0;
		if( !( hitEvent.attackData.WasBulletBlocked() || hitEvent.attackData.WasBulletDeflected() ) )
		{
			return;
		}
		blockingItem = GameInstance.GetTransactionSystem( hitEvent.target.GetGame() ).GetItemInSlot( hitEvent.target, T"AttachmentSlots.WeaponRight" );
		attackingItem = hitEvent.attackData.GetWeapon();
		if( !( blockingItem ) || !( attackingItem ) )
		{
			return;
		}
		playerTarget = ( ( PlayerPuppet )( hitEvent.target ) );
		if( !( playerTarget ) )
		{
			return;
		}
		playerDevelopmentData = PlayerDevelopmentSystem.GetData( playerTarget );
		perkLevel = playerDevelopmentData.IsNewPerkBought( gamedataNewPerkType.Reflexes_Right_Milestone_2 );
		if( perkLevel < 2 )
		{
			return;
		}
		if( !( ( ( WeaponObject )( blockingItem ) ).IsBlade() ) )
		{
			return;
		}
		computedDamageFactor = 0.0;
		statsSystem = GameInstance.GetStatsSystem( playerTarget.GetGame() );
		statPoolsSystem = GameInstance.GetStatPoolsSystem( playerTarget.GetGame() );
		targetID = playerTarget.GetEntityID();
		currentStamina = statPoolsSystem.GetStatPoolValue( hitEvent.target.GetEntityID(), gamedataStatPoolType.Stamina, false );
		isBulletTimeActive = ( playerDevelopmentData.IsNewPerkBought( gamedataNewPerkType.Reflexes_Right_Perk_2_3 ) > 0 ) && GameInstance.GetTimeSystem( playerTarget.GetGame() ).IsTimeDilationActive();
		if( !( isBulletTimeActive ) )
		{
			meleeCostToBlock = statsSystem.GetStatValue( blockingItem.GetEntityID(), gamedataStatType.StaminaCostToBlock );
			staminaReduction = meleeCostToBlock / 2.0;
			totalOriginalDamage = 0.0;
			originalDamages = hitEvent.attackComputed.GetOriginalAttackValues();
			for( i = 0; i < originalDamages.Size(); i += 1 )
			{
				totalOriginalDamage += originalDamages[ i ];
			}
			playerMaxHealth = GameInstance.GetStatsSystem( playerTarget.GetGame() ).GetStatValue( playerTarget.GetEntityID(), gamedataStatType.Health );
			if( playerMaxHealth > 0.0 )
			{
				damagePerc = totalOriginalDamage / playerMaxHealth;
				maxStaminaDamagePerc = 0.5;
				if( damagePerc < maxStaminaDamagePerc )
				{
					staminaReduction *= MaxF( 0.2, damagePerc / maxStaminaDamagePerc );
				}
			}
			newStamina = MaxF( currentStamina - staminaReduction, 0.0 );
			if( newStamina <= 0.0 )
			{
				computedDamageFactor = TDB.GetFloat( T"Constants.DamageSystem.blockBreakPlayerDamageFactor", 0.5 );
			}
			PlayerStaminaHelpers.ModifyStamina( playerTarget, -( staminaReduction ) );
			PlayerStaminaHelpers.OnPlayerBlock( playerTarget );
		}
		if( computedDamageFactor != 1.0 )
		{
			hitEvent.attackComputed.MultAttackValue( computedDamageFactor );
		}
		isDeflect = ( hitEvent.attackData.HasFlag( hitFlag.WasBulletDeflected ) && ( playerDevelopmentData.IsNewPerkBought( gamedataNewPerkType.Reflexes_Right_Perk_2_1 ) > 0 ) ) && ( currentStamina > ( statsSystem.GetStatValue( targetID, gamedataStatType.Stamina ) * statsSystem.GetStatValue( targetID, gamedataStatType.Reflexes_Right_Milestone_2_StaminaDeflectPerc ) ) );
		if( hitEvent.attackData.HasFlag( hitFlag.WasBulletParried ) || isDeflect )
		{
			ProcessBulletDeflect( hitEvent, isBulletTimeActive, blockingItem );
		}
		else
		{
			GameObject.PlaySound( playerTarget, 'w_perk_lead_and_steel' );
		}
	}

	private function ProcessBulletDeflect( hitEvent : gameHitEvent, isBulletTimeActive : Bool, blockingItem : weak< ItemObject > )
	{
		var player : PlayerPuppet;
		var attackingItem : weak< ItemObject >;
		var attackingObject : WeaponObject;
		var target : GameObject;
		var targetComponent : weak< TargetingComponent >;
		var angleDist : EulerAngles;
		var attack : Attack_GameEffect;
		var attackContext : AttackInitContext;
		var effect : EffectInstance;
		var baseOffset : Float;
		var offsetMult : Float;
		var offset : Vector3;
		var minOffset, maxOffset : Float;
		var currentStaminaPerc : Float;
		var exactPosition, position, forward : Vector4;
		var muzzlePosition, slotPosition : Vector4;
		var statsSystem : StatsSystem;
		var i : Int32;
		var originalDamages : array< Float >;
		var totalOriginalDamage : Float;
		var playerWeaponDamage : Float;
		var damageMultiplier : Float;
		var damageType : gamedataDamageType;
		var damageStat : gamedataStatType;
		var statMods : array< gameStatModifierData >;
		player = ( ( PlayerPuppet )( hitEvent.target ) );
		attackingItem = hitEvent.attackData.GetWeapon();
		attackingObject = ( ( WeaponObject )( attackingItem ) );
		baseOffset = TweakDBInterface.GetFloat( T"NewPerks.Reflexes_Right_Milestone_2.baseOffset", 0.0 );
		offsetMult = TweakDBInterface.GetFloat( T"NewPerks.Reflexes_Right_Milestone_2.offsetMult", 0.0 );
		WeaponObject.TriggerWeaponEffects( ( ( WeaponObject )( blockingItem ) ), gamedataFxAction.MeleeBlock );
		if( hitEvent.attackData.HasFlag( hitFlag.WasBulletParried ) )
		{
			GameObject.PlaySound( player, 'w_melee_katana_impact_block' );
		}
		else
		{
			GameObject.PlaySound( player, 'w_melee_perks_katana_bullet_deflecting' );
		}
		muzzlePosition = blockingItem.GetWorldPosition();
		GameInstance.GetTargetingSystem( player.GetGame() ).GetDefaultCrosshairData( player, exactPosition, forward );
		if( isBulletTimeActive || hitEvent.attackData.HasFlag( hitFlag.WasBulletParried ) )
		{
			target = GameInstance.GetTargetingSystem( GetGameInstance() ).GetObjectClosestToCrosshair( player, angleDist, TSQ_EnemyNPC() );
			if( target )
			{
				targetComponent = ( ( TargetingComponent )( GameInstance.GetTargetingSystem( GetGameInstance() ).GetComponentClosestToCrosshair( player, angleDist, TSQ_EnemyNPC() ) ) );
				if( ( targetComponent && ( AbsF( angleDist.Yaw ) < 6.0 ) ) && ( AbsF( angleDist.Pitch ) < 4.0 ) )
				{
					exactPosition = Matrix.GetTranslation( targetComponent.GetLocalToWorld() );
				}
				else if( AIActionHelper.GetTargetSlotPosition( target, 'Chest', slotPosition ) )
				{
					exactPosition = slotPosition;
				}
				else if( targetComponent )
				{
					exactPosition = Matrix.GetTranslation( targetComponent.GetLocalToWorld() );
				}
				else
				{
					exactPosition = target.GetWorldPosition();
				}
				forward = exactPosition - player.GetWorldPosition();
			}
		}
		currentStaminaPerc = GameInstance.GetStatPoolsSystem( player.GetGame() ).GetStatPoolValue( player.GetEntityID(), gamedataStatPoolType.Stamina, true ) / 100.0;
		minOffset = baseOffset + ( offsetMult * ( 1.0 - currentStaminaPerc ) );
		maxOffset = minOffset;
		offset = GetRandomOffset( Vector3( -( minOffset ), -( minOffset ), -( minOffset ) ), Vector3( maxOffset, maxOffset, maxOffset ) );
		position = exactPosition + Vector4.Vector3To4( offset );
		attackContext.record = TweakDBInterface.GetAttackRecord( T"Attacks.Reflexes_Right_Milestone_2_Deflect" );
		if( isBulletTimeActive || hitEvent.attackData.HasFlag( hitFlag.WasBulletParried ) )
		{
			attackContext.record = TweakDBInterface.GetAttackRecord( T"Attacks.Reflexes_Right_Milestone_2_ParryDeflect" );
		}
		attackContext.source = player;
		attackContext.instigator = attackContext.source;
		attack = ( ( Attack_GameEffect )( IAttack.Create( attackContext ) ) );
		originalDamages = hitEvent.attackComputed.GetOriginalAttackValues();
		damageMultiplier = 1.0;
		if( hitEvent.attackData.HasFlag( hitFlag.WasBulletParried ) )
		{
			totalOriginalDamage = 0.0;
			for( i = 0; i < originalDamages.Size(); i += 1 )
			{
				totalOriginalDamage += originalDamages[ i ];
			}
			if( totalOriginalDamage > 0.0 )
			{
				statsSystem = GameInstance.GetStatsSystem( player.GetGame() );
				playerWeaponDamage = statsSystem.GetStatValue( blockingItem.GetEntityID(), gamedataStatType.EffectiveDamagePerHit );
				if( playerWeaponDamage > totalOriginalDamage )
				{
					damageMultiplier = playerWeaponDamage / totalOriginalDamage;
				}
			}
		}
		for( i = 0; i < originalDamages.Size(); i += 1 )
		{
			damageType = ( ( gamedataDamageType )( i ) );
			switch( damageType )
			{
				case gamedataDamageType.Thermal:
					damageStat = gamedataStatType.ThermalDamage;
				break;
				case gamedataDamageType.Electric:
					damageStat = gamedataStatType.ElectricDamage;
				break;
				case gamedataDamageType.Chemical:
					damageStat = gamedataStatType.ChemicalDamage;
				break;
				default:
					damageStat = gamedataStatType.PhysicalDamage;
				break;
			}
			attack.AddStatModifier( RPGManager.CreateStatModifier( damageStat, gameStatModifierType.Additive, originalDamages[ i ] * damageMultiplier ) );
		}
		attack.GetStatModList( statMods );
		effect = attack.PrepareAttack( player );
		EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, position );
		EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.muzzlePosition, muzzlePosition );
		EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.forward, Vector4.Normalize( forward ) );
		EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.playerOwnedWeapon, true );
		EffectData.SetVariant( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.fxPackage, attackingObject.GetFxPackage() );
		EffectData.SetVariant( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.attack, ( ( IAttack )( attack ) ) );
		EffectData.SetVariant( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.attackStatModList, statMods );
		attack.StartAttack();
	}

	private static function GetRandomOffset( minOffset : Vector3, maxOffset : Vector3 ) : Vector3
	{
		var randomOffset : Vector3;
		randomOffset.X = RandRangeF( minOffset.X, maxOffset.X );
		randomOffset.Y = RandRangeF( minOffset.Y, maxOffset.Y );
		randomOffset.Z = RandRangeF( minOffset.Z, maxOffset.Z );
		return randomOffset;
	}

	private function ProcessLevelDifference( const hitEvent : gameHitEvent )
	{
		var targetLevel : Float;
		var instigatorLevel : Float;
		var levelDiff : Float;
		var multiplier : Float;
		var curveName : CName;
		var statsSystem : StatsSystem;
		if( hitEvent.target == ( ( VehicleObject )( hitEvent.target ) ) )
		{
			return;
		}
		if( hitEvent.target.IsPlayer() || hitEvent.attackData.GetInstigator().IsPlayer() )
		{
			statsSystem = GameInstance.GetStatsSystem( hitEvent.target.GetGame() );
			instigatorLevel = statsSystem.GetStatValue( hitEvent.attackData.GetInstigator().GetEntityID(), gamedataStatType.PowerLevel );
			targetLevel = statsSystem.GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.PowerLevel );
			levelDiff = instigatorLevel - targetLevel;
			if( hitEvent.target.IsPlayer() )
			{
				curveName = 'pl_diff_to_npc_damage_multiplier';
			}
			else if( hitEvent.attackData.GetAttackType() == gamedataAttackType.Hack || hitEvent.attackData.HasFlag( hitFlag.QuickHack ) )
			{
				curveName = 'pl_diff_to_hackdamage_multiplier';
			}
			else
			{
				curveName = 'pl_diff_to_damage_multiplier';
			}
			multiplier = GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() ).GetValueFromCurve( 'puppet_dynamic_scaling', levelDiff, curveName );
			hitEvent.attackComputed.MultAttackValue( multiplier );
		}
	}

	private function ScalePlayerDamage( const hitEvent : gameHitEvent )
	{
		var baseNPCHealth : Float;
		var targetHealth : Float;
		var multiplier : Float;
		var targetPuppet : weak< NPCPuppet >;
		var weaponLevel : Float;
		var statsSystem : StatsSystem;
		var playerLevel : Float;
		var targetLevel : Float;
		targetPuppet = ( ( NPCPuppet )( hitEvent.target ) );
		targetHealth = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.target.GetEntityID(), gamedataStatType.Health );
		if( targetPuppet.GetNPCRarity() != gamedataNPCRarity.Boss && targetPuppet.GetNPCRarity() != gamedataNPCRarity.MaxTac )
		{
			statsSystem = GameInstance.GetStatsSystem( targetPuppet.GetGame() );
			baseNPCHealth = GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() ).GetValueFromCurve( 'puppet_powerLevelToHealth', 1.0, 'puppet_powerLevelToHealth' );
			baseNPCHealth *= RPGManager.GetRarityMultiplier( targetPuppet, 'power_level_to_health_mod' );
			multiplier = targetHealth / baseNPCHealth;
			if( hitEvent.attackData.GetInstigator().IsPlayer() )
			{
				playerLevel = statsSystem.GetStatValue( hitEvent.attackData.GetInstigator().GetEntityID(), gamedataStatType.PowerLevel );
				targetLevel = statsSystem.GetStatValue( targetPuppet.GetEntityID(), gamedataStatType.PowerLevel );
				if( playerLevel < targetLevel )
				{
					weaponLevel = GameInstance.GetStatsSystem( hitEvent.target.GetGame() ).GetStatValue( hitEvent.attackData.GetWeapon().GetEntityID(), gamedataStatType.PowerLevel );
					multiplier *= GameInstance.GetStatsDataSystem( hitEvent.target.GetGame() ).GetValueFromCurve( 'puppet_dynamic_scaling', weaponLevel, 'story_mode_weapon_multiplier' );
				}
			}
			hitEvent.attackComputed.MultAttackValue( multiplier );
		}
		if( ( ( hitEvent.target.IsPlayer() && ( targetHealth > hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health ) ) ) && !( hitEvent.attackData.GetInstigator().IsPrevention() ) ) && !( hitEvent.attackData.HasFlag( hitFlag.IgnoreDifficulty ) ) )
		{
			hitEvent.attackComputed.MultAttackValue( 0.001 );
		}
	}

	public import function RegisterListener( damageListener : ScriptedDamageSystemListener, registereeID : EntityID, callbackType : gameDamageCallbackType, optional damagePipelineType : DMGPipelineType );
	public import function UnregisterListener( damageListener : ScriptedDamageSystemListener, registereeID : EntityID, callbackType : gameDamageCallbackType, optional damagePipelineType : DMGPipelineType );
	public import function RegisterSyncListener( damageListener : ScriptedDamageSystemListener, registereeID : EntityID, callbackType : gameDamageCallbackType, stage : gameDamagePipelineStage, optional damagePipelineType : DMGPipelineType );
	public import function UnregisterSyncListener( damageListener : ScriptedDamageSystemListener, registereeID : EntityID, callbackType : gameDamageCallbackType, stage : gameDamagePipelineStage, optional damagePipelineType : DMGPipelineType );
	public import function ProcessSyncStageCallbacks( stage : gameDamagePipelineStage, hitEvent : gameHitEvent, damagePipelineType : DMGPipelineType );
	public import function ProcessSyncStageMissCallbacks( missEvent : gameMissEvent );

	private function SetTutorialFact( factName : CName )
	{
		var questSystem : QuestsSystem;
		questSystem = GameInstance.GetQuestsSystem( GetGameInstance() );
		questSystem.SetFact( factName, questSystem.GetFact( factName ) + 1 );
	}

	private function DebugDraw_VehicleHit( gameInstance : GameInstance, hitPosition : Vector4, hitDirection : Vector4, hitMagnitude : Float )
	{
		if( IsFinal() )
		{
			return;
		}
		GameInstance.GetDebugVisualizerSystem( gameInstance ).DrawArrow( hitPosition, hitPosition + hitDirection, Color( 255, 0, 0, 255 ), 3.0 );
		GameInstance.GetDebugVisualizerSystem( gameInstance ).DrawText3D( hitPosition, FloatToString( hitMagnitude ), Color( 255, 0, 0, 255 ), 3.0 );
	}

}

importonly abstract class IDamageSystemListener extends IScriptable
{
}

import class ScriptedDamageSystemListener extends IDamageSystemListener
{
	protected virtual function OnHitTriggered( hitEvent : gameHitEvent );
	protected virtual function OnMissTriggered( missEvent : gameMissEvent );
	protected virtual function OnHitReceived( hitEvent : gameHitEvent );
	protected virtual function OnPipelineProcessed( hitEvent : gameHitEvent );
}

