function IsFastForwardPossibleInVehicle( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
{
	var isMounted : Bool;
	var mountingInfo : MountingInfo;
	var vehicleObject : VehicleObject;
	var isFFPossibleInVehicle : Bool;
	var hasStatusEffect : Bool;
	var isStationary : Bool;
	isMounted = scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToVehicle );
	isStationary = false;
	isFFPossibleInVehicle = true;
	if( isMounted )
	{
		mountingInfo = scriptInterface.GetMountingInfo( scriptInterface.owner );
		if( vehicleObject = ( ( VehicleObject )( GameInstance.FindEntityByID( scriptInterface.GetGame(), mountingInfo.parentId ) ) ) )
		{
			isStationary = vehicleObject.GetCurrentSpeed() < 0.01;
		}
		hasStatusEffect = StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'AllowFastForwardInVehicle' );
		isFFPossibleInVehicle = hasStatusEffect || isStationary;
	}
	return isFFPossibleInVehicle;
}

abstract class ScenesFastForwardTransition extends DefaultTransition
{

	protected const function SetFastForwardAvailableBB( const scriptInterface : StateGameScriptInterface, newVal : Bool ) {}

	protected const function SetFastForwardActiveBB( const scriptInterface : StateGameScriptInterface, newVal : Bool ) {}

	protected const function GetDebugFFConditionParam( const stateContext : StateContext ) : Bool
	{
		var result : StateResultBool;
		result = stateContext.GetConditionBoolParameter( 'debugFF' );
		return result.valid && result.value;
	}

	protected function CleanupFastForward( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		HideFFButtonPrompt( scriptInterface.executionOwner );
		StopGlitchFx( scriptInterface );
		stateContext.RemovePermanentBoolParameter( 'FFRestriction' );
		stateContext.RemovePermanentBoolParameter( 'FFCrouchLock' );
		stateContext.RemovePermanentBoolParameter( 'FFhintActive' );
		stateContext.RemovePermanentBoolParameter( 'FFHoldLock' );
		stateContext.RemovePermanentBoolParameter( 'HoldInputFastForwardLock' );
		StatusEffectHelper.RemoveStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FastForward" );
		StatusEffectHelper.RemoveStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FastForwardCrouchLock" );
	}

	protected const function ActivateFastForward( const scriptInterface : StateGameScriptInterface, mode : scnFastForwardMode )
	{
		scriptInterface.GetSceneSystem().GetScriptInterface().FastForwardingActivate( mode );
	}

	protected const function IsFastForwardModeActive( const scriptInterface : StateGameScriptInterface, mode : scnFastForwardMode ) : Bool
	{
		return scriptInterface.GetSceneSystem().GetScriptInterface().IsFastForwardingActive( mode );
	}

	protected const function DeActivateFastForward( const scriptInterface : StateGameScriptInterface )
	{
		scriptInterface.GetSceneSystem().GetScriptInterface().FastForwardingDeactivate();
	}

	protected const function IsFastForwardAvailable( const scriptInterface : StateGameScriptInterface, mode : scnFastForwardMode ) : Bool
	{
		return scriptInterface.GetSceneSystem().GetScriptInterface().IsFastForwardingAllowed( mode );
	}

	protected const function FastForwardInputValid( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return scriptInterface.IsActionJustPressed( 'SceneFastForward' );
	}

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

	protected const function ProcessHoldInputFastForwardLock( stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		if( stateContext.GetBoolParameter( 'HoldInputFastForwardLock', true ) )
		{
			if( scriptInterface.GetActionValue( 'ToggleCrouch' ) == 0.0 )
			{
				stateContext.RemovePermanentBoolParameter( 'HoldInputFastForwardLock' );
			}
		}
	}

	protected const function StartGlitchFx( const scriptInterface : StateGameScriptInterface )
	{
		var blackboard : worldEffectBlackboard;
		blackboard = new worldEffectBlackboard;
		GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'transition_glitch_loop', false, blackboard );
	}

	protected const function StopGlitchFx( const scriptInterface : StateGameScriptInterface )
	{
		GameObjectEffectHelper.StopEffectEvent( scriptInterface.executionOwner, 'transition_glitch_loop' );
	}

	protected const function GetFFSceneThrehsoldFromBraindanceSystem( const scriptInterface : StateGameScriptInterface ) : Int32
	{
		var bdSystem : BraindanceSystem;
		bdSystem = ( ( BraindanceSystem )( scriptInterface.GetScriptableSystem( 'BraindanceSystem' ) ) );
		return bdSystem.GetDebugFFSceneThreshold();
	}

	public static function DisplayFFButtonPrompt( owner : GameObject )
	{
		var evt : UpdateInputHintEvent;
		var data : InputHintData;
		data.action = 'SceneFastForward';
		data.source = 'FastForward';
		data.localizedLabel = "LocKey#35482";
		data.sortingPriority = ( ( Int32 )( gameuiInputHintSortingPriority.Top ) );
		data.holdIndicationType = GetFFButtonType( owner );
		evt = new UpdateInputHintEvent;
		evt.data = data;
		evt.show = true;
		evt.targetHintContainer = 'GameplayInputHelper';
		StatusEffectHelper.ApplyStatusEffect( owner, T"GameplayRestriction.FastForwardHintActive" );
		GameInstance.GetUISystem( owner.GetGame() ).QueueEvent( evt );
	}

	public static function GetFFButtonType( owner : GameObject ) : inkInputHintHoldIndicationType
	{
		if( IsFastForwardByLine( owner ) )
		{
			return inkInputHintHoldIndicationType.Press;
		}
		else
		{
			return inkInputHintHoldIndicationType.Hold;
		}
	}

	public static function HideFFButtonPrompt( owner : GameObject )
	{
		var evt : UpdateInputHintEvent;
		var data : InputHintData;
		data.action = 'SceneFastForward';
		data.source = 'FastForward';
		evt = new UpdateInputHintEvent;
		evt.data = data;
		evt.show = false;
		evt.targetHintContainer = 'GameplayInputHelper';
		StatusEffectHelper.RemoveStatusEffect( owner, T"GameplayRestriction.FastForwardHintActive" );
		GameInstance.GetUISystem( owner.GetGame() ).QueueEvent( evt );
	}

	protected const function IsLookingAtDialogueEntity( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var hudManager : HUDManager;
		var npc : ScriptedPuppet;
		hudManager = ( ( HUDManager )( scriptInterface.GetScriptableSystem( 'HUDManager' ) ) );
		if( hudManager )
		{
			npc = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( scriptInterface.GetGame(), hudManager.GetCurrentTargetID() ) ) );
			if( npc && ( npc != scriptInterface.executionOwner ) )
			{
				return scriptInterface.GetSceneSystem().GetScriptInterface().IsEntityInDialogue( hudManager.GetCurrentTargetID() ) || scriptInterface.GetSceneSystem().GetScriptInterface().IsEntityInDialogue( scriptInterface.executionOwnerEntityID );
			}
		}
		return false;
	}

	protected const function IsBlockedByPhoneCallRestriction( scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsFastForwardByLine( scriptInterface.executionOwner ) )
		{
			return StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'PhoneCall' );
		}
		else
		{
			return false;
		}
	}

	protected const function PhoneBBStateBlockingFF( scriptInterface : StateGameScriptInterface ) : Bool
	{
		var blackboardDef : UI_ComDeviceDef;
		var blackboard : IBlackboard;
		var infoVariant : Variant;
		var lastPhoneCallInformation : PhoneCallInformation;
		blackboardDef = GetAllBlackboardDefs().UI_ComDevice;
		blackboard = scriptInterface.GetBlackboardSystem().Get( blackboardDef );
		infoVariant = blackboard.GetVariant( GetAllBlackboardDefs().UI_ComDevice.PhoneCallInformation );
		if( infoVariant.IsValid() )
		{
			lastPhoneCallInformation = ( ( PhoneCallInformation )infoVariant );
			return lastPhoneCallInformation.callPhase == questPhoneCallPhase.StartCall && ( GetSceneTier( scriptInterface ) < 2 );
		}
		return false;
	}

}

class FastForwardUnavailableDecisions extends ScenesFastForwardTransition
{

	protected export const function ToFastForwardAvailable( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ( ( ( ( ( ( ( !( scriptInterface.IsPlayerInBraindance() ) && !( IsPlayerInCombat( scriptInterface ) ) ) && ( GetSceneTier( scriptInterface ) < 5 ) ) && IsFastForwardPossibleInVehicle( stateContext, scriptInterface ) ) && !( scriptInterface.IsEntityInCombat() ) ) && !( scriptInterface.IsFalling() ) ) && !( IsBlockedByPhoneCallRestriction( scriptInterface ) ) ) && !( scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsPlayerInsideElevator ) ) ) && ( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) || IsFastForwardAvailable( scriptInterface, scnFastForwardMode.GameplayReview ) );
	}

}

class FastForwardUnavailableEvents extends ScenesFastForwardTransition
{

	public export virtual function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		DeActivateFastForward( scriptInterface );
		CleanupFastForward( stateContext, scriptInterface );
	}

}

class FastForwardAvailableDecisions extends ScenesFastForwardTransition
{

	protected export const function ToFastForwardUnavailable( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ( ( ( ( ( ( scriptInterface.IsPlayerInBraindance() || IsPlayerInCombat( scriptInterface ) ) || ( GetSceneTier( scriptInterface ) > 4 ) ) || scriptInterface.IsEntityInCombat() ) || !( IsFastForwardPossibleInVehicle( stateContext, scriptInterface ) ) ) || scriptInterface.IsFalling() ) || IsBlockedByPhoneCallRestriction( scriptInterface ) ) || scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsPlayerInsideElevator );
	}

	protected export const function ToFastForwardActive( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( GetSceneTier( scriptInterface ) < 3 )
		{
			if( IsFastForwardByLine( scriptInterface.executionOwner ) )
			{
				if( ( ( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) && FastForwardInputValid( scriptInterface ) ) && !( stateContext.GetBoolParameter( 'FFRestriction', true ) ) ) && !( stateContext.GetBoolParameter( 'HoldInputFastForwardLock', true ) ) )
				{
					return true;
				}
			}
			else
			{
				if( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) && stateContext.GetBoolParameter( 'TriggerFF', true ) )
				{
					return true;
				}
			}
		}
		else if( ( GetSceneTier( scriptInterface ) == 4 ) || ( GetSceneTier( scriptInterface ) == 3 ) )
		{
			if( IsFastForwardByLine( scriptInterface.executionOwner ) )
			{
				if( ( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) && FastForwardInputValid( scriptInterface ) ) && !( stateContext.GetBoolParameter( 'FFRestriction', true ) ) )
				{
					return true;
				}
			}
			else
			{
				if( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) && stateContext.GetBoolParameter( 'TriggerFF', true ) )
				{
					return true;
				}
			}
		}
		return false;
	}

}

class FastForwardAvailableEvents extends ScenesFastForwardTransition
{
	var forceCloseFX : Bool;
	var delay : Float;

	public export virtual function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( IsFastForwardModeActive( scriptInterface, scnFastForwardMode.GameplayReview ) || IsFastForwardModeActive( scriptInterface, scnFastForwardMode.Default ) )
		{
			DeActivateFastForward( scriptInterface );
		}
		HideFFButtonPrompt( scriptInterface.executionOwner );
		stateContext.RemovePermanentBoolParameter( 'FFhintActive' );
		forceCloseFX = false;
		delay = 0.0;
	}

	public export virtual function OnUpdate( timeDelta : Float, stateContext : StateContext, const scriptInterface : StateGameScriptInterface )
	{
		var isControllingCamera : Bool;
		var isQuestNotificationPoppedUp : Bool;
		var psmBlackboard : IBlackboard;
		var uiBlackboard : IBlackboard;
		psmBlackboard = scriptInterface.GetBlackboardSystem().GetLocalInstanced( scriptInterface.GetPlayerSystem().GetLocalPlayerControlledGameObject().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		uiBlackboard = scriptInterface.GetBlackboardSystem().Get( GetAllBlackboardDefs().UIInteractions );
		isControllingCamera = psmBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsControllingCamera );
		isQuestNotificationPoppedUp = uiBlackboard.GetBool( GetAllBlackboardDefs().UIInteractions.IsQuestNotificationUp );
		if( ( !( forceCloseFX ) && ( GetInStateTime() > 0.30000001 ) ) && !( IsTimeDilationActive( stateContext, scriptInterface, 'SceneSystemFastForward' ) ) )
		{
			StopGlitchFx( scriptInterface );
			forceCloseFX = true;
		}
		if( stateContext.GetBoolParameter( 'FFRestriction', true ) && !( IsTimeDilationActive( stateContext, scriptInterface, 'SceneSystemFastForward' ) ) )
		{
			delay += 0.1;
			if( delay > 0.1 )
			{
				stateContext.SetPermanentBoolParameter( 'FFCrouchLock', true, true );
				stateContext.RemovePermanentBoolParameter( 'FFRestriction' );
				StatusEffectHelper.RemoveStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FastForward" );
				if( IsFastForwardByLine( scriptInterface.executionOwner ) )
				{
					StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FastForwardCrouchLock" );
				}
				delay = 0.0;
			}
		}
		else if( stateContext.GetBoolParameter( 'FFRestriction', true ) && IsTimeDilationActive( stateContext, scriptInterface, 'SceneSystemFastForward' ) )
		{
			timeDelta = 0.0;
		}
		else if( stateContext.GetBoolParameter( 'FFCrouchLock', true ) && !( StatusEffectSystem.ObjectHasStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FastForwardCrouchLock" ) ) )
		{
			stateContext.RemovePermanentBoolParameter( 'FFCrouchLock' );
		}
		if( !( IsFastForwardByLine( scriptInterface.executionOwner ) ) )
		{
			if( ( ( scriptInterface.IsActionJustHeld( 'SceneFastForward' ) && !( stateContext.GetBoolParameter( 'TriggerFF', true ) ) ) && !( isControllingCamera ) ) && !( isQuestNotificationPoppedUp ) )
			{
				stateContext.SetPermanentBoolParameter( 'TriggerFF', true, true );
				stateContext.SetPermanentBoolParameter( 'FFHoldLock', true, true );
			}
			else if( stateContext.GetBoolParameter( 'TriggerFF', true ) && ( scriptInterface.GetActionValue( 'SceneFastForward' ) == 0.0 ) )
			{
				stateContext.RemovePermanentBoolParameter( 'TriggerFF' );
				stateContext.RemovePermanentBoolParameter( 'FFHoldLock' );
			}
		}
		if( GetSceneTier( scriptInterface ) < 3 )
		{
			if( ( ( ( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) && !( IsFastForwardModeActive( scriptInterface, scnFastForwardMode.GameplayReview ) ) ) && !( stateContext.GetBoolParameter( 'FFhintActive', true ) ) ) && !( isControllingCamera ) ) && !( isQuestNotificationPoppedUp ) )
			{
				stateContext.SetPermanentBoolParameter( 'FFhintActive', true, true );
				DisplayFFButtonPrompt( scriptInterface.executionOwner );
			}
			else if( ( !( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) ) || IsFastForwardModeActive( scriptInterface, scnFastForwardMode.GameplayReview ) ) && stateContext.GetBoolParameter( 'FFhintActive', true ) )
			{
				HideFFButtonPrompt( scriptInterface.executionOwner );
				stateContext.RemovePermanentBoolParameter( 'FFhintActive' );
			}
		}
		else if( ( GetSceneTier( scriptInterface ) == 4 ) || ( GetSceneTier( scriptInterface ) == 3 ) )
		{
			if( ( ( ( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) && !( IsFastForwardModeActive( scriptInterface, scnFastForwardMode.GameplayReview ) ) ) && !( stateContext.GetBoolParameter( 'FFhintActive', true ) ) ) && !( isControllingCamera ) ) && !( isQuestNotificationPoppedUp ) )
			{
				stateContext.SetPermanentBoolParameter( 'FFhintActive', true, true );
				DisplayFFButtonPrompt( scriptInterface.executionOwner );
			}
			else if( ( !( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) ) || IsFastForwardModeActive( scriptInterface, scnFastForwardMode.GameplayReview ) ) && stateContext.GetBoolParameter( 'FFhintActive', true ) )
			{
				HideFFButtonPrompt( scriptInterface.executionOwner );
				stateContext.RemovePermanentBoolParameter( 'FFhintActive' );
			}
		}
		else if( ( ( GetSceneTier( scriptInterface ) == 1 ) || !( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) ) ) && stateContext.GetBoolParameter( 'FFhintActive', true ) )
		{
			HideFFButtonPrompt( scriptInterface.executionOwner );
			stateContext.RemovePermanentBoolParameter( 'FFhintActive' );
		}
		if( ( IsFastForwardByLine( scriptInterface.executionOwner ) && !( isControllingCamera ) ) && !( isQuestNotificationPoppedUp ) )
		{
			ProcessHoldInputFastForwardLock( stateContext, scriptInterface );
		}
	}

}

class FastForwardActiveDecisions extends ScenesFastForwardTransition
{

	protected export const function ToFastForwardAvailable( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return !( IsFastForwardModeActive( scriptInterface, scnFastForwardMode.Default ) ) && ( GetInStateTime() > 0.1 );
	}

	protected export const function ToFastForwardUnavailable( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ( ( ( ( ( scriptInterface.IsPlayerInBraindance() || IsPlayerInCombat( scriptInterface ) ) || scriptInterface.IsEntityInCombat() ) || !( IsFastForwardPossibleInVehicle( stateContext, scriptInterface ) ) ) || scriptInterface.IsFalling() ) || IsBlockedByPhoneCallRestriction( scriptInterface ) ) || scriptInterface.localBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsPlayerInsideElevator );
	}

}

class FastForwardActiveEvents extends ScenesFastForwardTransition
{

	public export virtual function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var psmBlackboard : IBlackboard;
		var uiBlackboard : IBlackboard;
		var isControllingCamera : Bool;
		var isQuestNotificationPoppedUp : Bool;
		psmBlackboard = scriptInterface.GetBlackboardSystem().GetLocalInstanced( scriptInterface.GetPlayerSystem().GetLocalPlayerControlledGameObject().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		uiBlackboard = scriptInterface.GetBlackboardSystem().Get( GetAllBlackboardDefs().UIInteractions );
		isControllingCamera = psmBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsControllingCamera );
		isQuestNotificationPoppedUp = uiBlackboard.GetBool( GetAllBlackboardDefs().UIInteractions.IsQuestNotificationUp );
		if( isControllingCamera || isQuestNotificationPoppedUp )
		{
			return;
		}
		stateContext.RemovePermanentBoolParameter( 'FFhintActive' );
		stateContext.RemovePermanentBoolParameter( 'HoldInputFastForwardLock' );
		stateContext.SetPermanentBoolParameter( 'FFRestriction', true, true );
		StatusEffectHelper.ApplyStatusEffect( scriptInterface.executionOwner, T"GameplayRestriction.FastForward" );
		if( GetDebugFFConditionParam( stateContext ) )
		{
			ActivateFastForward( scriptInterface, scnFastForwardMode.GameplayReview );
		}
		else
		{
			ActivateFastForward( scriptInterface, scnFastForwardMode.Default );
		}
		HideFFButtonPrompt( scriptInterface.executionOwner );
		StartGlitchFx( scriptInterface );
		GameInstance.GetAudioSystem( scriptInterface.owner.GetGame() ).Play( 'motion_light_fast_2d' );
	}

	public export virtual function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( GetDebugFFConditionParam( stateContext ) )
		{
			stateContext.RemoveConditionBoolParameter( 'debugFF' );
		}
	}

}

class FastForwardSelfRemovalDecisions extends ScenesFastForwardTransition
{

	protected export const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ( ( !( IsFastForwardModeActive( scriptInterface, scnFastForwardMode.GameplayReview ) ) && !( IsFastForwardModeActive( scriptInterface, scnFastForwardMode.Default ) ) ) && !( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.Default ) ) ) && !( IsFastForwardAvailable( scriptInterface, scnFastForwardMode.GameplayReview ) );
	}

}

class FastForwardSelfRemovalEvents extends ScenesFastForwardTransition
{

	public export function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var psmRemove : PSMRemoveOnDemandStateMachine;
		var psmIdent : StateMachineIdentifier;
		psmRemove = new PSMRemoveOnDemandStateMachine;
		CleanupFastForward( stateContext, scriptInterface );
		psmIdent.definitionName = 'ScenesFastForward';
		psmIdent.referenceName = '';
		psmRemove.stateMachineIdentifier = psmIdent;
		scriptInterface.executionOwner.QueueEvent( psmRemove );
	}

}

