//============================================================================= // PlayerInput // Object within playercontroller that manages player input. // only spawned on client //============================================================================= class PlayerInput extends Object within PlayerController config(User) native transient; var globalconfig bool bInvertMouse; var bool bWasForward; // used for doubleclick move var bool bWasBack; var bool bWasLeft; var bool bWasRight; var bool bEdgeForward; var bool bEdgeBack; var bool bEdgeLeft; var bool bEdgeRight; var bool bAdjustSampling; // Mouse smoothing var globalconfig byte MouseSmoothingMode; var globalconfig float MouseSmoothingStrength; var globalconfig float MouseSensitivity; var globalconfig float MouseSamplingTime; var globalconfig float MouseAccelThreshold; var float SmoothedMouse[2], ZeroTime[2], SamplingTime[2], MaybeTime[2], OldSamples[4]; var int MouseSamples[2]; var float DoubleClickTimer; // max double click interval for double click move var globalconfig float DoubleClickTime; var globalconfig bool bEnableDodging; //============================================================================= // Input related functions. function bool InvertLook(); // Postprocess the player's input. event PlayerInput( float DeltaTime ) { local float FOVScale, MouseScale; // Ignore input if we're playing back a client-side demo. if( Outer.bDemoOwner && !Outer.default.bDemoOwner ) return; // Check for Double click move // flag transitions 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); // Smooth and amplify mouse movement FOVScale = DesiredFOV * 0.01111; // 0.01111 = 1/90 MouseScale = MouseSensitivity * FOVScale; aMouseX = SmoothMouse(aMouseX*MouseScale, DeltaTime,bXAxis,0); aMouseY = SmoothMouse(aMouseY*MouseScale, DeltaTime,bYAxis,1); aMouseX = AccelerateMouse(aMouseX); aMouseY = AccelerateMouse(aMouseY); // adjust keyboard and joystick movements aLookUp *= FOVScale; aTurn *= FOVScale; // Remap raw x-axis movement. if( bStrafe!=0 ) // strafe aStrafe += aBaseX * 7.5 + aMouseX; else // forward aTurn += aBaseX * FOVScale + aMouseX; aBaseX = 0; // Remap mouse y-axis movement. if( (bStrafe == 0) && (bAlwaysMouseLook || (bLook!=0)) ) { // Look up/down. if ( bInvertMouse ) aLookUp -= aMouseY; else aLookUp += aMouseY; } else // Move forward/backward. aForward += aMouseY; if ( bSnapLevel != 0 ) { bCenterView = true; bKeyboardLook = false; } else if (aLookUp != 0) { bCenterView = false; bKeyboardLook = true; } else if ( bSnapToLevel && !bAlwaysMouseLook ) { bCenterView = true; bKeyboardLook = false; } // Remap other y-axis movement. if ( bFreeLook != 0 ) { bKeyboardLook = true; aLookUp += 0.5 * aBaseY * FOVScale; } else aForward += aBaseY; aBaseY = 0; // Handle walking. HandleWalking(); } exec function SetSmoothingMode(byte B) { MouseSmoothingMode = B; // log("Smoothing mode "$MouseSmoothingMode); } exec function SetSmoothingStrength(float F) { MouseSmoothingStrength = FClamp(F,0,1); default.MouseSmoothingStrength = MouseSmoothingStrength; class'PlayerInput'.static.StaticSaveConfig(); } function float AccelerateMouse(float aMouse) { local float Accel; if ( abs(aMouse) == 0 ) return 0; Accel = MouseAccelThreshold * MouseSensitivity; if ( abs(aMouse) < Accel ) { if ( abs(aMouse) < 0.1 * Accel ) aMouse *= 0.1; else aMouse = aMouse * abs(aMouse)/Accel; } return aMouse; } function float SmoothMouse(float aMouse, float DeltaTime, out byte SampleCount, int Index) { local int i, sum; if ( MouseSmoothingMode == 0 ) return aMouse; if ( aMouse == 0 ) { ZeroTime[Index] += DeltaTime; if ( ZeroTime[Index] < MouseSamplingTime ) { SamplingTime[Index] += DeltaTime; MaybeTime[Index] += DeltaTime; aMouse = SmoothedMouse[Index]; } else { if ( bAdjustSampling && (MouseSamples[Index] > 9) ) { SamplingTime[Index] -= MaybeTime[Index]; MouseSamplingTime = 0.9 * MouseSamplingTime + 0.1 * SamplingTime[Index]/MouseSamples[Index]; } SamplingTime[Index] = 0; SmoothedMouse[Index] = 0; MouseSamples[Index] = 0; } } else { MaybeTime[Index] = 0; if ( SmoothedMouse[Index] != 0 ) { MouseSamples[Index] += SampleCount; if ( DeltaTime > MouseSamplingTime * (SampleCount + 1) ) SamplingTime[Index] += MouseSamplingTime * SampleCount; else { SamplingTime[Index] += DeltaTime; aMouse = aMouse * DeltaTime/(MouseSamplingTime * SampleCount); } } else SamplingTime[Index] = 0.5 * MouseSamplingTime; SmoothedMouse[Index] = aMouse/SampleCount; ZeroTime[Index] = 0; } SampleCount = 0; if ( MouseSmoothingMode > 1 ) { if ( aMouse == 0 ) { // stop in next tick for ( i=0; i<3; i++ ) { sum += (i+1) * 0.1; aMouse += sum * OldSamples[i]; OldSamples[i] = 0; } OldSamples[3] = 0; } else { aMouse = 0.4 * aMouse; OldSamples[3] = aMouse; for ( i=0; i<3; i++ ) { aMouse += (i+1) * 0.1 * OldSamples[i]; OldSamples[i] = OldSamples[i+1]; } } } return aMouse; } function UpdateSensitivity(float F) { MouseSensitivity = FMax(0,F); default.MouseSensitivity = MouseSensitivity; class'PlayerInput'.static.StaticSaveConfig(); } function UpdateAccel(float F) { MouseAccelThreshold = FMax(0,f); default.MouseAccelThreshold = MouseAccelThreshold; class'PlayerInput'.static.StaticSaveConfig(); } function UpdateSmoothing( int Mode ) { MouseSmoothingMode = Mode; default.MouseSmoothingMode = MouseSmoothingMode; class'PlayerInput'.static.StaticSaveConfig(); } function InvertMouse(optional string Invert) { if (Invert != "") bInvertMouse = bool(Invert); else bInvertMouse = !bInvertMouse; SaveConfig(); default.bInvertMouse = bInvertMouse; } function ChangeSnapView( bool B ) { bSnapToLevel = B; } // check for double click move function Actor.eDoubleClickDir CheckForDoubleClickMove(float DeltaTime) { local Actor.eDoubleClickDir DoubleClickMove, OldDoubleClick; if (!bEnableDodging) { DoubleClickMove = DCLICK_None; return DoubleClickMove; } 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; } defaultproperties { bInvertMouse=false bAdjustSampling=true MouseSamplingTime=+0.008333 MouseSmoothingStrength=+0.3 MouseSmoothingMode=1 MouseSensitivity=3.000000 MouseAccelThreshold=0.0 DoubleClickTime=0.250000 bEnableDodging=true }