584 lines
16 KiB
Ucode
584 lines
16 KiB
Ucode
//=============================================================================
|
|
// PlayerInput
|
|
// Object within playercontroller that manages player input.
|
|
// only spawned on client
|
|
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
|
//=============================================================================
|
|
|
|
class PlayerInput extends Input within PlayerController
|
|
config(Input)
|
|
transient
|
|
native(UserInterface);
|
|
|
|
cpptext
|
|
{
|
|
/**
|
|
* Generates an IE_Released event for each key in the PressedKeys array, then clears the array. Should be called when another
|
|
* interaction which swallows some (but perhaps not all) input is activated.
|
|
*/
|
|
virtual void FlushPressedKeys();
|
|
|
|
/** Override to detect input from a gamepad */
|
|
virtual UBOOL InputKey(INT ControllerId, FName Key, enum EInputEvent Event, FLOAT AmountDepressed = 1.f, UBOOL bGamepad = FALSE );
|
|
virtual UBOOL InputAxis(INT ControllerId, FName Key, FLOAT Delta, FLOAT DeltaTime, UBOOL bGamepad=FALSE);
|
|
virtual UBOOL InputTouch(INT ControllerId, UINT Handle, ETouchType Type, FVector2D TouchLocation, DOUBLE DeviceTimestamp, UINT TouchpadIndex=0);
|
|
virtual UBOOL InputMotion(INT ControllerId, const FVector& Tilt, const FVector& RotationRate, const FVector& Gravity, const FVector& Acceleration);
|
|
virtual void UpdateAxisValue( FLOAT* Axis, FLOAT Delta );
|
|
virtual UBOOL IsGamepadKey(FName Name) const;
|
|
}
|
|
|
|
/** Player is giving input through a gamepad */
|
|
var const bool bUsingGamepad;
|
|
var const Name LastAxisKeyName;
|
|
|
|
var globalconfig bool bInvertMouse; /** if true, mouse y axis is inverted from normal FPS mode */
|
|
var globalconfig bool bInvertTurn; /** if true, mouse x axis is inverted from normal FPS mode */
|
|
|
|
// Double click move flags
|
|
var bool bWasForward;
|
|
var bool bWasBack;
|
|
var bool bWasLeft;
|
|
var bool bWasRight;
|
|
var bool bEdgeForward;
|
|
var bool bEdgeBack;
|
|
var bool bEdgeLeft;
|
|
var bool bEdgeRight;
|
|
|
|
var float DoubleClickTimer; /** max double click interval for double click move */
|
|
var globalconfig float DoubleClickTime; /** stores time of first click for potential double click */
|
|
|
|
var globalconfig float MouseSensitivity;
|
|
|
|
// Input axes.
|
|
var input float aBaseX;
|
|
var input float aBaseY;
|
|
var input float aBaseZ;
|
|
var input float aMouseX;
|
|
var input float aMouseY;
|
|
var input float aForward;
|
|
var input float aTurn;
|
|
var input float aStrafe;
|
|
var input float aUp;
|
|
var input float aLookUp;
|
|
|
|
// analog trigger axes
|
|
var input float aRightAnalogTrigger;
|
|
var input float aLeftAnalogTrigger;
|
|
|
|
// PS3 SIXAXIS axes
|
|
var input float aPS3AccelX;
|
|
var input float aPS3AccelY;
|
|
var input float aPS3AccelZ;
|
|
var input float aPS3Gyro;
|
|
|
|
// WiiU IR Pointer
|
|
var input float aWiiUPointerX;
|
|
var input float aWiiUPointerY;
|
|
|
|
|
|
// device tilt - NOTE: These are set directly, they DO NOT go through the .ini Bindings
|
|
var input vector aTilt;
|
|
var input vector aRotationRate;
|
|
var input vector aGravity;
|
|
var input vector aAcceleration;
|
|
// touch locations, the Z component is > 0 if the touch is currently held down
|
|
var input vector aTouch[5];
|
|
var input vector aBackTouch[5];
|
|
|
|
//
|
|
// Joy Raw Input
|
|
//
|
|
/** Joypad left thumbstick, vertical axis. Range [-1,+1] */
|
|
var transient float RawJoyUp;
|
|
/** Joypad left thumbstick, horizontal axis. Range [-1,+1] */
|
|
var transient float RawJoyRight;
|
|
/** Joypad right thumbstick, horizontal axis. Range [-1,+1] */
|
|
var transient float RawJoyLookRight;
|
|
/** Joypad right thumbstick, vertical axis. Range [-1,+1] */
|
|
var transient float RawJoyLookUp;
|
|
|
|
/** move forward speed scaling */
|
|
var() config float MoveForwardSpeed;
|
|
/** strafe speed scaling */
|
|
var() config float MoveStrafeSpeed;
|
|
/** Yaw turn speed scaling */
|
|
var() config float LookRightScale;
|
|
/** pitch turn speed scaling */
|
|
var() config float LookUpScale;
|
|
|
|
|
|
// Input buttons.
|
|
var input byte bStrafe;
|
|
var input byte bXAxis;
|
|
var input byte bYAxis;
|
|
|
|
// Mouse smoothing control
|
|
var globalconfig bool bEnableMouseSmoothing; /** if true, mouse smoothing is enabled */
|
|
|
|
// Zoom Scaling
|
|
var bool bEnableFOVScaling;
|
|
|
|
// Mouse smoothing sample data
|
|
var float ZeroTime[2]; /** How long received mouse movement has been zero. */
|
|
var float SmoothedMouse[2]; /** Current average mouse movement/sample */
|
|
var int MouseSamples; /** Number of mouse samples since mouse movement has been zero */
|
|
var float MouseSamplingTotal; /** DirectInput's mouse sampling total time */
|
|
|
|
/** If TRUE turn input will be ignored until the stick is released */
|
|
var transient bool bLockTurnUntilRelease;
|
|
/** Time remaining to disable bLockTurnUntilRelease */
|
|
var transient float AutoUnlockTurnTime;
|
|
|
|
/**
|
|
* Function to cancel all mobile input before a Scaleform movie confuses the state
|
|
*/
|
|
native function CancelMobileInput();
|
|
|
|
//=============================================================================
|
|
// Input related functions.
|
|
|
|
exec function bool InvertMouse()
|
|
{
|
|
bInvertMouse = !bInvertMouse;
|
|
SaveConfig();
|
|
return bInvertMouse;
|
|
}
|
|
|
|
exec function bool InvertTurn()
|
|
{
|
|
bInvertTurn = !bInvertTurn;
|
|
SaveConfig();
|
|
return bInvertTurn;
|
|
}
|
|
|
|
exec function SetSensitivity(Float F)
|
|
{
|
|
MouseSensitivity = F;
|
|
}
|
|
|
|
/** Hook called from HUD actor. Gives access to HUD and Canvas */
|
|
function DrawHUD( HUD H );
|
|
|
|
function PreProcessInput(float DeltaTime);
|
|
function PostProcessInput(float DeltaTime);
|
|
|
|
function AdjustMouseSensitivity(float FOVScale)
|
|
{
|
|
// Apply mouse sensitivity.
|
|
aMouseX *= MouseSensitivity * FOVScale;
|
|
aMouseY *= MouseSensitivity * FOVScale;
|
|
}
|
|
|
|
// Postprocess the player's input.
|
|
event PlayerInput( float DeltaTime )
|
|
{
|
|
local float FOVScale, TimeScale;
|
|
|
|
// Save Raw values
|
|
RawJoyUp = aBaseY;
|
|
RawJoyRight = aStrafe;
|
|
RawJoyLookRight = aTurn;
|
|
RawJoyLookUp = aLookUp;
|
|
|
|
// PlayerInput shouldn't take timedilation into account
|
|
DeltaTime /= WorldInfo.TimeDilation;
|
|
if (Outer.bDemoOwner && WorldInfo.NetMode == NM_Client)
|
|
{
|
|
DeltaTime /= WorldInfo.DemoPlayTimeDilation;
|
|
}
|
|
|
|
PreProcessInput( DeltaTime );
|
|
|
|
// Scale to game speed
|
|
TimeScale = 100.f*DeltaTime;
|
|
aBaseY *= TimeScale * MoveForwardSpeed;
|
|
aStrafe *= TimeScale * MoveStrafeSpeed;
|
|
aUp *= TimeScale * MoveStrafeSpeed;
|
|
aTurn *= TimeScale * LookRightScale;
|
|
aLookUp *= TimeScale * LookUpScale;
|
|
|
|
PostProcessInput( DeltaTime );
|
|
|
|
ProcessInputMatching(DeltaTime);
|
|
|
|
// Check for Double click movement.
|
|
CatchDoubleClickInput();
|
|
|
|
// Take FOV into account (lower FOV == less sensitivity).
|
|
|
|
if ( bEnableFOVScaling )
|
|
{
|
|
FOVScale = GetFOVAngle() * 0.01111; // 0.01111 = 1 / 90.0
|
|
}
|
|
else
|
|
{
|
|
FOVScale = 1.0;
|
|
}
|
|
|
|
AdjustMouseSensitivity(FOVScale);
|
|
|
|
// mouse smoothing
|
|
if ( bEnableMouseSmoothing )
|
|
{
|
|
aMouseX = SmoothMouse(aMouseX, DeltaTime,bXAxis,0);
|
|
aMouseY = SmoothMouse(aMouseY, DeltaTime,bYAxis,1);
|
|
}
|
|
|
|
aLookUp *= FOVScale;
|
|
aTurn *= FOVScale;
|
|
|
|
// Turning and strafing share the same axis.
|
|
if( bStrafe > 0 )
|
|
aStrafe += aBaseX + aMouseX;
|
|
else
|
|
aTurn += aBaseX + aMouseX;
|
|
|
|
// Look up/down.
|
|
aLookup += aMouseY;
|
|
if (bInvertMouse)
|
|
{
|
|
aLookup *= -1.f;
|
|
}
|
|
|
|
if (bInvertTurn)
|
|
{
|
|
aTurn *= -1.f;
|
|
}
|
|
|
|
// Forward/ backward movement
|
|
aForward += aBaseY;
|
|
|
|
// Handle walking.
|
|
HandleWalking();
|
|
|
|
// check for turn locking
|
|
if (bLockTurnUntilRelease)
|
|
{
|
|
if (RawJoyLookRight != 0)
|
|
{
|
|
aTurn = 0.f;
|
|
if (AutoUnlockTurnTime > 0.f)
|
|
{
|
|
AutoUnlockTurnTime -= DeltaTime;
|
|
if (AutoUnlockTurnTime < 0.f)
|
|
{
|
|
bLockTurnUntilRelease = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bLockTurnUntilRelease = FALSE;
|
|
}
|
|
}
|
|
|
|
// ignore move input
|
|
// Do not clear RawJoy flags, as we still want to be able to read input.
|
|
if( IsMoveInputIgnored() )
|
|
{
|
|
aForward = 0.f;
|
|
aStrafe = 0.f;
|
|
aUp = 0.f;
|
|
}
|
|
|
|
// ignore look input
|
|
// Do not clear RawJoy flags, as we still want to be able to read input.
|
|
if( IsLookInputIgnored() )
|
|
{
|
|
aTurn = 0.f;
|
|
aLookup = 0.f;
|
|
}
|
|
}
|
|
|
|
function CatchDoubleClickInput()
|
|
{
|
|
if (!IsMoveInputIgnored())
|
|
{
|
|
bEdgeForward = (bWasForward ^^ (aBaseY > 0));
|
|
bEdgeBack = (bWasBack ^^ (aBaseY < 0));
|
|
bEdgeLeft = (bWasLeft ^^ (aStrafe < 0));
|
|
bEdgeRight = (bWasRight ^^ (aStrafe > 0));
|
|
bWasForward = (aBaseY > 0);
|
|
bWasBack = (aBaseY < 0);
|
|
bWasLeft = (aStrafe < 0);
|
|
bWasRight = (aStrafe > 0);
|
|
}
|
|
}
|
|
|
|
// check for double click move
|
|
function Actor.EDoubleClickDir CheckForDoubleClickMove(float DeltaTime)
|
|
{
|
|
local Actor.EDoubleClickDir DoubleClickMove, OldDoubleClick;
|
|
|
|
if ( DoubleClickDir == DCLICK_Active )
|
|
DoubleClickMove = DCLICK_Active;
|
|
else
|
|
DoubleClickMove = DCLICK_None;
|
|
if (DoubleClickTime > 0.0)
|
|
{
|
|
if ( DoubleClickDir == DCLICK_Active )
|
|
{
|
|
if ( (Pawn != None) && (Pawn.Physics == PHYS_Walking) )
|
|
{
|
|
DoubleClickTimer = 0;
|
|
DoubleClickDir = DCLICK_Done;
|
|
}
|
|
}
|
|
else if ( DoubleClickDir != DCLICK_Done )
|
|
{
|
|
OldDoubleClick = DoubleClickDir;
|
|
DoubleClickDir = DCLICK_None;
|
|
|
|
if (bEdgeForward && bWasForward)
|
|
DoubleClickDir = DCLICK_Forward;
|
|
else if (bEdgeBack && bWasBack)
|
|
DoubleClickDir = DCLICK_Back;
|
|
else if (bEdgeLeft && bWasLeft)
|
|
DoubleClickDir = DCLICK_Left;
|
|
else if (bEdgeRight && bWasRight)
|
|
DoubleClickDir = DCLICK_Right;
|
|
|
|
if ( DoubleClickDir == DCLICK_None)
|
|
DoubleClickDir = OldDoubleClick;
|
|
else if ( DoubleClickDir != OldDoubleClick )
|
|
DoubleClickTimer = DoubleClickTime + 0.5 * DeltaTime;
|
|
else
|
|
DoubleClickMove = DoubleClickDir;
|
|
}
|
|
|
|
if (DoubleClickDir == DCLICK_Done)
|
|
{
|
|
DoubleClickTimer = FMin(DoubleClickTimer-DeltaTime,0);
|
|
if (DoubleClickTimer < -0.35)
|
|
{
|
|
DoubleClickDir = DCLICK_None;
|
|
DoubleClickTimer = DoubleClickTime;
|
|
}
|
|
}
|
|
else if ((DoubleClickDir != DCLICK_None) && (DoubleClickDir != DCLICK_Active))
|
|
{
|
|
DoubleClickTimer -= DeltaTime;
|
|
if (DoubleClickTimer < 0)
|
|
{
|
|
DoubleClickDir = DCLICK_None;
|
|
DoubleClickTimer = DoubleClickTime;
|
|
}
|
|
}
|
|
}
|
|
return DoubleClickMove;
|
|
}
|
|
|
|
/**
|
|
* Iterates through all InputRequests on the PlayerController and
|
|
* checks to see if a new input has been matched, or if the entire
|
|
* match sequence should be reset.
|
|
*
|
|
* @param DeltaTime - time since last tick
|
|
*/
|
|
final function ProcessInputMatching(float DeltaTime)
|
|
{
|
|
local float Value;
|
|
local int i,MatchIdx;
|
|
local bool bMatch;
|
|
// iterate through each request,
|
|
for (i = 0; i < InputRequests.Length; i++)
|
|
{
|
|
// if we have a valid match idx
|
|
if (InputRequests[i].MatchIdx >= 0 &&
|
|
InputRequests[i].MatchIdx < InputRequests[i].Inputs.Length)
|
|
{
|
|
if (InputRequests[i].MatchActor == None)
|
|
{
|
|
InputRequests[i].MatchActor = Outer;
|
|
}
|
|
MatchIdx = InputRequests[i].MatchIdx;
|
|
// if we've exceeded the delta,
|
|
// ignore the delta for the first match
|
|
if (MatchIdx != 0 &&
|
|
InputRequests[i].Inputs[MatchIdx].TimeDelta > 0.f &&
|
|
WorldInfo.TimeSeconds - InputRequests[i].LastMatchTime >= InputRequests[i].Inputs[MatchIdx].TimeDelta)
|
|
{
|
|
// reset this match
|
|
InputRequests[i].LastMatchTime = 0.f;
|
|
InputRequests[i].MatchIdx = 0;
|
|
|
|
// fire off the cancel event
|
|
if (InputRequests[i].FailedFuncName != 'None')
|
|
{
|
|
InputRequests[i].MatchActor.SetTimer(0.01f, false, InputRequests[i].FailedFuncName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// grab the current input value of the matching type
|
|
Value = 0.f;
|
|
switch (InputRequests[i].Inputs[MatchIdx].Type)
|
|
{
|
|
case IT_XAxis:
|
|
Value = aStrafe;
|
|
break;
|
|
case IT_YAxis:
|
|
Value = aBaseY;
|
|
break;
|
|
}
|
|
// check to see if this matches
|
|
switch (InputRequests[i].Inputs[MatchIdx].Action)
|
|
{
|
|
case IMA_GreaterThan:
|
|
bMatch = Value >= InputRequests[i].Inputs[MatchIdx].Value;
|
|
break;
|
|
case IMA_LessThan:
|
|
bMatch = Value <= InputRequests[i].Inputs[MatchIdx].Value;
|
|
break;
|
|
}
|
|
if (bMatch)
|
|
{
|
|
// mark it as matched
|
|
InputRequests[i].LastMatchTime = WorldInfo.TimeSeconds;
|
|
InputRequests[i].MatchIdx++;
|
|
// check to see if we've matched all inputs
|
|
if (InputRequests[i].MatchIdx >= InputRequests[i].Inputs.Length)
|
|
{
|
|
if (InputRequests[i].MatchDelegate != None)
|
|
{
|
|
InputMatchDelegate = InputRequests[i].MatchDelegate;
|
|
InputMatchDelegate();
|
|
}
|
|
// fire off the event
|
|
if (InputRequests[i].MatchFuncName != 'None')
|
|
{
|
|
InputRequests[i].MatchActor.SetTimer(0.01f,false,InputRequests[i].MatchFuncName);
|
|
}
|
|
// reset this match
|
|
InputRequests[i].LastMatchTime = 0.f;
|
|
InputRequests[i].MatchIdx = 0;
|
|
// as well as all others
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//*************************************************************************************
|
|
// Normal gameplay execs
|
|
// Type the name of the exec function at the console to execute it
|
|
|
|
exec function Jump()
|
|
{
|
|
if ( WorldInfo.Pauser == PlayerReplicationInfo )
|
|
SetPause( False );
|
|
else
|
|
bPressedJump = true;
|
|
}
|
|
|
|
exec function SmartJump()
|
|
{
|
|
Jump();
|
|
}
|
|
|
|
//*************************************************************************************
|
|
// Mouse smoothing
|
|
|
|
exec function ClearSmoothing()
|
|
{
|
|
local int i;
|
|
|
|
for ( i=0; i<2; i++ )
|
|
{
|
|
//`Log(i$" zerotime "$zerotime[i]$" smoothedmouse "$SmoothedMouse[i]);
|
|
ZeroTime[i] = 0;
|
|
SmoothedMouse[i] = 0;
|
|
}
|
|
//`Log("MouseSamplingTotal "$MouseSamplingTotal$" MouseSamples "$MouseSamples);
|
|
MouseSamplingTotal = Default.MouseSamplingTotal;
|
|
MouseSamples = Default.MouseSamples;
|
|
}
|
|
|
|
/** SmoothMouse()
|
|
Smooth mouse movement, because mouse sampling doesn't match up with tick time.
|
|
* @note: if we got sample event for zero mouse samples (so we
|
|
didn't have to guess whether a 0 was caused by no sample occuring during the tick (at high frame rates) or because the mouse actually stopped)
|
|
* @param: aMouse is the mouse axis movement received from DirectInput
|
|
* @param: DeltaTime is the tick time
|
|
* @param: SampleCount is the number of mouse samples received from DirectInput
|
|
* @param: Index is 0 for X axis, 1 for Y axis
|
|
* @return the smoothed mouse axis movement
|
|
*/
|
|
function float SmoothMouse(float aMouse, float DeltaTime, out byte SampleCount, int Index)
|
|
{
|
|
local float MouseSamplingTime;
|
|
|
|
if (DeltaTime < 0.25)
|
|
{
|
|
MouseSamplingTime = MouseSamplingTotal/MouseSamples;
|
|
|
|
if ( aMouse == 0 )
|
|
{
|
|
// no mouse movement received
|
|
ZeroTime[Index] += DeltaTime;
|
|
if ( ZeroTime[Index] < MouseSamplingTime )
|
|
{
|
|
// zero mouse movement is possibly because less than the mouse sampling interval has passed
|
|
aMouse = SmoothedMouse[Index] * DeltaTime/MouseSamplingTime;
|
|
}
|
|
else
|
|
{
|
|
SmoothedMouse[Index] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ZeroTime[Index] = 0;
|
|
if ( SmoothedMouse[Index] != 0 )
|
|
{
|
|
// this isn't the first tick with non-zero mouse movement
|
|
if ( DeltaTime < MouseSamplingTime * (SampleCount + 1) )
|
|
{
|
|
// smooth mouse movement so samples/tick is constant
|
|
aMouse = aMouse * DeltaTime/(MouseSamplingTime * SampleCount);
|
|
}
|
|
else
|
|
{
|
|
// fewer samples, so going slow
|
|
// use number of samples we should have had for sample count
|
|
SampleCount = DeltaTime/MouseSamplingTime;
|
|
}
|
|
}
|
|
SmoothedMouse[Index] = aMouse/SampleCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we had an abnormally long frame, clear everything so it doesn't distort the results
|
|
ClearSmoothing();
|
|
}
|
|
SampleCount = 0;
|
|
return aMouse;
|
|
}
|
|
|
|
/**
|
|
* The player controller will call this function directly after creating the input system
|
|
*/
|
|
native function InitInputSystem();
|
|
|
|
/**
|
|
* Called via replication to tell the Client to Init it's input system
|
|
*/
|
|
function ClientInitInputSystem();
|
|
|
|
/**
|
|
* The player controll will call this function directly before traveling
|
|
*/
|
|
|
|
function PreClientTravel( string PendingURL, ETravelType TravelType, bool bIsSeamlessTravel)
|
|
{
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
MouseSamplingTotal=+0.0083
|
|
MouseSamples=1
|
|
}
|
|
|