//=============================================================================
// KFMG_SwingRide
//=============================================================================
// Custom code for swing ride minigame. Rotates in code to avoid Matinee
//      client/server desync issues doing it directly in the level.  Client
//      simulates movement to reduce hitching caused by lower server tick rate.
//
// Note: Ticking enabled while rotation is occuring.  Otherwise uses optimized
//      setup from the skeletal actor base class.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Dan Weiss
//=============================================================================

class KFMG_SwingRide extends SkeletalMeshActor
    placeable
    implements(KFInterface_MinigameActor);

/** Amount of time to full speed */
var() float SpinUpTime;

/** Amount of time from deactivation to full stop */
var() float SpinDownTime;

/** Amount of rotation per second (in Rotator units) */
var() float RotationPerSecond;

/** Trigger to start client simulation */
var repnotify bool bSwingActive;

/** Curves for bringing the ride up to speed and to stop */
var InterpCurveFloat SpinUpCurve;
var InterpCurveFloat SpinDownCurve;

replication
{
    if (bNetDirty)
        bSwingActive;
}

simulated event ReplicatedEvent(name VarName)
{
    super.ReplicatedEvent(VarName);

    if (VarName == 'bSwingActive')
    {
        if (bSwingActive)
        {
            Activated(none);
        }
        else
        {
            Deactivated();
        }
    }
}

/** Whether this is in a valid state */
function bool CanBeActivated(KFTrigger_MinigameButton ActivationSource)
{
    return true;
}

/** Called on rigged games to check whether a target at a specific bone can be played */
simulated event bool CanPlayAkEvent(Actor inOwner, name BoneName)
{
    return true;
}

/** Setup accel curves here.  Since they're dynamic based on times, poke around in code to get the values you want */
simulated function SetSpinUpCurve()
{
    SpinUpCurve.Points.Length = 4;

    SpinUpCurve.Points[0].InVal = 0;
    SpinUpCurve.Points[0].OutVal = 0;
    SpinUpCurve.Points[0].InterpMode = CIM_CurveAuto;

    SpinUpCurve.Points[1].InVal = 0.25 * SpinUpTime;
    SpinUpCurve.Points[1].OutVal = RotationPerSecond * 0.15f;
    SpinUpCurve.Points[1].InterpMode = CIM_CurveAuto;

    SpinUpCurve.Points[2].InVal = 0.75 * SpinUpTime;
    SpinUpCurve.Points[2].OutVal = RotationPerSecond * 0.85f;
    SpinUpCurve.Points[2].InterpMode = CIM_CurveAuto;

    SpinUpCurve.Points[3].InVal = SpinUpTime;
    SpinUpCurve.Points[3].OutVal = RotationPerSecond;
    SpinUpCurve.Points[3].InterpMode = CIM_CurveAuto;
}

simulated function SetSpinDownCurve()
{
    SpinDownCurve.Points.Length = 4;

    SpinDownCurve.Points[0].InVal = 0;
    SpinDownCurve.Points[0].OutVal = RotationPerSecond;
    SpinDownCurve.Points[0].InterpMode = CIM_CurveAuto;

    SpinDownCurve.Points[1].InVal = 0.25 * SpinDownTime;
    SpinDownCurve.Points[1].OutVal = RotationPerSecond * 0.85f;
    SpinDownCurve.Points[1].InterpMode = CIM_CurveAuto;

    SpinDownCurve.Points[2].InVal = 0.75 * SpinDownTime;
    SpinDownCurve.Points[2].OutVal = RotationPerSecond * 0.15f;
    SpinDownCurve.Points[2].InterpMode = CIM_CurveAuto;

    SpinDownCurve.Points[3].InVal = SpinDownTime;
    SpinDownCurve.Points[3].OutVal = 0;
    SpinDownCurve.Points[3].InterpMode = CIM_CurveAuto;
}

simulated function Activated(KFTrigger_MinigameButton ActivationSource)
{
    SetTickIsDisabled(false);
    bSwingActive = true;
    SetSpinUpCurve();
    SetTimer(SpinUpTime, false, 'EnableFullRide');
    GoToState('SpinUp');
}

simulated function Deactivated()
{
    if (IsInState('Active') || IsInState('SpinUp'))
    {
        bSwingActive = false;
        SetTimer(SpinDownTime, false, 'RideStopped');
        SetSpinDownCurve();
        GoToState('SpinDown');
    }    
}

/** Default state, do nothing */
auto simulated state Stopped
{
    simulated event BeginState(Name PreviousStateName)
    {
        super.BeginState(PreviousStateName);

        SetTickIsDisabled(true);
    }
}

/** Spin up state.  Accelerate fake rotation up to full speed */
simulated state SpinUp
{
    simulated event Tick(float DeltaTime)
    {
        local Rotator NewRotation;
        global.Tick(DeltaTime);

        NewRotation = Rotation;
        NewRotation.Yaw = (NewRotation.Yaw + (EvalInterpCurveFloat(SpinUpCurve, GetTimerCount('EnableFullRide')) * DeltaTime)) % 65536;

        SetRotation(NewRotation);
    }

    simulated function EnableFullRide()
    {
        GoToState('Active');
    }

    /** Don't allow activation while it's spinning up or spinning down */
    function bool CanBeActivated(KFTrigger_MinigameButton ActivationSource)
    {
        return false;
    }
}

/** Active state.  Maintain rotation until ride is shut down */
simulated state Active
{
    simulated event Tick(float DeltaTime)
    {
        local Rotator NewRotation;
        global.Tick(DeltaTime);

        NewRotation = Rotation;
        NewRotation.Yaw = (NewRotation.Yaw + (RotationPerSecond * DeltaTime)) % 65536;

        SetRotation(NewRotation);
    }
}

/** Spin down state.  Decelerate fake rotation until stopped */
function RideStopped();
simulated state SpinDown
{
    simulated event Tick(float DeltaTime)
    {
        local Rotator NewRotation;
        global.Tick(DeltaTime);

        NewRotation = Rotation;
        NewRotation.Yaw = (NewRotation.Yaw + (EvalInterpCurveFloat(SpinDownCurve, GetTimerCount('RideStopped')) * DeltaTime)) % 65536;

        SetRotation(NewRotation);
    }

    simulated function RideStopped()
    {
        GoToState('Stopped');
    }

    /** Don't allow activation while it's spinning up or spinning down */
    function bool CanBeActivated(KFTrigger_MinigameButton ActivationSource)
    {
        return false;
    }
}

defaultproperties
{
    SpinUpTime = 1.5f
    SpinDownTime = 1.5f

    RemoteRole = ROLE_SimulatedProxy
    NetPriority = +00002.500000
    bUpdateSimulatedPosition = true
    bAlwaysRelevant = true
}