class LocomotionSwimming extends LocomotionTransition
{

	protected const function IsFallingSpeedToEnterDiveAcceptable( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var fallingSpeedThreshold : Float;
		var verticalSpeed : Float;
		var height : Float;
		height = GetStaticFloatParameterDefault( "minFallHeight", 2.0 );
		fallingSpeedThreshold = GetFallingSpeedBasedOnHeight( scriptInterface, height );
		verticalSpeed = GetVerticalSpeed( scriptInterface );
		if( verticalSpeed <= fallingSpeedThreshold )
		{
			return true;
		}
		return false;
	}

	protected const function IsDivingBlockedByPhoneCall( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'PhoneCall' ) && !( scriptInterface.HasStatFlag( gamedataStatType.CanUsePhoneUnderWater ) );
	}

	protected const function IsDeepEnoughToDive( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var currentDepth : Float;
		var depthTreshold : Float;
		var tolerance : Float;
		depthTreshold = GetStaticFloatParameterDefault( "depthTreshold", 0.0 );
		tolerance = GetStaticFloatParameterDefault( "tolerance", 0.0 );
		currentDepth = GetCurrentDepth( stateContext, scriptInterface );
		if( currentDepth <= ( depthTreshold + tolerance ) )
		{
			return true;
		}
		return false;
	}

	protected const function GetCurrentDepth( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Float
	{
		var playerFeetPosition : Vector4;
		var depthRaycastDestination : Vector4;
		var waterLevel : Float;
		var currentDepth : Float;
		var maxDepth : Float;
		maxDepth = 100.0;
		playerFeetPosition = GetPlayerPosition( scriptInterface );
		depthRaycastDestination = playerFeetPosition;
		depthRaycastDestination.Z = depthRaycastDestination.Z - maxDepth;
		currentDepth = maxDepth;
		if( scriptInterface.GetWaterLevel( playerFeetPosition, depthRaycastDestination, waterLevel ) )
		{
			currentDepth = playerFeetPosition.Z - waterLevel;
		}
		return currentDepth;
	}

	protected const function CanEnterFastSwimming( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var minLinearVelocityThreshold : Float;
		var minStickInputThreshold : Float;
		var enterAngleThreshold : Float;
		var isAiming : Bool;
		var isReloading : Bool;
		var isFocusMode : Bool;
		var isChargingCyberware : Bool;
		isAiming = scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody ) == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) );
		isReloading = scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Weapon ) == ( ( Int32 )( gamePSMRangedWeaponStates.Reload ) );
		isFocusMode = scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) );
		isChargingCyberware = scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware ) == ( ( Int32 )( gamePSMLeftHandCyberware.Charge ) );
		minLinearVelocityThreshold = GetStaticFloatParameterDefault( "minLinearVelocityThreshold", 0.5 );
		minStickInputThreshold = GetStaticFloatParameterDefault( "minStickInputThreshold", 0.89999998 );
		enterAngleThreshold = GetStaticFloatParameterDefault( "enterAngleThreshold", -180.0 );
		if( ( ( !( scriptInterface.IsMoveInputConsiderable() ) || ( AbsF( scriptInterface.GetInputHeading() ) > enterAngleThreshold ) ) || ( GetMovementInputActionValue( stateContext, scriptInterface ) <= minStickInputThreshold ) ) || ( scriptInterface.GetOwnerStateVectorParameterFloat( physicsStateValue.LinearSpeed ) < minLinearVelocityThreshold ) )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
			return false;
		}
		if( isAiming )
		{
			return false;
		}
		if( isChargingCyberware )
		{
			return false;
		}
		if( ( isFocusMode || stateContext.GetConditionBool( 'VisionToggled' ) ) && !( IsInRpgContext( scriptInterface ) ) )
		{
			return false;
		}
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.CombatGadget ) == ( ( Int32 )( gamePSMCombatGadget.Charging ) ) )
		{
			return false;
		}
		if( ( isReloading && ( scriptInterface.GetActionValue( 'Sprint' ) > 0.0 ) ) && !( scriptInterface.IsActionJustPressed( 'Sprint' ) ) )
		{
			return false;
		}
		if( stateContext.GetConditionBool( 'SprintToggled' ) )
		{
			return true;
		}
		if( scriptInterface.GetActionValue( 'ToggleSprint' ) > 0.0 )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', true, true );
		}
		if( scriptInterface.GetActionValue( 'Sprint' ) > 0.0 )
		{
			return true;
		}
		return false;
	}

	protected const function ShouldExitFastSwimming( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var minLinearVelocityThreshold : Float;
		var enterAngleThreshold : Float;
		var minStickInputThreshold : Float;
		if( stateContext.GetBoolParameter( 'InterruptSprint' ) )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
			return true;
		}
		minLinearVelocityThreshold = GetStaticFloatParameterDefault( "minLinearVelocityThreshold", 0.5 );
		if( scriptInterface.GetOwnerStateVectorParameterFloat( physicsStateValue.LinearSpeed ) < minLinearVelocityThreshold )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
			return true;
		}
		enterAngleThreshold = GetStaticFloatParameterDefault( "enterAngleThreshold", 45.0 );
		if( !( scriptInterface.IsMoveInputConsiderable() ) || !( ( AbsF( scriptInterface.GetInputHeading() ) <= enterAngleThreshold ) ) )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
			return true;
		}
		minStickInputThreshold = GetStaticFloatParameterDefault( "minStickInputThreshold", 0.89999998 );
		if( stateContext.GetConditionBool( 'SprintToggled' ) && ( GetMovementInputActionValue( stateContext, scriptInterface ) <= minStickInputThreshold ) )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
			return true;
		}
		if( scriptInterface.IsActionJustReleased( 'Sprint' ) )
		{
			stateContext.SetConditionBoolParameter( 'SprintToggled', false, true );
			return true;
		}
		return false;
	}

}

class LocomotionSwimmingEvents extends LocomotionEventsTransition
{

	protected function SetSwimmingState( stateContext : StateContext, scriptInterface : StateGameScriptInterface, value : Int32 )
	{
		if( value > 0 )
		{
			PlayerPuppet.ReevaluateAllBreathingEffects( ( ( PlayerPuppet )( scriptInterface.owner ) ) );
		}
		stateContext.SetPermanentIntParameter( 'swimmingState', value, true );
		UpdateSwimmingAnimFeatureData( stateContext, scriptInterface );
	}

	protected function UpdateSwimmingAnimFeatureData( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var animFeature : AnimFeature_SwimmingData;
		animFeature = new AnimFeature_SwimmingData;
		animFeature.state = stateContext.GetIntParameter( 'swimmingState', true );
		scriptInterface.SetAnimationParameterFeature( 'SwimmingData', animFeature );
	}

	public override function SetLocomotionParameters( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : LocomotionParameters
	{
		var locomotionParameters : LocomotionSwimmingParameters;
		SetModifierGroupForState( scriptInterface );
		locomotionParameters = new LocomotionSwimmingParameters;
		GetStateDefaultLocomotionParameters( ( ( LocomotionParameters )( locomotionParameters ) ) );
		locomotionParameters.SetBuoyancyLineFraction( GetStaticFloatParameterDefault( "buoyancyLineFraction", 0.5 ) );
		locomotionParameters.SetDragCoefficient( GetStaticFloatParameterDefault( "dragCoefficient", 0.47 ) );
		stateContext.SetTemporaryScriptableParameter( 'locomotionParameters', locomotionParameters, true );
		return locomotionParameters;
	}

	protected function IsSwimmingForward( stateContext : StateContext, scriptInterface : StateGameScriptInterface ) : Bool
	{
		var minLinearVelocityThreshold : Float;
		var enterAngleThreshold : Float;
		var minStickInputThreshold : Float;
		minLinearVelocityThreshold = GetStaticFloatParameterDefault( "minLinearVelocityThreshold", 0.5 );
		enterAngleThreshold = GetStaticFloatParameterDefault( "enterAngleThreshold", 45.0 );
		minStickInputThreshold = GetStaticFloatParameterDefault( "minStickInputThreshold", 0.89999998 );
		if( ( ( !( scriptInterface.IsMoveInputConsiderable() ) || ( AbsF( scriptInterface.GetInputHeading() ) > enterAngleThreshold ) ) || ( GetMovementInputActionValue( stateContext, scriptInterface ) <= minStickInputThreshold ) ) || ( scriptInterface.GetOwnerStateVectorParameterFloat( physicsStateValue.LinearSpeed ) < minLinearVelocityThreshold ) )
		{
			return false;
		}
		return true;
	}

	public function GetSwimmingImpulseMultiplier( scriptInterface : StateGameScriptInterface, timeDelta : Float ) : Float
	{
		var verticalSpeed : Float;
		var impulseMultiplier : Float;
		verticalSpeed = AbsF( GetVerticalSpeed( scriptInterface ) );
		impulseMultiplier = ( timeDelta * 100.0 ) / ( 1.0 + verticalSpeed );
		return impulseMultiplier;
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ScriptedPuppet.ReevaluateOxygenConsumption( ( ( ScriptedPuppet )( scriptInterface.owner ) ) );
		super.OnEnter( stateContext, scriptInterface );
		SetSwimmingState( stateContext, scriptInterface, GetStaticIntParameterDefault( "swimmingType", 1 ) );
	}

	public function ExitCleanup( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetSwimmingState( stateContext, scriptInterface, 0 );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Default ) ) );
		PlayerPuppet.ReevaluateAllBreathingEffects( ( ( PlayerPuppet )( scriptInterface.owner ) ) );
		GameObjectEffectHelper.StopEffectEvent( scriptInterface.executionOwner, 'swimming_surface' );
		GameObjectEffectHelper.StopEffectEvent( scriptInterface.executionOwner, 'swimming_diving' );
		ScriptedPuppet.ReevaluateOxygenConsumption( ( ( ScriptedPuppet )( scriptInterface.owner ) ) );
	}

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

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

}

class SwimmingInitialDecisions extends LocomotionSwimming
{

	protected const function IsUnderwater( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var currentDepth : Float;
		var depthTreshold : Float;
		depthTreshold = GetStaticFloatParameterDefault( "skipTransitionDepthTreshold", 0.0 );
		currentDepth = GetCurrentDepth( stateContext, scriptInterface );
		return currentDepth <= depthTreshold;
	}

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

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

}

class SwimmingSurfaceDecisions extends LocomotionSwimming
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return !( CanEnterFastSwimming( stateContext, scriptInterface ) ) && !( IsDeepEnoughToDive( stateContext, scriptInterface ) );
	}

	protected const function ToDiving( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var currentDepth : Float;
		var depthTreshold : Float;
		if( CanEnterFastSwimming( stateContext, scriptInterface ) )
		{
			return false;
		}
		depthTreshold = GetStaticFloatParameterDefault( "skipTransitionDepthTreshold", 0.0 );
		currentDepth = GetCurrentDepth( stateContext, scriptInterface );
		return currentDepth <= depthTreshold;
	}

}

class SwimmingSurfaceEvents extends LocomotionSwimmingEvents
{
	var m_lapsedTime : Float;
	default m_lapsedTime = 0.f;
	var m_isDead : Bool;
	var m_timeSinceLastImpulse : Float;
	var m_minSpeedForMovementImpulses : Float;
	var m_timeBetweenIdleImpulses : Float;
	var m_timeBetweenMovementImpulses : Float;
	var m_idleImpulseRadius : Float;
	var m_idleImpulseStrength : Float;
	var m_movementImpulseRadius : Float;
	var m_movementImpulseStrength : Float;
	var m_movementImpulseOffset : Float;

	protected override function OnAttach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		super.OnAttach( stateContext, scriptInterface );
		m_minSpeedForMovementImpulses = GetStaticFloatParameterDefault( "minSpeedForMovementImpulses", 0.30000001 );
		m_timeBetweenIdleImpulses = GetStaticFloatParameterDefault( "timeBetweenIdleImpulses", 1.60000002 );
		m_timeBetweenMovementImpulses = GetStaticFloatParameterDefault( "timeBetweenMovementImpulses", 0.1 );
		m_idleImpulseRadius = GetStaticFloatParameterDefault( "idleImpulseRadius", 0.1 );
		m_idleImpulseStrength = GetStaticFloatParameterDefault( "idleImpulseStrength", 0.0007 );
		m_movementImpulseRadius = GetStaticFloatParameterDefault( "movementImpulseRadius", 0.30000001 );
		m_movementImpulseStrength = GetStaticFloatParameterDefault( "movementImpulseStrength", 0.0005 );
		m_movementImpulseOffset = GetStaticFloatParameterDefault( "movementImpulseOffset", 0.2 );
	}

	protected override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( !( m_isDead ) )
		{
			super.OnUpdate( timeDelta, stateContext, scriptInterface );
			UpdateSwimmingStroke( timeDelta, stateContext, scriptInterface );
			CreateWaterImpulse( timeDelta, stateContext, scriptInterface );
		}
	}

	private function UpdateSwimmingStroke( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var timeBetweenSwimmingStroke : Float;
		m_lapsedTime += timeDelta;
		timeBetweenSwimmingStroke = GetStaticFloatParameterDefault( "timeBetweenSurfaceSwimmingStroke", 1.0 );
		if( ( m_lapsedTime >= timeBetweenSwimmingStroke ) && IsSwimmingForward( stateContext, scriptInterface ) )
		{
			m_lapsedTime = 0.0;
			AddImpulseInMovingDirection( stateContext, scriptInterface, GetStaticFloatParameterDefault( "surfaceSwimmingStrokeImpulseForce", 1.0 ) );
			GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_surface_stroke', false );
		}
	}

	private function CreateWaterImpulse( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var applyImpulse : Bool;
		var impulsePosition : Vector4;
		var impulseRadius : Float;
		var impulseStrength : Float;
		applyImpulse = false;
		m_timeSinceLastImpulse += timeDelta;
		if( scriptInterface.GetOwnerStateVectorParameterFloat( physicsStateValue.LinearSpeed ) < m_minSpeedForMovementImpulses )
		{
			if( m_timeSinceLastImpulse >= m_timeBetweenIdleImpulses )
			{
				applyImpulse = true;
				impulsePosition = GetPlayerPosition( scriptInterface );
				impulseRadius = m_idleImpulseRadius;
				impulseStrength = m_idleImpulseStrength;
			}
		}
		else if( m_timeSinceLastImpulse >= m_timeBetweenMovementImpulses )
		{
			applyImpulse = true;
			impulsePosition = GetPlayerPosition( scriptInterface );
			impulsePosition += ( scriptInterface.GetOwnerMovingDirection() * m_movementImpulseOffset );
			impulseRadius = m_idleImpulseRadius;
			impulseStrength = m_idleImpulseStrength;
		}
		if( applyImpulse )
		{
			m_timeSinceLastImpulse = 0.0;
			StateGameScriptInterface.CreateWaterImpulse( impulsePosition, impulseRadius, impulseStrength, 2 );
		}
	}

	public function OnEnterFromDiving( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		PlaySound( 'surface_from_dive', scriptInterface );
		OnEnter( stateContext, scriptInterface );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_lapsedTime = 0.0;
		m_timeSinceLastImpulse = 0.0;
		StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"BaseStatusEffect.WaterCollision" );
		SetLocomotionParameters( stateContext, scriptInterface );
		GameObjectEffectHelper.StopEffectEvent( scriptInterface.executionOwner, 'falling' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Surface ) ) );
		if( !( scriptInterface.GetStatPoolsSystem().HasStatPoolValueReachedMin( scriptInterface.ownerEntityID, gamedataStatPoolType.Health ) ) )
		{
			m_isDead = false;
			GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_surface', false );
			TutorialSetFact( scriptInterface, 'swimming_tutorial' );
			super.OnEnter( stateContext, scriptInterface );
		}
		else
		{
			m_isDead = true;
			GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_enter_dive', false );
		}
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		GameObjectEffectHelper.StopEffectEvent( scriptInterface.executionOwner, 'swimming_surface' );
		super.OnExit( stateContext, scriptInterface );
	}

}

class SwimmingSurfaceFastDecisions extends LocomotionSwimming
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return CanEnterFastSwimming( stateContext, scriptInterface ) && !( IsDeepEnoughToDive( stateContext, scriptInterface ) );
	}

}

class SwimmingSurfaceFastEvents extends LocomotionSwimmingEvents
{
	var m_lapsedTime : Float;
	default m_lapsedTime = 0.f;
	var m_timeSinceLastImpulse : Float;
	var m_timeBetweenMovementImpulses : Float;
	var m_movementImpulseRadius : Float;
	var m_movementImpulseStrength : Float;
	var m_movementImpulseOffset : Float;

	protected override function OnAttach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		super.OnAttach( stateContext, scriptInterface );
		m_timeBetweenMovementImpulses = GetStaticFloatParameterDefault( "timeBetweenMovementImpulses", 0.1 );
		m_movementImpulseRadius = GetStaticFloatParameterDefault( "movementImpulseRadius", 0.30000001 );
		m_movementImpulseStrength = GetStaticFloatParameterDefault( "movementImpulseStrength", 0.0007 );
		m_movementImpulseOffset = GetStaticFloatParameterDefault( "movementImpulseOffset", 0.2 );
	}

	protected override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		UpdateSwimmingStroke( timeDelta, stateContext, scriptInterface );
		CreateWaterImpulse( timeDelta, stateContext, scriptInterface );
	}

	private function UpdateSwimmingStroke( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var timeBetweenSwimmingStroke : Float;
		m_lapsedTime += timeDelta;
		timeBetweenSwimmingStroke = GetStaticFloatParameterDefault( "timeBetweenSwimmingStroke", 1.0 );
		if( m_lapsedTime >= timeBetweenSwimmingStroke )
		{
			m_lapsedTime = 0.0;
			AddImpulseInMovingDirection( stateContext, scriptInterface, GetStaticFloatParameterDefault( "swimmingStrokeImpulseForce", 1.0 ) );
			GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_surface_stroke_fast', false );
		}
	}

	private function CreateWaterImpulse( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var impulsePosition : Vector4;
		m_timeSinceLastImpulse += timeDelta;
		if( m_timeSinceLastImpulse >= m_timeBetweenMovementImpulses )
		{
			m_timeSinceLastImpulse = 0.0;
			impulsePosition = GetPlayerPosition( scriptInterface );
			impulsePosition += ( scriptInterface.GetOwnerMovingDirection() * m_movementImpulseOffset );
			StateGameScriptInterface.CreateWaterImpulse( impulsePosition, m_movementImpulseRadius, m_movementImpulseStrength, 2 );
		}
	}

	public function OnEnterFromFastDiving( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		PlaySound( 'surface_from_fast_dive', scriptInterface );
		OnEnter( stateContext, scriptInterface );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_lapsedTime = 0.0;
		m_timeSinceLastImpulse = 0.0;
		SetLocomotionParameters( stateContext, scriptInterface );
		AddImpulseInMovingDirection( stateContext, scriptInterface, GetStaticFloatParameterDefault( "swimmingStrokeImpulseForce", 1.0 ) );
		super.OnEnter( stateContext, scriptInterface );
	}

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

}

class SwimmingTransitionDecisions extends LocomotionSwimming
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var isFallSpeedToEnterDiveAcceptable : Bool;
		isFallSpeedToEnterDiveAcceptable = IsFallingSpeedToEnterDiveAcceptable( stateContext, scriptInterface );
		if( ( ( isFallSpeedToEnterDiveAcceptable || scriptInterface.IsActionJustPressed( 'Dive' ) ) || ( scriptInterface.GetActionValue( 'Crouch' ) > 0.0 ) ) || ( IsCameraPitchAcceptable( stateContext, scriptInterface, GetStaticFloatParameterDefault( "cameraPitchThreshold", -30.0 ) ) && ( ( scriptInterface.GetActionValue( 'Jump' ) == 0.0 ) && !( scriptInterface.IsActionJustPressed( 'Jump' ) ) ) ) )
		{
			if( IsDivingBlockedByPhoneCall( stateContext, scriptInterface ) && !( isFallSpeedToEnterDiveAcceptable ) )
			{
				return false;
			}
			if( stateContext.GetBoolParameter( 'enteredWaterFromSceneTierII', true ) )
			{
				return false;
			}
			if( !( IsDeepEnoughToDive( stateContext, scriptInterface ) ) )
			{
				return false;
			}
			return true;
		}
		return false;
	}

	protected const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 0.2 );
	}

}

class SwimmingTransitionEvents extends LocomotionSwimmingEvents
{
	var m_maxDownwardSpeed : Float;
	var m_minDownwardsSpeed : Float;
	var m_upwardsImpulseStrength : Float;

	protected override function OnAttach( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		super.OnAttach( stateContext, scriptInterface );
		m_minDownwardsSpeed = GetStaticFloatParameterDefault( "minDownwardVerticalSpeed", 1.0 );
		m_maxDownwardSpeed = GetStaticFloatParameterDefault( "maxDownwardVerticalSpeed", 1.0 );
		m_upwardsImpulseStrength = GetStaticFloatParameterDefault( "upwardsImpulseStrength", 1.0 );
		if( m_maxDownwardSpeed <= 0.0 )
		{
			m_maxDownwardSpeed = 1.0;
		}
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var minDownwardsSpeed : Float;
		var verticalVelocity : Float;
		super.OnEnter( stateContext, scriptInterface );
		verticalVelocity = AbsF( GetVerticalSpeed( scriptInterface ) );
		if( verticalVelocity < minDownwardsSpeed )
		{
			AddVerticalImpulse( stateContext, GetStaticFloatParameterDefault( "downwardsImpulseStrength", 1.0 ) );
		}
		PlaySound( 'hold_breath', scriptInterface );
		PlayRumble( scriptInterface, "light_slow" );
		GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_enter_dive', false );
	}

	public override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var verticalVelocity : Float;
		var upwardsImpulseStrengthMod : Float;
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		verticalVelocity = GetVerticalSpeed( scriptInterface );
		if( ( verticalVelocity < 0.0 ) && ( AbsF( verticalVelocity ) > m_maxDownwardSpeed ) )
		{
			upwardsImpulseStrengthMod = AbsF( verticalVelocity / m_maxDownwardSpeed );
			AddVerticalImpulse( stateContext, upwardsImpulseStrengthMod * m_upwardsImpulseStrength );
		}
	}

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

}

class SwimmingDivingDecisions extends LocomotionSwimming
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsDeepEnoughToDive( stateContext, scriptInterface ) && !( CanEnterFastSwimming( stateContext, scriptInterface ) );
	}

}

class SwimmingDivingEvents extends LocomotionSwimmingEvents
{
	var m_lapsedTime : Float;
	default m_lapsedTime = 0.f;

	protected override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		UpdateDivingStroke( timeDelta, stateContext, scriptInterface );
		UpdateAscendingDescending( timeDelta, stateContext, scriptInterface );
		ScriptedPuppet.ReevaluateOxygenConsumption( ( ( ScriptedPuppet )( scriptInterface.owner ) ) );
	}

	private function UpdateAscendingDescending( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( scriptInterface.GetActionValue( 'Jump' ) > 0.0 )
		{
			AddVerticalImpulse( stateContext, GetStaticFloatParameterDefault( "divingUpwardsImpulseStrength", 1.0 ) * GetSwimmingImpulseMultiplier( scriptInterface, timeDelta ) );
		}
		if( ( scriptInterface.GetActionValue( 'Dive' ) > 0.0 ) || ( scriptInterface.GetActionValue( 'Crouch' ) > 0.0 ) )
		{
			AddVerticalImpulse( stateContext, GetStaticFloatParameterDefault( "divingDownwardsImpulseStrength", 1.0 ) * GetSwimmingImpulseMultiplier( scriptInterface, timeDelta ) );
		}
	}

	private function UpdateDivingStroke( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var timeBetweenSwimmingStroke : Float;
		m_lapsedTime += timeDelta;
		timeBetweenSwimmingStroke = GetStaticFloatParameterDefault( "timeBetweenDivingStroke", 1.0 );
		if( ( m_lapsedTime >= timeBetweenSwimmingStroke ) && IsSwimmingForward( stateContext, scriptInterface ) )
		{
			m_lapsedTime = 0.0;
			AddImpulseInMovingDirection( stateContext, scriptInterface, GetStaticFloatParameterDefault( "divingStrokeImpulseForce", 1.0 ) );
			GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_diving_stroke', false );
		}
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var speedSquared : Float;
		speedSquared = Vector4.LengthSquared( GetLinearVelocity( scriptInterface ) );
		m_lapsedTime = 0.0;
		GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_diving', false );
		if( speedSquared > ( 0.01 * 0.01 ) )
		{
			AddImpulseInMovingDirection( stateContext, scriptInterface, GetStaticFloatParameterDefault( "divingStrokeImpulseForce", 1.0 ) );
		}
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Diving ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		GameObjectEffectHelper.StopEffectEvent( scriptInterface.executionOwner, 'swimming_diving' );
		super.OnExit( stateContext, scriptInterface );
	}

}

class SwimmingFastDivingDecisions extends LocomotionSwimming
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return IsDeepEnoughToDive( stateContext, scriptInterface ) && CanEnterFastSwimming( stateContext, scriptInterface );
	}

}

class SwimmingFastDivingEvents extends LocomotionSwimmingEvents
{
	var m_lapsedTime : Float;
	default m_lapsedTime = 0.f;

	protected override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnUpdate( timeDelta, stateContext, scriptInterface );
		if( scriptInterface.GetActionValue( 'Jump' ) > 0.0 )
		{
			AddVerticalImpulse( stateContext, GetStaticFloatParameterDefault( "divingUpwardsImpulseStrength", 1.0 ) * GetSwimmingImpulseMultiplier( scriptInterface, timeDelta ) );
		}
		if( ( scriptInterface.GetActionValue( 'Dive' ) > 0.0 ) || ( scriptInterface.GetActionValue( 'Crouch' ) > 0.0 ) )
		{
			AddVerticalImpulse( stateContext, GetStaticFloatParameterDefault( "divingDownwardsImpulseStrength", 1.0 ) * GetSwimmingImpulseMultiplier( scriptInterface, timeDelta ) );
		}
		UpdateFastDivingStroke( timeDelta, stateContext, scriptInterface );
	}

	private function UpdateFastDivingStroke( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var timeBetweenSwimmingStroke : Float;
		m_lapsedTime += timeDelta;
		timeBetweenSwimmingStroke = GetStaticFloatParameterDefault( "timeBetweenFastDivingStroke", 1.0 );
		if( m_lapsedTime >= timeBetweenSwimmingStroke )
		{
			m_lapsedTime = 0.0;
			AddImpulseInMovingDirection( stateContext, scriptInterface, GetStaticFloatParameterDefault( "fastDivingStrokeImpulseForce", 1.0 ) );
			GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'swimming_diving_stroke_fast', false );
		}
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Diving ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

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

}

class SwimmingClimbDecisions extends LocomotionGroundDecisions
{

	protected const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var climbInfo : PlayerClimbInfo;
		var enterAngleThreshold : Float;
		var isObstacleSuitable : Bool;
		var preClimbAnimFeature : AnimFeature_PreClimbing;
		climbInfo = scriptInterface.GetSpatialQueriesSystem().GetPlayerObstacleSystem().GetCurrentClimbInfo( scriptInterface.owner );
		isObstacleSuitable = climbInfo.climbValid && OverlapFitTest( scriptInterface, climbInfo );
		preClimbAnimFeature = new AnimFeature_PreClimbing;
		preClimbAnimFeature.valid = 0.0;
		if( isObstacleSuitable )
		{
			preClimbAnimFeature.edgePositionLS = scriptInterface.TransformInvPointFromObject( climbInfo.descResult.topPoint );
			preClimbAnimFeature.valid = 1.0;
		}
		stateContext.SetConditionScriptableParameter( 'PreClimbAnimFeature', preClimbAnimFeature, true );
		if( GetStaticBoolParameterDefault( "requireForwardEnterAngleToClimb", false ) && !( ForwardAngleTest( stateContext, scriptInterface, climbInfo ) ) )
		{
			return false;
		}
		if( !( GetStaticBoolParameterDefault( "requireDirectionalInputToClimb", false ) ) && ( ( AbsF( scriptInterface.GetInputHeading() ) > 45.0 ) || IsPlayerMovingBackwards( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		if( GetStaticBoolParameterDefault( "requireMinCameraPitchAngleToClimb", false ) && IsCameraPitchAcceptable( stateContext, scriptInterface, GetStaticFloatParameterDefault( "cameraPitchThreshold", -30.0 ) ) )
		{
			return false;
		}
		enterAngleThreshold = GetStaticFloatParameterDefault( "inputAngleThreshold", -180.0 );
		if( !( AbsF( scriptInterface.GetInputHeading() ) <= enterAngleThreshold ) )
		{
			return false;
		}
		return isObstacleSuitable && scriptInterface.IsActionJustPressed( 'Jump' );
	}

	private const function ForwardAngleTest( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface, climbInfo : PlayerClimbInfo ) : Bool
	{
		var obstaclePosition : Vector4;
		var playerForward : Vector4;
		var enterAngleThreshold : Float;
		var forwardAngleDifference : Float;
		playerForward = scriptInterface.GetOwnerForward();
		obstaclePosition = climbInfo.descResult.collisionNormal;
		forwardAngleDifference = Vector4.GetAngleBetween( -( obstaclePosition ), playerForward );
		enterAngleThreshold = GetStaticFloatParameterDefault( "obstacleEnterAngleThreshold", -180.0 );
		if( ( forwardAngleDifference < enterAngleThreshold ) && ( ( forwardAngleDifference - 180.0 ) < enterAngleThreshold ) )
		{
			return true;
		}
		return false;
	}

	private const function OverlapFitTest( const scriptInterface : StateGameScriptInterface, climbInfo : PlayerClimbInfo ) : Bool
	{
		var rotation : EulerAngles;
		var crouchOverlap : Bool;
		var fitTestOvelap : TraceResult;
		var playerCapsuleDimensions : Vector4;
		var queryPosition : Vector4;
		var tolerance : Float;
		tolerance = 0.15000001;
		playerCapsuleDimensions.X = GetStaticFloatParameterDefault( "capsuleRadius", 0.40000001 );
		playerCapsuleDimensions.Y = -1.0;
		playerCapsuleDimensions.Z = -1.0;
		queryPosition = climbInfo.descResult.topPoint + ( GetUpVector() * ( playerCapsuleDimensions.X + tolerance ) );
		crouchOverlap = scriptInterface.OverlapWithASingleGroup( playerCapsuleDimensions, queryPosition, rotation, 'PlayerBlocker', fitTestOvelap );
		return !( crouchOverlap );
	}

}

class SwimmingClimbEvents extends ClimbEvents
{

	protected override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnEnter( stateContext, scriptInterface );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Climbing ) ) );
	}

	protected override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Default ) ) );
		super.OnExit( stateContext, scriptInterface );
	}

	protected override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Default ) ) );
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class SwimmingLadderDecisions extends LocomotionGroundDecisions
{

	protected const function TestParameters( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface, out ladderParameter : LadderDescription ) : Bool
	{
		var ladderFinishedParameter : StateResultBool;
		ladderParameter = ( ( LadderDescription )( stateContext.GetTemporaryScriptableParameter( 'usingLadder' ) ) );
		if( !( ladderParameter ) )
		{
			ladderParameter = ( ( LadderDescription )( stateContext.GetConditionScriptableParameter( 'usingLadder' ) ) );
			ladderFinishedParameter = stateContext.GetTemporaryBoolParameter( 'exitLadder' );
			if( ladderFinishedParameter.valid && ladderFinishedParameter.value )
			{
				stateContext.RemoveConditionScriptableParameter( 'usingLadder' );
				return false;
			}
			if( !( ladderParameter ) )
			{
				return false;
			}
		}
		else
		{
			stateContext.SetConditionScriptableParameter( 'usingLadder', ladderParameter, true );
		}
		return true;
	}

	protected const function TestMath( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface, ladderParameter : LadderDescription ) : Bool
	{
		var onGround : Bool;
		var playerPosition : Vector4;
		var ladderPosition : Vector4;
		var playerVelocity : Vector4;
		var directionToLadder : Vector4;
		var playerForward : Vector4;
		var ladderEntityAngle : Float;
		var playerMoveDirection : Float;
		var fromBottomFactor : Float;
		var enterAngleThreshold : Float;
		playerPosition = GetPlayerPosition( scriptInterface );
		playerForward = scriptInterface.GetOwnerForward();
		playerVelocity = Vector4.Normalize2D( ( ( Vector4 )( Vector4.RotByAngleXY( playerForward, scriptInterface.GetInputHeading() ) ) ) );
		ladderPosition = ( ladderParameter.position - ladderParameter.normal ) + ( ladderParameter.up * ladderParameter.verticalStepBottom );
		directionToLadder = ladderPosition - playerPosition;
		directionToLadder = Vector4.Normalize2D( directionToLadder );
		ladderEntityAngle = Rad2Deg( AcosF( Vector4.Dot( playerForward, directionToLadder ) ) );
		playerMoveDirection = Rad2Deg( AcosF( Vector4.Dot( playerVelocity, -( ladderParameter.normal ) ) ) );
		enterAngleThreshold = GetStaticFloatParameterDefault( "enterAngleThreshold", 35.0 );
		fromBottomFactor = SgnF( Vector4.Dot( ladderParameter.up, directionToLadder ) );
		onGround = IsTouchingGround( scriptInterface );
		if( !( onGround ) )
		{
			if( ( ( fromBottomFactor > 0.0 ) && ( AbsF( ladderEntityAngle ) < enterAngleThreshold ) ) || ( ( ( fromBottomFactor < 0.0 ) && ( AbsF( ladderEntityAngle ) < enterAngleThreshold ) ) || ( AbsF( ladderEntityAngle - 180.0 ) < enterAngleThreshold ) ) )
			{
				return true;
			}
		}
		else if( scriptInterface.IsMoveInputConsiderable() && ( ( ( ( fromBottomFactor > 0.0 ) && ( AbsF( ladderEntityAngle ) < enterAngleThreshold ) ) && ( AbsF( playerMoveDirection ) < enterAngleThreshold ) ) || ( ( ( fromBottomFactor < 0.0 ) && ( ( AbsF( ladderEntityAngle ) < enterAngleThreshold ) || ( AbsF( ladderEntityAngle - 180.0 ) < enterAngleThreshold ) ) ) && ( AbsF( playerMoveDirection - 180.0 ) < enterAngleThreshold ) ) ) )
		{
			return true;
		}
		return false;
	}

	protected const override function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var ladderParameter : LadderDescription;
		var testParameters : Bool;
		var testMath : Bool;
		testParameters = TestParameters( stateContext, scriptInterface, ladderParameter );
		if( ladderParameter == NULL )
		{
			return false;
		}
		if( !( MeleeTransition.MeleeUseExplorationCondition( stateContext, scriptInterface ) ) )
		{
			return false;
		}
		testMath = TestMath( stateContext, scriptInterface, ladderParameter );
		return testParameters && testMath;
	}

	protected const function ExitCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var finishedLadder : StateResultBool;
		finishedLadder = stateContext.GetTemporaryBoolParameter( 'finishedLadderAction' );
		return finishedLadder.valid && finishedLadder.value;
	}

}

class SwimmingLadderEvents extends LadderEvents
{

	protected override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnEnter( stateContext, scriptInterface );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.Swimming, ( ( Int32 )( gamePSMSwimming.Default ) ) );
	}

	protected function OnTick( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ScriptedPuppet.ReevaluateOxygenConsumption( ( ( ScriptedPuppet )( scriptInterface.owner ) ) );
	}

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

	protected override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.HighLevel ) == ( ( Int32 )( gamePSMHighLevel.SceneTier1 ) ) )
		{
			return;
		}
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class SwimmingForceFreezeDecisions extends LocomotionSwimming
{

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

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

}

class SwimmingForceFreezeEvents extends LocomotionSwimmingEvents
{
}

