enum EMeleeAttackType
{
	Combo = 0,
	Strong = 1,
	Final = 2,
	Block = 3,
	Safe = 4,
	Slide = 5,
	Crouch = 6,
	Jump = 7,
	Sprint = 8,
	Fall = 9,
	Throw = 10,
	Dodge = 11,
	Equip = 12,
}

enum EMoveAssistLevel
{
	Off = 0,
	SpecialAttacks = 1,
	AllAttacks = 2,
}

class DriverCombatListener
{
	var m_mountedCallback : CallbackHandle;
	var m_tppCallback : CallbackHandle;
	var m_isMounted : Bool;
	var m_isInTPP : Bool;

	public function Init( const scriptInterface : StateGameScriptInterface )
	{
		var allBlackboardDef : AllBlackboardDefinitions;
		allBlackboardDef = GetAllBlackboardDefs();
		m_mountedCallback = scriptInterface.localBlackboard.RegisterListenerBool( allBlackboardDef.PlayerStateMachine.MountedToVehicleInDriverSeat, this, 'OnMountedInDriverSeatChanged' );
		m_tppCallback = scriptInterface.localBlackboard.RegisterListenerBool( allBlackboardDef.PlayerStateMachine.IsDriverCombatInTPP, this, 'OnDriveCombatTPPChanged' );
		m_isMounted = scriptInterface.localBlackboard.GetBool( allBlackboardDef.PlayerStateMachine.MountedToVehicleInDriverSeat );
		m_isInTPP = scriptInterface.localBlackboard.GetBool( allBlackboardDef.PlayerStateMachine.IsDriverCombatInTPP );
	}

	public function UnInit( const scriptInterface : StateGameScriptInterface )
	{
		var allBlackboardDef : AllBlackboardDefinitions;
		allBlackboardDef = GetAllBlackboardDefs();
		scriptInterface.localBlackboard.UnregisterListenerBool( allBlackboardDef.PlayerStateMachine.MountedToVehicleInDriverSeat, m_mountedCallback );
		scriptInterface.localBlackboard.UnregisterListenerBool( allBlackboardDef.PlayerStateMachine.IsDriverCombatInTPP, m_tppCallback );
		m_mountedCallback = NULL;
		m_tppCallback = NULL;
	}

	protected event OnMountedInDriverSeatChanged( value : Bool )
	{
		m_isMounted = value;
	}

	protected event OnDriveCombatTPPChanged( value : Bool )
	{
		m_isInTPP = value;
	}

	public const function IsMounted() : Bool
	{
		return m_isMounted;
	}

	public const function IsMountedInTPP() : Bool
	{
		return m_isMounted && m_isInTPP;
	}

}

abstract class MeleeTransition extends DefaultTransition
{
	var m_stateNameString : String;
	protected var m_driverCombatListener : DriverCombatListener;

	protected export virtual function OnAttach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		m_stateNameString = NameToString( GetStateName() );
		UppercaseFirstChar( m_stateNameString );
		m_driverCombatListener = new DriverCombatListener;
		m_driverCombatListener.Init( scriptInterface );
	}

	protected export virtual function OnDetach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		m_driverCombatListener.UnInit( scriptInterface );
		m_driverCombatListener = NULL;
	}

	public constexpr static function GetMeleeAttackCooldownName() : CName
	{
		return 'MeleeAttackCooldown';
	}

	public static function GetHoldEnterDuration( const scriptInterface : StateGameScriptInterface ) : Float
	{
		return scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.HoldEnterDuration );
	}

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

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

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

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

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

	protected constexpr static function HasOnePunchManStatusEffect( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'OnePunchMan' );
	}

	protected constexpr static function HasGrandFinaleStatusEffect( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'GrandFinale' );
	}

	protected constexpr static function PlayerLeapedToTargetCheck( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'JustLeaped' );
	}

	protected function ConsumeStamina( scriptInterface : StateGameScriptInterface, attackData : MeleeAttackData )
	{
		if( attackData.staminaCost > 0.0 )
		{
			PlayerStaminaHelpers.ModifyStamina( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ), -( attackData.staminaCost ) );
		}
	}

	protected constexpr function IsCloseEnoughForOnePunch( const scriptInterface : StateGameScriptInterface, target : weak< GameObject > ) : Bool
	{
		var targetPosition : Vector4;
		var cameraWorldTransform : Transform;
		var distance : Float;
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		targetPosition = target.GetWorldPosition();
		distance = Vector4.Length( targetPosition - cameraWorldTransform.position );
		return distance <= GetStaticFloatParameterDefault( "dashOnePunchAttackEnemyMaxRange", 8.0 );
	}

	protected const function CanPerformRelicLeap( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var hasMantisBlades : Bool;
		var hasPerkPurchased : Bool;
		hasMantisBlades = GetWeaponType( scriptInterface ) == gamedataItemType.Cyb_MantisBlades;
		hasPerkPurchased = HasNewSpyAttackStatFlag( scriptInterface );
		return ( hasPerkPurchased && hasMantisBlades ) && WeaponIsCharged( scriptInterface );
	}

	protected const function GetSlotTransformToTarget( const scriptInterface : StateGameScriptInterface, target : GameObject, leapDuration : Float, isTargetKnockedOver : Bool, out targetPos : Vector4 )
	{
		var rotationDuration : Float;
		GetSlotTransformToTarget( scriptInterface, target, leapDuration, isTargetKnockedOver, targetPos, rotationDuration );
	}

	protected const function GetSlotTransformToTarget( const scriptInterface : StateGameScriptInterface, target : GameObject, leapDuration : Float, isTargetKnockedOver : Bool, out targetPos : Vector4, out rotationDuration : Float )
	{
		var scriptedPuppet : ScriptedPuppet;
		var slotComponent : SlotComponent;
		var slotTransform : WorldTransform;
		var slotTransformPos : Vector4;
		var playerPos : Vector4;
		var lerpAlpha : Float;
		scriptedPuppet = ( ( ScriptedPuppet )( target ) );
		if( !( scriptedPuppet ) )
		{
			if( ( ( WeakspotObject )( target ) ) )
			{
				targetPos = target.GetWorldPosition();
				targetPos.Z -= 0.5;
			}
			return;
		}
		slotComponent = scriptedPuppet.GetSlotComponent();
		if( !( slotComponent ) )
		{
			return;
		}
		if( slotComponent.GetSlotTransform( 'Center', slotTransform ) )
		{
			slotTransformPos = WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) );
			if( target.IsDrone() )
			{
				slotTransformPos.Z -= 1.5;
			}
			targetPos = slotTransformPos;
		}
		else
		{
			if( GetClosestSlotTransform( scriptInterface, isTargetKnockedOver, slotComponent, slotTransform ) )
			{
				slotTransformPos = WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) );
				targetPos = target.GetWorldPosition();
				playerPos = scriptInterface.executionOwner.GetWorldPosition();
				if( slotTransformPos.Z < playerPos.Z )
				{
					lerpAlpha = ( playerPos.Z - targetPos.Z ) / Vector4.Distance( playerPos, targetPos );
					targetPos.Z = LerpF( lerpAlpha, targetPos.Z, slotTransformPos.Z, true );
				}
				targetPos.X = slotTransformPos.X;
				targetPos.Y = slotTransformPos.Y;
				rotationDuration = leapDuration;
			}
		}
	}

	protected export const function ToMeleeChargedHold( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( WantsToStrongAttack( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( scriptInterface.GetActionValue( 'MeleeStrongAttack' ) > 0.0 )
		{
			return true;
		}
		if( GetInStateTime() > GetHoldEnterDuration( scriptInterface ) )
		{
			return true;
		}
		return false;
	}

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

	protected static function WeaponIsCharged( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var weaponObject : WeaponObject;
		weaponObject = GetWeaponObject( scriptInterface );
		return weaponObject.IsCharged();
	}

	protected static function HasMonowireWithQuickhackSelected( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var weaponObject : WeaponObject;
		var quickhackRecord : Item_Record;
		weaponObject = GetWeaponObject( scriptInterface );
		quickhackRecord = RPGManager.GetMonoWireQuickhackRecord( weaponObject );
		return ( quickhackRecord != NULL ) && TDBID.IsValid( quickhackRecord.GetID() );
	}

	protected static function GetWeaponType( const scriptInterface : StateGameScriptInterface ) : gamedataItemType
	{
		var weaponType : gamedataItemType;
		var weaponObject : WeaponObject;
		weaponObject = GetWeaponObject( scriptInterface );
		return weaponType = WeaponObject.GetWeaponType( weaponObject.GetItemID() );
	}

	protected const function IsChoice1Released( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustReleased( 'Choice1' );
	}

	protected const function IsBlockPressed( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustPressed( 'MeleeBlock' );
	}

	protected const function IsBlockHeld( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.GetActionValue( 'MeleeBlock' ) > 0.5;
	}

	public static function LightMeleeAttackPressed( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustPressed( 'MeleeLightAttack' );
	}

	protected const function LightMeleeAttackReleased( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustReleased( 'MeleeLightAttack' );
	}

	public static function MeleeAttackPressed( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustPressed( 'MeleeAttack' );
	}

	public static function MeleeAttackReleased( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustReleased( 'MeleeAttack' );
	}

	protected static function QuickMeleePressed( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustPressed( 'QuickMelee' );
	}

	protected const function QuickMeleeHeld( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustHeld( 'QuickMelee' );
	}

	protected static function QuickMeleeReleased( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustReleased( 'QuickMelee' );
	}

	protected static function QuickMeleeTapped( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustTapped( 'QuickMelee' );
	}

	public static function StrongMeleeAttackPressed( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustPressed( 'MeleeStrongAttack' );
	}

	public static function StrongMeleeAttackReleased( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustReleased( 'MeleeStrongAttack' );
	}

	public static function AnyMeleeAttack( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.GetActionValue( 'MeleeAttack' ) > 0.0 )
		{
			return true;
		}
		if( scriptInterface.GetActionValue( 'MeleeLightAttack' ) > 0.0 )
		{
			return true;
		}
		if( scriptInterface.GetActionValue( 'MeleeHeavyAttack' ) > 0.0 )
		{
			return true;
		}
		if( scriptInterface.GetActionValue( 'QuickMelee' ) > 0.0 )
		{
			return true;
		}
		return false;
	}

	public static function GetAimAssistMeleeRecord( const scriptInterface : StateGameScriptInterface ) : AimAssistMelee_Record
	{
		var aimAsisstRecordId : TweakDBID;
		var aimAssistRecord : AimAssistConfigPreset_Record;
		var record : AimAssistMelee_Record;
		aimAssistRecord = NULL;
		record = NULL;
		aimAsisstRecordId = scriptInterface.GetTargetingSystem().GetAimAssistConfig( scriptInterface.executionOwner );
		aimAssistRecord = TweakDBInterface.GetAimAssistConfigPresetRecord( aimAsisstRecordId );
		if( aimAssistRecord )
		{
			record = aimAssistRecord.MeleeParams();
		}
		return record;
	}

	public static function AnyMeleeAttackPressed( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ( ( MeleeAttackPressed( scriptInterface ) || LightMeleeAttackPressed( scriptInterface ) ) || StrongMeleeAttackPressed( scriptInterface ) ) || QuickMeleeTapped( scriptInterface );
	}

	public static function NoMeleeAttack( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.GetActionValue( 'MeleeAttack' ) > 0.0 )
		{
			return false;
		}
		if( scriptInterface.GetActionValue( 'MeleeLightAttack' ) > 0.0 )
		{
			return false;
		}
		if( scriptInterface.GetActionValue( 'MeleeStrongAttack' ) > 0.0 )
		{
			return false;
		}
		if( scriptInterface.GetActionValue( 'QuickMelee' ) > 0.0 )
		{
			return false;
		}
		return true;
	}

	protected const function NoStrongAttackPressed( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.GetActionValue( 'MeleeAttack' ) > 0.0 )
		{
			return false;
		}
		if( scriptInterface.GetActionValue( 'MeleeStrongAttack' ) > 0.0 )
		{
			return false;
		}
		return true;
	}

	protected const function ShouldHold( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface, optional skipDurationCheck : Bool, optional skipPressCount : Bool ) : Bool
	{
		if( stateContext.GetConditionBool( 'StrongMeleeAttackPressed' ) )
		{
			return true;
		}
		if( skipPressCount || CheckMeleeAttackPressCount( stateContext, scriptInterface ) )
		{
			if( scriptInterface.GetActionValue( 'MeleeStrongAttack' ) > 0.0 )
			{
				return true;
			}
			if( ( scriptInterface.GetActionValue( 'MeleeAttack' ) > 0.5 ) && ( skipDurationCheck || ( scriptInterface.GetActionStateTime( 'MeleeAttack' ) >= GetHoldEnterDuration( scriptInterface ) ) ) )
			{
				return true;
			}
		}
		return false;
	}

	public static function CheckMeleeAttackPressCount( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var actionPressCount : Uint32;
		var lastChargePressCount : StateResultInt;
		actionPressCount = scriptInterface.GetActionPressCount( 'MeleeAttack' );
		actionPressCount += scriptInterface.GetActionPressCount( 'MeleeLightAttack' );
		actionPressCount += scriptInterface.GetActionPressCount( 'MeleeStrongAttack' );
		actionPressCount += scriptInterface.GetActionPressCount( 'QuickMelee' );
		lastChargePressCount = stateContext.GetPermanentIntParameter( 'LastMeleePressCount' );
		if( lastChargePressCount.valid && ( lastChargePressCount.value == ( ( Int32 )( actionPressCount ) ) ) )
		{
			return false;
		}
		return true;
	}

	protected function SetMeleeAttackPressCount( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var actionPressCount : Uint32;
		actionPressCount = scriptInterface.GetActionPressCount( 'MeleeAttack' );
		actionPressCount += scriptInterface.GetActionPressCount( 'MeleeLightAttack' );
		actionPressCount += scriptInterface.GetActionPressCount( 'MeleeStrongAttack' );
		actionPressCount += scriptInterface.GetActionPressCount( 'QuickMelee' );
		stateContext.SetPermanentIntParameter( 'LastMeleePressCount', ( ( Int32 )( actionPressCount ) ), true );
	}

	protected function ClearMeleePressCount( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		stateContext.SetPermanentIntParameter( 'LastMeleePressCount', 0, true );
	}

	public static function WantsToStrongAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsPlayingSyncedAnimation( scriptInterface ) )
		{
			return false;
		}
		if( stateContext.GetConditionBool( 'StrongMeleeAttackPressed' ) )
		{
			return true;
		}
		if( CheckMeleeAttackPressCount( stateContext, scriptInterface ) )
		{
			if( StrongMeleeAttackReleased( scriptInterface ) )
			{
				return true;
			}
			if( MeleeAttackReleased( scriptInterface ) && ( scriptInterface.GetActionPrevStateTime( 'MeleeAttack' ) > GetHoldEnterDuration( scriptInterface ) ) )
			{
				return true;
			}
		}
		return false;
	}

	public static function WantsToLightAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsPlayingSyncedAnimation( scriptInterface ) )
		{
			return false;
		}
		if( stateContext.GetConditionBool( 'LightMeleeAttackPressed' ) )
		{
			return true;
		}
		if( CheckMeleeAttackPressCount( stateContext, scriptInterface ) )
		{
			if( MeleeAttackReleased( scriptInterface ) )
			{
				return true;
			}
			if( LightMeleeAttackPressed( scriptInterface ) )
			{
				return true;
			}
		}
		return false;
	}

	public static function WantsToQuickMelee( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsPlayingSyncedAnimation( scriptInterface ) )
		{
			return false;
		}
		if( stateContext.GetConditionBool( 'QuickMeleeAttackTapped' ) )
		{
			return true;
		}
		if( CheckMeleeAttackPressCount( stateContext, scriptInterface ) )
		{
			if( QuickMeleeTapped( scriptInterface ) )
			{
				return true;
			}
		}
		return false;
	}

	public static function IsThrownWeaponReloading( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.GetStatPoolsSystem().HasStatPoolValueReachedMax( GetWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.ThrowRecovery ) )
		{
			return false;
		}
		else
		{
			return true;
		}
	}

	protected const function ShouldInterruptHoldStates( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var interruptEvent : StateResultBool;
		var finisherTargetID : EntityID;
		finisherTargetID = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		if( EntityID.IsDefined( finisherTargetID ) )
		{
			return true;
		}
		if( !( IsWeaponReady( stateContext, scriptInterface ) ) )
		{
			return true;
		}
		if( IsAttackParried( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( IsSafeStateForced( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) ) ) && !( IsInRpgContext( scriptInterface ) ) )
		{
			return true;
		}
		interruptEvent = stateContext.GetPermanentBoolParameter( 'InterruptMelee' );
		if( interruptEvent.value )
		{
			return true;
		}
		return false;
	}

	public static function UpdateMeleeInputBuffer( stateContext : StateContext, scriptInterface : StateGameScriptInterface, optional onlyLightMeleeAttack : Bool )
	{
		if( IsPlayingSyncedAnimation( scriptInterface ) )
		{
			ClearInputBuffer( stateContext );
			return;
		}
		if( onlyLightMeleeAttack )
		{
			if( WantsToLightAttack( stateContext, scriptInterface ) || WantsToStrongAttack( stateContext, scriptInterface ) )
			{
				stateContext.SetConditionBoolParameter( 'StrongMeleeAttackPressed', false, true );
				stateContext.SetConditionBoolParameter( 'LightMeleeAttackPressed', true, true );
				stateContext.SetConditionBoolParameter( 'QuickMeleeAttackTapped', false, true );
			}
			return;
		}
		if( WantsToStrongAttack( stateContext, scriptInterface ) )
		{
			stateContext.SetConditionBoolParameter( 'StrongMeleeAttackPressed', true, true );
			stateContext.SetConditionBoolParameter( 'LightMeleeAttackPressed', false, true );
			stateContext.SetConditionBoolParameter( 'QuickMeleeAttackTapped', false, true );
		}
		else if( WantsToLightAttack( stateContext, scriptInterface ) )
		{
			stateContext.SetConditionBoolParameter( 'StrongMeleeAttackPressed', false, true );
			stateContext.SetConditionBoolParameter( 'LightMeleeAttackPressed', true, true );
			stateContext.SetConditionBoolParameter( 'QuickMeleeAttackTapped', false, true );
		}
		else if( WantsToQuickMelee( stateContext, scriptInterface ) )
		{
			stateContext.SetConditionBoolParameter( 'StrongMeleeAttackPressed', false, true );
			stateContext.SetConditionBoolParameter( 'LightMeleeAttackPressed', false, true );
			stateContext.SetConditionBoolParameter( 'QuickMeleeAttackTapped', true, true );
		}
	}

	public static function ClearInputBuffer( stateContext : StateContext )
	{
		stateContext.SetConditionBoolParameter( 'LightMeleeAttackPressed', false, true );
		stateContext.SetConditionBoolParameter( 'StrongMeleeAttackPressed', false, true );
		stateContext.SetConditionBoolParameter( 'QuickMeleeAttackTapped', false, true );
	}

	protected const function CheckItemType( const scriptInterface : StateGameScriptInterface, const itemType : gamedataItemType ) : Bool
	{
		var currentItemType : gamedataItemType;
		if( !( GetWeaponItemType( scriptInterface, GetWeaponObject( scriptInterface ), currentItemType ) ) || currentItemType != itemType )
		{
			return false;
		}
		return true;
	}

	public static function MeleeSprintStateCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Melee ) == ( ( Int32 )( gamePSMMelee.Block ) ) ) && ( ( ( PlayerDevelopmentSystem.GetInstance( scriptInterface.executionOwner ).IsNewPerkBought( scriptInterface.executionOwner, gamedataNewPerkType.Body_Right_Milestone_2 ) < 2 ) || !( WeaponObject.IsBlunt( GameObject.GetActiveWeapon( scriptInterface.executionOwner ).GetItemID() ) ) ) || ( scriptInterface.GetStatPoolsSystem().GetStatPoolValue( scriptInterface.executionOwner.GetEntityID(), gamedataStatPoolType.Stamina, true ) <= 0.0 ) ) )
		{
			return false;
		}
		if( !( stateContext.GetBoolParameter( 'canSprintWhileCharging', true ) ) && stateContext.GetStateMachineCurrentState( 'Melee' ) == 'meleeChargedHold' )
		{
			return false;
		}
		if( stateContext.GetBoolParameter( 'isAttacking', true ) && !( stateContext.IsStateActive( 'Melee', 'meleeBodySlamAttack' ) ) )
		{
			return false;
		}
		return true;
	}

	public static function MeleeUseExplorationCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Melee ) == ( ( Int32 )( gamePSMMelee.Block ) ) )
		{
			return false;
		}
		if( stateContext.GetStateMachineCurrentState( 'Melee' ) == 'meleeChargedHold' )
		{
			return false;
		}
		if( stateContext.GetBoolParameter( 'isAttacking', true ) )
		{
			return false;
		}
		if( !( HasMeleeWeaponEquipped( scriptInterface ) ) )
		{
			return true;
		}
		return true;
	}

	protected function IncrementAttackNumber( scriptInterface : StateGameScriptInterface, stateContext : StateContext )
	{
		var currentValue : StateResultInt;
		var value : Int32;
		var attacksNumber : Int32;
		currentValue = stateContext.GetPermanentIntParameter( 'meleeAttackNumber' );
		value = currentValue.value;
		value += 1;
		attacksNumber = ( ( Int32 )( scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.AttacksNumber ) ) );
		if( value >= attacksNumber )
		{
			if( CheckIfInfiniteCombo( stateContext, scriptInterface ) )
			{
				value = 1;
			}
			else
			{
				value = 0;
			}
		}
		SetAttackNumber( stateContext, value );
	}

	protected function IncrementTotalComboAttackNumber( scriptInterface : StateGameScriptInterface, stateContext : StateContext )
	{
		var currentValue : StateResultInt;
		var value : Int32;
		currentValue = stateContext.GetPermanentIntParameter( 'totalMeleeAttacksInCombo' );
		value = currentValue.value;
		value += 1;
		stateContext.SetPermanentIntParameter( 'totalMeleeAttacksInCombo', value, true );
	}

	protected const function CheckIfFinalAttack( const scriptInterface : StateGameScriptInterface, const stateContext : StateContext ) : Bool
	{
		var currentValue : StateResultInt;
		var value : Int32;
		var attacksNumber : Int32;
		if( CheckIfInfiniteCombo( stateContext, scriptInterface ) )
		{
			return false;
		}
		currentValue = stateContext.GetPermanentIntParameter( 'meleeAttackNumber' );
		value = currentValue.value + 1;
		attacksNumber = ( ( Int32 )( scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.AttacksNumber ) ) );
		return value >= attacksNumber;
	}

	protected const function CheckIfInfiniteCombo( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( HasWeaponStatFlag( scriptInterface, gamedataStatType.CanWeaponInfinitlyCombo ) ) )
		{
			return false;
		}
		if( !( scriptInterface.HasStatFlag( gamedataStatType.CanMeleeInfinitelyCombo ) ) )
		{
			return false;
		}
		return true;
	}

	protected function ResetAttackNumber( stateContext : StateContext )
	{
		stateContext.SetPermanentIntParameter( 'meleeAttackNumber', 0, true );
		stateContext.SetPermanentIntParameter( 'totalMeleeAttacksInCombo', 0, true );
	}

	protected function SetAttackNumber( stateContext : StateContext, value : Int32 )
	{
		stateContext.SetPermanentIntParameter( 'meleeAttackNumber', value, true );
	}

	protected function SetCanSprintWhileCharging( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'canSprintWhileCharging', value, true );
	}

	protected function SetIsAttacking( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isAttacking', value, true );
	}

	protected function SetIsBlocking( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isBlocking', value, true );
	}

	protected function SetIsParried( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isParried', value, true );
	}

	protected function SetIsThrowReloading( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isThrowReloading', value, true );
	}

	protected function SetThrowReloadTime( stateContext : StateContext, value : Float )
	{
		stateContext.SetPermanentFloatParameter( 'throwReloadTime', value, true );
	}

	protected function SetIsTargeting( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isTargeting', value, true );
	}

	protected function SetIsHolding( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isHolding', value, true );
	}

	protected function SetIsSafe( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isSafe', value, true );
	}

	protected const function ApplyThrowAttackGameplayRestrictions( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.NoRadialMenus" );
		StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FirearmsNoUnequipNoSwitch" );
	}

	protected const function RemoveAllMeleeGameplayRestrictions( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		RemoveThrowAttackGameplayRestrictions( stateContext, scriptInterface );
	}

	protected const function RemoveThrowAttackGameplayRestrictions( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		StatusEffectHelper.RemoveStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.NoRadialMenus" );
		StatusEffectHelper.RemoveStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FirearmsNoUnequipNoSwitch" );
	}

	protected const function IsWeaponReady( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var isTakedown : Bool;
		var isInFocusMode : Bool;
		var isUsingCombatGadget : Bool;
		isTakedown = ( ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Takedown ) == ( ( Int32 )( gamePSMTakedown.Grapple ) ) ) || ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Takedown ) == ( ( Int32 )( gamePSMTakedown.Leap ) ) ) ) || ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Takedown ) == ( ( Int32 )( gamePSMTakedown.Takedown ) ) );
		isInFocusMode = scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) );
		isUsingCombatGadget = stateContext.IsStateMachineActive( 'CombatGadget' );
		if( IsNoCombatActionsForced( scriptInterface ) )
		{
			return false;
		}
		if( stateContext.IsStateMachineActive( 'Consumable' ) || stateContext.IsStateMachineActive( 'CombatGadget' ) )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( scriptInterface.executionOwner, gamedataStatusEffectType.Stunned ) )
		{
			return false;
		}
		if( IsRightHandInUnequippingState( stateContext ) )
		{
			return false;
		}
		if( IsPlayingSyncedAnimation( scriptInterface ) )
		{
			return false;
		}
		if( isInFocusMode && !( IsInRpgContext( scriptInterface ) ) )
		{
			return false;
		}
		if( isUsingCombatGadget )
		{
			return false;
		}
		if( isTakedown )
		{
			return false;
		}
		return true;
	}

	protected const function HasWeaponStatFlag( const scriptInterface : StateGameScriptInterface, flag : gamedataStatType ) : Bool
	{
		var flagOn : Bool;
		flagOn = scriptInterface.GetStatsSystem().GetStatBoolValue( scriptInterface.ownerEntityID, flag );
		return flagOn;
	}

	protected constexpr function GetPlayerAimingStatusEffectID() : TweakDBID
	{
		return T"BaseStatusEffect.PlayerAiming";
	}

	protected function DrawDebugText( scriptInterface : StateGameScriptInterface, out textLayerId : Uint32, const text : ref< String > )
	{
		textLayerId = GameInstance.GetDebugVisualizerSystem( scriptInterface.GetGame() ).DrawText( Vector4( 500.0, 550.0, 0.0, 0.0 ), text, gameDebugViewETextAlignment.Left, Color( 255, 255, 0, 255 ) );
		GameInstance.GetDebugVisualizerSystem( scriptInterface.GetGame() ).SetScale( textLayerId, Vector4( 1.0, 1.0, 0.0, 0.0 ) );
	}

	protected function ClearDebugText( scriptInterface : StateGameScriptInterface, textLayerId : Uint32 )
	{
		GameInstance.GetDebugVisualizerSystem( scriptInterface.GetGame() ).ClearLayer( textLayerId );
	}

	protected const function CheckLeapCollision( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var geometryDescription : GeometryDescriptionQuery;
		var queryFilter : QueryFilter;
		var geometryDescriptionResult : GeometryDescriptionResult;
		var cameraWorldTransform : Transform;
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		QueryFilter.AddGroup( queryFilter, 'Static' );
		QueryFilter.AddGroup( queryFilter, 'PlayerBlocker' );
		geometryDescription = new GeometryDescriptionQuery;
		geometryDescription.refPosition = Transform.GetPosition( cameraWorldTransform );
		geometryDescription.refDirection = Transform.GetForward( cameraWorldTransform );
		geometryDescription.filter = queryFilter;
		geometryDescription.primitiveDimension = Vector4( 0.1, 0.1, 0.1, 0.0 );
		geometryDescription.maxDistance = 5.0;
		geometryDescription.maxExtent = 5.0;
		geometryDescription.probingPrecision = 0.05;
		geometryDescription.probingMaxDistanceDiff = 5.0;
		geometryDescription.AddFlag( worldgeometryDescriptionQueryFlags.DistanceVector );
		geometryDescriptionResult = scriptInterface.GetSpatialQueriesSystem().GetGeometryDescriptionSystem().QueryExtents( geometryDescription );
		if( geometryDescriptionResult.queryStatus == worldgeometryDescriptionQueryStatus.NoGeometry )
		{
			return true;
		}
		return false;
	}

	protected function GetPerfectAimSnapParams() : AimRequest
	{
		var aimSnapParams : AimRequest;
		aimSnapParams.duration = 0.33000001;
		aimSnapParams.adjustPitch = true;
		aimSnapParams.adjustYaw = true;
		aimSnapParams.endOnAimingStopped = true;
		aimSnapParams.precision = 0.1;
		aimSnapParams.easeIn = true;
		aimSnapParams.easeOut = true;
		aimSnapParams.checkRange = true;
		aimSnapParams.processAsInput = true;
		aimSnapParams.bodyPartsTracking = true;
		aimSnapParams.bptMaxDot = 0.5;
		aimSnapParams.bptMaxSwitches = -1.0;
		aimSnapParams.bptMinInputMag = 0.5;
		aimSnapParams.bptMinResetInputMag = 0.1;
		return aimSnapParams;
	}

	protected function GetBlockLookAtParams() : AimRequest
	{
		var aimSnapParams : AimRequest;
		aimSnapParams.duration = 30.0;
		aimSnapParams.adjustPitch = true;
		aimSnapParams.adjustYaw = true;
		aimSnapParams.endOnAimingStopped = false;
		aimSnapParams.precision = 0.1;
		aimSnapParams.easeIn = true;
		aimSnapParams.easeOut = true;
		aimSnapParams.checkRange = true;
		aimSnapParams.processAsInput = true;
		aimSnapParams.bodyPartsTracking = false;
		aimSnapParams.bptMaxDot = 0.5;
		aimSnapParams.bptMaxSwitches = -1.0;
		aimSnapParams.bptMinInputMag = 0.5;
		aimSnapParams.bptMinResetInputMag = 0.1;
		return aimSnapParams;
	}

	protected function SendAnimFeatureData( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var animFeature : AnimFeature_MeleeData;
		var weaponObject : WeaponObject;
		animFeature = new AnimFeature_MeleeData;
		animFeature.attackType = stateContext.GetIntParameter( 'attackType', true );
		animFeature.attackNumber = stateContext.GetIntParameter( 'meleeAttackNumber', true );
		animFeature.attackSpeed = stateContext.GetFloatParameter( 'attackSpeed', true );
		animFeature.hasDeflectAnim = stateContext.GetBoolParameter( 'hasDeflectAnim', true );
		animFeature.hasHitAnim = stateContext.GetBoolParameter( 'hasHitAnim', true );
		weaponObject = GetWeaponObject( scriptInterface );
		animFeature.isAttacking = stateContext.GetBoolParameter( 'isAttacking', true );
		animFeature.isTargeting = stateContext.GetBoolParameter( 'isTargeting', true );
		animFeature.isBlocking = stateContext.GetBoolParameter( 'isBlocking', true );
		animFeature.isParried = stateContext.GetBoolParameter( 'isParried', true );
		animFeature.isHolding = stateContext.GetBoolParameter( 'isHolding', true );
		animFeature.shouldHandsDisappear = weaponObject.HasTag( 'Cyberware' );
		animFeature.keepRenderPlane = weaponObject.HasTag( 'KeepRenderPlane' );
		animFeature.isSafe = stateContext.GetBoolParameter( 'isSafe', true );
		animFeature.isThrowReloading = stateContext.GetBoolParameter( 'isThrowReloading', true );
		animFeature.throwReloadTime = stateContext.GetFloatParameter( 'throwReloadTime', true );
		animFeature.isMeleeWeaponEquipped = true;
		scriptInterface.SetAnimationParameterFeature( 'MeleeData', animFeature );
	}

	protected function DisableNanoWireIK( scriptInterface : StateGameScriptInterface )
	{
		UpdateNanoWireEndPositionAnimFeature( scriptInterface, 'ikRightNanoWire', false );
		UpdateNanoWireEndPositionAnimFeature( scriptInterface, 'ikLeftNanoWire', false );
	}

	protected function UpdateNanoWireEndPositionAnimFeature( scriptInterface : StateGameScriptInterface, animFeatureName : CName, enable : Bool, optional setPosition : Bool, optional slotPosition : Vector4 )
	{
		var animFeature : AnimFeature_SimpleIkSystem;
		animFeature = new AnimFeature_SimpleIkSystem;
		animFeature.isEnable = enable;
		animFeature.setPosition = setPosition;
		animFeature.position = slotPosition;
		scriptInterface.SetAnimationParameterFeature( animFeatureName, animFeature );
	}

	protected function GetMeleeMovementDirection( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : meleeMoveDirection
	{
		var direction : meleeMoveDirection;
		var currentYaw : Float;
		currentYaw = GetYawMovementDirection( stateContext, scriptInterface );
		if( ( currentYaw >= -45.0 ) && ( currentYaw <= 45.0 ) )
		{
			direction = meleeMoveDirection.Forward;
		}
		else if( ( currentYaw > 45.0 ) && ( currentYaw < 135.0 ) )
		{
			direction = meleeMoveDirection.Right;
		}
		else if( ( ( currentYaw >= 135.0 ) && ( currentYaw <= 180.0 ) ) || ( ( currentYaw <= -135.0 ) && ( currentYaw >= -180.0 ) ) )
		{
			direction = meleeMoveDirection.Back;
		}
		else if( ( currentYaw > -135.0 ) && ( currentYaw < -45.0 ) )
		{
			direction = meleeMoveDirection.Left;
		}
		return direction;
	}

	public static function GetWeaponObject( const scriptInterface : StateGameScriptInterface ) : WeaponObject
	{
		var owner : GameObject;
		var weapon : WeaponObject;
		owner = scriptInterface.owner;
		weapon = ( ( WeaponObject )( owner ) );
		return weapon;
	}

	public static function CanThrowWeaponObject( const owner : GameObject, const weapon : WeaponObject ) : Bool
	{
		if( !( RPGManager.HasStatFlag( weapon, gamedataStatType.HasMeleeTargeting ) ) )
		{
			return false;
		}
		if( weapon.WeaponHasTag( 'Throwable' ) && !( RPGManager.HasStatFlag( owner, gamedataStatType.CanThrowWeapon ) ) )
		{
			return false;
		}
		return true;
	}

	protected const function GetAttackDataFromCurrentState( stateContext : StateContext, scriptInterface : StateGameScriptInterface, attackNumber : Int32, out outgoingStruct : MeleeAttackData ) : Bool
	{
		return GetAttackDataFromState( stateContext, scriptInterface, m_stateNameString, attackNumber, outgoingStruct );
	}

	protected const function GetAttackDataFromState( stateContext : StateContext, scriptInterface : StateGameScriptInterface, stateName : String, attackNumber : Int32, out outgoingStruct : MeleeAttackData ) : Bool
	{
		var recordID : TweakDBID;
		var ownerID : EntityID;
		var statsSystem : StatsSystem;
		var attackSpeed : Float;
		var attackSpeedMult : Float;
		var staminaCostMods : array< weak< StatModifier_Record > >;
		var effectToPlay : CName;
		var attackRecord : weak< Attack_Melee_Record >;
		if( !( GetAttackRecord( scriptInterface, stateName, attackNumber, attackRecord ) ) )
		{
			return false;
		}
		recordID = attackRecord.GetID();
		if( !( TDBID.IsValid( recordID ) ) )
		{
			return false;
		}
		attackSpeedMult = 1.0;
		ownerID = scriptInterface.ownerEntityID;
		statsSystem = scriptInterface.GetStatsSystem();
		if( attackRecord.DontScaleWithAttackSpeed() )
		{
			attackSpeed = 1.0;
		}
		else
		{
			attackSpeed = statsSystem.GetStatValue( scriptInterface.ownerEntityID, gamedataStatType.AttackSpeed );
			if( scriptInterface.HasStatFlag( gamedataStatType.CanMeleeBerserk ) )
			{
				attackSpeedMult *= LerpF( ( ( Float )( stateContext.GetIntParameter( 'totalMeleeAttacksInCombo', true ) ) ) / ( ( Float )( GetStaticIntParameterDefault( "maxBerserkASAttack", 1 ) ) ), 1.0, GetStaticFloatParameterDefault( "maxBerserkAS", 1.0 ), true );
			}
			if( IsPlayerTired( scriptInterface ) && !( scriptInterface.HasStatFlag( gamedataStatType.CanIgnoreWeaponStaminaPenaties ) ) )
			{
				attackSpeedMult *= GetStaticFloatParameterDefault( "lowStaminaAttackSpeedMult", 0.60000002 );
			}
			attackSpeed *= attackSpeedMult;
		}
		attackRecord.StaminaCost( staminaCostMods );
		outgoingStruct = scriptInterface.GetMeleeAttackData( attackRecord, RPGManager.CalculateStatModifiers( staminaCostMods, scriptInterface.GetGame(), scriptInterface.owner, ownerID ), attackSpeed );
		GetWeaponObject( scriptInterface ).SetAttack( recordID );
		stateContext.SetPermanentFloatParameter( 'idleTransitionTime', outgoingStruct.idleTransitionTime, true );
		stateContext.SetPermanentFloatParameter( 'attackSpeed', attackSpeed, true );
		effectToPlay = attackRecord.VfxName();
		GameObjectEffectHelper.StartEffectEvent( scriptInterface.owner, effectToPlay, false );
		return true;
	}

	protected const function HasAttackRecord( const scriptInterface : StateGameScriptInterface, optional const attackNumber : Int32 ) : Bool
	{
		var attackRecord : weak< Attack_Melee_Record >;
		if( GetAttackRecord( scriptInterface, m_stateNameString, attackNumber, attackRecord ) )
		{
			return true;
		}
		return false;
	}

	protected const function GetAttackRecord( const scriptInterface : StateGameScriptInterface, const stateName : String, const attackNumber : Int32, out attackRecord : weak< Attack_Melee_Record > ) : Bool
	{
		attackRecord = ( ( Attack_Melee_Record )( GetWeaponObject( scriptInterface ).GetAttack( StringToName( stateName + IntToString( attackNumber ) ) ).GetRecord() ) );
		return attackRecord != NULL;
	}

	public function SpawnMeleeWeaponProjectile( scriptInterface : StateGameScriptInterface )
	{
		var transactionSystem : TransactionSystem;
		var itemObj : ItemObject;
		var projectileTemplateName : CName;
		var appearanceName : CName;
		var isThrowable : Bool;
		var useAppearance : Bool;
		var weaponObj : WeaponObject;
		transactionSystem = scriptInterface.GetTransactionSystem();
		itemObj = transactionSystem.GetItemInSlot( scriptInterface.executionOwner, T"AttachmentSlots.WeaponRight" );
		weaponObj = GetWeaponObject( scriptInterface );
		isThrowable = weaponObj.WeaponHasTag( 'Throwable' );
		if( isThrowable )
		{
			useAppearance = weaponObj.GetBoolPropertyFromWeaponDefinition( T".useProjectileAppearance" );
			if( useAppearance )
			{
				appearanceName = weaponObj.GetAppearanceNameFromComponent( 'Grip' );
			}
		}
		projectileTemplateName = weaponObj.GetNamePropertyFromWeaponDefinition( T".projectileTemplateName" );
		if( itemObj && IsNameValid( projectileTemplateName ) )
		{
			if( m_driverCombatListener.IsMountedInTPP() )
			{
				ProjectileLaunchHelper.SpawnProjectileFromRightHand( scriptInterface.executionOwner, projectileTemplateName, appearanceName, itemObj );
			}
			else
			{
				ProjectileLaunchHelper.SpawnProjectileFromScreenCenter( scriptInterface.executionOwner, projectileTemplateName, appearanceName, itemObj );
			}
		}
	}

	protected const function GetMeleeWeaponFriendlyName( scriptInterface : StateGameScriptInterface ) : CName
	{
		return StringToName( TweakDBInterface.GetItemRecord( ItemID.GetTDBID( GetWeaponObject( scriptInterface ).GetItemID() ) ).FriendlyName() );
	}

	public static function IsPlayingSyncedAnimation( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.GetWorkspotSystem().IsActorInWorkspot( scriptInterface.executionOwner ) && !( scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToVehicle ) ) )
		{
			return true;
		}
		return false;
	}

	protected function AdjustAttackPosition( scriptInterface : StateGameScriptInterface, stateContext : StateContext, attackData : MeleeAttackData ) : Bool
	{
		var impulseVector : Vector4;
		var adjustPosition : Vector4;
		if( !( attackData.useAdjustmentInsteadOfImpulse ) )
		{
			return false;
		}
		impulseVector = AddCameraSpaceImpulse( scriptInterface, stateContext, attackData );
		impulseVector += AddForwardImpulse( scriptInterface, stateContext, attackData );
		impulseVector += AddUpImpulse( scriptInterface, stateContext, attackData );
		adjustPosition = scriptInterface.executionOwner.GetWorldPosition() + impulseVector;
		RequestPlayerPositionAdjustment( stateContext, scriptInterface, NULL, attackData.attackEffectDelay, GetStaticFloatParameterDefault( "distanceRadiusToTarget", 0.89999998 ), GetStaticFloatParameterDefault( "rotationDuration", -1.0 ), adjustPosition, false );
		return true;
	}

	protected function AddAttackImpulse( scriptInterface : StateGameScriptInterface, stateContext : StateContext, attackData : MeleeAttackData )
	{
		var impulseEvent : PSMImpulse;
		var impulseVector : Vector4;
		var targetTooCloseRange : Float;
		targetTooCloseRange = GetStaticFloatParameterDefault( "minDistToTarget", 5.0 );
		if( ( attackData.forwardImpulse > 0.0 ) || ( attackData.cameraSpaceImpulse > 0.0 ) )
		{
			if( GetTargetObject( scriptInterface, targetTooCloseRange ) )
			{
				return;
			}
		}
		impulseVector = AddCameraSpaceImpulse( scriptInterface, stateContext, attackData );
		impulseVector += AddForwardImpulse( scriptInterface, stateContext, attackData );
		impulseVector += AddUpImpulse( scriptInterface, stateContext, attackData );
		impulseEvent = new PSMImpulse;
		impulseEvent.id = 'impulse';
		impulseEvent.impulse = impulseVector;
		scriptInterface.executionOwner.QueueEvent( impulseEvent );
	}

	protected function AddCameraSpaceImpulse( scriptInterface : StateGameScriptInterface, stateContext : StateContext, attackData : MeleeAttackData ) : Vector4
	{
		var impulseVector : Vector4;
		var impulseValue : Float;
		var cameraWorldTransform : Transform;
		if( attackData.cameraSpaceImpulse == 0.0 )
		{
			return Vector4.EmptyVector();
		}
		impulseValue = attackData.cameraSpaceImpulse;
		if( !( scriptInterface.IsOnGround() ) )
		{
			impulseValue *= GetStaticFloatParameterDefault( "inAirImpulseMultiplier", 1.0 );
		}
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		impulseVector = Transform.GetForward( cameraWorldTransform );
		impulseVector = impulseVector * impulseValue;
		return impulseVector;
	}

	protected function AddForwardImpulse( scriptInterface : StateGameScriptInterface, stateContext : StateContext, attackData : MeleeAttackData ) : Vector4
	{
		var impulseVector : Vector4;
		var impulseValue : Float;
		if( attackData.forwardImpulse == 0.0 )
		{
			return Vector4.EmptyVector();
		}
		impulseValue = attackData.forwardImpulse;
		if( !( scriptInterface.IsOnGround() ) )
		{
			impulseValue *= GetStaticFloatParameterDefault( "inAirImpulseMultiplier", 1.0 );
		}
		impulseVector = scriptInterface.executionOwner.GetWorldForward();
		impulseVector = impulseVector * impulseValue;
		return impulseVector;
	}

	protected function AddUpImpulse( scriptInterface : StateGameScriptInterface, stateContext : StateContext, attackData : MeleeAttackData ) : Vector4
	{
		var impulseVector : Vector4;
		var impulseValue : Float;
		if( attackData.upImpulse == 0.0 )
		{
			return Vector4.EmptyVector();
		}
		impulseValue = attackData.upImpulse;
		if( !( scriptInterface.IsOnGround() ) )
		{
			impulseValue *= GetStaticFloatParameterDefault( "inAirImpulseMultiplier", 1.0 );
		}
		impulseVector = scriptInterface.executionOwner.GetWorldUp();
		impulseVector = impulseVector * impulseValue;
		return impulseVector;
	}

	protected function GetMovementInput( scriptInterface : StateGameScriptInterface ) : Float
	{
		var x : Float;
		var y : Float;
		var res : Float;
		x = scriptInterface.GetActionValue( 'MoveX' );
		y = scriptInterface.GetActionValue( 'MoveY' );
		res = SqrtF( SqrF( x ) + SqrF( y ) );
		return res;
	}

	protected function IsPlayerInputDirectedForward( scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( AbsF( scriptInterface.GetInputHeading() ) < 45.0 )
		{
			return true;
		}
		return false;
	}

	protected const function GetNanoWireTargetObject( const scriptInterface : StateGameScriptInterface ) : GameObject
	{
		var targetingSystem : TargetingSystem;
		var targetObject : GameObject;
		var angleOut : EulerAngles;
		var wireAttackRange : Float;
		targetingSystem = scriptInterface.GetTargetingSystem();
		targetObject = targetingSystem.GetObjectClosestToCrosshair( scriptInterface.executionOwner, angleOut, TSQ_NPC() );
		wireAttackRange = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.Range );
		wireAttackRange *= 2.0;
		if( ( targetObject.IsPuppet() && ScriptedPuppet.IsActive( targetObject ) ) && ( GameObject.GetAttitudeTowards( targetObject, scriptInterface.executionOwner ) == EAIAttitude.AIA_Neutral || GameObject.GetAttitudeTowards( targetObject, scriptInterface.executionOwner ) == EAIAttitude.AIA_Hostile ) )
		{
			if( ( wireAttackRange <= 0.0 ) || ( Vector4.Distance( scriptInterface.executionOwner.GetWorldPosition(), targetObject.GetWorldPosition() ) <= wireAttackRange ) )
			{
				return targetObject;
			}
		}
		return NULL;
	}

	protected function IsTargetAPuppet( scriptInterface : StateGameScriptInterface ) : Bool
	{
		return GetTargetObject( scriptInterface ).IsPuppet();
	}

	protected function IsTargetOfficer( scriptInterface : StateGameScriptInterface, object : weak< GameObject > ) : Bool
	{
		var isOfficer : Bool;
		var puppet : NPCPuppet;
		puppet = ( ( NPCPuppet )( object ) );
		isOfficer = puppet.GetNPCRarity() == gamedataNPCRarity.Officer;
		return isOfficer;
	}

	protected const function IsAttackParried( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.GetStatusEffectSystem().HasStatusEffect( scriptInterface.executionOwnerEntityID, T"BaseStatusEffect.Parry" );
	}

	protected const function HasMeleeTargeting( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return HasWeaponStatFlag( scriptInterface, gamedataStatType.HasMeleeTargeting );
	}

	protected const function CanWeaponBlock( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return HasWeaponStatFlag( scriptInterface, gamedataStatType.CanWeaponBlock );
	}

	protected const function CanWeaponDeflect( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return HasWeaponStatFlag( scriptInterface, gamedataStatType.CanWeaponDeflect );
	}

	protected const function CanThrowWeapon( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.HasStatFlag( gamedataStatType.CanThrowWeapon );
	}

	protected function ResetFlags( stateContext : StateContext )
	{
		SetIsBlocking( stateContext, false );
		SetIsTargeting( stateContext, false );
		SetIsAttacking( stateContext, false );
		SetIsHolding( stateContext, false );
		SetIsParried( stateContext, false );
		SetIsSafe( stateContext, false );
		SetIsThrowReloading( stateContext, false );
	}

}

abstract class MeleeEventsTransition extends MeleeTransition
{

	public virtual function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SendAnimFeatureData( stateContext, scriptInterface );
	}

	public export virtual function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SendAnimFeatureData( stateContext, scriptInterface );
		ToggleWireVisualEffect( stateContext, scriptInterface, 'monowire_idle', false );
	}

	public export virtual function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface ) {}

	public export virtual function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var animFeature : AnimFeature_MeleeData;
		animFeature = new AnimFeature_MeleeData;
		scriptInterface.SetAnimationParameterFeature( 'MeleeData', animFeature );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Default ) ) );
		ResetFlags( stateContext );
		ToggleWireVisualEffect( stateContext, scriptInterface, 'monowire_idle', false );
		RemoveAllMeleeGameplayRestrictions( stateContext, scriptInterface );
		MeleeTransitionRemoveTriggerEffects( GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ) );
	}

	protected function ToggleWireVisualEffect( stateContext : StateContext, scriptInterface : StateGameScriptInterface, effectName : CName, b : Bool )
	{
		if( GetMeleeWeaponFriendlyName( scriptInterface ) == 'mono_wires' )
		{
			if( b == true )
			{
				GameObjectEffectHelper.StartEffectEvent( scriptInterface.owner, effectName );
			}
			else
			{
				GameObjectEffectHelper.StopEffectEvent( scriptInterface.owner, effectName );
			}
		}
	}

	protected function MeleeTransitionRemoveTriggerEffects( audioSystem : AudioSystem )
	{
		audioSystem.RemoveTriggerEffect( 'PSM_MeleeTargetingOnEnter_trigger' );
		audioSystem.RemoveTriggerEffect( 'PSM_MeleeReloadOnEnter_feedback' );
		audioSystem.RemoveTriggerEffect( 'PSM_MeleeAttackGeneric' );
	}

	protected function TargetPrediction( targetPosition : Vector4, targetPuppet : ScriptedPuppet, deltaTime : Float, effectStrength : Float ) : Vector4
	{
		var targetVelocity : Vector4;
		var predictedPos : Vector4;
		var targetPos : Vector4;
		var distanceLimit : Float;
		targetVelocity = targetPuppet.GetVelocity();
		distanceLimit = 2.0;
		targetPos = targetPosition;
		predictedPos = targetPos + ( ( targetVelocity * deltaTime ) * effectStrength );
		if( Vector4.DistanceSquared( targetPos, predictedPos ) >= ( distanceLimit * distanceLimit ) )
		{
			predictedPos = targetPos + ( Vector4.Normalize( targetVelocity ) * distanceLimit );
		}
		targetPos = predictedPos;
		return targetPos;
	}

	protected function CheckThrowableCooldown( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( !( GetWeaponObject( scriptInterface ).WeaponHasTag( 'Throwable' ) ) )
		{
			return;
		}
		if( IsThrownWeaponReloading( stateContext, scriptInterface ) )
		{
			SetIsThrowReloading( stateContext, true );
		}
	}

}

enum meleeMoveDirection
{
	Forward = 0,
	Right = 1,
	Back = 2,
	Left = 3,
}

enum meleeQueuedAttack
{
	Block = 0,
	Combo = 1,
	Crouch = 2,
	Final = 3,
	Jump = 4,
	Safe = 5,
	Sprint = 6,
	Strong = 7,
	Throw = 8,
}

import class MeleeAttackData extends IScriptable
{
	import var attackName : CName;
	import var attackSpeed : Float;
	import var attackWindowOpen : Float;
	import var attackWindowClosed : Float;
	import var idleTransitionTime : Float;
	import var holdTransitionTime : Float;
	import var blockTransitionTime : Float;
	import var attackEffectDirection : CName;
	import var attackEffectDuration : Float;
	import var attackEffectDelay : Float;
	import var impactFxSlot : CName;
	import var impulseDelay : Float;
	import var cameraSpaceImpulse : Float;
	import var forwardImpulse : Float;
	import var upImpulse : Float;
	import var useAdjustmentInsteadOfImpulse : Bool;
	import var enableAdjustingPlayerPositionToTarget : Bool;
	import var isThrust : Bool;
	import var useMiddlePosition : Bool;
	import var startPosition : Vector4;
	import var middlePosition : Vector4;
	import var endPosition : Vector4;
	import var staminaCost : Float;
	import var weaponChargeCost : Float;
	import var hasDeflectAnim : Bool;
	import var hasHitAnim : Bool;
	import var trailStartDelay : Float;
	import var trailStopDelay : Float;
	import var trailAttackSide : String;
	import var incrementsCombo : Bool;
	import var startupDuration : Float;
	import var activeDuration : Float;
	import var recoverDuration : Float;
	import var activeHitDuration : Float;
	import var recoverHitDuration : Float;
	import var standUpDelay : Float;
	import var ikOffset : Vector3;
}

class MeleeNotReadyDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return !( IsWeaponReady( stateContext, scriptInterface ) );
	}

	protected export const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsWeaponReady( stateContext, scriptInterface );
	}

}

class MeleeNotReadyEvents extends MeleeEventsTransition
{

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var weapon : weak< WeaponObject >;
		weapon = ( ( WeaponObject )( scriptInterface.owner ) );
		ResetFlags( stateContext );
		ResetAttackNumber( stateContext );
		scriptInterface.PushAnimationEvent( 'MeleeNotReady' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.NotReady ) ) );
		stateContext.RemovePermanentFloatParameter( 'meleeRecoveryDuration' );
		TutorialSetFact( scriptInterface, 'melee_combat_tutorial' );
		CheckThrowableCooldown( stateContext, scriptInterface );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).AddTriggerEffect( weapon.GetTriggerEffectName(), 'PSM_MeleeAttackGeneric' );
		if( StatusEffectSystem.ObjectHasStatusEffect( scriptInterface.executionOwner, PlayerStaminaHelpers.GetExhaustedStatusEffectID() ) )
		{
			GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).ReplaceTriggerEffect( 'te_wea_melee_swing_exhausted', 'PSM_MeleeAttackGeneric', false );
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeParriedDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsAttackParried( stateContext, scriptInterface );
	}

	protected const function ToMeleeIdle( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return !( IsAttackParried( stateContext, scriptInterface ) );
	}

	protected const function ToMeleeDeflect( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsBlockPressed( stateContext, scriptInterface );
	}

}

class MeleeParriedEvents extends MeleeEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ClearInputBuffer( stateContext );
		ResetFlags( stateContext );
		ResetAttackNumber( stateContext );
		scriptInterface.PushAnimationEvent( 'MeleeParried' );
		SetIsParried( stateContext, true );
		PlayRumble( scriptInterface, "heavy_fast" );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Parried ) ) );
		stateContext.RemovePermanentFloatParameter( 'meleeRecoveryDuration' );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetIsParried( stateContext, false );
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeRecoveryDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var paramResult : StateResultFloat;
		paramResult = stateContext.GetPermanentFloatParameter( 'meleeRecoveryDuration' );
		if( paramResult.valid )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	protected const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var paramResult : StateResultFloat;
		paramResult = stateContext.GetPermanentFloatParameter( 'meleeRecoveryDuration' );
		return GetInStateTime() >= paramResult.value;
	}

}

class MeleeRecoveryEvents extends MeleeNotReadyEvents
{

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		stateContext.RemovePermanentFloatParameter( 'meleeRecoveryDuration' );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		stateContext.RemovePermanentFloatParameter( 'meleeRecoveryDuration' );
	}

}

class MeleeEquippingDecisions extends MeleeIdleDecisions
{
	var m_hasEquipAttack : Bool;

	protected export override function OnAttach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		var attackRecord : weak< Attack_Melee_Record >;
		m_hasEquipAttack = GetAttackRecord( scriptInterface, "MeleeEquipAttack", 0, attackRecord );
	}

	protected export const function ToMeleeIdle( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsRightHandInEquippedState( stateContext );
	}

	protected export const override function ToMeleeHold( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( m_hasEquipAttack )
		{
			return false;
		}
		return super.ToMeleeHold( stateContext, scriptInterface );
	}

	protected export const function ToMeleeEquipAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( m_hasEquipAttack ) )
		{
			return false;
		}
		return scriptInterface.IsActionJustHeld( 'MeleeAttack' );
	}

}

class MeleeEquippingEvents extends MeleeRumblingEvents
{

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetFlags( stateContext );
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Default ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Equipping ) ) );
		stateContext.SetPermanentBoolParameter( 'isSafe', false, true );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).RemoveTriggerEffect( 'PSM_MeleeTargetingOnEnter_trigger' );
		CheckThrowableCooldown( stateContext, scriptInterface );
		super.OnEnter( stateContext, scriptInterface );
	}

	protected function CleanupFirstEquipFX( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var weapon : ItemObject;
		if( !( CheckItemType( scriptInterface, gamedataItemType.Wea_Chainsword ) ) )
		{
			return;
		}
		if( !( IsInFirstEquip( stateContext ) ) )
		{
			return;
		}
		weapon = ( ( ItemObject )( scriptInterface.owner ) );
		if( weapon )
		{
			GameInstance.GetAudioSystem( weapon.GetGame() ).Stop( 'w_melee_cut_o_matic_first_equip', weapon.GetEntityID(), 'firearm_emitter' );
		}
	}

	protected override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		CleanupFirstEquipFX( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	protected override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		CleanupFirstEquipFX( stateContext, scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class MeleeIdleDecisions extends MeleeTransition
{

	protected export const function ToMeleePublicSafe( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat ) == ( ( Int32 )( gamePSMCombat.InCombat ) ) )
		{
			return false;
		}
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Zones ) == ( ( Int32 )( gamePSMZones.Dangerous ) ) )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'Fists' ) )
		{
			return false;
		}
		if( stateContext.IsStateMachineActive( 'Vehicle' ) )
		{
			return false;
		}
		if( stateContext.GetBoolParameter( 'InPublicZone', true ) )
		{
			if( GetInStateTime() > GetStaticFloatParameterDefault( "safeTransition", 1.0 ) )
			{
				return true;
			}
			return false;
		}
		return false;
	}

	protected export const virtual function ToMeleeHold( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ShouldHold( stateContext, scriptInterface, true ) )
		{
			return true;
		}
		return false;
	}

}

class MeleeIdleEvents extends MeleeRumblingEvents
{

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetFlags( stateContext );
		ResetAttackNumber( stateContext );
		ClearMeleePressCount( stateContext, scriptInterface );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Default ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Idle ) ) );
		stateContext.SetPermanentBoolParameter( 'isSafe', false, true );
		SetFlags( stateContext );
		ToggleWireVisualEffect( stateContext, scriptInterface, 'monowire_idle', true );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).RemoveTriggerEffect( 'PSM_MeleeTargetingOnEnter_trigger' );
		super.OnEnter( stateContext, scriptInterface );
	}

	protected virtual function SetFlags( stateContext : StateContext ) {}
}

abstract class MeleeRumblingEvents extends MeleeEventsTransition
{

	public virtual function GetIntensity() : String
	{
		return "light";
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var itemTags : array< CName >;
		var itemRecord : weak< Item_Record >;
		var weaponItem : ItemID;
		var hasRumblingWeaponTag : Bool;
		weaponItem = EquipmentSystem.GetData( GameInstance.GetPlayerSystem( scriptInterface.owner.GetGame() ).GetLocalPlayerMainGameObject() ).GetActiveItem( gamedataEquipmentArea.Weapon );
		itemRecord = RPGManager.GetItemRecord( weaponItem );
		itemTags = itemRecord.Tags();
		hasRumblingWeaponTag = itemTags.Contains( 'MeleeRumblingWeapon' );
		if( hasRumblingWeaponTag )
		{
			PlayRumbleLoop( scriptInterface, GetIntensity() );
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		StopRumbleLoop( scriptInterface, GetIntensity() );
		super.OnExit( stateContext, scriptInterface );
	}

	public export override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		StopRumbleLoop( scriptInterface, GetIntensity() );
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleePublicSafeDecisions extends MeleeTransition
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat ) == ( ( Int32 )( gamePSMCombat.InCombat ) ) )
		{
			return false;
		}
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Zones ) == ( ( Int32 )( gamePSMZones.Dangerous ) ) )
		{
			return false;
		}
		if( stateContext.IsStateActive( 'Locomotion', 'sprint' ) )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'Fists' ) )
		{
			return false;
		}
		return false;
	}

	protected export const function ToMeleeIdle( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.IsStateActive( 'Locomotion', 'sprint' ) )
		{
			return true;
		}
		if( scriptInterface.IsActionJustPressed( 'Reload' ) )
		{
			return true;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'Fists' ) )
		{
			return true;
		}
		return false;
	}

}

class MeleePublicSafeEvents extends MeleeRumblingEvents
{
	var m_unequipTime : Float;

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetFlags( stateContext );
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Default ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.PublicSafe ) ) );
		SetIsSafe( stateContext, true );
		if( GetWeaponItemTag( stateContext, scriptInterface, 'Meleeware' ) )
		{
			m_unequipTime = GetStaticFloatParameterDefault( "timeToUnequipMeleeware", 15.0 );
		}
		else if( stateContext.GetBoolParameter( 'InPublicZone', true ) )
		{
			m_unequipTime = GetStaticFloatParameterDefault( "timeToAutoUnequipWeapon", 15.0 );
		}
		else
		{
			m_unequipTime = -1.0;
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	protected function OnTick( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( ( m_unequipTime > 0.0 ) && ( GetInStateTime() >= m_unequipTime ) )
		{
			SendEquipmentSystemWeaponManipulationRequest( scriptInterface, EquipmentManipulationAction.UnequipWeapon );
		}
	}

}

class MeleeSafeDecisions extends MeleeTransition
{

	protected const function IsFriendlyTargetInMeleeRange( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface, const attackRangeSquared : Float ) : Bool
	{
		var player : PlayerPuppet;
		var isTargeting : StateResultBool;
		player = ( ( PlayerPuppet )( scriptInterface.executionOwner ) );
		if( !( player ) )
		{
			return false;
		}
		if( !( player.IsAimingAtFriendly() ) )
		{
			return false;
		}
		isTargeting = stateContext.GetPermanentBoolParameter( 'isTargeting' );
		if( !( isTargeting.value ) && ( player.DistanceFromTargetSquared() > attackRangeSquared ) )
		{
			return false;
		}
		return true;
	}

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var isTargeting : StateResultBool;
		var attackRangeSquared : Float;
		isTargeting = stateContext.GetPermanentBoolParameter( 'isTargeting' );
		attackRangeSquared = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.Range );
		attackRangeSquared = attackRangeSquared * attackRangeSquared;
		if( IsFriendlyTargetInMeleeRange( stateContext, scriptInterface, attackRangeSquared ) )
		{
			return true;
		}
		if( ShouldEnterSafe( stateContext, scriptInterface, ( ( isTargeting.value ) ? ( 0.0 ) : ( attackRangeSquared ) ) ) )
		{
			return true;
		}
		return false;
	}

	protected export const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( EnterCondition( stateContext, scriptInterface ) ) )
		{
			return true;
		}
		return false;
	}

}

class MeleeSafeEvents extends MeleePublicSafeEvents
{

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Safe ) ) );
	}

}

class MeleeHoldDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ShouldHold( stateContext, scriptInterface ) && !( IsPlayingSyncedAnimation( scriptInterface ) ) )
		{
			return true;
		}
		return false;
	}

	protected export const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ShouldInterruptHoldStates( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( ToMeleeChargedHold( stateContext, scriptInterface ) && !( IsPlayingSyncedAnimation( scriptInterface ) ) )
		{
			return true;
		}
		if( ( scriptInterface.GetActionValue( 'MeleeStrongAttack' ) <= 0.0 ) && ( scriptInterface.GetActionValue( 'MeleeAttack' ) <= 0.0 ) )
		{
			return true;
		}
		if( IsBlockHeld( stateContext, scriptInterface ) )
		{
			if( !( GetWeaponObject( scriptInterface ).IsThrowable() ) || CanThrowWeapon( stateContext, scriptInterface ) )
			{
				return true;
			}
		}
		return false;
	}

	protected export const function ToMeleeFinalAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( GameInstance.GetPhotoModeSystem( scriptInterface.executionOwner.GetGame() ).IsPhotoModeActive() )
		{
			return false;
		}
		return CheckIfFinalAttack( scriptInterface, stateContext ) && WantsToLightAttack( stateContext, scriptInterface );
	}

	protected export const function ToMeleeMountedFinalAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( m_driverCombatListener.IsMountedInTPP() ) )
		{
			return false;
		}
		return ToMeleeFinalAttack( stateContext, scriptInterface );
	}

}

class MeleeHoldEvents extends MeleeEventsTransition
{

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetIsHolding( stateContext, true );
		SetIsBlocking( stateContext, false );
		SetIsAttacking( stateContext, false );
		SetIsTargeting( stateContext, false );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Hold ) ) );
		stateContext.SetTemporaryBoolParameter( 'InterruptHoldForLeap', false, true );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetIsHolding( stateContext, false );
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeChargedHoldDecisions extends MeleeTransition
{

	protected export const virtual function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ShouldHold( stateContext, scriptInterface ) )
		{
			return true;
		}
		return false;
	}

	protected const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ShouldInterruptHoldStates( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( !( ShouldHold( stateContext, scriptInterface ) ) )
		{
			return true;
		}
		if( ToMeleeStrongAttack( stateContext, scriptInterface ) && !( IsPlayingSyncedAnimation( scriptInterface ) ) )
		{
			return true;
		}
		if( IsBlockHeld( stateContext, scriptInterface ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToMeleeStrongAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var timeoutDuration : Float;
		if( GameInstance.GetPhotoModeSystem( scriptInterface.executionOwner.GetGame() ).IsPhotoModeActive() )
		{
			return false;
		}
		if( NoStrongAttackPressed( scriptInterface ) )
		{
			return true;
		}
		timeoutDuration = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.HoldTimeoutDuration );
		if( ( timeoutDuration > 0.0 ) && ( GetInStateTime() >= timeoutDuration ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToMeleeMountedStrongAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( m_driverCombatListener.IsMountedInTPP() ) )
		{
			return false;
		}
		return ToMeleeStrongAttack( stateContext, scriptInterface );
	}

}

class MeleeChargedHoldEvents extends MeleeRumblingEvents
{
	var m_clearWeaponCharge : Bool;
	var m_effectiveRangeMod : gameStatModifierData;

	public override function GetIntensity() : String
	{
		return "heavy";
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var chargeValuePerSec : Float;
		var executionOwner : weak< GameObject >;
		executionOwner = scriptInterface.executionOwner;
		m_clearWeaponCharge = true;
		PlayRumble( scriptInterface, "light_fast" );
		SetIsHolding( stateContext, true );
		SetIsBlocking( stateContext, false );
		SetIsAttacking( stateContext, false );
		SetIsTargeting( stateContext, false );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.ChargedHold ) ) );
		stateContext.SetPermanentBoolParameter( 'VisionToggled', false, true );
		ForceDisableVisionMode( stateContext );
		if( CheckItemType( scriptInterface, gamedataItemType.Cyb_MantisBlades ) )
		{
			SetCanSprintWhileCharging( stateContext, true );
		}
		else
		{
			stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
			SetCanSprintWhileCharging( stateContext, false );
		}
		if( ( scriptInterface.GetStatsSystem().GetStatValue( scriptInterface.executionOwnerEntityID, gamedataStatType.CanMeleeLeap ) > 0.0 ) || ( scriptInterface.GetStatsSystem().GetStatValue( scriptInterface.executionOwnerEntityID, gamedataStatType.CanMeleeLeapInAir ) > 0.0 ) )
		{
			UpdateEffectiveRange( scriptInterface, TweakDBInterface.GetFloat( T"playerStateMachineMelee.meleeLeap.maxDistToTarget", 0.0 ) );
		}
		else if( ( GetWeaponType( scriptInterface ) == gamedataItemType.Cyb_StrongArms && HasNewSpyAttackStatFlag( scriptInterface ) ) && WeaponIsCharged( scriptInterface ) )
		{
			UpdateEffectiveRange( scriptInterface, TweakDBInterface.GetFloat( T"playerStateMachineLocomotion.dodge.dashOnePunchAttackEnemyMaxRange", 0.0 ) );
		}
		else if( CanPerformRelicLeap( scriptInterface ) )
		{
			UpdateEffectiveRange( scriptInterface, TweakDBInterface.GetFloat( T"playerStateMachineMelee.meleeLeap.maxDistToTargetMantisBladesRelic", 0.0 ) );
		}
		chargeValuePerSec = GetChargeValuePerSec( scriptInterface );
		if( chargeValuePerSec > 0.0 )
		{
			if( !( CheckItemType( scriptInterface, gamedataItemType.Cyb_NanoWires ) ) || IsMonowireQuickhackChargedAttack( scriptInterface ) )
			{
				StartPool( scriptInterface.GetStatPoolsSystem(), GetWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge, 0.0, 100.0, chargeValuePerSec );
			}
		}
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).AddTriggerEffect( 'te_wea_melee_charge', 'PSM_MeleeChargeOnEnter_charge' );
		GameInstance.GetStatusEffectSystem( scriptInterface.owner.GetGame() ).ApplyStatusEffect( executionOwner.GetEntityID(), GetPlayerAimingStatusEffectID() );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	protected function OnExitToMeleeStrongAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( !( CheckItemType( scriptInterface, gamedataItemType.Cyb_NanoWires ) ) )
		{
			m_clearWeaponCharge = false;
		}
		OnExit( stateContext, scriptInterface );
	}

	private function OnExitCommon( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var executionOwner : weak< GameObject >;
		executionOwner = scriptInterface.executionOwner;
		SetIsHolding( stateContext, false );
		StopPool( scriptInterface.GetStatPoolsSystem(), GetWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge, false );
		if( ( m_clearWeaponCharge && !( CheckItemType( scriptInterface, gamedataItemType.Cyb_MantisBlades ) ) ) && !( IsMonowireQuickhackChargedAttack( scriptInterface ) ) )
		{
			ChangeStatPoolValue( scriptInterface, scriptInterface.ownerEntityID, gamedataStatPoolType.WeaponCharge, -100.0, true );
		}
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).RemoveTriggerEffect( 'PSM_MeleeChargeOnEnter_charge' );
		GameInstance.GetStatusEffectSystem( scriptInterface.owner.GetGame() ).RemoveStatusEffect( executionOwner.GetEntityID(), GetPlayerAimingStatusEffectID() );
		if( m_effectiveRangeMod )
		{
			scriptInterface.GetStatsSystem().RemoveModifier( GetWeaponObject( scriptInterface ).GetEntityID(), m_effectiveRangeMod );
			m_effectiveRangeMod = NULL;
		}
	}

	private function IsMonowireQuickhackChargedAttack( scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ( CheckItemType( scriptInterface, gamedataItemType.Cyb_NanoWires ) && HasNewSpyAttackStatFlag( scriptInterface ) ) && HasMonowireWithQuickhackSelected( scriptInterface );
	}

	private function GetChargeValuePerSec( scriptInterface : StateGameScriptInterface ) : Float
	{
		var weapon : WeaponObject;
		var chargeDuration : Float;
		var statsSystem : StatsSystem;
		statsSystem = scriptInterface.GetStatsSystem();
		if( !( statsSystem ) )
		{
			return -1.0;
		}
		weapon = GetWeaponObject( scriptInterface );
		if( !( weapon ) )
		{
			return -1.0;
		}
		chargeDuration = statsSystem.GetStatValue( weapon.GetEntityID(), gamedataStatType.ChargeTime );
		if( chargeDuration <= 0.0 )
		{
			return -1.0;
		}
		return 100.0 / chargeDuration;
	}

	private function UpdateEffectiveRange( scriptInterface : StateGameScriptInterface, effectiveRange : Float )
	{
		effectiveRange -= scriptInterface.GetStatsSystem().GetStatValue( GetWeaponObject( scriptInterface ).GetEntityID(), gamedataStatType.EffectiveRange );
		m_effectiveRangeMod = RPGManager.CreateStatModifier( gamedataStatType.EffectiveRange, gameStatModifierType.Additive, effectiveRange );
		scriptInterface.GetStatsSystem().AddModifier( GetWeaponObject( scriptInterface ).GetEntityID(), m_effectiveRangeMod );
	}

}

abstract class MeleeAttackGenericDecisions extends MeleeTransition
{

	protected const virtual function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var uninterruptableHealingEvent : StateResultBool;
		if( !( HasAttackRecord( scriptInterface, stateContext.GetIntParameter( 'meleeAttackNumber', true ) ) ) )
		{
			return false;
		}
		if( GameObject.IsCooldownActive( scriptInterface.owner, GetMeleeAttackCooldownName() ) )
		{
			return false;
		}
		uninterruptableHealingEvent = stateContext.GetPermanentBoolParameter( 'UninterruptableHealing' );
		if( uninterruptableHealingEvent.valid && uninterruptableHealingEvent.value )
		{
			return false;
		}
		return true;
	}

	protected export const virtual function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var interruptEvent : StateResultBool;
		var attackData : MeleeAttackData;
		var inStateTime : Float;
		interruptEvent = stateContext.GetPermanentBoolParameter( 'InterruptMelee' );
		attackData = GetAttackData( stateContext );
		inStateTime = GetInStateTime();
		if( interruptEvent.value )
		{
			if( inStateTime >= attackData.attackWindowClosed )
			{
				return true;
			}
			return false;
		}
		if( IsBlockHeld( stateContext, scriptInterface ) )
		{
			if( ( attackData.blockTransitionTime > 0.0 ) && ( inStateTime >= attackData.blockTransitionTime ) )
			{
				return true;
			}
			if( inStateTime >= attackData.attackWindowClosed )
			{
				if( !( HasMeleeTargeting( stateContext, scriptInterface ) ) && IsBlockPressed( stateContext, scriptInterface ) )
				{
					return true;
				}
			}
		}
		if( ( inStateTime >= attackData.attackWindowClosed ) && !( IsPlayingSyncedAnimation( scriptInterface ) ) )
		{
			if( stateContext.GetConditionBool( 'LightMeleeAttackPressed' ) )
			{
				return true;
			}
			else if( stateContext.GetConditionBool( 'StrongMeleeAttackPressed' ) )
			{
				return true;
			}
			else if( ShouldHold( stateContext, scriptInterface, false, true ) )
			{
				return true;
			}
			else if( stateContext.GetConditionBool( 'QuickMeleeAttackTapped' ) )
			{
				return true;
			}
		}
		if( inStateTime >= attackData.idleTransitionTime )
		{
			return true;
		}
		if( IsAttackParried( stateContext, scriptInterface ) )
		{
			return true;
		}
		return false;
	}

	protected const function GetAttackData( const stateContext : StateContext ) : MeleeAttackData
	{
		return ( ( MeleeAttackData )( stateContext.GetConditionScriptableParameter( 'MeleeAttackData' ) ) );
	}

}

abstract class MeleeAttackGenericEvents extends MeleeEventsTransition
{
	var m_effect : EffectInstance;
	var m_attackCreated : Bool;
	var m_blockImpulseCreation : Bool;
	var m_standUpSend : Bool;
	var m_trailCreated : Bool;
	var m_finisherTarget : weak< ScriptedPuppet >;
	var m_finisherCameraRotReseted : Bool;
	var m_textLayer : Uint32;
	var m_rumblePlayed : Bool;
	var m_shouldBlockImpulseUpdate : Bool;
	var m_enteredFromMeleeLeap : Bool;
	var m_effectPositionUpdated : Bool;
	var m_tppYawOverride : Float;

	protected virtual function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Combo;
	}

	protected const virtual function IsMountedTPPAttack() : Bool
	{
		return false;
	}

	protected virtual function SetTPPYawOverride( stateContext : StateContext )
	{
		var yawOverride : StateResultFloat;
		yawOverride = stateContext.GetPermanentFloatParameter( 'TPPVehiclePlayerYaw' );
		if( yawOverride.valid )
		{
			m_tppYawOverride = yawOverride.value;
		}
		else
		{
			m_tppYawOverride = 0.0;
		}
	}

	protected virtual function IsMoveToTargetEnabled( attackData : MeleeAttackData, assistRecord : AimAssistMelee_Record ) : Bool
	{
		var assistLevel : EMoveAssistLevel;
		var attackType : EMeleeAttackType;
		if( !( attackData.enableAdjustingPlayerPositionToTarget ) )
		{
			return false;
		}
		if( assistRecord )
		{
			assistLevel = ( ( EMoveAssistLevel )( assistRecord.MoveToTargetEnabledAttacks() ) );
			if( assistLevel == EMoveAssistLevel.AllAttacks )
			{
				return true;
			}
			else if( assistLevel == EMoveAssistLevel.Off )
			{
				return false;
			}
			else if( assistLevel == EMoveAssistLevel.SpecialAttacks )
			{
				attackType = GetAttackType();
				return attackType == EMeleeAttackType.Strong;
			}
		}
		return false;
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var attackData : MeleeAttackData;
		var attackRange : Float;
		var adjustmentDistParam : Float;
		var adjustmentTargetRadius : Float;
		var adjustRadius : Float;
		var adjustRadiusParam : Float;
		var broadcaster : StimBroadcasterComponent;
		var aimAssistRecord : AimAssistMelee_Record;
		var shouldCheckForWeakspots : Bool;
		var playerPerkDataBB : IBlackboard;
		var targetObject : GameObject;
		var distanceToTarget : Float;
		var leapTargetWithinAngle : Float;
		var canMeleeLeap : Bool;
		var slotTransformPos : Vector4;
		var vecToTarget : Vector4;
		var leapAngle : EulerAngles;
		var leapingWasSuccessful : Bool;
		var uninterruptableHealingEvent : StateResultBool;
		var ts : TransactionSystem;
		var healingItem : gameItemData;
		var isBloodPumpEquipped : Bool;
		var canPerformLeap : Bool;
		var isMeleeThrow : Bool;
		var finisherTargetID : EntityID;
		aimAssistRecord = GetAimAssistMeleeRecord( scriptInterface );
		if( uninterruptableHealingEvent.valid && uninterruptableHealingEvent.value )
		{
			return;
		}
		m_attackCreated = false;
		m_blockImpulseCreation = false;
		m_standUpSend = false;
		m_effect = NULL;
		m_trailCreated = false;
		m_rumblePlayed = false;
		m_shouldBlockImpulseUpdate = false;
		isMeleeThrow = GetAttackType() == EMeleeAttackType.Throw;
		if( broadcaster = scriptInterface.executionOwner.GetStimBroadcasterComponent() )
		{
			broadcaster.TriggerSingleBroadcast( scriptInterface.executionOwner, gamedataStimType.MeleeAttack, , , isMeleeThrow );
		}
		SetMeleeAttackPressCount( stateContext, scriptInterface );
		ClearInputBuffer( stateContext );
		stateContext.SetPermanentBoolParameter( 'InterruptMelee', false, true );
		SetIsAttacking( stateContext, true );
		SetIsBlocking( stateContext, false );
		if( ( !( isMeleeThrow ) && aimAssistRecord ) && aimAssistRecord.AimSnapOnAttack() )
		{
			scriptInterface.GetTargetingSystem().AimSnap( scriptInterface.executionOwner );
		}
		GameObject.PlayVoiceOver( scriptInterface.executionOwner, 'meleeAttack', 'Scripts:MeleeAttackGenericEvents' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Attack ) ) );
		GetAttackDataFromCurrentState( stateContext, scriptInterface, stateContext.GetIntParameter( 'meleeAttackNumber', true ), attackData );
		stateContext.SetPermanentBoolParameter( 'hasDeflectAnim', attackData.hasDeflectAnim, true );
		stateContext.SetPermanentBoolParameter( 'hasHitAnim', attackData.hasHitAnim, true );
		stateContext.SetConditionScriptableParameter( 'MeleeAttackData', attackData, true );
		stateContext.SetPermanentBoolParameter( 'VisionToggled', false, true );
		ForceDisableVisionMode( stateContext );
		stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
		canPerformLeap = !( m_enteredFromMeleeLeap );
		if( canPerformLeap )
		{
			uninterruptableHealingEvent = stateContext.GetPermanentBoolParameter( 'UninterruptableHealing' );
			ts = scriptInterface.GetTransactionSystem();
			healingItem = ts.GetItemData( scriptInterface.owner, EquipmentSystem.GetData( scriptInterface.owner ).GetActiveConsumable() );
			isBloodPumpEquipped = 'BloodPump' == TweakDBInterface.GetCName( ItemID.GetTDBID( healingItem.GetID() ) + T".cyberwareType", '' );
			if( ( !( isBloodPumpEquipped ) && uninterruptableHealingEvent.valid ) && uninterruptableHealingEvent.value )
			{
				canPerformLeap = false;
			}
		}
		m_finisherTarget = NULL;
		m_finisherCameraRotReseted = false;
		finisherTargetID = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		if( EntityID.IsDefined( finisherTargetID ) )
		{
			m_finisherTarget = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( scriptInterface.executionOwner.GetGame(), finisherTargetID ) ) );
		}
		if( canPerformLeap && IsMoveToTargetEnabled( attackData, aimAssistRecord ) )
		{
			attackRange = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.Range );
			adjustmentDistParam = aimAssistRecord.MoveToTargetSearchDistance();
			adjustmentTargetRadius = attackRange + adjustmentDistParam;
			shouldCheckForWeakspots = WeaponObject.IsBlade( GetWeaponObject( scriptInterface ).GetItemID() ) && PlayerDevelopmentSystem.GetData( scriptInterface.executionOwner ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Reflexes_Inbetween_Right_2 );
			adjustRadiusParam = aimAssistRecord.MoveToTargetDistanceIntoAttackRange();
			adjustRadius = MaxF( attackRange - adjustRadiusParam, 1.5 );
			leapTargetWithinAngle = GetStaticFloatParameterDefault( "leapTargetWithinAngle", 10.0 );
			targetObject = GetTargetObject( scriptInterface, adjustmentTargetRadius, shouldCheckForWeakspots, leapTargetWithinAngle );
			if( targetObject )
			{
				vecToTarget = targetObject.GetWorldPosition() - scriptInterface.executionOwner.GetWorldPosition();
				leapAngle = Vector4.ToRotation( vecToTarget );
				GetSlotTransformToTarget( scriptInterface, targetObject, attackData.attackEffectDelay, false, slotTransformPos );
				if( -( leapAngle.Pitch ) <= GetStaticFloatParameterDefault( "leapMaxPitch", 45.0 ) )
				{
					leapingWasSuccessful = AdjustPlayerPosition( stateContext, scriptInterface, targetObject, attackData.attackEffectDelay, adjustRadius, -1.0, '', false, slotTransformPos );
				}
				if( leapingWasSuccessful )
				{
					m_blockImpulseCreation = true;
					playerPerkDataBB = GameInstance.GetBlackboardSystem( scriptInterface.executionOwner.GetGame() ).Get( GetAllBlackboardDefs().PlayerPerkData );
					if( playerPerkDataBB )
					{
						distanceToTarget = Vector4.Distance( targetObject.GetWorldPosition(), scriptInterface.executionOwner.GetWorldPosition() );
						playerPerkDataBB.SetFloat( GetAllBlackboardDefs().PlayerPerkData.LeapedDistance, distanceToTarget, true );
						canMeleeLeap = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.CanMeleeLeap ) > 0.0;
						if( !( canMeleeLeap ) && shouldCheckForWeakspots )
						{
							PlayerStaminaHelpers.ModifyStaminaBasedOnLeapAttackDistance( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ), scriptInterface.IsOnGround(), distanceToTarget, adjustmentTargetRadius );
						}
					}
				}
			}
		}
		SendAnimationSlotData( stateContext, scriptInterface, attackData );
		scriptInterface.PushAnimationEvent( 'Attack' );
		stateContext.SetPermanentIntParameter( 'attackType', ( ( Int32 )( GetAttackType() ) ), true );
		super.OnEnter( stateContext, scriptInterface );
		if( attackData.standUpDelay == 0.0 )
		{
			stateContext.SetConditionBoolParameter( 'CrouchToggled', false, true );
			m_standUpSend = true;
		}
		if( attackData.incrementsCombo )
		{
			IncrementAttackNumber( scriptInterface, stateContext );
		}
		SetIsSafe( stateContext, false );
		SendDataTrackingRequest( scriptInterface, ETelemetryData.MeleeAttacksMade, 1 );
		GameInstance.GetTelemetrySystem( scriptInterface.owner.GetGame() ).LogWeaponAttackPerformed( GetWeaponObject( scriptInterface ) );
	}

	protected function SendAnimationSlotData( stateContext : StateContext, scriptInterface : StateGameScriptInterface, const attackData : MeleeAttackData ) : Bool
	{
		var slotData : AnimFeature_MeleeSlotData;
		slotData = new AnimFeature_MeleeSlotData;
		slotData.attackType = ( ( Int32 )( GetAttackType() ) );
		slotData.comboNumber = stateContext.GetIntParameter( 'meleeAttackNumber', true );
		slotData.startupDuration = attackData.startupDuration;
		slotData.activeDuration = attackData.activeDuration;
		slotData.recoverDuration = attackData.recoverDuration;
		slotData.activeHitDuration = attackData.activeHitDuration;
		slotData.recoverHitDuration = attackData.recoverHitDuration;
		scriptInterface.SetAnimationParameterFeature( 'MeleeSlotData', slotData );
		return true;
	}

	protected function ConsumeWeaponCharge( scriptInterface : StateGameScriptInterface, attackData : MeleeAttackData )
	{
		if( !( scriptInterface.GetStatPoolsSystem().IsStatPoolAdded( scriptInterface.ownerEntityID, gamedataStatPoolType.WeaponCharge ) ) )
		{
			return;
		}
		if( attackData.weaponChargeCost > 0.0 )
		{
			ChangeStatPoolValue( scriptInterface, scriptInterface.ownerEntityID, gamedataStatPoolType.WeaponCharge, -( attackData.weaponChargeCost ), true );
		}
	}

	public export override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var attackData : MeleeAttackData;
		var duration : Float;
		var weapon : WeaponObject;
		var interruptEvent : StateResultBool;
		var uninterruptableHealingEvent : StateResultBool;
		weapon = GetWeaponObject( scriptInterface );
		uninterruptableHealingEvent = stateContext.GetPermanentBoolParameter( 'UninterruptableHealing' );
		if( uninterruptableHealingEvent.valid && uninterruptableHealingEvent.value )
		{
			return;
		}
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		interruptEvent = stateContext.GetPermanentBoolParameter( 'InterruptMelee' );
		attackData = GetAttackData( stateContext );
		duration = GetInStateTime();
		if( ( !( m_standUpSend ) && ( attackData.standUpDelay > 0.0 ) ) && ( duration > attackData.standUpDelay ) )
		{
			stateContext.SetConditionBoolParameter( 'CrouchToggled', false, true );
			m_standUpSend = true;
		}
		UpdateIKData( scriptInterface, attackData );
		if( ( duration >= attackData.attackEffectDelay ) && !( m_attackCreated ) )
		{
			if( ( !( interruptEvent.valid ) || !( interruptEvent.value ) ) && ( !( uninterruptableHealingEvent.valid ) || !( uninterruptableHealingEvent.value ) ) )
			{
				stateContext.SetPermanentBoolParameter( 'MeleeAttackDone', true, true );
				if( m_finisherTarget )
				{
					if( WeaponObject.IsOfType( weapon.GetItemID(), gamedataItemType.Wea_Knife ) )
					{
						m_attackCreated = CreateMeleeAttackForFinisher( stateContext, scriptInterface, attackData );
					}
					RotateCameraToFinisherTarget( scriptInterface, attackData.attackEffectDuration );
				}
				if( !( m_attackCreated ) )
				{
					CreateMeleeAttack( stateContext, scriptInterface, attackData );
				}
				m_attackCreated = true;
				ConsumeStamina( scriptInterface, attackData );
			}
		}
		if( m_effect )
		{
			UpdateEffectPosition( stateContext, scriptInterface, attackData, duration );
			if( duration >= ( ( attackData.attackEffectDelay + attackData.attackEffectDuration ) + 0.1 ) )
			{
				m_effect = NULL;
				ConsumeWeaponCharge( scriptInterface, attackData );
			}
			if( weapon.WeaponHasTag( 'StrongArms' ) || weapon.WeaponHasTag( 'MantisBlades' ) )
			{
				ConsumeWeaponCharge( scriptInterface, attackData );
			}
		}
		if( ( m_finisherTarget && !( m_finisherCameraRotReseted ) ) && ( duration >= ( attackData.attackEffectDelay + attackData.attackEffectDuration ) ) )
		{
			m_finisherCameraRotReseted = true;
			ResetCameraRotation( scriptInterface );
		}
		if( m_trailCreated && ( duration >= attackData.trailStopDelay ) )
		{
			GetWeaponObject( scriptInterface ).StopCurrentMeleeTrailEffect( attackData.trailAttackSide );
		}
		else if( ( duration >= attackData.trailStartDelay ) && !( m_trailCreated ) )
		{
			GetWeaponObject( scriptInterface ).StartCurrentMeleeTrailEffect( attackData.trailAttackSide );
			m_trailCreated = true;
		}
		if( ShouldBlockMovementImpulseUpdate( timeDelta, attackData, stateContext, scriptInterface ) )
		{
			m_shouldBlockImpulseUpdate = true;
		}
		if( UpdateMovementImpulse( timeDelta, attackData, stateContext, scriptInterface ) )
		{
			m_blockImpulseCreation = true;
		}
		if( duration >= attackData.attackEffectDelay )
		{
			if( !( m_rumblePlayed ) )
			{
				PlayRumble( scriptInterface, GetStaticStringParameterDefault( "rumbleStrength", "light_fast" ) );
				m_rumblePlayed = true;
			}
		}
		if( GetAttackType() == EMeleeAttackType.Final )
		{
			ClearInputBuffer( stateContext );
		}
		else if( IsAttackWindowOpen( stateContext, scriptInterface ) )
		{
			UpdateMeleeInputBuffer( stateContext, scriptInterface );
		}
		if( duration >= attackData.attackWindowClosed )
		{
			stateContext.SetPermanentBoolParameter( 'MeleeAttackDone', false, true );
			SetIsAttacking( stateContext, false );
		}
	}

	protected function UpdateIKData( scriptInterface : StateGameScriptInterface, const attackData : MeleeAttackData )
	{
		var animFeature : AnimFeature_MeleeIKData;
		var target : GameObject;
		var slotPosition : Vector4;
		animFeature = new AnimFeature_MeleeIKData;
		target = GetTargetObject( scriptInterface );
		if( target )
		{
			if( AIActionHelper.GetTargetSlotPosition( target, 'Head', slotPosition ) )
			{
				animFeature.headPosition = slotPosition;
				animFeature.isValid = true;
			}
			if( AIActionHelper.GetTargetSlotPosition( target, 'Chest', slotPosition ) )
			{
				animFeature.chestPosition = slotPosition;
				animFeature.isValid = true;
			}
			animFeature.ikOffset.X = attackData.ikOffset.X;
			animFeature.ikOffset.Y = attackData.ikOffset.Y;
			animFeature.ikOffset.Z = attackData.ikOffset.Z;
		}
		scriptInterface.SetAnimationParameterFeature( 'MeleeIKData', animFeature );
	}

	protected function ShouldBlockMovementImpulseUpdate( timeDelta : Float, attackData : MeleeAttackData, stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( scriptInterface.IsOnGround() ) && ( ( attackData.forwardImpulse < 0.0 ) || ( attackData.forwardImpulse > 0.0 ) ) )
		{
			return true;
		}
		if( scriptInterface.GetOwnerStateVectorParameterFloat( physicsStateValue.LinearSpeed ) >= 10.0 )
		{
			return true;
		}
		if( scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsPlayerInsideMovingElevator ) )
		{
			return true;
		}
		return false;
	}

	protected function UpdateMovementImpulse( timeDelta : Float, attackData : MeleeAttackData, stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( m_blockImpulseCreation )
		{
			return true;
		}
		if( IsPlayerExhausted( scriptInterface ) )
		{
			return true;
		}
		if( m_shouldBlockImpulseUpdate )
		{
			return false;
		}
		if( ( ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.LocomotionDetailed ) == ( ( Int32 )( gamePSMDetailedLocomotionStates.Slide ) ) ) || ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.LocomotionDetailed ) == ( ( Int32 )( gamePSMDetailedLocomotionStates.SlideFall ) ) ) ) || ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.LocomotionDetailed ) == ( ( Int32 )( gamePSMDetailedLocomotionStates.Felled ) ) ) )
		{
			return false;
		}
		if( GetInStateTime() < attackData.impulseDelay )
		{
			return false;
		}
		if( !( CheckItemType( scriptInterface, gamedataItemType.Cyb_MantisBlades ) ) )
		{
			if( ( ( attackData.cameraSpaceImpulse > 0.0 ) || ( attackData.forwardImpulse > 0.0 ) ) && !( IsPlayerInputDirectedForward( scriptInterface ) ) )
			{
				return true;
			}
			if( IsCameraPitchAcceptable( stateContext, scriptInterface, GetStaticFloatParameterDefault( "cameraPitchThreshold", -30.0 ) ) )
			{
				return true;
			}
		}
		if( !( AdjustAttackPosition( scriptInterface, stateContext, attackData ) ) )
		{
			AddAttackImpulse( scriptInterface, stateContext, attackData );
		}
		return true;
	}

	protected function UpdateEffectPosition( stateContext : StateContext, scriptInterface : StateGameScriptInterface, attackData : MeleeAttackData, duration : Float )
	{
		var startPosition : Vector4;
		var cameraWorldTransform : Transform;
		var attackRange : Float;
		var attackProgress : Float;
		var colliderBox : Vector4;
		var playerPuppet : PlayerPuppet;
		var mountedBike : BikeObject;
		var bikeVelMag : Float;
		var attackDirectionWorld : Vector4;
		cameraWorldTransform = GetCameraTransformForMelee( scriptInterface );
		EffectData.GetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.box, colliderBox );
		attackRange = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.Range );
		attackProgress = ( duration - attackData.attackEffectDelay ) / attackData.attackEffectDuration;
		if( ( attackProgress > 0.5 ) && attackData.useMiddlePosition )
		{
			startPosition = attackData.middlePosition - ( attackData.endPosition - attackData.middlePosition );
			if( !( m_effectPositionUpdated ) )
			{
				m_effectPositionUpdated = false;
				attackDirectionWorld = Transform.TransformPoint( cameraWorldTransform, attackData.endPosition ) - Transform.TransformPoint( cameraWorldTransform, attackData.middlePosition );
				attackDirectionWorld *= 2.0;
				attackDirectionWorld.W = 0.0;
				EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.forward, Vector4.Normalize( attackDirectionWorld ) );
				EffectData.SetFloat( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.range, Vector4.Length( attackDirectionWorld ) );
			}
		}
		else
		{
			startPosition = attackData.startPosition;
		}
		playerPuppet = ( ( PlayerPuppet )( scriptInterface.executionOwner ) );
		mountedBike = ( ( BikeObject )( playerPuppet.GetMountedVehicle() ) );
		if( mountedBike )
		{
			attackRange = MaxF( GetStaticFloatParameterDefault( "mountedMinBaseAttackRange", 2.0 ), attackRange );
			bikeVelMag = Vector4.Length( mountedBike.GetLinearVelocity() );
			attackRange *= ProportionalClampF( 0.0, 20.0, bikeVelMag, 1.0, 1.5 );
			colliderBox.Y = attackRange;
			EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.box, colliderBox );
		}
		if( !( attackData.isThrust ) )
		{
			startPosition.Y += ( attackRange * 0.5 );
		}
		EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, Transform.TransformPoint( cameraWorldTransform, startPosition ) );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var interruptEvent : StateResultBool;
		var attackData : MeleeAttackData;
		attackData = GetAttackData( stateContext );
		interruptEvent = stateContext.GetPermanentBoolParameter( 'InterruptMelee' );
		scriptInterface.SetAnimationParameterFloat( 'safe', 0.0 );
		stateContext.SetPermanentBoolParameter( 'safe', false, true );
		if( interruptEvent.value )
		{
			GetAttackDataFromCurrentState( stateContext, scriptInterface, stateContext.GetIntParameter( 'meleeAttackNumber', true ), attackData );
			stateContext.SetPermanentFloatParameter( 'meleeRecoveryDuration', attackData.idleTransitionTime - GetInStateTime(), true );
		}
		stateContext.SetPermanentBoolParameter( 'InterruptMelee', false, true );
		stateContext.SetPermanentBoolParameter( 'IsInMeleeLeapState', false, true );
		m_enteredFromMeleeLeap = false;
		super.OnExit( stateContext, scriptInterface );
		ClearDebugText( scriptInterface, m_textLayer );
		ClearLeapedDistanceBlackboardValue( scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_enteredFromMeleeLeap = false;
		ClearDebugText( scriptInterface, m_textLayer );
		ClearLeapedDistanceBlackboardValue( scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

	private constexpr function ClearLeapedDistanceBlackboardValue( const scriptInterface : StateGameScriptInterface )
	{
		var playerPerkDataBB : IBlackboard;
		playerPerkDataBB = GameInstance.GetBlackboardSystem( scriptInterface.executionOwner.GetGame() ).Get( GetAllBlackboardDefs().PlayerPerkData );
		if( playerPerkDataBB )
		{
			playerPerkDataBB.SetFloat( GetAllBlackboardDefs().PlayerPerkData.LeapedDistance, 0.0, true );
		}
	}

	private function RotateCameraToFinisherTarget( scriptInterface : StateGameScriptInterface, rotateDuration : Float )
	{
		var aimRequest : AimRequest;
		var slotTransform : WorldTransform;
		var maxDuration : Float;
		maxDuration = rotateDuration;
		if( m_finisherTarget && GetClosestSlotTransform( scriptInterface, false, m_finisherTarget.GetSlotComponent(), slotTransform ) )
		{
			maxDuration += TDB.GetFloat( T"playerStateMachineMelee.meleeLeap.attackStartupDuration" );
			aimRequest = FillLookAtRequestData( WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) ), rotateDuration, maxDuration * 2.0 );
			GameInstance.GetTargetingSystem( scriptInterface.executionOwner.GetGame() ).LookAt( scriptInterface.executionOwner, aimRequest );
		}
	}

	private function ResetCameraRotation( scriptInterface : StateGameScriptInterface )
	{
		var aimRequest : AimRequest;
		aimRequest = FillLookAtRequestData( Vector4(), 0.0, 0.0 );
		GameInstance.GetTargetingSystem( scriptInterface.executionOwner.GetGame() ).LookAt( scriptInterface.executionOwner, aimRequest );
	}

	private function CreateMeleeAttackForFinisher( stateContext : StateContext, scriptInterface : StateGameScriptInterface, out attackData : MeleeAttackData ) : Bool
	{
		var sweepBoxColliderSize : Float;
		var slotTransform : WorldTransform;
		var slotTransformPos : Vector4;
		var vecToTarget : Vector4;
		var colliderBox : Vector4;
		var time : Float;
		var cameraWorldTransform : Transform;
		var worldToLocal : Transform;
		var newForward : Vector4;
		sweepBoxColliderSize = 0.40000001;
		if( m_finisherTarget && GetClosestSlotTransform( scriptInterface, false, m_finisherTarget.GetSlotComponent(), slotTransform ) )
		{
			cameraWorldTransform = GetCameraTransformForMelee( scriptInterface );
			slotTransformPos = WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) );
			cameraWorldTransform.position += attackData.startPosition;
			vecToTarget = slotTransformPos - cameraWorldTransform.position;
			vecToTarget = 2.0 * Vector4.Normalize( vecToTarget );
			worldToLocal = Transform.GetInverse( cameraWorldTransform );
			newForward = Transform.TransformPoint( worldToLocal, cameraWorldTransform.position + vecToTarget );
			attackData.endPosition = newForward;
			time = attackData.attackEffectDuration;
			colliderBox.X = sweepBoxColliderSize;
			colliderBox.Y = sweepBoxColliderSize;
			colliderBox.Z = sweepBoxColliderSize;
			SpawnAttackGameEffect( stateContext, scriptInterface, attackData.startPosition, attackData.middlePosition, attackData.endPosition, time, colliderBox, attackData );
			return true;
		}
		return false;
	}

	protected function CreateMeleeAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface, attackData : MeleeAttackData )
	{
		var sweepBoxColliderSize : Float;
		var startPosition : Vector4;
		var middlePosition : Vector4;
		var endPosition : Vector4;
		var time : Float;
		var colliderBox : Vector4;
		var attackRange : Float;
		var weaponType : gamedataItemType;
		sweepBoxColliderSize = 0.25;
		startPosition = attackData.startPosition;
		middlePosition = attackData.middlePosition;
		endPosition = attackData.endPosition;
		attackRange = scriptInterface.GetStatsSystem().GetStatValue( ( ( EntityID )( scriptInterface.ownerEntityID ) ), gamedataStatType.Range );
		colliderBox.X = sweepBoxColliderSize;
		colliderBox.Y = sweepBoxColliderSize;
		colliderBox.Z = sweepBoxColliderSize;
		if( attackData.isThrust )
		{
			endPosition.Y = attackRange;
			weaponType = GetWeaponType( scriptInterface );
			switch( weaponType )
			{
				case gamedataItemType.Cyb_StrongArms:
					if( HasOnePunchManStatusEffect( scriptInterface ) )
					{
						colliderBox.X = TDB.GetFloat( T"Items.StrongArms.colliderBoxWidth" );
						colliderBox.Z = TDB.GetFloat( T"Items.StrongArms.colliderBoxHeight" );
					}
				break;
				case gamedataItemType.Cyb_MantisBlades:
					if( HasGrandFinaleStatusEffect( scriptInterface ) )
					{
						colliderBox.X = TDB.GetFloat( T"Items.MantisBlades.colliderBoxWidth" );
						colliderBox.Z = TDB.GetFloat( T"Items.MantisBlades.colliderBoxHeight" );
					}
				break;
				default:
					break;
			}
		}
		else
		{
			startPosition.Y += ( attackRange * 0.5 );
			middlePosition.Y += ( attackRange * 0.5 );
			endPosition.Y += ( attackRange * 0.5 );
			colliderBox.Y = attackRange;
		}
		time = attackData.attackEffectDuration;
		SpawnAttackGameEffect( stateContext, scriptInterface, startPosition, middlePosition, endPosition, time, colliderBox, attackData );
		GameObject.StartCooldown( scriptInterface.owner, GetMeleeAttackCooldownName(), attackData.attackWindowClosed - attackData.attackEffectDelay, true );
	}

	protected function GetCameraTransformForMelee( scriptInterface : StateGameScriptInterface ) : Transform
	{
		var cameraWorldTransform : Transform;
		var playerPuppet : PlayerPuppet;
		var fppL2W : Matrix;
		var cameraEulerAngles : EulerAngles;
		var toTarget : Vector4;
		var targetComponent : weak< TargetingComponent >;
		var bikeEulerAngles : EulerAngles;
		var mountedBike : BikeObject;
		var bikeVelMag : Float;
		var pitchConstraint : Float;
		playerPuppet = ( ( PlayerPuppet )( scriptInterface.executionOwner ) );
		fppL2W = playerPuppet.GetFPPCameraComponent().GetLocalToWorld();
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		Transform.SetPosition( cameraWorldTransform, Matrix.GetTranslation( fppL2W ) );
		targetComponent = GameInstance.GetTargetingSystem( playerPuppet.GetGame() ).GetTrackedTargetComponent( playerPuppet );
		if( targetComponent )
		{
			toTarget = Matrix.GetTranslation( targetComponent.GetLocalToWorld() ) - cameraWorldTransform.position;
		}
		if( IsMountedTPPAttack() )
		{
			if( targetComponent )
			{
				cameraEulerAngles = Quaternion.ToEulerAngles( Quaternion.BuildFromDirectionVector( toTarget ) );
			}
			else
			{
				cameraEulerAngles = Quaternion.ToEulerAngles( cameraWorldTransform.orientation );
			}
			playerPuppet = ( ( PlayerPuppet )( scriptInterface.executionOwner ) );
			mountedBike = ( ( BikeObject )( playerPuppet.GetMountedVehicle() ) );
			if( mountedBike )
			{
				bikeEulerAngles = Vector4.ToRotation( mountedBike.GetWorldForward() );
			}
			playerPuppet = ( ( PlayerPuppet )( scriptInterface.executionOwner ) );
			mountedBike = ( ( BikeObject )( playerPuppet.GetMountedVehicle() ) );
			if( mountedBike )
			{
				bikeVelMag = Vector4.Length( mountedBike.GetLinearVelocity() );
				pitchConstraint = ProportionalClampF( 0.0, 20.0, bikeVelMag, 20.0, 12.0 );
			}
			else
			{
				pitchConstraint = 20.0;
			}
			if( mountedBike.IsLeaningOnOneWheel() )
			{
				cameraEulerAngles.Pitch = ( cameraEulerAngles.Pitch + 3.0 ) / 2.0;
			}
			else
			{
				cameraEulerAngles.Pitch = ClampF( ( cameraEulerAngles.Pitch + bikeEulerAngles.Pitch ) / 2.0, -( pitchConstraint ), pitchConstraint ) - bikeEulerAngles.Pitch;
			}
			if( mountedBike )
			{
				cameraWorldTransform.position.Z -= ( ProportionalClampF( 5.0, 20.0, bikeVelMag, 0.0, 0.25 ) * ProportionalClampF( -( pitchConstraint ), -( pitchConstraint ) / 2.0, cameraEulerAngles.Pitch, 1.0, 0.0 ) );
			}
			cameraEulerAngles.Yaw = ClampF( m_tppYawOverride, GetStaticFloatParameterDefault( "mountedMinYaw", -120.0 ), GetStaticFloatParameterDefault( "mountedMaxYaw", 120.0 ) );
			cameraEulerAngles.Yaw += playerPuppet.GetWorldYaw();
			if( cameraEulerAngles.Yaw > 180.0 )
			{
				cameraEulerAngles.Yaw -= 360.0;
			}
			else if( cameraEulerAngles.Yaw < -180.0 )
			{
				cameraEulerAngles.Yaw += 360.0;
			}
			Transform.SetOrientation( cameraWorldTransform, EulerAngles.ToQuat( cameraEulerAngles ) );
		}
		else if( targetComponent )
		{
			Transform.SetOrientation( cameraWorldTransform, Quaternion.BuildFromDirectionVector( toTarget ) );
		}
		return cameraWorldTransform;
	}

	protected function SpawnAttackGameEffect( stateContext : StateContext, scriptInterface : StateGameScriptInterface, startPosition : Vector4, middlePosition : Vector4, endPosition : Vector4, time : Float, colliderBox : Vector4, attackData : MeleeAttackData ) : Bool
	{
		var effect : EffectInstance;
		var attackStartPositionWorld : Vector4;
		var attackDirectionWorld : Vector4;
		var cameraWorldTransform : Transform;
		var weapon : WeaponObject;
		var meleeAttack : Attack_GameEffect;
		var success : Bool;
		success = false;
		cameraWorldTransform = GetCameraTransformForMelee( scriptInterface );
		attackStartPositionWorld = Transform.TransformPoint( cameraWorldTransform, startPosition );
		attackStartPositionWorld.W = 0.0;
		m_effectPositionUpdated = !( attackData.useMiddlePosition );
		if( attackData.useMiddlePosition )
		{
			attackDirectionWorld = Transform.TransformPoint( cameraWorldTransform, middlePosition ) - attackStartPositionWorld;
			attackDirectionWorld *= 2.0;
		}
		else
		{
			attackDirectionWorld = Transform.TransformPoint( cameraWorldTransform, endPosition ) - attackStartPositionWorld;
		}
		attackDirectionWorld.W = 0.0;
		weapon = ( ( WeaponObject )( scriptInterface.owner ) );
		if( weapon )
		{
			meleeAttack = ( ( Attack_GameEffect )( weapon.GetCurrentAttack() ) );
			if( meleeAttack )
			{
				effect = meleeAttack.PrepareAttack( scriptInterface.executionOwner );
				EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.box, colliderBox );
				EffectData.SetFloat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.duration, time );
				EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, attackStartPositionWorld );
				EffectData.SetQuat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.rotation, Transform.GetOrientation( cameraWorldTransform ) );
				EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.forward, Vector4.Normalize( attackDirectionWorld ) );
				EffectData.SetFloat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.range, Vector4.Length( attackDirectionWorld ) );
				EffectData.SetInt( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.attackNumber, stateContext.GetIntParameter( 'meleeAttackNumber', true ) );
				EffectData.SetName( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.impactOrientationSlot, attackData.impactFxSlot );
				EffectData.SetFloat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.charge, WeaponObject.GetWeaponChargeNormalized( weapon ) );
				EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.ignoreMountedVehicleCollision, true );
				if( GetAttackType() == EMeleeAttackType.Strong || scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToVehicle ) )
				{
					EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.meleeCleave, true );
				}
				if( IsMountedTPPAttack() )
				{
					EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.inTPP, true );
				}
				EffectData.SetVariant( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.fxPackage, ( ( WeaponObject )( scriptInterface.owner ) ).GetFxPackage() );
				EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.playerOwnedWeapon, true );
				m_effect = effect;
				success = meleeAttack.StartAttack();
			}
		}
		return success;
	}

	protected function BroadcastStimuli( scriptInterface : StateGameScriptInterface, radius : Float )
	{
		var effect : EffectInstance;
		var position : Vector4;
		var stimuliEvent : StimuliEvent;
		position = scriptInterface.executionOwner.GetWorldPosition();
		stimuliEvent = new StimuliEvent;
		stimuliEvent.sourcePosition = position;
		stimuliEvent.name = 'run';
		effect = GameInstance.GetGameEffectSystem( scriptInterface.GetGame() ).CreateEffectStatic( 'stimuli', 'stimuli_range', scriptInterface.executionOwner, scriptInterface.owner );
		EffectData.SetVariant( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.stimuliEvent, stimuliEvent );
		EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, position );
		EffectData.SetFloat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.radius, radius );
		EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.stimuliRaycastTest, true );
		GameInstance.GetStimuliSystem( scriptInterface.owner.GetGame() ).BroadcastStimuli( effect );
	}

	protected const function GetAttackData( const stateContext : StateContext ) : MeleeAttackData
	{
		return ( ( MeleeAttackData )( stateContext.GetConditionScriptableParameter( 'MeleeAttackData' ) ) );
	}

	protected const function IsAttackWindowOpen( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var attackData : MeleeAttackData;
		var inStateTime : Float;
		attackData = GetAttackData( stateContext );
		inStateTime = GetInStateTime();
		if( inStateTime >= attackData.attackWindowOpen )
		{
			return true;
		}
		return false;
	}

}

class MeleeComboAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( WantsToLightAttack( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( super.EnterCondition( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( m_driverCombatListener.IsMountedInTPP() && GameInstance.GetPhotoModeSystem( scriptInterface.executionOwner.GetGame() ).IsPhotoModeActive() )
		{
			return false;
		}
		return true;
	}

}

class MeleeComboAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Combo;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.ComboAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
		IncrementTotalComboAttackNumber( scriptInterface, stateContext );
	}

}

class MeleeMountedComboAttackDecisions extends MeleeComboAttackDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( m_driverCombatListener.IsMountedInTPP() ) )
		{
			return false;
		}
		if( !( super.EnterCondition( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeMountedComboAttackEvents extends MeleeComboAttackEvents
{

	protected const override final function IsMountedTPPAttack() : Bool
	{
		return true;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetTPPYawOverride( stateContext );
		super.OnEnter( stateContext, scriptInterface );
	}

}

class MeleeFinalAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( CheckIfFinalAttack( scriptInterface, stateContext ) ) )
		{
			return false;
		}
		if( WantsToStrongAttack( stateContext, scriptInterface ) )
		{
			return false;
		}
		if( !( WantsToLightAttack( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( super.EnterCondition( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeFinalAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Final;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		stateContext.SetPermanentBoolParameter( 'finalAttack', true, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.FinalAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		stateContext.SetPermanentBoolParameter( 'finalAttack', false, true );
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeMountedFinalAttackDecisions extends MeleeFinalAttackDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( m_driverCombatListener.IsMountedInTPP() ) )
		{
			return false;
		}
		if( !( super.EnterCondition( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeMountedFinalAttackEvents extends MeleeFinalAttackEvents
{

	protected const override final function IsMountedTPPAttack() : Bool
	{
		return true;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetTPPYawOverride( stateContext );
		super.OnEnter( stateContext, scriptInterface );
	}

}

class MeleeSafeAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( WantsToLightAttack( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( HasAttackRecord( scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeSafeAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Safe;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.SafeAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeStrongAttackDecisions extends MeleeAttackGenericDecisions
{

	protected const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.GetConditionBool( 'StrongMeleeAttackPressed' ) )
		{
			return true;
		}
		return false;
	}

	protected const override function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var isBossFinisher : StateResultBool;
		var finisherTargetID : EntityID;
		isBossFinisher = stateContext.GetPermanentBoolParameter( 'bossFinisherAttack' );
		finisherTargetID = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		if( ( !( isBossFinisher.valid ) || !( isBossFinisher.value ) ) && EntityID.IsDefined( finisherTargetID ) )
		{
			return true;
		}
		return super.ExitCondition( stateContext, scriptInterface );
	}

}

class MeleeStrongAttackEvents extends MeleeAttackGenericEvents
{
	var m_slowMoSet : Bool;
	var m_crouchedAfterLeapAttack : Bool;

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Strong;
	}

	protected function LeapToTarget( stateContext : StateContext, scriptInterface : StateGameScriptInterface, target : weak< GameObject > )
	{
		var slideDuration : Float;
		var cameraWorldTransform : Transform;
		var leapAngle : EulerAngles;
		var targetPos : Vector4;
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		leapAngle = Transform.ToEulerAngles( cameraWorldTransform );
		if( leapAngle.Pitch > GetStaticFloatParameterDefault( "noTargetMaxPitch", 45.0 ) )
		{
			leapAngle.Pitch = GetStaticFloatParameterDefault( "noTargetMaxPitch", 45.0 );
			Transform.SetOrientationEuler( cameraWorldTransform, leapAngle );
		}
		slideDuration = GetStaticFloatParameterDefault( "dashAttackSlideDuration", 0.30000001 );
		targetPos = target.GetWorldPosition();
		if( GetWeaponType( scriptInterface ) == gamedataItemType.Cyb_StrongArms )
		{
			targetPos = TargetPrediction( targetPos, ( ( ScriptedPuppet )( target ) ), slideDuration, 1.0 );
		}
		RequestPlayerPositionAdjustment( stateContext, scriptInterface, NULL, slideDuration, 0.0, -1.0, targetPos );
	}

	public function ApplyRelicMeleewareDamageToTarget( scriptInterface : StateGameScriptInterface, playerPuppet : weak< GameObject >, target : NPCPuppet )
	{
		var newHitEvent : gameHitEvent;
		var applyDamageEvt : ApplyRelicMeleewareDamageOnNPCEvent;
		var attackContext : AttackInitContext;
		var attackComputed : gameAttackComputed;
		var attack : IAttack;
		var weapon : WeaponObject;
		var attackDelay : Float;
		weapon = GameObject.GetActiveWeapon( playerPuppet );
		attackComputed = new gameAttackComputed;
		newHitEvent = new gameHitEvent;
		newHitEvent.attackData = new AttackData;
		newHitEvent.target = target;
		newHitEvent.attackComputed = attackComputed;
		if( CheckItemType( scriptInterface, gamedataItemType.Cyb_StrongArms ) )
		{
			attackDelay = TDB.GetFloat( T"Items.StrongArms.onePunchAttackDelay" );
			attackContext.record = TweakDBInterface.GetAttackRecord( T"StrongArmsAttacks.OnePunchAttackTargetDamage" );
		}
		else
		{
			attackDelay = TDB.GetFloat( T"Items.MantisBlades.grandFinaleAttackDelay" );
			attackContext.record = TweakDBInterface.GetAttackRecord( T"MantisBladesAttacks.GrandFinaleAttackTargetDamage" );
		}
		attackContext.instigator = playerPuppet;
		attackContext.source = playerPuppet;
		attackContext.weapon = weapon;
		attack = IAttack.Create( attackContext );
		newHitEvent.attackData.SetAttackDefinition( attack );
		if( CheckItemType( scriptInterface, gamedataItemType.Cyb_StrongArms ) )
		{
			newHitEvent.attackData.AddFlag( hitFlag.OnePunch, 'OnePunch' );
		}
		else
		{
			newHitEvent.attackData.AddFlag( hitFlag.GrandFinale, 'GrandFinale' );
		}
		newHitEvent.attackData.AddFlag( hitFlag.RelicGoldenNumbers, 'RelicGoldenNumbers' );
		newHitEvent.attackData.SetSource( playerPuppet );
		newHitEvent.attackData.SetInstigator( playerPuppet );
		newHitEvent.attackData.SetWeapon( weapon );
		applyDamageEvt = new ApplyRelicMeleewareDamageOnNPCEvent;
		applyDamageEvt.newHitEvent = newHitEvent;
		applyDamageEvt.target = ( ( NPCPuppet )( target ) );
		applyDamageEvt.weapon = GetWeaponObject( scriptInterface );
		applyDamageEvt.weaponType = GetWeaponType( scriptInterface );
		GameInstance.GetDelaySystem( target.GetGame() ).DelayEvent( target, applyDamageEvt, attackDelay, true );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var weaponType : gamedataItemType;
		var weapon : WeaponObject;
		var target : NPCPuppet;
		var crosshairTarget : NPCPuppet;
		var player : weak< GameObject >;
		var npcdata : IBlackboard;
		var crossHairNPC : NPCNextToTheCrosshair;
		var isBossFinisher : StateResultBool;
		var targetSearchQuery : TargetSearchQuery;
		var maxDistToTarget : Float;
		var isFinisher : Bool;
		var playerPerkDataBB : IBlackboard;
		var finisherTarget : EntityID;
		var leapTarget : EntityID;
		var LeapPosition : Vector4;
		weapon = GameObject.GetActiveWeapon( scriptInterface.executionOwner );
		isBossFinisher = stateContext.GetPermanentBoolParameter( 'bossFinisherAttack' );
		playerPerkDataBB = GameInstance.GetBlackboardSystem( scriptInterface.executionOwner.GetGame() ).Get( GetAllBlackboardDefs().PlayerPerkData );
		finisherTarget = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		leapTarget = playerPerkDataBB.GetEntityID( GetAllBlackboardDefs().PlayerPerkData.LeapTarget );
		LeapPosition = playerPerkDataBB.GetVector4( GetAllBlackboardDefs().PlayerPerkData.LeapPosition );
		stateContext.SetPermanentBoolParameter( 'strongAttack', true, true );
		isFinisher = EntityID.IsDefined( finisherTarget );
		if( ( HasNewSpyAttackStatFlag( scriptInterface ) && WeaponIsCharged( scriptInterface ) ) && !( isFinisher ) )
		{
			weaponType = GetWeaponType( scriptInterface );
			player = scriptInterface.executionOwner;
			target = ( ( NPCPuppet )( GameInstance.GetTargetingSystem( player.GetGame() ).GetLookAtObject( player, true ) ) );
			if( !( target ) )
			{
				maxDistToTarget = GetStaticFloatParameterDefault( "maxDistToTarget", 5.0 );
				targetSearchQuery = TSQ_NPC();
				targetSearchQuery.testedSet = TargetingSet.ClearlyVisible;
				targetSearchQuery.maxDistance = maxDistToTarget;
				targetSearchQuery.filterObjectByDistance = true;
				target = ( ( NPCPuppet )( GetTargetObject( scriptInterface, targetSearchQuery, maxDistToTarget ) ) );
			}
			switch( weaponType )
			{
				case gamedataItemType.Cyb_StrongArms:
					if( IsCloseEnoughForOnePunch( scriptInterface, target ) )
					{
						LeapToTarget( stateContext, scriptInterface, target );
						ApplyRelicMeleewareDamageToTarget( scriptInterface, player, target );
						StatusEffectHelper.ApplyStatusEffect( player, GetGorillaArmsSpyTreeStatusEffectID() );
						SetAttackNumber( stateContext, TDB.GetInt( T"Items.StrongArms.specialAttackNumber" ) );
					}
				break;
				case gamedataItemType.Cyb_MantisBlades:
					if( PlayerLeapedToTargetCheck( scriptInterface ) )
					{
						weapon = GetWeaponObject( scriptInterface );
						npcdata = GameInstance.GetBlackboardSystem( scriptInterface.GetGame() ).Get( GetAllBlackboardDefs().UI_NPCNextToTheCrosshair );
						crossHairNPC = ( ( NPCNextToTheCrosshair )( npcdata.GetVariant( GetAllBlackboardDefs().UI_NPCNextToTheCrosshair.NameplateData ) ) );
						crosshairTarget = ( ( NPCPuppet )( crossHairNPC.npc ) );
						target = ( ( NPCPuppet )( GameInstance.FindEntityByID( scriptInterface.executionOwner.GetGame(), leapTarget ) ) );
						if( crosshairTarget && ( ( !( target ) || !( GameInstance.GetTargetingSystem( player.GetGame() ).IsVisibleTarget( player, target ) ) ) || ( Vector4.DistanceSquared( crosshairTarget.GetWorldPosition(), LeapPosition ) < Vector4.DistanceSquared( target.GetWorldPosition(), LeapPosition ) ) ) )
						{
							target = crosshairTarget;
						}
						ApplyRelicMeleewareDamageToTarget( scriptInterface, player, target );
						GameObjectEffectHelper.StartEffectEvent( weapon, 'RelicTree_Meleeware_Attack_MantisBlades' );
						SetAttackNumber( stateContext, TDB.GetInt( T"Items.MantisBlades.specialAttackNumber" ) );
						StatusEffectHelper.ApplyStatusEffect( player, GetMantisBladesSpecialAttackSpyTreeStatusEffectID() );
					}
				break;
				case gamedataItemType.Cyb_NanoWires:
					if( !( HasMonowireWithQuickhackSelected( scriptInterface ) ) )
					{
						break;
					}
				SetAttackNumber( stateContext, TDB.GetInt( T"Items.NanoWires.specialAttackNumber" ) );
				GameObject.PlaySound( player, 'w_cyb_nanowire_spy_perk_attack' );
				break;
				default:
					break;
			}
		}
		else
		{
			SetAttackNumber( stateContext, stateContext.GetIntParameter( 'meleeAttackNumber', true ) % ( ( Int32 )( 2 ) ) );
		}
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.StrongAttack ) ) );
		if( isBossFinisher.valid && isBossFinisher.value )
		{
			m_slowMoSet = false;
			m_blockImpulseCreation = true;
			GameObject.PlaySound( scriptInterface.executionOwner, GetStaticCNameParameterDefault( "slowMoStartSound", '' ) );
			PlayRumble( scriptInterface, GetStaticStringParameterDefault( "rumbleOnStartStrength", "light_fast" ) );
			GameInstance.GetRazerChromaEffectsSystem( scriptInterface.GetGame() ).PlayAnimation( 'SlowMotion', true );
		}
		if( m_enteredFromMeleeLeap && stateContext.GetBoolParameter( 'crouchAfterLeaping', true ) )
		{
			m_crouchedAfterLeapAttack = true;
			scriptInterface.SetAnimationParameterFloat( 'crouch', 1.0 );
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	public function OnEnterFromMeleeLeap( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_enteredFromMeleeLeap = true;
		OnEnter( stateContext, scriptInterface );
	}

	public override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var slowMoStart : Float;
		var isBossFinisher : StateResultBool;
		slowMoStart = GetStaticFloatParameterDefault( "slowMoStart", 0.1 );
		isBossFinisher = stateContext.GetPermanentBoolParameter( 'bossFinisherAttack' );
		if( ( ( ( !( m_slowMoSet ) && isBossFinisher.valid ) && isBossFinisher.value ) && ( GetInStateTime() > slowMoStart ) ) && !( IsTimeDilationActive( stateContext, scriptInterface, '' ) ) )
		{
			scriptInterface.GetTimeSystem().SetTimeDilation( 'deflect', GetStaticFloatParameterDefault( "slowMoAmount", 0.1 ), GetStaticFloatParameterDefault( "slowDuration", 0.1 ), GetStaticCNameParameterDefault( "slowMoEaseIn", 'Linear' ), GetStaticCNameParameterDefault( "slowMoEaseOut", 'Linear' ) );
			m_slowMoSet = true;
		}
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var isBossFinisher : StateResultBool;
		var playerPuppet : PlayerPuppet;
		isBossFinisher = stateContext.GetPermanentBoolParameter( 'bossFinisherAttack' );
		playerPuppet = ( ( PlayerPuppet )( scriptInterface.executionOwner ) );
		if( isBossFinisher.value && playerPuppet )
		{
			FinisherEndEvents.ApplyFinisherBuffs( playerPuppet, false );
		}
		stateContext.SetPermanentBoolParameter( 'strongAttack', false, true );
		stateContext.SetPermanentBoolParameter( 'enableVaultFromleapAttack', false, true );
		stateContext.SetPermanentBoolParameter( 'crouchAfterLeaping', false, true );
		SetBlackboardBoolVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeLeap, false );
		stateContext.SetPermanentBoolParameter( 'bossFinisherAttack', false, true );
		GameInstance.GetRazerChromaEffectsSystem( scriptInterface.GetGame() ).StopAnimation( 'SlowMotion' );
		if( m_enteredFromMeleeLeap )
		{
			scriptInterface.localBlackboard.SetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget, EMPTY_ENTITY_ID() );
		}
		if( m_crouchedAfterLeapAttack )
		{
			scriptInterface.SetAnimationParameterFloat( 'crouch', 0.0 );
		}
		m_slowMoSet = false;
		m_crouchedAfterLeapAttack = false;
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExit( stateContext, scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

	protected function OnExitToMeleeComboAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExit( stateContext, scriptInterface );
	}

}

class MeleeMountedStrongAttackDecisions extends MeleeStrongAttackDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( m_driverCombatListener.IsMountedInTPP() ) )
		{
			return false;
		}
		if( super.EnterCondition( stateContext, scriptInterface ) )
		{
			return true;
		}
		return false;
	}

}

class MeleeMountedStrongAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Strong;
	}

	protected const override final function IsMountedTPPAttack() : Bool
	{
		return true;
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetAttackNumber( stateContext, stateContext.GetIntParameter( 'meleeAttackNumber', true ) % ( ( Int32 )( 2 ) ) );
		SetTPPYawOverride( stateContext );
		super.OnEnter( stateContext, scriptInterface );
	}

}

class MeleeDeflectDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsBlockPressed( stateContext, scriptInterface ) )
		{
			if( !( CanWeaponDeflect( stateContext, scriptInterface ) ) )
			{
				return false;
			}
			if( HasMeleeTargeting( stateContext, scriptInterface ) )
			{
				return false;
			}
			if( GetStaticBoolParameterDefault( "disabled", false ) )
			{
				return false;
			}
			if( GameObject.IsCooldownActive( scriptInterface.owner, 'Deflect' ) )
			{
				return false;
			}
			return true;
		}
		return false;
	}

	protected export const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsDeflectSuccessful( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( ShouldInterruptHoldStates( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "duration", 0.40000001 ) )
		{
			return true;
		}
		if( IsBlockHeld( stateContext, scriptInterface ) )
		{
			return AnyMeleeAttackPressed( scriptInterface ) || stateContext.GetConditionBool( 'LightMeleeAttackPressed' );
		}
		return false;
	}

	public function ToMeleeHold( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ( ShouldHold( stateContext, scriptInterface ) && ToMeleeComboAttack( stateContext, scriptInterface ) ) && !( IsPlayingSyncedAnimation( scriptInterface ) ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToMeleeComboAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
		{
			if( stateContext.GetConditionBool( 'LightMeleeAttackPressed' ) && !( IsPlayingSyncedAnimation( scriptInterface ) ) )
			{
				return true;
			}
		}
		return false;
	}

	public constexpr export function ToMeleeBlock( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return true;
	}

	protected export function ToMeleeDeflectAttack( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsDeflectSuccessful( stateContext, scriptInterface );
	}

	protected const function IsDeflectSuccessful( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var deflectEvent : StateResultBool;
		deflectEvent = stateContext.GetTemporaryBoolParameter( 'successfulDeflect' );
		return deflectEvent.valid;
	}

}

class MeleeDeflectEvents extends MeleeEventsTransition
{
	var deflectStatFlag : gameStatModifierData;

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var ownerID : StatsObjectID;
		ownerID = scriptInterface.executionOwnerEntityID;
		SetIsBlocking( stateContext, true );
		scriptInterface.PushAnimationEvent( 'Deflect' );
		scriptInterface.GetTargetingSystem().AimSnap( scriptInterface.executionOwner );
		deflectStatFlag = RPGManager.CreateStatModifier( gamedataStatType.IsDeflecting, gameStatModifierType.Additive, 1.0 );
		scriptInterface.GetStatsSystem().AddModifier( ownerID, deflectStatFlag );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Block ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Deflect ) ) );
		stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
		ClearInputBuffer( stateContext );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var ownerID : StatsObjectID;
		ownerID = scriptInterface.executionOwnerEntityID;
		scriptInterface.GetStatsSystem().RemoveModifier( ownerID, deflectStatFlag );
		GameObject.StartCooldown( scriptInterface.owner, 'Deflect', GetStaticFloatParameterDefault( "cooldown", -1.0 ), true );
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeDeflectAttackDecisions extends MeleeAttackGenericDecisions
{

	protected const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( HasAttackRecord( scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeDeflectAttackEvents extends MeleeAttackGenericEvents
{
	var m_slowMoSet : Bool;

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.DeflectAttack ) ) );
		TutorialAddFact( scriptInterface, 'melee_deflect_tutorial', 1 );
		PlayRumble( scriptInterface, GetStaticStringParameterDefault( "rumbleOnStartStrength", "light_fast" ) );
		GameObject.PlaySound( scriptInterface.executionOwner, GetStaticCNameParameterDefault( "slowMoStartSound", '' ) );
		m_slowMoSet = false;
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var slowMoStart : Float;
		slowMoStart = GetStaticFloatParameterDefault( "slowMoStart", 0.1 );
		if( ( !( m_slowMoSet ) && ( GetInStateTime() > slowMoStart ) ) && !( IsTimeDilationActive( stateContext, scriptInterface, '' ) ) )
		{
			scriptInterface.GetTimeSystem().SetTimeDilation( 'deflect', GetStaticFloatParameterDefault( "slowMoAmount", 0.1 ), GetStaticFloatParameterDefault( "slowDuration", 0.1 ), GetStaticCNameParameterDefault( "slowMoEaseIn", 'Linear' ), GetStaticCNameParameterDefault( "slowMoEaseOut", 'Linear' ) );
			m_slowMoSet = true;
		}
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
	}

}

class MeleeBlockDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsBlockHeld( stateContext, scriptInterface ) )
		{
			if( IsAttackParried( stateContext, scriptInterface ) )
			{
				return false;
			}
			if( !( CanWeaponBlock( stateContext, scriptInterface ) ) )
			{
				return false;
			}
			if( HasMeleeTargeting( stateContext, scriptInterface ) && CanThrowWeapon( stateContext, scriptInterface ) )
			{
				return false;
			}
			if( GameObject.IsCooldownActive( scriptInterface.owner, 'Block' ) )
			{
				return false;
			}
			if( GameObject.IsCooldownActive( scriptInterface.owner, 'Deflect' ) )
			{
				return false;
			}
			if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
			{
				return false;
			}
			return true;
		}
		return false;
	}

	protected export const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ShouldEnterSafe( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( ShouldInterruptHoldStates( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( AnyMeleeAttackPressed( scriptInterface ) )
		{
			return true;
		}
		if( IsBlockHeld( stateContext, scriptInterface ) && !( stateContext.IsStateActive( 'Locomotion', 'sprint' ) ) )
		{
			return false;
		}
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "minDuration", -1.0 ) )
		{
			return true;
		}
		return false;
	}

}

class MeleeBlockEvents extends MeleeRumblingEvents
{
	var blockStatFlag : gameStatModifierData;

	public override function GetIntensity() : String
	{
		return "medium";
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var ownerID : StatsObjectID;
		ownerID = scriptInterface.executionOwnerEntityID;
		ClearInputBuffer( stateContext );
		SetMeleeAttackPressCount( stateContext, scriptInterface );
		SetIsAttacking( stateContext, false );
		SetIsBlocking( stateContext, true );
		scriptInterface.PushAnimationEvent( 'Block' );
		stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
		stateContext.SetTemporaryBoolParameter( 'InterruptBlockForLeap', false, true );
		blockStatFlag = RPGManager.CreateStatModifier( gamedataStatType.IsBlocking, gameStatModifierType.Additive, 1.0 );
		scriptInterface.GetStatsSystem().AddModifier( ownerID, blockStatFlag );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Block ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Block ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	private function OnExitCommon( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var ownerID : StatsObjectID;
		ownerID = scriptInterface.executionOwnerEntityID;
		scriptInterface.GetStatsSystem().RemoveModifier( ownerID, blockStatFlag );
		GameObject.StartCooldown( scriptInterface.owner, 'Block', GetStaticFloatParameterDefault( "cooldown", -1.0 ), true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Default ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Default ) ) );
	}

}

class MeleeTargetingDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( CanThrowWeaponObject( scriptInterface.executionOwner, GetWeaponObject( scriptInterface ) ) ) )
		{
			return false;
		}
		if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

	protected const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( GameInstance.GetPhotoModeSystem( scriptInterface.executionOwner.GetGame() ).IsPhotoModeActive() )
		{
			return false;
		}
		if( ShouldInterruptHoldStates( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( !( EnterCondition( stateContext, scriptInterface ) ) )
		{
			return true;
		}
		if( MeleeAttackPressed( scriptInterface ) )
		{
			return true;
		}
		return false;
	}

}

class MeleeTargetingEvents extends MeleeEventsTransition
{
	private var m_aimInTimeRemaining : Float;

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var attackData : MeleeAttackData;
		var aimAssistRecord : AimAssistMelee_Record;
		aimAssistRecord = GetAimAssistMeleeRecord( scriptInterface );
		SetIsTargeting( stateContext, true );
		scriptInterface.PushAnimationEvent( 'Targeting' );
		ResetAttackNumber( stateContext );
		GetAttackDataFromState( stateContext, scriptInterface, "MeleeThrowAttack", stateContext.GetIntParameter( 'meleeAttackNumber', true ), attackData );
		stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
		m_aimInTimeRemaining = 0.25;
		SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTimeRemaining, m_aimInTimeRemaining );
		SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTime, m_aimInTimeRemaining );
		if( aimAssistRecord && aimAssistRecord.AimSnapOnAim() )
		{
			scriptInterface.GetTargetingSystem().AimSnap( scriptInterface.executionOwner );
		}
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Block ) ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.Targeting ) ) );
		super.OnEnter( stateContext, scriptInterface );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).AddTriggerEffect( 'te_wea_melee_throw', 'PSM_MeleeTargetingOnEnter_trigger' );
	}

	public override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var weaponObject : WeaponObject;
		weaponObject = GetWeaponObject( scriptInterface );
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		ShowAttackPreview( true, weaponObject, scriptInterface, stateContext );
		HandleDamagePreview( weaponObject, scriptInterface, stateContext );
		if( m_aimInTimeRemaining > 0.0 )
		{
			m_aimInTimeRemaining -= timeDelta;
			SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTimeRemaining, m_aimInTimeRemaining );
		}
	}

	public function OnCommonExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_aimInTimeRemaining = 0.0;
		SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTimeRemaining, m_aimInTimeRemaining );
		ActivateDamageProjection( false, GetWeaponObject( scriptInterface ), scriptInterface, stateContext );
		if( !( MeleeAttackPressed( scriptInterface ) ) )
		{
			stateContext.SetTemporaryBoolParameter( 'requestKerenzikovDeactivation', true, true );
		}
		stateContext.SetTemporaryBoolParameter( 'requestKerenzikovDeactivationWithEaseOut', true, true );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnCommonExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnCommonExit( stateContext, scriptInterface );
	}

}

class MeleeThrowAttackDecisions extends MeleeAttackGenericDecisions
{

	protected const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( HasAttackRecord( scriptInterface ) ) )
		{
			return false;
		}
		if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( MeleeAttackPressed( scriptInterface ) ) )
		{
			return false;
		}
		if( GetWeaponObject( scriptInterface ).WeaponHasTag( 'Throwable' ) && !( CanThrowWeapon( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeThrowAttackEvents extends MeleeAttackGenericEvents
{
	var m_projectileThrown : Bool;
	default m_projectileThrown = false;
	var m_targetObject : weak< GameObject >;

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Throw;
	}

	protected function EnableLockOnTarget( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var aimRequestData : AimRequest;
		aimRequestData = GetBlockLookAtParams();
		scriptInterface.GetTargetingSystem().LookAt( scriptInterface.executionOwner, aimRequestData );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var aimAssistRecord : AimAssistMelee_Record;
		var locomotionState : CName;
		aimAssistRecord = GetAimAssistMeleeRecord( scriptInterface );
		locomotionState = stateContext.GetStateMachineCurrentState( 'Locomotion' );
		m_projectileThrown = false;
		if( CheckItemType( scriptInterface, gamedataItemType.Cyb_NanoWires ) )
		{
			m_targetObject = GetNanoWireTargetObject( scriptInterface );
		}
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.ThrowAttack ) ) );
		if( aimAssistRecord && aimAssistRecord.AimSnapOnThrow() )
		{
			scriptInterface.GetTargetingSystem().AimSnap( scriptInterface.executionOwner );
		}
		super.OnEnter( stateContext, scriptInterface );
		if( PlayerDevelopmentSystem.GetInstance( scriptInterface.executionOwner ).IsNewPerkBought( scriptInterface.executionOwner, gamedataNewPerkType.Cool_Master_Perk_4 ) )
		{
			if( ( ( ( ( IsInSlidingState( stateContext ) || locomotionState == 'crouchSprint' ) || locomotionState == 'coolExitJump' ) || stateContext.IsStateActive( 'Locomotion', 'sprint' ) ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'JustDodged' ) ) || StatusEffectSystem.ObjectHasStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.DriverCombatVehicleManeuvers" ) )
			{
				StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.ForceCritOnNextThrowSE" );
			}
		}
		stateContext.SetTemporaryBoolParameter( 'requestKerenzikovDeactivationWithEaseOut', true, true );
		scriptInterface.GetTargetingSystem().SetPlayerAimWeaponPositionProvider( scriptInterface.executionOwner, IPositionProvider.CreateSlotPositionProvider( scriptInterface.executionOwner, 'RightHand' ) );
	}

	public override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var attackData : MeleeAttackData;
		var isThrowable : Bool;
		var isItemNanowire : Bool;
		var isValidNanowireAttack : Bool;
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		attackData = GetAttackData( stateContext );
		isThrowable = GetWeaponObject( scriptInterface ).WeaponHasTag( 'Throwable' );
		isItemNanowire = CheckItemType( scriptInterface, gamedataItemType.Cyb_NanoWires );
		isValidNanowireAttack = isItemNanowire && m_targetObject;
		if( ( ( GetInStateTime() > attackData.attackEffectDelay ) && !( m_projectileThrown ) ) && ( isValidNanowireAttack || isThrowable ) )
		{
			m_projectileThrown = true;
			if( isThrowable )
			{
				SetIsThrowReloading( stateContext, true );
				SendAnimFeatureData( stateContext, scriptInterface );
				SpawnMeleeWeaponProjectile( scriptInterface );
				ApplyThrowableCooldown( stateContext, scriptInterface );
				GetWeaponObject( scriptInterface ).SetVisible( false );
			}
		}
	}

	protected function UpdateNanoWireIKState( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "timeToEnableWireIK", 0.54000002 ) )
		{
			EnableNanoWireIK( scriptInterface, true );
		}
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "timeToDisableWireIK", 1.20000005 ) )
		{
			DisableNanoWireIK( scriptInterface );
		}
	}

	protected function EnableNanoWireIK( scriptInterface : StateGameScriptInterface, enable : Bool, optional setPosition : Bool )
	{
		var slotPosition : Vector4;
		var targetPosition : Vector4;
		var wireTargetSlot : CName;
		if( m_targetObject )
		{
			wireTargetSlot = GetStaticCNameParameterDefault( "wireTargetSlot", 'wireTargetSlot' );
			AIActionHelper.GetTargetSlotPosition( m_targetObject, wireTargetSlot, slotPosition );
			targetPosition = slotPosition;
		}
		else
		{
			targetPosition = Vector4( 0.0, 0.0, 0.0, 1.0 );
		}
		UpdateNanoWireEndPositionAnimFeature( scriptInterface, GetStaticCNameParameterDefault( "ikAnimFeatureName", 'ikLeftNanoWire' ), enable, setPosition, targetPosition );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		DisableNanoWireIK( scriptInterface );
		super.OnExit( stateContext, scriptInterface );
		scriptInterface.GetTargetingSystem().SetPlayerAimWeaponPositionProvider( scriptInterface.executionOwner, IPositionProvider.CreateEntityPositionProvider( GetWeaponObject( scriptInterface ) ) );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		scriptInterface.GetTargetingSystem().SetPlayerAimWeaponPositionProvider( scriptInterface.executionOwner, IPositionProvider.CreateEntityPositionProvider( GetWeaponObject( scriptInterface ) ) );
		super.OnForcedExit( stateContext, scriptInterface );
	}

	protected function ApplyThrowableCooldown( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var reloadTime : Float;
		reloadTime = GameInstance.GetStatsSystem( scriptInterface.GetGame() ).GetStatValue( scriptInterface.owner.GetEntityID(), gamedataStatType.ThrowRecovery );
		SetThrowReloadTime( stateContext, reloadTime );
	}

}

class MeleeThrowReloadDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var throwReloading : StateResultBool;
		throwReloading = stateContext.GetPermanentBoolParameter( 'isThrowReloading' );
		if( GetWeaponObject( scriptInterface ).WeaponHasTag( 'Throwable' ) && throwReloading.value )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	protected export const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsThrownWeaponReloading( stateContext, scriptInterface ) || ( GetInStateTime() <= TDB.GetFloat( T"Items.MeleeWeapon.minimumReloadTime", 2.0 ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeThrowReloadEvents extends MeleeEventsTransition
{
	var m_isSwitchingWeapon : Bool;

	protected function OnEnterFromMeleeEquipping( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		GetWeaponObject( scriptInterface ).SetVisible( false );
		OnEnter( stateContext, scriptInterface );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetIsThrowReloading( stateContext, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Default ) ) );
		ClearInputBuffer( stateContext );
		super.OnEnter( stateContext, scriptInterface );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).AddTriggerEffect( 'te_wea_melee_reload', 'PSM_MeleeReloadOnEnter_feedback' );
	}

	public override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( MeleeAttackPressed( scriptInterface ) )
		{
			if( !( m_isSwitchingWeapon ) && ( GetInStateTime() > TDB.GetFloat( T"Items.MeleeWeapon.weaponSwapOnAttackDelay", 0.40000001 ) ) )
			{
				m_isSwitchingWeapon = true;
				EquipNextWeapon( scriptInterface.executionOwner );
			}
		}
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnForcedExit( stateContext, scriptInterface );
		GetWeaponObject( scriptInterface ).SetVisible( true );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var weaponEquipAnimFeature : AnimFeature_EquipType;
		weaponEquipAnimFeature = new AnimFeature_EquipType;
		SetIsThrowReloading( stateContext, false );
		weaponEquipAnimFeature.firstEquip = false;
		scriptInterface.SetAnimationParameterFeature( 'WeaponEquipType', weaponEquipAnimFeature );
		if( GameInstance.GetStatsSystem( scriptInterface.owner.GetGame() ).GetStatBoolValue( scriptInterface.executionOwnerEntityID, gamedataStatType.HasKnifeSharpener ) )
		{
			StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.KnifeSharpenerBuff" );
		}
		super.OnExit( stateContext, scriptInterface );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).RemoveTriggerEffect( 'PSM_MeleeTargetingOnEnter_trigger' );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).RemoveTriggerEffect( 'PSM_MeleeReloadOnEnter_feedback' );
		GetWeaponObject( scriptInterface ).SetVisible( true );
	}

	private function EquipNextWeapon( owner : GameObject )
	{
		var equipmentManipulationRequest : EquipmentSystemWeaponManipulationRequest;
		var eqSystem : weak< EquipmentSystem >;
		equipmentManipulationRequest = new EquipmentSystemWeaponManipulationRequest;
		eqSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( owner.GetGame() ).Get( 'EquipmentSystem' ) ) );
		equipmentManipulationRequest.requestType = EquipmentManipulationAction.RequestNextThrowableWeapon;
		equipmentManipulationRequest.owner = owner;
		eqSystem.QueueRequest( equipmentManipulationRequest );
	}

}

class MeleeLeapDecisions extends MeleeTransition
{

	protected export const function EnterCondition( stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var performCheck : Bool;
		var finisherTarget : EntityID;
		var maxLeapLength : Float;
		var target : ScriptedPuppet;
		var checkForLeapableTarget : Bool;
		finisherTarget = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		maxLeapLength = GetStaticFloatParameterDefault( "maxDistToTarget", 5.0 );
		if( m_driverCombatListener.IsMounted() )
		{
			return false;
		}
		if( EntityID.IsDefined( finisherTarget ) )
		{
			return true;
		}
		if( IsInMeleeState( stateContext, 'meleeChargedHold' ) && MeleeAttackReleased( scriptInterface ) )
		{
			performCheck = true;
		}
		if( stateContext.IsStateActive( 'Locomotion', 'sprint' ) || ( scriptInterface.GetOwnerStateVectorParameterFloat( physicsStateValue.LinearSpeed ) > 5.0 ) )
		{
			if( ( scriptInterface.IsOnGround() && IsInMeleeState( stateContext, 'meleeChargedHold' ) ) && MeleeAttackReleased( scriptInterface ) )
			{
				performCheck = true;
			}
		}
		if( !( performCheck ) )
		{
			return false;
		}
		if( !( scriptInterface.IsOnGround() ) && !( scriptInterface.HasStatFlag( gamedataStatType.CanMeleeLeapInAir ) ) )
		{
			return false;
		}
		if( !( scriptInterface.HasStatFlag( gamedataStatType.CanMeleeLeap ) ) )
		{
			return false;
		}
		if( CanPerformRelicLeap( scriptInterface ) )
		{
			target = ( ( ScriptedPuppet )( GetTargetObject( scriptInterface ) ) );
			if( target && !( target.IsCivilian() ) )
			{
				maxLeapLength = GetStaticFloatParameterDefault( "maxDistToTargetMantisBladesRelic", 40.0 );
			}
		}
		checkForLeapableTarget = ShouldCheckForLeapableTarget( scriptInterface, GetWeaponObject( scriptInterface ) );
		if( !( GetStaticBoolParameterDefault( "canLeapWithoutTarget", false ) ) && !( GetTargetObject( scriptInterface, maxLeapLength, checkForLeapableTarget ) ) )
		{
			return false;
		}
		if( GetTargetObject( scriptInterface, GetStaticFloatParameterDefault( "minDistToTarget", 2.0 ), checkForLeapableTarget ) )
		{
			return false;
		}
		stateContext.SetPermanentBoolParameter( 'IsInMeleeLeapState', true, true );
		return true;
	}

	protected const function ToMeleeStrongAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		var leapExitTimeResult : StateResultFloat;
		var enableExitingToStrongAttack : StateResultBool;
		leapExitTimeResult = stateContext.GetConditionFloatParameter( 'LeapExitTime' );
		enableExitingToStrongAttack = stateContext.GetConditionBoolParameter( 'enableLeapExitToStrongAttack' );
		if( leapExitTimeResult.valid && enableExitingToStrongAttack.valid )
		{
			return enableExitingToStrongAttack.value && ( GetInStateTime() >= leapExitTimeResult.value );
		}
		else
		{
			return false;
		}
	}

	protected const function ToMeleeIdle( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		var result : StateResultFloat;
		result = stateContext.GetConditionFloatParameter( 'LeapExitTime' );
		if( result.valid )
		{
			return GetInStateTime() >= result.value;
		}
		else
		{
			return false;
		}
	}

}

class MeleeLeapEvents extends MeleeEventsTransition
{
	var m_enableVaultFromLeapAttack : Bool;
	var m_exitingToMeleeStrongAttack : Bool;
	var m_isFinisher : Bool;
	var m_isTargetKnockedOver : Bool;
	var m_textLayerId : Uint32;

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var finisherTarget : EntityID;
		var leapingWasSuccessful : Bool;
		finisherTarget = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		m_isFinisher = EntityID.IsDefined( finisherTarget );
		m_enableVaultFromLeapAttack = false;
		m_exitingToMeleeStrongAttack = false;
		m_isTargetKnockedOver = false;
		stateContext.SetConditionBoolParameter( 'enableLeapExitToStrongAttack', !( m_isFinisher ), true );
		stateContext.SetPermanentBoolParameter( 'enableVaultFromleapAttack', false, true );
		stateContext.SetPermanentBoolParameter( 'crouchAfterLeaping', false, true );
		scriptInterface.localBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MeleeLeap, false );
		leapingWasSuccessful = LeapToTarget( stateContext, scriptInterface );
		if( !( leapingWasSuccessful ) )
		{
			stateContext.SetConditionFloatParameter( 'LeapExitTime', 0.0, true );
			if( GetStaticBoolParameterDefault( "canLeapWithoutTarget", false ) )
			{
				Leap( stateContext, scriptInterface );
			}
		}
		if( m_isFinisher )
		{
			stateContext.SetPermanentBoolParameter( 'bossFinisherAttack', true, true );
		}
		if( ( HasNewSpyAttackStatFlag( scriptInterface ) && WeaponIsCharged( scriptInterface ) ) && GetWeaponType( scriptInterface ) == gamedataItemType.Cyb_MantisBlades )
		{
			StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, GetMantisBladesInvulnerableLeapRelicBufffStatusEffectID() );
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

	protected function OnExitToMeleeStrongAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_exitingToMeleeStrongAttack = true;
		OnExit( stateContext, scriptInterface );
	}

	protected function OnExitCommon( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ClearDebugText( scriptInterface, m_textLayerId );
		StatusEffectHelper.RemoveStatusEffect( scriptInterface.executionOwner, GetMantisBladesInvulnerableLeapRelicBufffStatusEffectID() );
		stateContext.SetConditionBoolParameter( 'enableLeapExitToStrongAttack', true, true );
		if( !( m_exitingToMeleeStrongAttack ) )
		{
			stateContext.SetPermanentBoolParameter( 'enableVaultFromleapAttack', false, true );
			stateContext.SetPermanentBoolParameter( 'crouchAfterLeaping', false, true );
			scriptInterface.localBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MeleeLeap, false );
			if( m_isFinisher )
			{
				scriptInterface.localBlackboard.SetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget, EMPTY_ENTITY_ID() );
			}
		}
	}

	private function LeapToTarget( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		var vecToTarget : Vector4;
		var target : GameObject;
		var leapDuration : Float;
		var leapAngle : EulerAngles;
		var exitTime : Float;
		var shouldCheckForLeapableTarget : Bool;
		var leapTargetWithinAngle : Float;
		var maxLeapLength : Float;
		var finisherTargetId : EntityID;
		var playerPerkDataBB : IBlackboard;
		var distanceToTarget : Float;
		var targetPuppet : ScriptedPuppet;
		var distanceRadiusToTarget : Float;
		var rotationDuration : Float;
		var targetPos : Vector4;
		var targetAbsPos : Vector4;
		var playerPos : Vector4;
		var targetIsOnTheSameLevel : Bool;
		var isWeaponMantisBlades : Bool;
		shouldCheckForLeapableTarget = false;
		maxLeapLength = GetStaticFloatParameterDefault( "maxDistToTarget", 5.0 );
		finisherTargetId = scriptInterface.localBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.FinisherTarget );
		isWeaponMantisBlades = GetWeaponType( scriptInterface ) == gamedataItemType.Cyb_MantisBlades;
		shouldCheckForLeapableTarget = ShouldCheckForLeapableTarget( scriptInterface, GetWeaponObject( scriptInterface ) );
		if( CanPerformRelicLeap( scriptInterface ) )
		{
			maxLeapLength = GetStaticFloatParameterDefault( "maxDistToTargetMantisBladesRelic", 40.0 );
		}
		if( !( isWeaponMantisBlades ) )
		{
			leapTargetWithinAngle = GetStaticFloatParameterDefault( "leapTargetWithinAngle", 10.0 );
		}
		target = GetTargetObject( scriptInterface, maxLeapLength, shouldCheckForLeapableTarget, leapTargetWithinAngle );
		if( !( target ) || ( ( target && EntityID.IsDefined( finisherTargetId ) ) && ( finisherTargetId != target.GetEntityID() ) ) )
		{
			target = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( scriptInterface.executionOwner.GetGame(), finisherTargetId ) ) );
			if( !( target ) || !( ScriptedPuppet.IsActive( target ) ) )
			{
				return false;
			}
		}
		targetPuppet = ( ( ScriptedPuppet )( target ) );
		if( targetPuppet )
		{
			targetPuppet.SetFinisherSoundPlayed( false );
		}
		vecToTarget = target.GetWorldPosition() - scriptInterface.executionOwner.GetWorldPosition();
		leapAngle = Vector4.ToRotation( vecToTarget );
		if( -( leapAngle.Pitch ) > GetStaticFloatParameterDefault( "leapMaxPitch", 45.0 ) )
		{
			return false;
		}
		leapDuration = CalculateAdjustmentDuration( Vector4.Length( vecToTarget ) );
		exitTime = GetExitTime( scriptInterface, leapDuration );
		stateContext.SetConditionFloatParameter( 'LeapExitTime', exitTime, true );
		distanceRadiusToTarget = GetStaticFloatParameterDefault( "distanceRadiusToTarget", 0.89999998 );
		rotationDuration = GetStaticFloatParameterDefault( "rotationDuration", -1.0 );
		targetAbsPos = target.GetWorldPosition();
		playerPos = scriptInterface.executionOwner.GetWorldPosition();
		targetIsOnTheSameLevel = ( targetAbsPos.Z - playerPos.Z ) < 0.2;
		if( targetIsOnTheSameLevel && StatusEffectSystem.ObjectHasStatusEffectOfType( target, gamedataStatusEffectType.Knockdown ) )
		{
			m_isTargetKnockedOver = true;
		}
		GetSlotTransformToTarget( scriptInterface, target, leapDuration, m_isTargetKnockedOver, targetPos, rotationDuration );
		if( isWeaponMantisBlades )
		{
			targetPos = TargetPrediction( targetPos, ( ( ScriptedPuppet )( target ) ), leapDuration, 1.0 );
		}
		if( !( m_isFinisher ) )
		{
			rotationDuration = GetStaticFloatParameterDefault( "rotationDuration", -1.0 );
		}
		if( SpatialQueriesHelper.IsTargetReachable( scriptInterface.executionOwner, target, targetPos, false, m_enableVaultFromLeapAttack ) )
		{
			scriptInterface.localBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MeleeLeap, true );
			if( m_isFinisher )
			{
				stateContext.SetConditionBoolParameter( 'enableLeapExitToStrongAttack', true, true );
			}
			if( m_enableVaultFromLeapAttack )
			{
				stateContext.SetPermanentBoolParameter( 'enableVaultFromleapAttack', true, true );
			}
			if( m_isTargetKnockedOver )
			{
				stateContext.SetPermanentBoolParameter( 'crouchAfterLeaping', true, true );
			}
			if( HasNewSpyAttackStatFlag( scriptInterface ) && isWeaponMantisBlades )
			{
				StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.LeapWithMantisBladesCheck" );
				StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.LeapCheck" );
			}
			if( GetWeaponType( scriptInterface ) == gamedataItemType.Wea_Katana || GetWeaponType( scriptInterface ) == gamedataItemType.Wea_Sword )
			{
				GameObject.PlaySound( scriptInterface.executionOwner, 'lcm_dash_katana' );
			}
			playerPerkDataBB = GameInstance.GetBlackboardSystem( scriptInterface.executionOwner.GetGame() ).Get( GetAllBlackboardDefs().PlayerPerkData );
			if( isWeaponMantisBlades )
			{
				playerPerkDataBB.SetEntityID( GetAllBlackboardDefs().PlayerPerkData.LeapTarget, target.GetEntityID() );
				playerPerkDataBB.SetVector4( GetAllBlackboardDefs().PlayerPerkData.LeapPosition, targetPos );
			}
			AdjustPlayerPosition( stateContext, scriptInterface, target, leapDuration, distanceRadiusToTarget, rotationDuration, '', true, targetPos );
			if( shouldCheckForLeapableTarget )
			{
				if( playerPerkDataBB )
				{
					distanceToTarget = Vector4.Distance( target.GetWorldPosition(), scriptInterface.executionOwner.GetWorldPosition() );
					playerPerkDataBB.SetFloat( GetAllBlackboardDefs().PlayerPerkData.LeapedDistance, distanceToTarget, true );
				}
			}
			PlayerStaminaHelpers.ModifyStaminaBasedOnLeapAttackDistance( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ), scriptInterface.IsOnGround(), Vector4.Length( vecToTarget ), maxLeapLength );
			return true;
		}
		return false;
	}

	private function Leap( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var adjustPosition : Vector4;
		var vecToTarget : Vector4;
		var slideDuration : Float;
		var cameraWorldTransform : Transform;
		var leapAngle : EulerAngles;
		var exitTime : Float;
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		leapAngle = Transform.ToEulerAngles( cameraWorldTransform );
		if( leapAngle.Pitch > GetStaticFloatParameterDefault( "noTargetMaxPitch", 45.0 ) )
		{
			leapAngle.Pitch = GetStaticFloatParameterDefault( "noTargetMaxPitch", 45.0 );
			Transform.SetOrientationEuler( cameraWorldTransform, leapAngle );
		}
		vecToTarget = Transform.GetForward( cameraWorldTransform ) * GetStaticFloatParameterDefault( "noTargetLeapDistance", 5.0 );
		adjustPosition = scriptInterface.executionOwner.GetWorldPosition() + vecToTarget;
		slideDuration = CalculateAdjustmentDuration( GetStaticFloatParameterDefault( "noTargetLeapDistance", 5.0 ) );
		exitTime = slideDuration - GetStaticFloatParameterDefault( "attackStartupDuration", 0.0 );
		stateContext.SetConditionFloatParameter( 'LeapExitTime', exitTime, true );
		RequestPlayerPositionAdjustment( stateContext, scriptInterface, NULL, slideDuration, GetStaticFloatParameterDefault( "distanceRadius", 0.0 ), GetStaticFloatParameterDefault( "rotationDuration", -1.0 ), adjustPosition, true );
	}

	private function CalculateAdjustmentDuration( distance : Float ) : Float
	{
		var minDist, maxDist : Float;
		var minDur, maxDur : Float;
		var duration : Float;
		minDist = GetStaticFloatParameterDefault( "minDistToTarget", 1.0 );
		maxDist = GetStaticFloatParameterDefault( "maxDistToTarget", 1.0 );
		minDur = GetStaticFloatParameterDefault( "minAdjustmentDuration", 1.0 );
		maxDur = GetStaticFloatParameterDefault( "maxAdjustmentDuration", 1.0 );
		distance -= minDist;
		maxDist -= minDist;
		duration = LerpF( distance / maxDist, minDur, maxDur, true );
		return duration;
	}

	private function GetExitTime( scriptInterface : StateGameScriptInterface, leapDuration : Float ) : Float
	{
		var exitTime : Float;
		if( GetWeaponType( scriptInterface ) == gamedataItemType.Wea_Knife )
		{
			if( m_isFinisher )
			{
				exitTime = leapDuration - TDB.GetFloat( T"playerStateMachineFinisher.finisherLeapToTarget.attackStartupDurationKnives" );
			}
			else
			{
				exitTime = leapDuration - GetStaticFloatParameterDefault( "attackStartupDurationKnives", 0.0 );
			}
		}
		else
		{
			if( m_isFinisher )
			{
				exitTime = leapDuration - TDB.GetFloat( T"playerStateMachineFinisher.finisherLeapToTarget.attackStartupDuration" );
			}
			else
			{
				exitTime = leapDuration - GetStaticFloatParameterDefault( "attackStartupDuration", 0.0 );
			}
		}
		return exitTime;
	}

}

class MeleeDashDecisions extends MeleeTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var performCheck : Bool;
		if( stateContext.IsStateActive( 'Locomotion', 'sprint' ) )
		{
			performCheck = true;
		}
		if( !( scriptInterface.IsOnGround() ) && ( Get2DLinearSpeed( scriptInterface ) > 5.0 ) )
		{
			performCheck = true;
		}
		if( !( AnyMeleeAttackPressed( scriptInterface ) ) )
		{
			performCheck = false;
		}
		if( !( performCheck ) )
		{
			return false;
		}
		if( !( scriptInterface.IsOnGround() ) )
		{
			return false;
		}
		if( !( HasWeaponStatFlag( scriptInterface, gamedataStatType.CanWeaponDash ) ) )
		{
			return false;
		}
		if( !( scriptInterface.HasStatFlag( gamedataStatType.CanMeleeDash ) ) )
		{
			return false;
		}
		if( !( GetWeaponObject( scriptInterface ).GetAttack( 'MeleeSprintAttack0' ) ) )
		{
			return false;
		}
		if( GetTargetObject( scriptInterface, GetStaticFloatParameterDefault( "minTargetDistanceToDash", 2.0 ) ) )
		{
			return false;
		}
		if( !( CheckDashCollision( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

	private static function ConvertArray4ToVector4( const arr : ref< array< Float > > ) : Vector4
	{
		var i : Int32;
		var tempVector4 : Vector4;
		tempVector4 = Vector4( 0.0, 0.0, 0.0, 0.0 );
		for( i = 0; i < arr.Size(); i += 1 )
		{
			if( i == 0 )
			{
				tempVector4.X = arr[ i ];
			}
			else if( i == 1 )
			{
				tempVector4.Y = arr[ i ];
			}
			else if( i == 2 )
			{
				tempVector4.Z = arr[ i ];
			}
			else if( i == 3 )
			{
				tempVector4.W = arr[ i ];
			}
		}
		return tempVector4;
	}

	protected const function CheckDashCollision( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var geometryDescription : GeometryDescriptionQuery;
		var staticQueryFilter : QueryFilter;
		var geometryDescriptionResult : GeometryDescriptionResult;
		var cameraWorldTransform : Transform;
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		QueryFilter.AddGroup( staticQueryFilter, 'Static' );
		geometryDescription = new GeometryDescriptionQuery;
		geometryDescription.refPosition = Transform.GetPosition( cameraWorldTransform );
		geometryDescription.refDirection = Transform.GetForward( cameraWorldTransform );
		geometryDescription.filter = staticQueryFilter;
		geometryDescription.primitiveDimension = ConvertArray4ToVector4( GetStaticFloatArrayParameter( "primitiveDimensionArr" ) );
		geometryDescription.maxDistance = GetStaticFloatParameterDefault( "maxDistance", 5.0 );
		geometryDescription.maxExtent = GetStaticFloatParameterDefault( "maxExtent", 5.0 );
		geometryDescription.probingPrecision = GetStaticFloatParameterDefault( "probingPrecision", 0.05 );
		geometryDescription.probingMaxDistanceDiff = GetStaticFloatParameterDefault( "probingMaxDistanceDiff", 5.0 );
		geometryDescription.AddFlag( worldgeometryDescriptionQueryFlags.DistanceVector );
		geometryDescriptionResult = scriptInterface.GetSpatialQueriesSystem().GetGeometryDescriptionSystem().QueryExtents( geometryDescription );
		if( geometryDescriptionResult.queryStatus == worldgeometryDescriptionQueryStatus.NoGeometry )
		{
			return true;
		}
		return false;
	}

	protected const function ToMeleeSprintAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		var attackData : MeleeAttackData;
		var duration : Float;
		if( GetWeaponObject( scriptInterface ).GetAttack( 'MeleeSprintAttack0' ) )
		{
			duration = GetStaticFloatParameterDefault( "slideDuration", 1.0 ) - attackData.attackEffectDelay;
		}
		else
		{
			duration = GetStaticFloatParameterDefault( "timeToStartAttack", 1.0 );
		}
		if( GetInStateTime() >= duration )
		{
			return true;
		}
		return false;
	}

	protected const function ToMeleeIdle( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		return GetInStateTime() >= GetStaticFloatParameterDefault( "timeout", 1.0 );
	}

}

class MeleeDashEvents extends MeleeEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( !( DashToTarget( stateContext, scriptInterface ) ) )
		{
			Dash( stateContext, scriptInterface );
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	private function DashToTarget( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		var adjustPosition : Vector4;
		var vecToTarget : Vector4;
		var safetyDisplacement : Vector4;
		var scaledSafetyDisplacement : Vector4;
		var playerPuppetOrientation : Quaternion;
		var horizontalDistanceFromTarget : Float;
		var additionalHorizontalDistance : Float;
		var target : GameObject;
		var slideDuration : Float;
		var leapAngle : EulerAngles;
		target = GetTargetObject( scriptInterface, GetStaticFloatParameterDefault( "maxDistToAquireTarget", 5.0 ) );
		if( !( target ) )
		{
			return false;
		}
		vecToTarget = target.GetWorldPosition() - scriptInterface.executionOwner.GetWorldPosition();
		playerPuppetOrientation = scriptInterface.executionOwner.GetWorldOrientation();
		leapAngle = Vector4.ToRotation( vecToTarget );
		if( -( leapAngle.Pitch ) > GetStaticFloatParameterDefault( "dashMaxPitch", 45.0 ) )
		{
			return false;
		}
		safetyDisplacement.Y = 2.0;
		if( vecToTarget.Z > 0.0 )
		{
			safetyDisplacement.Y = safetyDisplacement.Y * -1.0;
		}
		horizontalDistanceFromTarget = Vector4.Length2D( vecToTarget );
		additionalHorizontalDistance = MaxF( safetyDisplacement.Y - horizontalDistanceFromTarget, 0.0 );
		scaledSafetyDisplacement = safetyDisplacement * additionalHorizontalDistance;
		adjustPosition = Quaternion.Transform( playerPuppetOrientation, scaledSafetyDisplacement );
		slideDuration = GetStaticFloatParameterDefault( "slideDuration", 0.30000001 );
		RequestPlayerPositionAdjustment( stateContext, scriptInterface, target, slideDuration, GetStaticFloatParameterDefault( "distanceRadiusToTarget", 0.89999998 ), GetStaticFloatParameterDefault( "rotationDuration", -1.0 ), adjustPosition );
		return true;
	}

	private function Dash( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var adjustPosition : Vector4;
		var vecToTarget : Vector4;
		var slideDuration : Float;
		var cameraWorldTransform : Transform;
		var leapAngle : EulerAngles;
		cameraWorldTransform = scriptInterface.GetCameraWorldTransform();
		leapAngle = Transform.ToEulerAngles( cameraWorldTransform );
		if( leapAngle.Pitch > GetStaticFloatParameterDefault( "noTargetMaxPitch", 45.0 ) )
		{
			leapAngle.Pitch = GetStaticFloatParameterDefault( "noTargetMaxPitch", 45.0 );
			Transform.SetOrientationEuler( cameraWorldTransform, leapAngle );
		}
		vecToTarget = Transform.GetForward( cameraWorldTransform ) * GetStaticFloatParameterDefault( "noTargetDashDistance", 5.0 );
		adjustPosition = scriptInterface.executionOwner.GetWorldPosition() + vecToTarget;
		slideDuration = GetStaticFloatParameterDefault( "slideDuration", 0.30000001 );
		RequestPlayerPositionAdjustment( stateContext, scriptInterface, NULL, slideDuration, GetStaticFloatParameterDefault( "distanceRadius", 0.0 ), GetStaticFloatParameterDefault( "rotationDuration", -1.0 ), adjustPosition );
	}

}

class MeleeBlockAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( AnyMeleeAttackPressed( scriptInterface ) || stateContext.GetConditionBool( 'LightMeleeAttackPressed' ) ) )
		{
			return false;
		}
		if( !( HasAttackRecord( scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

	protected export const override function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var attackData : MeleeAttackData;
		var inStateTime : Float;
		attackData = GetAttackData( stateContext );
		inStateTime = GetInStateTime();
		if( IsBlockHeld( stateContext, scriptInterface ) )
		{
			if( ( attackData.blockTransitionTime > 0.0 ) && ( inStateTime >= attackData.blockTransitionTime ) )
			{
				return true;
			}
		}
		if( stateContext.GetConditionBool( 'LightMeleeAttackPressed' ) && ( inStateTime >= attackData.attackWindowClosed ) )
		{
			return true;
		}
		if( inStateTime >= attackData.idleTransitionTime )
		{
			return true;
		}
		return false;
	}

}

class MeleeBlockAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Block;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.BlockAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeBodySlamAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( PlayerDevelopmentSystem.GetInstance( scriptInterface.executionOwner ).IsNewPerkBought( scriptInterface.executionOwner, gamedataNewPerkType.Body_Right_Milestone_2 ) < 2 )
		{
			return false;
		}
		if( !( WeaponObject.IsBlunt( GameObject.GetActiveWeapon( scriptInterface.executionOwner ).GetItemID() ) ) )
		{
			return false;
		}
		if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( stateContext.IsStateActive( 'Locomotion', 'sprint' ) ) )
		{
			return false;
		}
		if( scriptInterface.GetStatPoolsSystem().GetStatPoolValue( scriptInterface.executionOwner.GetEntityID(), gamedataStatPoolType.Stamina, true ) <= 0.0 )
		{
			return false;
		}
		return true;
	}

	protected export const override function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var inAirDuration : StateResultFloat;
		var locomotionState : CName;
		var exitBodySlam : StateResultBool;
		if( !( IsBlockHeld( stateContext, scriptInterface ) ) )
		{
			return true;
		}
		if( QuickMeleeTapped( scriptInterface ) )
		{
			return true;
		}
		locomotionState = stateContext.GetStateMachineCurrentState( 'Locomotion' );
		if( locomotionState == 'fall' || locomotionState == 'bodySlamJump' )
		{
			inAirDuration = stateContext.GetPermanentFloatParameter( 'InAirDuration' );
			if( inAirDuration.valid && ( inAirDuration.value > GetStaticFloatParameterDefault( "maxAirTime", 0.5 ) ) )
			{
				return true;
			}
		}
		else if( locomotionState != 'sprint' && locomotionState != 'regularLand' )
		{
			return true;
		}
		exitBodySlam = stateContext.GetTemporaryBoolParameter( 'exitBodySlam' );
		if( exitBodySlam.value )
		{
			return true;
		}
		return false;
	}

}

class MeleeBodySlamAttackEvents extends MeleeEventsTransition
{
	var m_effect : EffectInstance;
	var m_speedModifier : gameStatModifierData;
	var m_stunModifier : gameStatModifierData;
	var m_chargeStage : Int32;
	var m_attackSpawnDelay : Float;
	var m_timeToFullAttack : Float;
	var m_nextAttackRefresh : Float;
	var m_playBumpSFX : Bool;
	var m_bumpCallback : CallbackHandle;
	var m_delayBetweenBumpSFX : Float;
	var m_bumpSFXCooldown : Float;
	var m_staminaCost : Float;
	const var m_fullAttackIndex : Int32;
	default m_fullAttackIndex = 2;
	const var m_weakAttackIndex : Int32;
	default m_weakAttackIndex = 1;

	protected export override function OnDetach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		RemoveStatModifiers( scriptInterface );
	}

	protected function AddSpeedModifier( scriptInterface : StateGameScriptInterface )
	{
		var modifierVal : Float;
		var speedParam : String;
		modifierVal = 0.0;
		speedParam = "initialSpeed";
		if( m_chargeStage > 1 )
		{
			speedParam = "finalSpeed";
		}
		modifierVal = GetStaticFloatParameterDefault( speedParam, 0.0 );
		m_speedModifier = RPGManager.CreateStatModifier( gamedataStatType.MaxSpeed, gameStatModifierType.Additive, modifierVal );
		scriptInterface.GetStatsSystem().AddModifier( scriptInterface.executionOwnerEntityID, m_speedModifier );
	}

	protected function RemoveSpeedModifier( scriptInterface : StateGameScriptInterface )
	{
		if( m_speedModifier )
		{
			scriptInterface.GetStatsSystem().RemoveModifier( scriptInterface.executionOwnerEntityID, m_speedModifier );
			m_speedModifier = NULL;
		}
	}

	protected function AddStunModifier( scriptInterface : StateGameScriptInterface )
	{
		m_stunModifier = RPGManager.CreateStatModifier( gamedataStatType.StunApplicationRate, gameStatModifierType.Multiplier, 0.0 );
		scriptInterface.GetStatsSystem().AddModifier( scriptInterface.ownerEntityID, m_stunModifier );
	}

	protected function RemoveStunModifier( scriptInterface : StateGameScriptInterface )
	{
		if( m_stunModifier )
		{
			scriptInterface.GetStatsSystem().RemoveModifier( scriptInterface.ownerEntityID, m_stunModifier );
			m_stunModifier = NULL;
		}
	}

	protected function RemoveStatModifiers( scriptInterface : StateGameScriptInterface )
	{
		RemoveSpeedModifier( scriptInterface );
		RemoveStunModifier( scriptInterface );
	}

	protected function SpawnBodySlamAttack( stateContext : StateContext, scriptInterface : StateGameScriptInterface, attackStage : Int32 )
	{
		var colliderBox : Vector4;
		var attackData : MeleeAttackData;
		var weapon : WeaponObject;
		var attack : Attack_GameEffect;
		colliderBox = Vector4( 0.25, 0.25, 0.25, 0.0 );
		GetAttackDataFromCurrentState( stateContext, scriptInterface, attackStage, attackData );
		weapon = ( ( WeaponObject )( scriptInterface.owner ) );
		attack = ( ( Attack_GameEffect )( weapon.GetCurrentAttack() ) );
		m_staminaCost = attackData.staminaCost;
		if( attack )
		{
			m_effect = attack.PrepareAttack( scriptInterface.executionOwner );
			colliderBox.X = GetStaticFloatParameterDefault( "hitboxWidth", 0.25 );
			colliderBox.Z = GetStaticFloatParameterDefault( "hitboxHeight", 1.0 );
			EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.box, colliderBox );
			EffectData.SetFloat( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.duration, attackData.attackEffectDuration );
			EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, Transform.TransformPoint( scriptInterface.GetCameraWorldTransform(), Vector4( 0.0, GetStaticFloatParameterDefault( "hitboxForwardOffset", 1.0 ), 0.0, 0.0 ) ) );
			EffectData.SetQuat( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.rotation, Transform.GetOrientation( scriptInterface.GetCameraWorldTransform() ) );
			EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.forward, Transform.GetForward( scriptInterface.GetCameraWorldTransform() ) );
			EffectData.SetFloat( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.range, GetStaticFloatParameterDefault( "range", 0.25 ) );
			EffectData.SetBool( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.meleeCleave, true );
			EffectData.SetInt( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.attackNumber, attackStage );
			EffectData.SetName( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.impactOrientationSlot, attackData.impactFxSlot );
			m_nextAttackRefresh = attackData.attackEffectDuration;
			EffectData.SetVariant( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.fxPackage, weapon.GetFxPackage() );
			EffectData.SetBool( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.playerOwnedWeapon, true );
			m_attackSpawnDelay = attackData.attackEffectDuration;
			attack.StartAttack();
		}
	}

	protected function UpdateEffectPosition( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var position : Vector4;
		position = Vector4( 0.0, GetStaticFloatParameterDefault( "hitboxForwardOffset", 1.0 ), 0.0, 0.0 );
		EffectData.SetVector( m_effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, Transform.TransformPoint( scriptInterface.GetCameraWorldTransform(), position ) );
	}

	protected function SetBodySlamAnimFeature( scriptInterface : StateGameScriptInterface )
	{
		var animFeature : AnimFeature_BodySlam;
		animFeature = new AnimFeature_BodySlam;
		if( m_chargeStage == 1 )
		{
			animFeature.chargeLevel = 3;
		}
		else if( m_chargeStage == 0 )
		{
			animFeature.chargeLevel = 0;
		}
		else
		{
			animFeature.chargeLevel = 2;
		}
		scriptInterface.SetAnimationParameterFeature( 'BodySlam', animFeature );
	}

	protected function SetChargeStage( scriptInterface : StateGameScriptInterface, chargeStage : Int32 )
	{
		RemoveSpeedModifier( scriptInterface );
		m_chargeStage = chargeStage;
		AddSpeedModifier( scriptInterface );
		SetBodySlamAnimFeature( scriptInterface );
	}

	protected function UpdateChargeStage( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( m_chargeStage == 1 )
		{
			if( GetInStateTime() > GetStaticFloatParameterDefault( "timeToFinalSpeed", 1.0 ) )
			{
				SetChargeStage( scriptInterface, 2 );
			}
		}
	}

	protected event OnBodySlamBump( value : Int32 )
	{
		if( m_bumpSFXCooldown <= 0.0 )
		{
			m_playBumpSFX = true;
			m_bumpSFXCooldown = m_delayBetweenBumpSFX;
		}
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var isExhausted : Bool;
		m_chargeStage = 1;
		SetBodySlamAnimFeature( scriptInterface );
		ResetAttackNumber( stateContext );
		SetIsAttacking( stateContext, true );
		SetIsBlocking( stateContext, true );
		stateContext.SetConditionBoolParameter( 'SprintToggled', true, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Block ) ) );
		SetMeleeAttackPressCount( stateContext, scriptInterface );
		ClearInputBuffer( stateContext );
		SpawnBodySlamAttack( stateContext, scriptInterface, m_weakAttackIndex );
		isExhausted = StatusEffectSystem.ObjectHasStatusEffect( scriptInterface.executionOwner, PlayerStaminaHelpers.GetExhaustedStatusEffectID() );
		if( !( isExhausted ) )
		{
			AddSpeedModifier( scriptInterface );
		}
		m_timeToFullAttack = GetStaticFloatParameterDefault( "timeToFullAttack", 1.0 );
		AddStunModifier( scriptInterface );
		m_bumpCallback = scriptInterface.localBlackboard.RegisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.BodySlamBump, this, 'OnBodySlamBump' );
		scriptInterface.localBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.IsInBodySlamState, true, true );
		m_playBumpSFX = false;
		m_delayBetweenBumpSFX = GetStaticFloatParameterDefault( "delayBetweenBumpSFX", 1.0 );
		m_bumpSFXCooldown = 0.0;
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var nextAttackType : Int32;
		var fullAttackStartFrame : Bool;
		nextAttackType = m_weakAttackIndex;
		fullAttackStartFrame = false;
		UpdateChargeStage( stateContext, scriptInterface );
		UpdateMeleeInputBuffer( stateContext, scriptInterface );
		if( m_timeToFullAttack > 0.0 )
		{
			m_timeToFullAttack = m_timeToFullAttack - timeDelta;
			if( m_timeToFullAttack <= 0.0 )
			{
				m_effect.Terminate();
				SpawnBodySlamAttack( stateContext, scriptInterface, m_fullAttackIndex );
				fullAttackStartFrame = true;
			}
		}
		if( !( fullAttackStartFrame ) )
		{
			m_nextAttackRefresh = m_nextAttackRefresh - timeDelta;
			if( m_nextAttackRefresh <= 0.0 )
			{
				if( m_timeToFullAttack <= 0.0 )
				{
					nextAttackType = m_fullAttackIndex;
				}
				SpawnBodySlamAttack( stateContext, scriptInterface, nextAttackType );
			}
		}
		UpdateEffectPosition( stateContext, scriptInterface );
		if( m_playBumpSFX )
		{
			m_playBumpSFX = false;
			if( m_timeToFullAttack <= 0.0 )
			{
				GameObject.PlaySoundEvent( scriptInterface.executionOwner, 'lcm_additional_bump_perk_body_slam_strong' );
			}
			else
			{
				GameObject.PlaySoundEvent( scriptInterface.executionOwner, 'lcm_additional_bump_perk_body_slam_mid' );
			}
			if( scriptInterface.GetStatPoolsSystem().GetStatPoolValue( scriptInterface.executionOwner.GetEntityID(), gamedataStatPoolType.Stamina, true ) <= GetStaticFloatParameterDefault( "staminaCostPerHit", 20.0 ) )
			{
				stateContext.SetTemporaryBoolParameter( 'exitBodySlam', true, true );
			}
			PlayerStaminaHelpers.ModifyStamina( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ), -( m_staminaCost ), true );
		}
		m_bumpSFXCooldown -= timeDelta;
	}

	protected function OnExitCommon( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_effect.Terminate();
		RemoveStatModifiers( scriptInterface );
		m_chargeStage = 0;
		SetBodySlamAnimFeature( scriptInterface );
		SetIsAttacking( stateContext, false );
		SetIsBlocking( stateContext, false );
		scriptInterface.localBlackboard.UnregisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.BodySlamBump, m_bumpCallback );
		scriptInterface.localBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.IsInBodySlamState, false, true );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	public export override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnExitCommon( stateContext, scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class MeleeCrouchAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( stateContext.IsStateActive( 'Locomotion', 'crouch' ) || stateContext.IsStateActive( 'Locomotion', 'slide' ) ) )
		{
			return false;
		}
		if( !( WantsToLightAttack( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( HasAttackRecord( scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeCrouchAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Crouch;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.CrouchAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeJumpAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( !( ( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Locomotion ) == ( ( Int32 )( gamePSMLocomotionStates.Jump ) ) ) || stateContext.IsStateActive( 'Locomotion', 'fall' ) ) )
		{
			return false;
		}
		if( !( WantsToLightAttack( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( super.EnterCondition( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeJumpAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Jump;
	}

	public export override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.JumpAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public export override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeSprintAttackDecisions extends MeleeAttackGenericDecisions
{

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var performCheck : Bool;
		if( stateContext.IsStateActive( 'Locomotion', 'sprint' ) )
		{
			performCheck = true;
		}
		if( !( scriptInterface.IsOnGround() ) && ( Get2DLinearSpeed( scriptInterface ) > 5.0 ) )
		{
			performCheck = true;
		}
		if( !( performCheck ) )
		{
			return false;
		}
		if( !( WantsToLightAttack( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( HasAttackRecord( scriptInterface ) ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeSprintAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Sprint;
	}

	public function OnEnterFromMeleeDash( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		OnEnter( stateContext, scriptInterface );
		m_blockImpulseCreation = true;
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.SprintAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeEquipAttackDecisions extends MeleeAttackGenericDecisions
{
}

class MeleeEquipAttackEvents extends MeleeAttackGenericEvents
{

	protected override final function GetAttackType() : EMeleeAttackType
	{
		return EMeleeAttackType.Equip;
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetAttackNumber( stateContext );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.MeleeWeapon, ( ( Int32 )( gamePSMMeleeWeapon.EquipAttack ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

}

class MeleeGroundSlamAttackDecisions extends MeleeAttackGenericDecisions
{

	protected const function IsGroundSlamming( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var curState : CName;
		curState = stateContext.GetStateMachineCurrentState( 'Locomotion' );
		if( curState == 'airHover' )
		{
			return true;
		}
		if( curState == 'superheroFall' )
		{
			return true;
		}
		if( curState == 'superheroLand' )
		{
			return true;
		}
		if( curState == 'superheroLandRecovery' )
		{
			return true;
		}
		return false;
	}

	protected const function IsValidLocomotionState( const state : CName ) : Bool
	{
		if( ( ( ( ( ( ( ( ( ( ( ( ( ( ( state == 'stand' || state == 'sprint' ) || state == 'crouch' ) || state == 'crouchSprint' ) || state == 'aimWalk' ) || state == 'jump' ) || state == 'doubleJump' ) || state == 'chargeJump' ) || state == 'hoverJump' ) || state == 'coolExitJump' ) || state == 'bodySlamJump' ) || state == 'ladderJump' ) || state == 'fall' ) || state == 'slideFall' ) || state == 'dodgeAir' ) || state == 'dodge' )
		{
			return true;
		}
		return false;
	}

	protected const function CanFit( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var capsuleHeight : Float;
		var capsuleRadius : Float;
		capsuleHeight = GetStaticFloatParameterDefault( "capsuleHeight", 1.0 );
		capsuleRadius = GetStaticFloatParameterDefault( "capsuleRadius", 1.0 );
		return scriptInterface.CanCapsuleFit( capsuleHeight, capsuleRadius );
	}

	protected export const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.IsOnGround() )
		{
			if( !( scriptInterface.GetStatsSystem().GetStatBoolValue( scriptInterface.executionOwnerEntityID, gamedataStatType.CanGroundSlamOnGround ) ) )
			{
				return false;
			}
		}
		else
		{
			if( !( scriptInterface.GetStatsSystem().GetStatBoolValue( scriptInterface.executionOwnerEntityID, gamedataStatType.CanGroundSlamInAir ) ) )
			{
				return false;
			}
		}
		if( !( WantsToQuickMelee( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( !( WeaponObject.IsBlunt( GameObject.GetActiveWeapon( scriptInterface.executionOwner ).GetItemID() ) ) )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.GroundSlamCooldown" ) )
		{
			stateContext.SetConditionBoolParameter( 'QuickMeleeAttackTapped', false, true );
			return false;
		}
		if( !( IsValidLocomotionState( stateContext.GetStateMachineCurrentState( 'Locomotion' ) ) ) )
		{
			return false;
		}
		if( !( CanFit( scriptInterface ) ) )
		{
			return false;
		}
		if( scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsPlayerInsideMovingElevator ) )
		{
			return false;
		}
		return true;
	}

	protected export const override function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var justEnteredGroundSlam : StateResultBool;
		justEnteredGroundSlam = stateContext.GetTemporaryBoolParameter( 'groundSlam' );
		if( justEnteredGroundSlam.value )
		{
			return false;
		}
		if( IsGroundSlamming( stateContext, scriptInterface ) )
		{
			return false;
		}
		return true;
	}

}

class MeleeGroundSlamAttackEvents extends MeleeAttackGenericEvents
{
	var m_knockdownImmunityModifier : gameStatModifierData;
	var m_stunImmunityModifier : gameStatModifierData;

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var attackData : MeleeAttackData;
		m_attackCreated = false;
		m_effect = NULL;
		m_trailCreated = false;
		ResetAttackNumber( stateContext );
		stateContext.SetTemporaryBoolParameter( 'groundSlam', true, true );
		ClearInputBuffer( stateContext );
		stateContext.SetPermanentBoolParameter( 'InterruptMelee', false, true );
		SetIsAttacking( stateContext, true );
		SetIsBlocking( stateContext, false );
		GameObject.PlayVoiceOver( scriptInterface.executionOwner, 'meleeAttack', 'Scripts:MeleeAttackGenericEvents' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Melee, ( ( Int32 )( gamePSMMelee.Attack ) ) );
		GetAttackDataFromCurrentState( stateContext, scriptInterface, stateContext.GetIntParameter( 'meleeAttackNumber', true ), attackData );
		ConsumeStamina( scriptInterface, attackData );
		stateContext.SetPermanentBoolParameter( 'hasDeflectAnim', attackData.hasDeflectAnim, true );
		stateContext.SetPermanentBoolParameter( 'hasHitAnim', attackData.hasHitAnim, true );
		stateContext.SetConditionScriptableParameter( 'MeleeAttackData', attackData, true );
		stateContext.SetPermanentBoolParameter( 'VisionToggled', false, true );
		ForceDisableVisionMode( stateContext );
		stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
		stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
		stateContext.SetConditionBoolParameter( 'SprintHoldCanStartWithoutNewInput', false, true );
		ClearDebugText( scriptInterface, m_textLayer );
		stateContext.SetPermanentIntParameter( 'attackType', ( ( Int32 )( GetAttackType() ) ), true );
		stateContext.SetConditionBoolParameter( 'CrouchToggled', false, true );
		SetIsSafe( stateContext, false );
		SendDataTrackingRequest( scriptInterface, ETelemetryData.MeleeAttacksMade, 1 );
		GameInstance.GetTelemetrySystem( scriptInterface.owner.GetGame() ).LogWeaponAttackPerformed( GetWeaponObject( scriptInterface ) );
		AddStatModifiers( scriptInterface );
	}

	public export override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var spawnAttack : StateResultBool;
		var attackData : MeleeAttackData;
		var broadcaster : StimBroadcasterComponent;
		attackData = GetAttackData( stateContext );
		if( !( m_attackCreated ) )
		{
			spawnAttack = stateContext.GetTemporaryBoolParameter( 'spawnGroundSlamSwing' );
			if( spawnAttack.value )
			{
				CreateMeleeAttack( stateContext, scriptInterface, attackData );
				m_attackCreated = true;
				if( broadcaster = scriptInterface.executionOwner.GetStimBroadcasterComponent() )
				{
					broadcaster.TriggerSingleBroadcast( scriptInterface.executionOwner, gamedataStimType.MeleeAttack );
				}
			}
		}
		if( m_effect )
		{
			UpdateEffectPosition( stateContext, scriptInterface, attackData, 0.0 );
		}
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
		RemoveStatModifiers( scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnForcedExit( stateContext, scriptInterface );
		RemoveStatModifiers( scriptInterface );
	}

	private function AddStatModifiers( scriptInterface : StateGameScriptInterface )
	{
		var statSys : StatsSystem;
		statSys = scriptInterface.GetStatsSystem();
		m_knockdownImmunityModifier = RPGManager.CreateStatModifier( gamedataStatType.KnockdownImmunity, gameStatModifierType.Additive, 1.0 );
		m_stunImmunityModifier = RPGManager.CreateStatModifier( gamedataStatType.StunImmunity, gameStatModifierType.Additive, 1.0 );
		statSys.AddModifier( scriptInterface.ownerEntityID, m_knockdownImmunityModifier );
		statSys.AddModifier( scriptInterface.ownerEntityID, m_stunImmunityModifier );
	}

	private function RemoveStatModifiers( scriptInterface : StateGameScriptInterface )
	{
		var statSys : StatsSystem;
		statSys = scriptInterface.GetStatsSystem();
		if( m_knockdownImmunityModifier )
		{
			statSys.RemoveModifier( scriptInterface.ownerEntityID, m_knockdownImmunityModifier );
			m_knockdownImmunityModifier = NULL;
		}
		if( m_stunImmunityModifier )
		{
			statSys.RemoveModifier( scriptInterface.ownerEntityID, m_stunImmunityModifier );
			m_stunImmunityModifier = NULL;
		}
	}

}

