2986 lines
83 KiB
Ucode
2986 lines
83 KiB
Ucode
//=============================================================================
|
|
// KFPlayerInput
|
|
//=============================================================================
|
|
// Base Player Input class
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
class KFPlayerInput extends MobilePlayerInput within KFPlayerController
|
|
config(Input)
|
|
native(Controller);
|
|
|
|
//The player is required to push to talk
|
|
var config bool bRequiresPushToTalk;
|
|
|
|
/** Set to false if we want to use KF1 style weapon switching */
|
|
var bool bQuickWeaponSelect;
|
|
|
|
/** Start time when player last pressed jump. Used by CannotJumpNow() */
|
|
var transient float PressedJumpTime;
|
|
|
|
/** Unlike other weapons states ironsights doesn't use PendingFire (see InventoryManager).
|
|
This concept is useful for Ironsights(HOLD), which is accomplished by this bool. */
|
|
var transient bool bIronsightsHeld;
|
|
|
|
/** Useful to automatically put some weapons in sight mode if the button was holded before the equip */
|
|
var transient bool bIronsightsActive;
|
|
|
|
/*********************************************************************************************
|
|
* @name Gamepad Specific Controls
|
|
********************************************************************************************* */
|
|
|
|
/** Set to true when we have opened the weapon select menu with a controller */
|
|
var bool bGamepadWeaponSelectOpen;
|
|
/** One-time on screen button hint */
|
|
var bool bShowGamepadWeaponSelectHint;
|
|
|
|
/** Set to true if we want to invert the Y on the controller */
|
|
var bool bInvertController;
|
|
|
|
/** The amount of time the weapon switch button must be held to perform various actions */
|
|
var config float GamepadButtonHoldTime;
|
|
|
|
/** The amount of time the interaction button must be held to perform autoupgrade*/
|
|
var config float AutoUpgradeHoldTime;
|
|
|
|
/** Amount thumbstick should be pressed to activate sprint */
|
|
var config float SprintAnalogThreshold;
|
|
|
|
/** Amount thumbstick should be pressed to auto-activate sprint when playing as a zed */
|
|
var const float ZedAutoSprintAnalogThreshold;
|
|
|
|
/** On tap weapon switch: TRUE for last weapon, FALSE for cycle next */
|
|
var config bool bUseGamepadLastWeapon;
|
|
|
|
/** Doing gamepad sprinting that requires the stick to be held most of the way to the side **/
|
|
var transient bool bExtendedSprinting;
|
|
|
|
/** Radial distance from analog center when sprint is pressed */
|
|
var transient float GamepadSprintAnalogStart;
|
|
|
|
/** The index of the preset layout to use for controller mappings. Used with GamepadLayoutManager */
|
|
var int CurrentLayoutIndex;
|
|
|
|
/** cached magnitude of 2d input move vector */
|
|
var transient float RawJoyMagnitude;
|
|
|
|
/** cached magnitude of 2d input look vector */
|
|
var transient float RawJoyLookMagnitude;
|
|
|
|
/*********************************************************************************************
|
|
* @name Aim assists
|
|
********************************************************************************************* */
|
|
|
|
/** Toggles for all aim assists (friction, adhesion, lock-on) */
|
|
var(AimAssistGlobal) config bool bAimAssistEnabled;
|
|
|
|
////////////////////////////////
|
|
// Sensitivity
|
|
|
|
/** Interp curve that allows for piece wise functions for the TargetFrictionDistance amount at different ranges **/
|
|
var(Sensitivity) InterpCurveFloat LookSensitivityScaleCurve;
|
|
var(Sensitivity) InterpCurveFloat MoveSensitivityScaleCurve;
|
|
/** Unified global scalar for joystick sensitivity */
|
|
var(Sensitivity) float GamepadSensitivityScale;
|
|
/** Multiplier used to scale look sensitivity while sprinting. */
|
|
var(Sensitivity) float SprintingSensitivityScale;
|
|
/** Used to scale the sensitivity of the mouse based on how zoomed the player is. */
|
|
var(Sensitivity) config float ZoomedSensitivityScale;
|
|
/** Used to scale the sensitivity of the joystick based on how zoomed the player is. */
|
|
var(Sensitivity) config float GamepadZoomedSensitivityScale;
|
|
/** Used to scale when start detecting input for looking. */
|
|
var(Sensitivity) config float GamepadDeadzoneScale;
|
|
/** Used to scale when to start using acceleration jump */
|
|
var(Sensitivity) config float GamepadAccelerationJumpScale;
|
|
|
|
////////////////////////////////
|
|
// View Smoothing
|
|
|
|
/** Whether to use turn smoothing / blending or not */
|
|
var(ViewSmoothing) bool bViewSmoothingEnabled;
|
|
/** Max acceleration units per second (since the joystick max value is 1, setting to 1 means take 1 second to get to max turn speed if starting at 0) */
|
|
var(ViewSmoothing) float ViewSmoothing_MaxAccel;
|
|
/** Similar to MaxAccel. Should be larger then accel. */
|
|
var(ViewSmoothing) float ViewSmoothing_MaxDecel;
|
|
|
|
/** Vars to keep track of values to blend between for smoothing */
|
|
var transient float PrevTurn, PrevLookUp, CurrTurn, CurrLookUp;
|
|
|
|
////////////////////////////////
|
|
// View Acceleration
|
|
|
|
/** Whether ViewAcceleration is enabled or not **/
|
|
var(ViewAcceleration) config bool bViewAccelerationEnabled;
|
|
/** Joystick vector must be greater than this to trigger view acceleration (magnitude range is 0 to 1, regardless of direction)*/
|
|
var(ViewAcceleration) float ViewAccel_JoyMagThreshold;
|
|
/** Joystick Y-value must be less than this to trigger view acceleration (Y-value range is from -1 to 1) */
|
|
var(ViewAcceleration) float ViewAccel_JoyPitchThreshold;
|
|
/** Max turn speed **/
|
|
var(ViewAcceleration) float ViewAccel_MaxTurnSpeed;
|
|
/** Min turn speed **/
|
|
var(ViewAcceleration) float ViewAccel_MinTurnSpeed;
|
|
/** How long to blend to max turn speed */
|
|
var(ViewAcceleration) float ViewAccel_BlendTime;
|
|
/** Timer for BlendTime */
|
|
var transient float ViewAccel_BlendTimer;
|
|
|
|
var float ViewAccel_TurnSpeed;
|
|
|
|
var config protected bool bDebugViewAcceleration;
|
|
/** Store previous remainder of aTurn for smoothing slow rotations. */
|
|
var float RemainingaTurn;
|
|
/** Store previous remainder of aLookUp for smoothing slow rotations. */
|
|
var float RemainingaLookUp;
|
|
|
|
////////////////////////////////
|
|
// Target Friction
|
|
|
|
/** Whether TargetFriction is enabled or not **/
|
|
var(Friction) bool bTargetFrictionEnabled;
|
|
|
|
/** How much friction reduces rotation */
|
|
var(Friction) float FrictionScale;
|
|
|
|
/** Enables Target Friction debugging **/
|
|
var config protected bool bDebugTargetFriction;
|
|
|
|
/** Last friction target */
|
|
var private Pawn LastFrictionTarget;
|
|
|
|
/** Last friction target acquire time */
|
|
var private float LastFrictionTargetTime;
|
|
|
|
// Friction/Adhesion debugging vars
|
|
var private float LastaTurn, LastaLookUp, LastaForward, LastaStrafe;
|
|
|
|
/** Interp curve to scale Friction angle for different ranges **/
|
|
var(Friction) InterpCurveFloat FrictionAngleCurve;
|
|
|
|
/** How much to scale friction when view acceleration (turn assist) is being applied */
|
|
var(Friction) float ViewAccelerationFrictionScale;
|
|
|
|
////////////////////////////////
|
|
// Target Adhesion
|
|
|
|
/** Whether TargetAdhesion is enabled or not **/
|
|
var(Adhesion) bool bTargetAdhesionEnabled;
|
|
|
|
/** Interp curve to scale Adhesion angle for different ranges **/
|
|
var(Adhesion) InterpCurveFloat AdhesionAngleCurve;
|
|
|
|
/** How strongly adhesion affects player view */
|
|
var(Adhesion) float AdhesionFactor;
|
|
|
|
/** Cached vars to help with adhesion */
|
|
var private Pawn LastAdhesionTarget;
|
|
var transient vector AdhesionTargetLastLoc, AdhesionPawnLastLoc;
|
|
|
|
////////////////////////////////
|
|
// Auto Target
|
|
|
|
/** Whether TargetAdhesion is enabled or not **/
|
|
var(AutoTarget) bool bAutoTargetEnabled;
|
|
|
|
/** How long to auto target for when going to iron sights **/
|
|
var(AutoTarget) float AutoTargetTimeLeft;
|
|
|
|
/** Where we were aiming at the autotargeted pawn was when we first locked on **/
|
|
var vector AutoTargetInitialLocation;
|
|
/** The current Pawn we're locked onto for auto targeting **/
|
|
var Pawn CurrentAutoTarget;
|
|
/** The Target's bone name being followed */
|
|
var name CurrentAutoTargetBone;
|
|
|
|
/** How fast to rotate towards autotarget location**/
|
|
var(AutoTarget) float AutoTargetRotationRate;
|
|
|
|
/** Interp curve to scale autotarget angle for different ranges **/
|
|
var(AutoTarget) InterpCurveFloat AutoTargetAngleCurve;
|
|
var(AutoTarget) InterpCurveFloat AutoTargetWeakspotCurve;
|
|
|
|
/** Disallow auto-target spamming */
|
|
var(AutoTarget) float AutoTargetCooldown;
|
|
var transient float LastAutoTargetTime;
|
|
|
|
/** Force rotation to within this angle when using the ForceLookAtPawn functionality **/
|
|
var(AutoTarget) float ForceLookAtPawnMinAngle;
|
|
/** How fast to rotate towards ForceLookAtPawn location**/
|
|
var(AutoTarget) float ForceLookAtPawnRotationRate;
|
|
/** How fast to rotate towards ForceLookAtPawn location dampened for closer rotation**/
|
|
var(AutoTarget) float ForceLookAtPawnDampenedRotationRate;
|
|
|
|
/*********************************************************************************************
|
|
* @name Force Feedback
|
|
********************************************************************************************* */
|
|
|
|
var(ForceFeedback) bool bForceFeedbackEnabled;
|
|
|
|
/*********************************************************************************************
|
|
* @name Flashlight / Night vision
|
|
********************************************************************************************* */
|
|
var const float DoubleTapDelay;
|
|
|
|
/*********************************************************************************************
|
|
* @name Run/Sprint
|
|
********************************************************************************************* */
|
|
var bool bToggleToRun;
|
|
|
|
/*********************************************************************************************
|
|
* @name Run/Sprint
|
|
********************************************************************************************* */
|
|
var bool bAllowSwapTo9mm;
|
|
|
|
/*********************************************************************************************
|
|
* @name Game class
|
|
********************************************************************************************* */
|
|
var bool bVersusInput;
|
|
/** Cached value of bUsingGamepad so we can handle button releases across devices */
|
|
var bool bUsingVersusGamepadScheme;
|
|
|
|
cpptext
|
|
{
|
|
/** Searches the bind and skips the mainCommand */
|
|
void GetKeyBind(FKeyBind& MyKeyBind, const FString& KeyCommand, UBOOL bAlt, INT* StartBindIndex = NULL );
|
|
|
|
/** Override to detect input from a gamepad */
|
|
virtual UBOOL InputKey(INT ControllerId, FName Key, enum EInputEvent Event, FLOAT AmountDepressed = 1.f, UBOOL bGamepad = FALSE );
|
|
}
|
|
|
|
/** Will return the BindName based on the BindCommand
|
|
* Adds check for gamepad bindings which have _Gamepad appended to them (for the special cases where a bind was modified to work special on the gamepad.)
|
|
*/
|
|
native function GetKeyBindFromCommand( out KeyBind MyKeyBind, String BindCommand, optional bool bAlt );
|
|
|
|
/** Swaps two keys in the .ini so that the first one to be read will be identified as the main bind, and the second will be the alt
|
|
* Called in KFKeyBinding */
|
|
native function SwapBind( out KeyBind MainKeyBind, out KeyBind AltKeyBind );
|
|
|
|
/** Sets the given keybinds command, to command */
|
|
native function SetKeyBind( out KeyBind MyKeyBind, string Command, bool overwritePrevCommand );
|
|
|
|
/** Removes this command from KFInput.ini */
|
|
native function RemoveCommandFromBind( out KeyBind MyKeyBind, string CommandToRemove );
|
|
|
|
/** Returns the index that this key bind was found at */
|
|
native function int GetBindingsIndex( out KeyBind MyKeyBind );
|
|
|
|
/** resets the KFInput.ini */
|
|
native static function ResetKeysToDefault();
|
|
|
|
native exec function SetGamepadLayout(int layoutIndex);
|
|
|
|
/** Used to display in-game controls */
|
|
native function string GetBindDisplayName(out KeyBind MyKeyBind);
|
|
|
|
/** Used to display in-game controls */
|
|
native function string GetGameBindableAction(const out Name Key);
|
|
|
|
event bool FilterButtonInput(int ControllerId, Name Key, EInputEvent Event, float AmountDepressed, bool bGamepad)
|
|
{
|
|
if( !class'Engine'.static.IsEditor() && Event == IE_Pressed && (Key == 'XboxTypeS_Start' || Key == 'Escape') && !class'WorldInfo'.static.IsMenuLevel()
|
|
&& (MyGfxManager.bAfterLobby || WorldInfo.GRI.bMatchIsOver)
|
|
)
|
|
{
|
|
MyGFxManager.ToggleMenus();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Debugging
|
|
********************************************************************************************* */
|
|
|
|
// Displays the number of loaded objects for a particular class and its size
|
|
simulated function DisplayDebug(HUD HUD, out float out_YL, out float out_YPos)
|
|
{
|
|
if (HUD.ShouldDisplayDebug('input'))
|
|
{
|
|
HUD.Canvas.SetDrawColor(0,255,0);
|
|
HUD.Canvas.DrawText("AXIS: ATurn: "$LastaTurn$" aLookUp: "$LastaLookUp);
|
|
out_YPos += out_YL;
|
|
HUD.Canvas.SetPos(4, out_YPos);
|
|
HUD.Canvas.DrawText("Raw: RawJoyLookRight: "$RawJoyLookRight$" RawJoyLookUp: "$RawJoyLookUp);
|
|
out_YPos += out_YL;
|
|
HUD.Canvas.SetPos(4, out_YPos);
|
|
HUD.Canvas.DrawText("Remaining: RemainingaTurn: "$RemainingaTurn$" RemainingaLookUp: "$RemainingaLookUp);
|
|
out_YPos += out_YL;
|
|
HUD.Canvas.SetPos(4, out_YPos);
|
|
HUD.Canvas.DrawText("AXIS: aForward: "$LastaForward$" aStrafe: "$LastaStrafe);
|
|
out_YPos += out_YL;
|
|
HUD.Canvas.SetPos(4, out_YPos);
|
|
HUD.Canvas.DrawText("Raw: RawJoyRight: "$RawJoyRight$" RawJoyUp: "$RawJoyUp);
|
|
out_YPos += out_YL;
|
|
HUD.Canvas.DrawText("Raw: TotalRawStrafe: "$Abs(RawJoyRight) + Abs(RawJoyUp));
|
|
out_YPos += out_YL;
|
|
}
|
|
}
|
|
|
|
function ClientInitInputSystem()
|
|
{
|
|
Super.ClientInitInputSystem();
|
|
|
|
if(bRequiresPushToTalk)
|
|
{
|
|
ClientStopNetworkedVoice();
|
|
}
|
|
else
|
|
{
|
|
ClientStartNetworkedVoice();
|
|
}
|
|
}
|
|
|
|
function UpdatePushToTalk(bool bValue)
|
|
{
|
|
if(bValue != bRequiresPushToTalk)
|
|
{
|
|
if(bValue)
|
|
{
|
|
ClientStopNetworkedVoice();
|
|
}
|
|
else
|
|
{
|
|
ClientStartNetworkedVoice();
|
|
}
|
|
bRequiresPushToTalk = bValue;
|
|
SaveConfig();
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Aiming/Mouse Sensitivity
|
|
********************************************************************************************* */
|
|
|
|
exec function SetSensitivity(Float F)
|
|
{
|
|
MouseSensitivity = F;
|
|
}
|
|
|
|
exec function SetZoomedSensitivity(Float F)
|
|
{
|
|
ZoomedSensitivityScale = F;
|
|
}
|
|
|
|
/** No one ever uses double clicks, so we hijack this function and the variable to store an
|
|
* approximation of the output of the movement sensitivity curve */
|
|
function EDoubleClickDir CheckForDoubleClickMove(float DeltaTime)
|
|
{
|
|
local int MappedOutput;
|
|
local float MinCurveOut, MaxCurveOut, CurveOut, CurvePct;
|
|
|
|
if( !bUsingGamepad || bRun > 0 )
|
|
{
|
|
// max speed (modifer will be 1)
|
|
return EDoubleClickDir( 0 );
|
|
}
|
|
|
|
// Check for autorun in Versus
|
|
if( Pawn != none && Pawn.GetTeamNum() != 0 )
|
|
{
|
|
if( IsDirectingJoyStick(fMax(ZedAutoSprintAnalogThreshold, SprintAnalogThreshold)) )
|
|
{
|
|
bRun = 1;
|
|
bExtendedSprinting = true;
|
|
}
|
|
}
|
|
|
|
// get curve out value and range
|
|
CurveOut = EvalInterpCurveFloat( MoveSensitivityScaleCurve, RawJoyMagnitude );
|
|
MinCurveOut = MoveSensitivityScaleCurve.Points[0].OutVal;
|
|
MaxCurveOut = MoveSensitivityScaleCurve.Points[MoveSensitivityScaleCurve.Points.Length - 1].OutVal;
|
|
|
|
CurvePct = (CurveOut - MinCurveOut) / (MaxCurveOut - MinCurveOut);
|
|
|
|
MappedOutput = EAnalogMovementSpeed.AMOVESPEED_Max - (CurvePct * EAnalogMovementSpeed.AMOVESPEED_Max);
|
|
|
|
return EDoubleClickDir( MappedOutput );
|
|
}
|
|
|
|
// Overridden to not double apply FOV scaling
|
|
event PlayerInput( float DeltaTime )
|
|
{
|
|
local float FOVScale, TimeScale;
|
|
local vector RawJoyVector;
|
|
|
|
// Save Raw values
|
|
RawJoyUp = aBaseY;
|
|
RawJoyRight = aStrafe;
|
|
RawJoyLookRight = aTurn;
|
|
RawJoyLookUp = aLookUp;
|
|
|
|
// cache raw input (left stick) vector
|
|
RawJoyVector.x = RawJoyRight;
|
|
RawJoyVector.y = RawJoyUp;
|
|
RawJoyMagnitude = VSize2d( RawJoyVector );
|
|
|
|
// cache raw input look (right stick) vector
|
|
RawJoyVector.x = RawJoyLookRight;
|
|
RawJoyVector.y = RawJoyLookUp;
|
|
RawJoyLookMagnitude = VSize2d( RawJoyVector );
|
|
|
|
// 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). Only applicable to
|
|
// mouse, Gamepad handled elsewhere
|
|
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 ( (!bUsingGamepad && bInvertMouse) || (bInvertController && bUsingGamepad) )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Add in float remainders from previous turn and look values so that we
|
|
// can get very smooth and very slow movement if we want it
|
|
if( Abs((Aturn%1) + RemainingaTurn) >= 1.0 )
|
|
{
|
|
aTurn += RemainingaTurn;
|
|
RemainingaTurn = 0;
|
|
}
|
|
|
|
if( Abs((aLookup%1) + RemainingaLookUp) >= 1.0 )
|
|
{
|
|
aLookup += RemainingaLookUp;
|
|
RemainingaLookUp = 0;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Store for debugging
|
|
LastaTurn = aTurn;
|
|
LastaLookUp = aLookup;
|
|
LastaForward = aForward;
|
|
LastaStrafe = aStrafe;
|
|
|
|
// Store float remainders for turn and look values so that we
|
|
// can get very smooth and very slow movement if we want it
|
|
if( aTurn != 0 )
|
|
{
|
|
RemainingaTurn += (Aturn%1);
|
|
}
|
|
else
|
|
{
|
|
RemainingaTurn = 0;
|
|
}
|
|
|
|
if( aLookup != 0 )
|
|
{
|
|
RemainingaLookUp += (aLookup%1);
|
|
}
|
|
else
|
|
{
|
|
RemainingaLookUp = 0;
|
|
}
|
|
}
|
|
|
|
/** Overridden to do custom FOV scaling, especially for weapons with 3D scopes */
|
|
function AdjustMouseSensitivity(float FOVScale)
|
|
{
|
|
if ( bEnableFOVScaling )
|
|
{
|
|
FOVScale = GetSensitivityByFOV( ZoomedSensitivityScale );
|
|
}
|
|
|
|
Super.AdjustMouseSensitivity(FOVScale);
|
|
}
|
|
|
|
|
|
// Reinitialize control settings. Used for E3 when settings change via button hacks
|
|
event ReInitializeControlsUI()
|
|
{
|
|
if( MyGFxManager != none &&
|
|
MyGFxManager.OptionsControlsMenu != none &&
|
|
MyGFxManager.OptionsControlsMenu.InputContainer != none )
|
|
{
|
|
MyGFxManager.OptionsControlsMenu.InputContainer.InitializeOptions();
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Keybinding helpers
|
|
********************************************************************************************* */
|
|
|
|
/** Sets and stores a new key binding */
|
|
function BindKey(KeyBind NewKeyBind, string BindCommand, bool bIsAlt)
|
|
{
|
|
local KeyBind CurrentKeyBind;
|
|
|
|
// Set CurrentKeyBind to the first (bIsAlt = false) or second (bIsAlt = true)
|
|
// key bound to a command (commands can never be bound to > 2 keys)
|
|
GetKeyBindFromCommand(CurrentKeyBind, BindCommand, bIsAlt);
|
|
|
|
// Clear the previous key bound to this command. Ensures no more than 2 key binds
|
|
if (CurrentKeyBind.Name != 'None')
|
|
{
|
|
// Set the first or second Key bound to a command and set it to none
|
|
RemoveCommandFromBind(CurrentKeyBind, BindCommand);
|
|
|
|
if (NewKeyBind.Name == 'Delete')
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Set new key binding to a command. If no matching key is found creates a new array entry
|
|
SetKeyBind(NewKeyBind, BindCommand, false);
|
|
SaveConfig();
|
|
|
|
// if the main key is higher than the alternate key in Game.ini swap there positions
|
|
SwapPositions(NewKeyBind, BindCommand, bIsAlt);
|
|
}
|
|
|
|
|
|
/** Swaps key binding locations of two buttons in KFGame.ini */
|
|
function SwapPositions(KeyBind MyKeyBind, string BindCommand, bool bIsAlt)
|
|
{
|
|
local KeyBind NewKeyBind;
|
|
// Get the first item in the ini and set it to NewKeyBind
|
|
GetKeyBindFromCommand(NewKeyBind, BindCommand, false);
|
|
|
|
// If we are setting the alternate, and the current binding is the first found binding, swap
|
|
if ( bIsAlt && MyKeyBind.Name == NewKeyBind.Name )
|
|
{
|
|
GetKeyBindFromCommand(NewKeyBind, BindCommand, true);
|
|
SwapBind(MyKeyBind, NewKeyBind);
|
|
}
|
|
// If we are setting the main, and the current binding is not the first binding found, swap
|
|
else if ( !bIsAlt && MyKeyBind.Name != NewKeyBind.Name )
|
|
{
|
|
SwapBind(MyKeyBind, NewKeyBind);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Gamepad Specific GBAs
|
|
********************************************************************************************* */
|
|
|
|
exec function GamepadSprint()
|
|
{
|
|
bRun = 0;
|
|
bExtendedSprinting = false;
|
|
GamepadSprintAnalogStart = GetLeftAnalogDistance();
|
|
|
|
GamepadSprintTimer();
|
|
if ( bRun == 0 )
|
|
{
|
|
`TimerHelper.SetTimer(0.05, true, nameof(GamepadSprintTimer), self);
|
|
}
|
|
}
|
|
|
|
/** Keep checking if thumbstick has exceeded sprint threshold */
|
|
function GamepadSprintTimer()
|
|
{
|
|
if ( ShouldActivateGamepadSprint() )
|
|
{
|
|
bRun = 1;
|
|
`TimerHelper.ClearTimer(nameof(GamepadSprintTimer), self);
|
|
}
|
|
}
|
|
|
|
/** On release: Crouch or switch to 'ExtendedSprint' */
|
|
exec function GamepadSprintRelease()
|
|
{
|
|
if( ShouldActivateGamepadSprint() && bToggleToRun)
|
|
{
|
|
// end sprint; begin extended (non-pressed) sprint
|
|
bExtendedSprinting = true;
|
|
}
|
|
else if ( bRun == 0 )
|
|
{
|
|
ToggleCrouch();
|
|
}
|
|
|
|
bRun = 0;
|
|
`TimerHelper.ClearTimer(nameof(GamepadSprintTimer), self);
|
|
}
|
|
|
|
/** Returns a radial distance from center */
|
|
function float GetLeftAnalogDistance()
|
|
{
|
|
local vector vAnalog;
|
|
|
|
// Take 2d vector mag to get (more) circular threshold. Inputs don't make an
|
|
// exact circle however. They are wide at diagonals and vary by hardware.
|
|
vAnalog.x = RawJoyRight;
|
|
vAnalog.y = RawJoyUp;
|
|
|
|
return VSize2D(vAnalog);
|
|
}
|
|
|
|
/** Determine whether the player is trying to sprint or crouch */
|
|
function bool ShouldActivateGamepadSprint()
|
|
{
|
|
local float Distance, Bias, Delta;
|
|
|
|
Distance = GetLeftAnalogDistance();
|
|
Delta = Distance - GamepadSprintAnalogStart;
|
|
|
|
// sprint bias down to original 0.3 threshold
|
|
if ( Delta > 0.2 )
|
|
{
|
|
Bias = 0.3f;
|
|
}
|
|
|
|
//`log("current:"$Distance@"Delta:"$Delta@"Bias:"$Bias);
|
|
return (Distance + Bias) > SprintAnalogThreshold;
|
|
}
|
|
|
|
/**
|
|
* GBA_Reload_Gamepad
|
|
* Tap: Reload
|
|
* Hold: Quick Heal
|
|
*/
|
|
exec function GamepadReload()
|
|
{
|
|
if ( bVersusInput && CustomStartFireVersus(2) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
`TimerHelper.SetTimer(GamepadButtonHoldTime, false, nameof(GamepadReloadTimer), self);
|
|
}
|
|
|
|
/** GBA_Reload_Gamepad */
|
|
exec function GamepadReloadRelease()
|
|
{
|
|
if ( `TimerHelper.IsTimerActive(nameof(GamepadReloadTimer), self) )
|
|
{
|
|
if ( Pawn != None )
|
|
{
|
|
Pawn.StartFire(2);
|
|
|
|
// It's unusual to kill a pending fire on the same frame since it
|
|
// has to replicate. Seems to work well enough for reload.
|
|
Pawn.StopFire(2);
|
|
}
|
|
|
|
`TimerHelper.ClearTimer(nameof(GamepadReloadTimer), self);
|
|
}
|
|
else if ( bVersusInput )
|
|
{
|
|
CustomStopFireVersus(2);
|
|
}
|
|
}
|
|
|
|
/** GBA_Reload_Gamepad */
|
|
function GamepadReloadTimer()
|
|
{
|
|
QuickHeal();
|
|
}
|
|
|
|
/** GBA_GamepadCrouch
|
|
* Tap: Toggle crouch
|
|
* Hold: Hold crouch
|
|
*/
|
|
exec function GamepadCrouch()
|
|
{
|
|
ToggleCrouch();
|
|
|
|
if ( bDuck == 1 )
|
|
{
|
|
`TimerHelper.SetTimer(GamepadButtonHoldTime, false, nameof(GamepadCrouchTimer), self);
|
|
}
|
|
}
|
|
|
|
/** Handle hold functionality for GBA_GamepadCrouch */
|
|
exec function GamepadCrouchRelease()
|
|
{
|
|
local bool bWasButtonHeld;
|
|
|
|
bWasButtonHeld = !`TimerHelper.IsTimerActive(nameof(GamepadCrouchTimer), self);
|
|
if ( bWasButtonHeld )
|
|
{
|
|
StopCrouch();
|
|
}
|
|
}
|
|
|
|
/** empty function for timer */
|
|
function GamepadCrouchTimer();
|
|
|
|
/**
|
|
* GBA_Jump_Gamepad
|
|
* Tap: Jump
|
|
* Hold: Toggle crouch
|
|
*/
|
|
exec function GamepadJump()
|
|
{
|
|
`TimerHelper.SetTimer(GamepadButtonHoldTime, false, nameof(GamepadJumpTimer), self);
|
|
}
|
|
|
|
/** Tap function for GBA_Jump_Gamepad */
|
|
exec function GamepadJumpRelease()
|
|
{
|
|
if ( `TimerHelper.IsTimerActive(nameof(GamepadJumpTimer), self) )
|
|
{
|
|
Jump();
|
|
`TimerHelper.ClearTimer(nameof(GamepadJumpTimer), self);
|
|
}
|
|
}
|
|
|
|
/** Hold function for GBA_Jump_Gamepad */
|
|
function GamepadJumpTimer()
|
|
{
|
|
ToggleCrouch();
|
|
}
|
|
|
|
/** GBA_Altfire_Gamepad */
|
|
exec function GamepadSwitchFire()
|
|
{
|
|
local Weapon W;
|
|
|
|
if ( bGamepadWeaponSelectOpen )
|
|
{
|
|
if(MyGFxHUD.WeaponSelectWidget != none)
|
|
{
|
|
W = Pawn.InvManager.PendingWeapon != none ? Pawn.InvManager.PendingWeapon : Pawn.Weapon;
|
|
if( W != None && W.CanThrow() )
|
|
{
|
|
ServerThrowOtherWeapon(W);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SwitchFire();
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Crouch/Duck
|
|
********************************************************************************************* */
|
|
|
|
/** Abort/Cancel crouch when jumping */
|
|
exec function Jump()
|
|
{
|
|
if( bVersusInput )
|
|
{
|
|
JumpVersus();
|
|
return;
|
|
}
|
|
|
|
bDuck = 0;
|
|
PressedJumpTime = WorldInfo.TimeSeconds;
|
|
Super.Jump();
|
|
}
|
|
|
|
/** GBA_CrouchHold */
|
|
exec function StartCrouch()
|
|
{
|
|
if (Pawn != none && Pawn.bCanCrouch)
|
|
{
|
|
bDuck = 1;
|
|
}
|
|
}
|
|
|
|
/** GBA_CrouchHold */
|
|
exec function StopCrouch()
|
|
{
|
|
if( bDuck == 1 )
|
|
{
|
|
bDuck = 0;
|
|
}
|
|
}
|
|
|
|
/** GBA_CrouchToggle */
|
|
exec function ToggleCrouch()
|
|
{
|
|
if (Pawn != none && Pawn.bCanCrouch)
|
|
{
|
|
bDuck = (bDuck == 0) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Sprint
|
|
********************************************************************************************* */
|
|
|
|
/** GBA_Sprint */
|
|
exec function ToggleSprint()
|
|
{
|
|
bRun = (bRun == 0) ? 1 : 0;
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Ironsights
|
|
********************************************************************************************* */
|
|
|
|
/** GBA_IronsightsToggle, GBA_IronsightsHold
|
|
* @params bHoldButtonMode If true, use hold mode instead of toggle
|
|
*/
|
|
simulated exec function IronSights(optional bool bHoldButtonMode)
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
// No sighting in cinematics
|
|
if( bCinematicMode )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( bVersusInput && CustomStartFireVersus(5) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( bHoldButtonMode )
|
|
{
|
|
bIronsightsHeld = true;
|
|
}
|
|
|
|
if ( bExtendedSprinting )
|
|
{
|
|
bRun = 0;
|
|
bExtendedSprinting = false;
|
|
}
|
|
|
|
bIronsightsActive = true;
|
|
|
|
if( Pawn != none )
|
|
{
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None )
|
|
{
|
|
KFW.SetIronSights((bHoldButtonMode) ? true : !KFW.IsUsingSights());
|
|
}
|
|
}
|
|
}
|
|
|
|
/** GBA_IronsightsToggle, GBA_IronsightsHold
|
|
* @params bHoldButtonMode If true, use hold mode instead of toggle
|
|
*/
|
|
simulated exec function IronSightsRelease(optional bool bHoldButtonMode)
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
if( bVersusInput && CustomStopFireVersus(5) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( bHoldButtonMode )
|
|
{
|
|
bIronsightsHeld = false;
|
|
}
|
|
|
|
bIronsightsActive = false;
|
|
|
|
if( Pawn != none )
|
|
{
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None )
|
|
{
|
|
// For weapons that override ironsights (e.g. melee) we always
|
|
// need to register a button release
|
|
if ( !KFW.bHasIronSights || bHoldButtonMode )
|
|
{
|
|
KFW.SetIronSights(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Flashlight
|
|
********************************************************************************************* */
|
|
|
|
/**
|
|
* Button toggles the weapons flashlight on and off
|
|
*/
|
|
simulated exec function ToggleFlashlight()
|
|
{
|
|
local KFPawn_Human KFP;
|
|
local bool bPerkHasNightVision;
|
|
|
|
if( Pawn == none || Pawn.InvManager == none || bGamepadWeaponSelectOpen )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bPerkHasNightVision = GetPerk().HasNightVision();
|
|
|
|
KFP = KFPawn_Human(Pawn);
|
|
if( KFP != None && KFP.MyKFWeapon != None )
|
|
{
|
|
// if anything is on, tap to turn off
|
|
if( bNightVisionActive )
|
|
{
|
|
InternalToggleNightVision();
|
|
}
|
|
// if able to use NVG, handle hold press
|
|
else if( bPerkHasNightVision )
|
|
{
|
|
`TimerHelper.SetTimer(GamepadButtonHoldTime, false, nameof(FlashlightTimer), self);
|
|
}
|
|
// if unable to use NVG, simply tap to toggle flashlight
|
|
else
|
|
{
|
|
InternalToggleFlashlight();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Tap function for GBA_ToggleFlashlight */
|
|
exec function FlashlightRelease()
|
|
{
|
|
if ( `TimerHelper.IsTimerActive(nameof(FlashlightTimer), self) )
|
|
{
|
|
`TimerHelper.ClearTimer(nameof(FlashlightTimer), self);
|
|
InternalToggleFlashlight();
|
|
}
|
|
}
|
|
|
|
/** Hold function for GBA_ToggleFlashlight */
|
|
function FlashlightTimer()
|
|
{
|
|
InternalToggleNightVision();
|
|
}
|
|
|
|
/**
|
|
* Actually pass the toggle flashlight command onto the pawn. Seperated to simplify and
|
|
* alleviate confusion between flashlight/equipment/nightvision terminology in legacy code.
|
|
*/
|
|
function InternalToggleFlashlight()
|
|
{
|
|
local KFPawn_Human KFP;
|
|
|
|
KFP = KFPawn_Human(Pawn);
|
|
if( KFP != None )
|
|
{
|
|
if( bNightVisionActive )
|
|
{
|
|
SetNightVision( false );
|
|
KFP.PlaySoundBase(NightVisionOffEvent);
|
|
}
|
|
|
|
KFP.ToggleEquipment();
|
|
KFP.PlaySoundBase((KFP.bFlashlightOn) ? FlashlightOnEvent : FlashlightOffEvent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Actually pass the toggle night vision command onto the controller. Seperated to simplify and
|
|
* alleviate confusion between flashlight/equipment/nightvision terminology in legacy code.
|
|
*/
|
|
function InternalToggleNightVision()
|
|
{
|
|
local KFPawn_Human KFP;
|
|
|
|
KFP = KFPawn_Human(Pawn);
|
|
if( KFP != None )
|
|
{
|
|
if( KFP.bFlashlightOn )
|
|
{
|
|
InternalToggleFlashlight();
|
|
}
|
|
|
|
SetNightVision( !bNightVisionActive );
|
|
KFP.PlaySoundBase((bNightVisionActive) ? NightVisionOnEvent : NightVisionOffEvent);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Weapon firemodes
|
|
********************************************************************************************* */
|
|
|
|
// The player wants to alternate-fire.
|
|
exec function CustomStartFire( optional Byte FireModeNum )
|
|
{
|
|
if ( bVersusInput && CustomStartFireVersus(FireModeNum) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
StartFire( FireModeNum );
|
|
}
|
|
|
|
exec function CustomStopFire( optional byte FireModeNum )
|
|
{
|
|
if ( bVersusInput && CustomStopFireVersus(FireModeNum) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
StopFire( FireModeNum );
|
|
}
|
|
|
|
/** GBA_SwitchAltFire */
|
|
exec function SwitchFire()
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
if( Pawn != none )
|
|
{
|
|
// No firemode switching in cinematics
|
|
if( bCinematicMode )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( bVersusInput && CustomStartFireVersus(1) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None )
|
|
{
|
|
KFW.AltFireMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** GBA_SwitchAltFire
|
|
* Weapons that override AltFireMode (e.g. welder) and call StartFire
|
|
* also need to call StopFire. For most weapons this is unnecessary.
|
|
*/
|
|
exec function SwitchFireRelease()
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
if( Pawn != none )
|
|
{
|
|
if( bVersusInput && CustomStopFireVersus(1) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None )
|
|
{
|
|
KFW.AltFireModeRelease();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Weapon Select
|
|
********************************************************************************************* */
|
|
|
|
/** Equips the next weapon in the inventory chain */
|
|
exec function SelectNextWeapon()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeapon KFW;
|
|
|
|
if (Pawn != none)
|
|
{
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFIM = KFInventoryManager(Pawn.InvManager);
|
|
if (bQuickWeaponSelect)
|
|
{
|
|
NextWeapon();
|
|
}
|
|
else
|
|
{
|
|
KFIM.HighlightNextWeapon();
|
|
}
|
|
KFIM.ShowAllHUDGroups();
|
|
}
|
|
}
|
|
|
|
/** Equips the previous weapon in the inventory chain */
|
|
exec function SelectPrevWeapon()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeapon KFW;
|
|
|
|
if( Pawn != none )
|
|
{
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFIM = KFInventoryManager( Pawn.InvManager );
|
|
if ( bQuickWeaponSelect )
|
|
{
|
|
PrevWeapon();
|
|
}
|
|
else
|
|
{
|
|
KFIM.HighlightPrevWeapon();
|
|
}
|
|
KFIM.ShowAllHUDGroups();
|
|
}
|
|
}
|
|
|
|
/** Equips the previous weapon in the inventory chain */
|
|
exec function SelectLastWeapon()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeapon KFW;
|
|
|
|
if( Pawn != none )
|
|
{
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFIM = KFInventoryManager( Pawn.InvManager );
|
|
if ( KFIM != None )
|
|
{
|
|
KFIM.SwitchToLastWeapon();
|
|
//KFIM.ShowAllHUDGroups();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Weapon select button for controllers
|
|
* Tap: Equip last weapon
|
|
* Hold: Open gamepad weapon select UI
|
|
*/
|
|
exec function GamepadWeaponSelect()
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
if ( bVersusInput && CustomStartFireVersus(6) )
|
|
{
|
|
return; // player zed move override
|
|
}
|
|
|
|
if (Pawn != none)
|
|
{
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
`TimerHelper.SetTimer(GamepadButtonHoldTime, false, nameof(GamepadWeaponMenuTimer), self);
|
|
}
|
|
}
|
|
|
|
/** Weapon select button for controllers
|
|
* Tap: Equip last weapon
|
|
* Hold: Open gamepad weapon select UI
|
|
*/
|
|
exec function ReleaseGamepadWeaponSelect()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeapon KFW;
|
|
|
|
if ( bVersusInput && CustomStopFireVersus(6) )
|
|
{
|
|
return; // player zed move override
|
|
}
|
|
|
|
// close menu
|
|
if ( bGamepadWeaponSelectOpen && MyGFxHUD.WeaponSelectWidget != none )
|
|
{
|
|
MyGFxHUD.WeaponSelectWidget.SetWeaponSwitchStayOpen(false);
|
|
bGamepadWeaponSelectOpen = false;
|
|
}
|
|
|
|
if (Pawn != none)
|
|
{
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW == None || KFW.CanSwitchWeapons())
|
|
{
|
|
KFIM = KFInventoryManager(Pawn.InvManager);
|
|
|
|
// On button tap, cycle weapons
|
|
if (`TimerHelper.IsTimerActive(nameof(GamepadWeaponMenuTimer), self))
|
|
{
|
|
`TimerHelper.ClearTimer(nameof(GamepadWeaponMenuTimer), self);
|
|
|
|
// once per match show hint for the hold function
|
|
if ( bShowGamepadWeaponSelectHint )
|
|
{
|
|
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_GamepadWeaponSelectHint);
|
|
}
|
|
|
|
// perform tap weapon switch action
|
|
if ( bUseGamepadLastWeapon )
|
|
{
|
|
KFIM.SwitchToLastWeapon();
|
|
}
|
|
else
|
|
{
|
|
KFIM.GamePadNextWeapon();
|
|
}
|
|
}
|
|
// Switch to selected weapon from the UI
|
|
else
|
|
{
|
|
KFIM.SetCurrentWeapon(KFIM.PendingWeapon);
|
|
}
|
|
}
|
|
|
|
// first time hint only
|
|
bShowGamepadWeaponSelectHint = false;
|
|
}
|
|
}
|
|
|
|
/** Checks if we can interrupt the weapon menu timer and open it immediately */
|
|
function bool CheckForWeaponMenuTimerInterrupt()
|
|
{
|
|
if( `TimerHelper.IsTimerActive(nameOf(GamepadWeaponMenuTimer), self) )
|
|
{
|
|
`TimerHelper.ClearTimer( nameOf(GamepadWeaponMenuTimer), self );
|
|
GamepadWeaponMenuTimer();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Called when 'GamepadWeaponSelect' is held down */
|
|
function GamepadWeaponMenuTimer()
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
if( MyGFxHUD != none && MyGFxHUD.VoiceCommsWidget != none && MyGFxHUD.VoiceCommsWidget.bActive )
|
|
{
|
|
return;
|
|
}
|
|
if (Pawn != none && bUsingGamepad)
|
|
{
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( MyGFxHUD.WeaponSelectWidget != None )
|
|
{
|
|
bGamepadWeaponSelectOpen = true;
|
|
MyGFxHUD.WeaponSelectWidget.SetWeaponSwitchStayOpen(true);
|
|
KFInventoryManager(Pawn.InvManager).HighlightWeapon(Pawn.Weapon);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Switch to a specific weapon/weapon group
|
|
* Keyboard: 1-4
|
|
* Gamepad: dpad
|
|
*/
|
|
exec function SwitchWeaponGroup( byte GroupID )
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeapon NextGroupedWeapon;
|
|
local KFWeapon KFW;
|
|
|
|
if ( Pawn == none || Pawn.InvManager == none )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't switch weapons if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( MyGFxHUD != none && MyGFxHUD.VoiceCommsWidget != none && MyGFxHUD.VoiceCommsWidget.bActive )
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFIM = KFInventoryManager(Pawn.InvManager);
|
|
|
|
if ( KFIM != none )
|
|
{
|
|
NextGroupedWeapon = KFIM.GetNextGroupedWeapon(GroupID, false, bUsingGamepad && bGamepadWeaponSelectOpen);
|
|
if (bUsingGamepad)
|
|
{
|
|
if (bGamepadWeaponSelectOpen)
|
|
{
|
|
KFIM.HighlightWeapon(NextGroupedWeapon);
|
|
}
|
|
else
|
|
{
|
|
KFIM.SetCurrentWeapon(NextGroupedWeapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KFIM.SetCurrentWeapon( NextGroupedWeapon );
|
|
}
|
|
|
|
KFIM.ShowOnlyHUDGroup(GroupID);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Misc Gameplay
|
|
********************************************************************************************* */
|
|
|
|
exec function QuickEmote ()
|
|
{
|
|
//DoEmote
|
|
}
|
|
|
|
/** Quick heal key was pressed */
|
|
exec function QuickHeal()
|
|
{
|
|
local KFWeapon KFW;
|
|
|
|
if( Pawn == none )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( bVersusInput && QuickHealVersus() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't quick heal if the current weapon prevents it
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if ( KFW != None && !KFW.CanSwitchWeapons())
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFInventoryManager(Pawn.InvManager).AttemptQuickHeal();
|
|
}
|
|
|
|
/** Throw some of this player's dosh */
|
|
exec function TossMoney()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
|
|
if( Pawn != none && Pawn.InvManager != none )
|
|
{
|
|
KFIM = KFInventoryManager(Pawn.InvManager);
|
|
if ( KFIM != none )
|
|
{
|
|
KFIM.ThrowMoney();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Gamepad d-pad bindings
|
|
********************************************************************************************* */
|
|
|
|
/** Implementation for pressing/holding D-pad left
|
|
* TAP: perform quick heal / select weapon group (if weapon select UI is active) / Previous player if spectating
|
|
* HOLD:
|
|
*/
|
|
exec function GamepadDpadLeft()
|
|
{
|
|
if(Outer.IsSpectating())
|
|
{
|
|
Outer.ServerViewPrevPlayer();
|
|
}
|
|
else if( bGamepadWeaponSelectOpen || CheckForWeaponMenuTimerInterrupt() )
|
|
{
|
|
SwitchWeaponGroup( 0 );
|
|
}
|
|
else
|
|
{
|
|
// taunt while playing as zed
|
|
if ( bVersusInput && ChangeZedCamera(true) )
|
|
{
|
|
return; // player zed move override
|
|
}
|
|
|
|
ShowVoiceComms();
|
|
}
|
|
}
|
|
|
|
/** Implementation for pressing/holding D-pad down
|
|
* TAP: switch fire modes / select weapon group (if weapon select UI is active) / next camera type if spectating
|
|
* HOLD:
|
|
*/
|
|
exec function GamepadDpadDown()
|
|
{
|
|
if(Outer.IsSpectating())
|
|
{
|
|
Outer.ServerNextSpectateMode();
|
|
}
|
|
else if( bGamepadWeaponSelectOpen || CheckForWeaponMenuTimerInterrupt() )
|
|
{
|
|
SwitchWeaponGroup( 2 );
|
|
}
|
|
else
|
|
{
|
|
// taunt while playing as zed
|
|
if ( bVersusInput && CustomStartFireVersus(7) )
|
|
{
|
|
return; // player zed move override
|
|
}
|
|
|
|
ToggleFlashlight();
|
|
}
|
|
}
|
|
|
|
/** Implementation for pressing/holding D-pad right
|
|
* TAP: toggle flashlight / select weapon group (if weapon select UI is active) / Next player if spectating
|
|
* HOLD:
|
|
*/
|
|
exec function GamepadDpadRight()
|
|
{
|
|
if(Outer.IsSpectating())
|
|
{
|
|
Outer.ServerViewNextPlayer();
|
|
}
|
|
else if( bGamepadWeaponSelectOpen || CheckForWeaponMenuTimerInterrupt() )
|
|
{
|
|
SwitchWeaponGroup( 1 );
|
|
}
|
|
else
|
|
{
|
|
// taunt while playing as zed
|
|
if ( bVersusInput && ChangeZedCamera(false) )
|
|
{
|
|
return; // player zed move override
|
|
}
|
|
|
|
TossMoney();
|
|
}
|
|
}
|
|
|
|
/** Implementation for pressing/holding D-pad up
|
|
* TAP: throw dosh / select weapon group (if weapon select UI is active)
|
|
* HOLD: throw weapon
|
|
*/
|
|
exec function GamepadDpadUp()
|
|
{
|
|
if( bGamepadWeaponSelectOpen || CheckForWeaponMenuTimerInterrupt() )
|
|
{
|
|
SwitchWeaponGroup( 3 );
|
|
}
|
|
else
|
|
{
|
|
// taunt while playing as zed
|
|
if ( bVersusInput && CustomStartFireVersus(7) )
|
|
{
|
|
return; // player zed move override
|
|
}
|
|
|
|
// toggle between healer and last weapon
|
|
if ( Pawn != None && Pawn.Weapon != None && Pawn.Weapon.IsA('KFWeap_HealerBase') )
|
|
{
|
|
KFInventoryManager( Pawn.InvManager ).SwitchToLastWeapon();
|
|
}
|
|
else
|
|
{
|
|
SwitchWeaponGroup(3);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Toggle between right/left side third person camera */
|
|
exec function bool ChangeZedCamera(bool bInvertY)
|
|
{
|
|
local KFPawn_Monster P;
|
|
|
|
P = KFPawn_Monster(Pawn);
|
|
|
|
if ( P != None )
|
|
{
|
|
KFThirdPersonCamera(KFPlayerCamera(PlayerCamera).ThirdPersonCam).InvertViewOffset(bInvertY);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
exec function ShowVoiceComms()
|
|
{
|
|
if( bVersusInput && PlayerReplicationInfo.GetTeamNum() == 255 )
|
|
{
|
|
return;
|
|
}
|
|
if(IsBossCameraMode())
|
|
{
|
|
return;
|
|
}
|
|
if( MyGFxHUD != none && MyGFxHUD.VoiceCommsWidget != none )
|
|
{
|
|
MyGFxHUD.ShowVoiceComms(true);
|
|
}
|
|
}
|
|
|
|
exec function HideVoiceComms()
|
|
{
|
|
if(MyGFxHUD != none && MyGFxHUD.VoiceCommsWidget != none)
|
|
{
|
|
MyGFxHUD.ShowVoiceComms(false);
|
|
}
|
|
}
|
|
|
|
exec function OnVoteYesPressed()
|
|
{
|
|
//Let the hud know
|
|
if(MyGFxHUD != none && MyGFxHUD.KickVoteWidget != none)
|
|
{
|
|
MyGFxHUD.KickVoteWidget.OnYesPressed();
|
|
}
|
|
}
|
|
|
|
/** Throw dosh if button was tapped */
|
|
exec function OnVoteYesRelease()
|
|
{
|
|
//let the HUD know
|
|
if(MyGFxHUD != none && MyGFxHUD.KickVoteWidget != none)
|
|
{
|
|
MyGFxHUD.KickVoteWidget.OnYesReleased();
|
|
}
|
|
}
|
|
|
|
exec function OnVoteNoPressed()
|
|
{
|
|
//Let the hud know
|
|
if(MyGFxHUD != none && MyGFxHUD.KickVoteWidget != none)
|
|
{
|
|
MyGFxHUD.KickVoteWidget.OnNoPressed();
|
|
}
|
|
}
|
|
|
|
/** Throw dosh if button was tapped */
|
|
exec function OnVoteNoRelease()
|
|
{
|
|
//let the HUD know
|
|
if(MyGFxHUD != none && MyGFxHUD.KickVoteWidget != none)
|
|
{
|
|
MyGFxHUD.KickVoteWidget.OnNoReleased();
|
|
}
|
|
}
|
|
|
|
//This takes is called in PlayerController::Use() place now.
|
|
exec function Interact()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFInterface_Usable UsableTrigger;
|
|
|
|
KFIM = KFInventoryManager(Pawn.InvManager);
|
|
if (KFIM != none && KFIM.Instigator != none)
|
|
{
|
|
UsableTrigger = GetCurrentUsableActor(KFIM.Instigator);
|
|
|
|
if (UsableTrigger != none && UsableTrigger.IsA('KFTraderTrigger'))
|
|
{
|
|
`TimerHelper.SetTimer(AutoUpgradeHoldTime, false, nameof(InteractTimer), self);
|
|
return;
|
|
}
|
|
}
|
|
|
|
`TimerHelper.SetTimer(GamepadButtonHoldTime, false, nameof(InteractTimer), self);
|
|
|
|
}
|
|
|
|
//This takes is called in PlayerController::Use() place now.
|
|
exec function InteractRelease()
|
|
{
|
|
local bool bButtonWasHeld;
|
|
|
|
bButtonWasHeld = !`TimerHelper.IsTimerActive( nameof(InteractTimer), self );
|
|
if( !bButtonWasHeld )
|
|
{
|
|
Outer.Use();
|
|
`TimerHelper.ClearTimer( nameof(InteractTimer), self );
|
|
}
|
|
}
|
|
|
|
exec function InteractTimer()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFInterface_Usable UsableTrigger;
|
|
|
|
KFIM = KFInventoryManager(Pawn.InvManager);
|
|
if( KFIM != none && KFIM.Instigator != none )
|
|
{
|
|
UsableTrigger = GetCurrentUsableActor( KFIM.Instigator );
|
|
|
|
if( UsableTrigger == none)
|
|
{
|
|
return;
|
|
}
|
|
if(UsableTrigger.IsA('KFDoorTrigger') || UsableTrigger.IsA('KFRepairableActorTrigger') || UsableTrigger.IsA('KFWeldableTrigger'))
|
|
{
|
|
KFIM.QuickWeld();
|
|
}
|
|
else if(UsableTrigger.IsA('KFTraderTrigger'))
|
|
{
|
|
DoAutoPurchase();
|
|
}
|
|
else if (UsableTrigger.IsA('KFUsableTrigger'))
|
|
{
|
|
Outer.Use();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name VOIP
|
|
********************************************************************************************* */
|
|
|
|
exec function StartVoiceChat(optional bool bPublicChat)
|
|
{
|
|
if(bRequiresPushToTalk)
|
|
{
|
|
if(bPublicChat)
|
|
{
|
|
CurrentVoiceChannel = EVC_ALL;
|
|
}
|
|
else
|
|
{
|
|
CurrentVoiceChannel = EVC_TEAM;
|
|
}
|
|
|
|
`TimerHelper.ClearTimer('ClientStopNetworkedVoice');
|
|
ClientStartNetworkedVoice();
|
|
}
|
|
}
|
|
|
|
exec function StopVoiceChat()
|
|
{
|
|
if(bRequiresPushToTalk)
|
|
{
|
|
`TimerHelper.SetTimer(0.25, false, 'ClientStopNetworkedVoice');
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Aim assists
|
|
********************************************************************************************* */
|
|
|
|
/**
|
|
* Overridden to add hooks for view acceleration, target friction, auto centering, controller sensitivity.
|
|
*/
|
|
function PreProcessInput( float DeltaTime )
|
|
{
|
|
Super.PreProcessInput(DeltaTime);
|
|
|
|
if ( Pawn != None && bUsingGamepad )
|
|
{
|
|
PreProcessGamepadInput(DeltaTime);
|
|
}
|
|
}
|
|
|
|
/** Tick gamepad aim assist, extended sprint, etc... */
|
|
function PreProcessGamepadInput( float DeltaTime )
|
|
{
|
|
local KFWeapon KFW;
|
|
local KFPawn KFP;
|
|
local float FOVScale;
|
|
local float ScaledJoyMagnitude;
|
|
local vector RawJoyLookVector;
|
|
|
|
if( bExtendedSprinting )
|
|
{
|
|
// Handle sprinting with joysticks and clicks
|
|
UpdateExtendedSprint( DeltaTime );
|
|
}
|
|
|
|
// Use these instead of aTurn and aLookUp so that we can blend between previous and current frames to smooth them.
|
|
// Then set aTurn and aLookUp to the blended value.
|
|
CurrTurn = 0.f;
|
|
CurrLookup = 0.f;
|
|
|
|
// Baseline rotation control, taking stick sensitivity into account
|
|
// Uses magnitude of input for both instead of individual axes, which makes diagonals feel wonky
|
|
if( RawJoyLookMagnitude > 0.f )
|
|
{
|
|
ScaledJoyMagnitude = EvalInterpCurveFloat(LookSensitivityScaleCurve, Abs(RawJoyLookMagnitude));
|
|
CurrTurn = ScaledJoyMagnitude * (RawJoyLookRight/RawJoyLookMagnitude);
|
|
CurrLookUp = ScaledJoyMagnitude * (RawJoyLookUp/RawJoyLookMagnitude);
|
|
}
|
|
|
|
//Checking if values are inside circular deadzone
|
|
RawJoyLookVector.X = RawJoyLookRight;
|
|
RawJoyLookVector.Y = RawJoyLookUp;
|
|
if (VSize2D(RawJoyLookVector) < GamepadDeadzoneScale)
|
|
{
|
|
CurrTurn = 0;
|
|
CurrLookUp = 0;
|
|
}
|
|
|
|
ViewAccel_TurnSpeed = Lerp(ViewAccel_MinTurnSpeed, ViewAccel_MaxTurnSpeed, GamepadAccelerationJumpScale);
|
|
// sets CurrTurn/CurrLookUp directly if applicable, does not multiply (aka Turn Assist)
|
|
if( CanApplyViewAcceleration() )
|
|
{
|
|
ApplyViewAcceleration( DeltaTime );
|
|
}
|
|
else
|
|
{
|
|
ViewAccel_BlendTimer = 0;
|
|
}
|
|
|
|
// Apply friction (right-stick aim assist). Adhesion is handled in the PC
|
|
if ( Pawn.Weapon != None )
|
|
{
|
|
KFW = KFWeapon(Pawn.Weapon);
|
|
if( IsAimAssistFrictionEnabled() && KFW != none )
|
|
{
|
|
ApplyTargetFriction( DeltaTime, KFW );
|
|
}
|
|
}
|
|
|
|
// Apply FOV zoomed/iron sight sensitivity scaling
|
|
FOVScale = GetSensitivityByFOV( GamepadZoomedSensitivityScale );
|
|
|
|
CurrTurn *= FOVScale;
|
|
CurrLookUp *= FOVScale;
|
|
|
|
// Globally scale the turning and up/down sensitivity
|
|
CurrTurn *= GamepadSensitivityScale;
|
|
CurrLookUp *= GamepadSensitivityScale;
|
|
|
|
// be less sensitive while sprinting
|
|
if( Pawn != none )
|
|
{
|
|
KFP = KFPawn( Pawn );
|
|
if( KFP != none && KFP.bIsSprinting )
|
|
{
|
|
CurrTurn *= SprintingSensitivityScale;
|
|
CurrLookUp *= SprintingSensitivityScale;
|
|
}
|
|
}
|
|
|
|
//`log( "aTurn: " $ aTurn $ " aLookUp: " $ aLookUp $ " DeltaTime: " $ DeltaTime );
|
|
|
|
if( bViewSmoothingEnabled )
|
|
{
|
|
// sets CurrTurn/CurrLookUp directly if applicable, does not multiply
|
|
ApplyViewSmoothing( DeltaTime );
|
|
}
|
|
|
|
aTurn = CurrTurn;
|
|
aLookUp = CurrLookUp;
|
|
|
|
//`log("aTurn In: "$RawJoyLookRight$"; aTurn Out: "$aTurn$"; aLookUp In: "$RawJoyLookUp$"; aLookUp Out: "$aLookUp$"; RawJoyLookMag: "$RawJoyLookMagnitude);
|
|
|
|
//`log("PrevTurn: "$PrevTurn$"; aTurn: "$aTurn$"; CurrTurn: "$CurrTurn$"; PrevLookUp: "$PrevLookUp$"; aLookUp: "$aLookUp$"; CurrLookUp: "$CurrLookUp);
|
|
PrevTurn = aTurn;
|
|
PrevLookUp = aLookUp;
|
|
}
|
|
|
|
/** Called from PreProcessInput when bExtendedSprinting=TRUE */
|
|
function UpdateExtendedSprint( float DeltaTime )
|
|
{
|
|
// extended sprint cannot be activated during ironsights
|
|
if ( bRun == 0 && Pawn.Weapon != None && Pawn.Weapon.ShouldOwnerWalk() )
|
|
{
|
|
bRun = 0;
|
|
bExtendedSprinting = false;
|
|
}
|
|
// If we've clicked the sprint button, and have the stick pressed
|
|
// most of the way to the side, keep sprinting
|
|
else if( IsDirectingJoyStick(SprintAnalogThreshold) )
|
|
{
|
|
bRun = 1;
|
|
}
|
|
else
|
|
{
|
|
bRun = 0;
|
|
bExtendedSprinting = false;
|
|
}
|
|
}
|
|
|
|
/** Returns true if we are pressing the joystick in a direction */
|
|
function bool IsDirectingJoyStick(float Threshold)
|
|
{
|
|
local vector vAnalog;
|
|
|
|
// Take 2d vector mag to get (more) circular threshold. Inputs don't make an
|
|
// exact circle however. They are wide at diagonals and vary by hardware.
|
|
vAnalog.x = RawJoyRight;
|
|
vAnalog.y = RawJoyUp;
|
|
|
|
if( VSize2d(vAnalog) > Threshold )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This will scale the player's rotation speed depending on the location of their thumbstick. Framerate independent.
|
|
**/
|
|
function ApplyViewAcceleration( float DeltaTime )
|
|
{
|
|
ViewAccel_BlendTimer += DeltaTime;
|
|
if( ViewAccel_BlendTimer >= ViewAccel_BlendTime )
|
|
{
|
|
ViewAccel_BlendTimer = ViewAccel_BlendTime;
|
|
}
|
|
|
|
if( CurrTurn > 0 )
|
|
{
|
|
CurrTurn = Lerp( CurrTurn, ViewAccel_TurnSpeed, ViewAccel_BlendTimer / ViewAccel_BlendTime );
|
|
}
|
|
else
|
|
{
|
|
CurrTurn = Lerp( CurrTurn, -ViewAccel_TurnSpeed, ViewAccel_BlendTimer / ViewAccel_BlendTime );
|
|
}
|
|
}
|
|
|
|
/** Gamepad turn assist/acceleration */
|
|
function bool CanApplyViewAcceleration()
|
|
{
|
|
if( !bViewAccelerationEnabled )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// don't accelerate in iron sights
|
|
if( Pawn == none || (KFWeapon(Pawn.Weapon) != none && KFWeapon(Pawn.Weapon).bUsingSights) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// only accelerate if the right stick is within certain bounds
|
|
if( RawJoyLookMagnitude < ViewAccel_JoyMagThreshold || Abs(RawJoyLookUp) > ViewAccel_JoyPitchThreshold )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Whether we're applying view acceleration (turn assist) */
|
|
function bool ApplyingViewAcceleration()
|
|
{
|
|
return ViewAccel_BlendTimer > 0;
|
|
}
|
|
|
|
/** Blends between last frame's look speed and this frame's, using MaxAccel as a constraint. Framerate independent. */
|
|
function ApplyViewSmoothing( float DeltaTime )
|
|
{
|
|
local float MaxAccel, MaxDecel;
|
|
|
|
// Only smooth values on the base sensitivity curve. Don't smooth once (if) view acceleration kicks in.
|
|
if( PrevTurn <= LookSensitivityScaleCurve.Points[LookSensitivityScaleCurve.Points.Length-1].OutVal )
|
|
{
|
|
MaxAccel = ViewSmoothing_MaxAccel * DeltaTime;
|
|
MaxDecel = ViewSmoothing_MaxDecel * DeltaTime;
|
|
|
|
if( (CurrTurn >= 0 && PrevTurn < 0) || (CurrTurn <= 0 && PrevTurn > 0) )
|
|
{
|
|
CurrTurn = PrevTurn + FClamp(CurrTurn - PrevTurn, -MaxDecel, MaxDecel);
|
|
}
|
|
else
|
|
{
|
|
CurrTurn = PrevTurn + FClamp(CurrTurn - PrevTurn, -MaxAccel, MaxAccel);
|
|
}
|
|
|
|
if( (CurrLookUp >= 0 && PrevLookUP < 0) || (CurrLookUp <= 0 && PrevLookUP > 0) )
|
|
{
|
|
CurrLookUp = PrevLookUP + FClamp(CurrLookUp - PrevLookUP, -MaxDecel, MaxDecel);
|
|
}
|
|
else
|
|
{
|
|
CurrLookUp = PrevLookUP + FClamp(CurrLookUp - PrevLookUP, -MaxAccel, MaxAccel);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Begin an AutoTargeting adhesion cycle
|
|
**/
|
|
function InitAutoTarget()
|
|
{
|
|
local Vector CamLoc, X;
|
|
local Rotator CamRot;
|
|
local float UsedTargetAngle, MaxDistance;
|
|
|
|
// No autotargeting without a gamepad!
|
|
if ( !bUsingGamepad || Pawn == None || Pawn.Weapon == None )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !IsAimAssistAutoTargetEnabled() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// auto-target cooldown (in real seconds)
|
|
if ( (WorldInfo.RealTimeSeconds - LastAutoTargetTime) < AutoTargetCooldown )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( KFWeapon(Pawn.Weapon) == none )
|
|
{
|
|
return;
|
|
}
|
|
|
|
AutoTargetTimeLeft = KFWeapon(Pawn.Weapon).default.ZoomInTime * 0.85;
|
|
LastAutoTargetTime = WorldInfo.RealTimeSeconds;
|
|
|
|
// Get camera location/rotation
|
|
GetPlayerViewPoint( CamLoc, CamRot );
|
|
|
|
// look for a new target
|
|
MaxDistance = AutoTargetAngleCurve.Points[AutoTargetAngleCurve.Points.Length-1].InVal;
|
|
CurrentAutoTarget = GetTargetAdhesionFrictionTarget( MaxDistance, CamLoc, CamRot, AutoTargetAngleCurve, true );
|
|
if (CurrentAutoTarget != None)
|
|
{
|
|
CurrentAutoTargetBone = '';
|
|
AutoTargetInitialLocation = GetBestAutoTargetLocation(CurrentAutoTarget, CurrentAutoTargetBone);
|
|
}
|
|
|
|
if( bDebugAutoTarget )
|
|
{
|
|
FlushPersistentDebugLines();
|
|
|
|
X = vector(CamRot);
|
|
UsedTargetAngle = EvalInterpCurveFloat(AutoTargetAngleCurve, VSize(CamLoc - AutoTargetInitialLocation));
|
|
DrawDebugCone(CamLoc + X * 5,X,500.0, Acos(UsedTargetAngle), Acos(UsedTargetAngle),16,MakeColor(255,0,0,255),true);
|
|
DrawDebugCone(CamLoc + X * 5,X,250.0, Acos(AutoTargetAngleCurve.Points[0].OutVal), Acos(AutoTargetAngleCurve.Points[0].OutVal),16,MakeColor(0,255,0,255),true);
|
|
DrawDebugCone(CamLoc + X * 5,X,AutoTargetAngleCurve.Points[AutoTargetAngleCurve.Points.Length - 1].InVal, Acos(AutoTargetAngleCurve.Points[AutoTargetAngleCurve.Points.Length - 1].OutVal), Acos(AutoTargetAngleCurve.Points[AutoTargetAngleCurve.Points.Length - 1].OutVal),16,MakeColor(0,255,0,255),true);
|
|
|
|
UsedTargetAngle = EvalInterpCurveFloat(AutoTargetWeakspotCurve, VSize(CamLoc - AutoTargetInitialLocation));
|
|
DrawDebugCone(CamLoc + X * 5,X,500.0, Acos(UsedTargetAngle), Acos(UsedTargetAngle),16,MakeColor(255,0,0,255),true);
|
|
DrawDebugCone(CamLoc + X * 5,X,250.0, Acos(AutoTargetWeakspotCurve.Points[0].OutVal), Acos(AutoTargetWeakspotCurve.Points[0].OutVal),16,MakeColor(255,255,0,255),true);
|
|
DrawDebugCone(CamLoc + X * 5,X,AutoTargetWeakspotCurve.Points[AutoTargetWeakspotCurve.Points.Length - 1].InVal, Acos(AutoTargetWeakspotCurve.Points[AutoTargetWeakspotCurve.Points.Length - 1].OutVal), Acos(AutoTargetWeakspotCurve.Points[AutoTargetWeakspotCurve.Points.Length - 1].OutVal),16,MakeColor(255,255,0,255),true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the best location for auto targeting a pawn
|
|
*/
|
|
function vector GetBestAutoTargetLocation(Pawn CheckTarget, out name outBoneName)
|
|
{
|
|
local KFPawn KFP;
|
|
local array<name> WeakBones;
|
|
local array<name> NormalBones;
|
|
local vector TestLoc, CamLoc, CamDir, HitLoc, HitNorm;
|
|
local rotator CamRot;
|
|
local Actor HitActor;
|
|
local TraceHitInfo HitInfo;
|
|
local int i;
|
|
|
|
if( CheckTarget == none )
|
|
{
|
|
return vect(0,0,0);
|
|
}
|
|
|
|
KFP = KFPawn(CheckTarget);
|
|
if( KFP != none )
|
|
{
|
|
// Get the location from the pawn we're targeting if we can
|
|
KFP.GetAutoTargetBones(WeakBones, NormalBones);
|
|
|
|
// cone setup
|
|
GetPlayerViewPoint( CamLoc, CamRot );
|
|
CamRot += WeaponBufferRotation;
|
|
CamDir = vector(CamRot);
|
|
|
|
for(i = 0; i < WeakBones.Length; ++i)
|
|
{
|
|
TestLoc = KFP.Mesh.GetBoneLocation(WeakBones[i]);
|
|
if ( !IsAutoTargetWithinCone(TestLoc, CamLoc, CamDir, AutoTargetWeakspotCurve) )
|
|
continue; // test each weakspot bone
|
|
|
|
HitActor = Pawn.Trace(HitLoc, HitNorm, TestLoc, CamLoc, TRUE, vect(0,0,0), HitInfo, TRACEFLAG_Bullet);
|
|
if( HitActor == none || HitActor == CheckTarget )
|
|
{
|
|
`log("Targeting P="$CheckTarget@"Bone="$WeakBones[i], bDebugAutoTarget);
|
|
outBoneName = WeakBones[i];
|
|
return TestLoc;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < NormalBones.Length; ++i)
|
|
{
|
|
TestLoc = KFP.Mesh.GetBoneLocation(NormalBones[i]);
|
|
|
|
// No need to bother with cone check, since we're going to return some
|
|
// location anyway, use line trace to determine which.
|
|
HitActor = Pawn.Trace(HitLoc, HitNorm, TestLoc, CamLoc, TRUE, vect(0,0,0), HitInfo, TRACEFLAG_Bullet);
|
|
if( HitActor == none || HitActor == CheckTarget )
|
|
{
|
|
`log("Targeting P="$CheckTarget@"Bone="$NormalBones[i], bDebugAutoTarget);
|
|
outBoneName = NormalBones[i];
|
|
return TestLoc;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Slightly above center
|
|
outBoneName = '';
|
|
return CheckTarget.Location + CheckTarget.BaseEyeHeight * vect(0,0,0.5);
|
|
}
|
|
|
|
/**
|
|
* Returns true if TargetLoc falls within a given view cone
|
|
*/
|
|
function bool IsAutoTargetWithinCone(vector TargetLoc, vector CamLoc, vector CamDir, const out InterpCurveFloat Curve)
|
|
{
|
|
local float DistToTarget, TargetRadius, TargetHeight;
|
|
local float DotDiffToTarget;
|
|
local float UsedTargetAngle;
|
|
local vector CamToTarget;
|
|
|
|
// Figure out the distance from aim to target
|
|
CamToTarget = (TargetLoc - CamLoc);
|
|
if ( VSizeSq(CamToTarget) > Square(Curve.Points[Curve.Points.Length-1].InVal) )
|
|
{
|
|
`log("Auto-target Cone Distance Exceeded Dist="$VSize(CamToTarget), bDebugAutoTarget);
|
|
return false;
|
|
}
|
|
|
|
// Figure out the angle to the target
|
|
DistToTarget = VSize(CamToTarget);
|
|
DotDiffToTarget = Normal(TargetLoc - CamLoc) dot CamDir;
|
|
UsedTargetAngle = EvalInterpCurveFloat(Curve, DistToTarget);
|
|
|
|
if( bDebugAutoTarget )
|
|
{
|
|
// Grab collision info from target
|
|
CurrentAutoTarget.GetBoundingCylinder( TargetRadius, TargetHeight );
|
|
|
|
DrawDebugCylinder(AutoTargetInitialLocation+vect(0,0,5), AutoTargetInitialLocation-vect(0,0,5), 10, 12, 255, 0, 0);
|
|
DrawDebugCylinder(CurrentAutoTarget.Location+vect(0,0,1)*TargetHeight, CurrentAutoTarget.Location-vect(0,0,1)*TargetHeight, TargetRadius, 12, 0, 255, 0);
|
|
|
|
//`log(GetFuncName()@"DotDiffToTarget = "$DotDiffToTarget$" AutoTargetAngle = "$UsedTargetAngle);
|
|
}
|
|
|
|
// Make sure the target is in front of us and close enough
|
|
if( UsedTargetAngle > DotDiffToTarget )
|
|
{
|
|
`log("Auto-target Cone Angle Exceeded by "$UsedTargetAngle - DotDiffToTarget, bDebugAutoTarget);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* This will attempt to auto aim at the target. It will forcibly aim the player at the target.
|
|
*
|
|
**/
|
|
function ApplyAutoTarget( float DeltaTime, KFWeapon W, out int out_YawRot, out int out_PitchRot )
|
|
{
|
|
local Vector RealTargetLoc, CamLoc, CamDir;
|
|
local Rotator CamRot, DeltaRot, RotToTarget;
|
|
local int AdjustY, AdjustZ;
|
|
local float BlendTimeToGo;
|
|
|
|
BlendTimeToGo = AutoTargetTimeLeft;
|
|
AutoTargetTimeLeft -= DeltaTime;
|
|
|
|
if( !bUsingGamepad || CurrentAutoTarget == None || AutoTargetTimeLeft <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the target is still alive and has its head
|
|
if( CurrentAutoTarget != none && CurrentAutoTarget.Health > 0 &&
|
|
(KFPawn_Monster(CurrentAutoTarget) == none || !KFPawn_Monster(CurrentAutoTarget).bIsHeadless) )
|
|
{
|
|
RealTargetLoc = AutoTargetInitialLocation;
|
|
|
|
// If the target supplied a bone name, try to follow it
|
|
if ( CurrentAutoTargetBone != '' )
|
|
{
|
|
RealTargetLoc = CurrentAutoTarget.Mesh.GetBoneLocation(CurrentAutoTargetBone);
|
|
}
|
|
|
|
// cone setup
|
|
GetPlayerViewPoint( CamLoc, CamRot );
|
|
CamRot += WeaponBufferRotation;
|
|
CamDir = vector(CamRot);
|
|
|
|
// Make sure the target is still front of us and close enough
|
|
if ( !IsAutoTargetWithinCone(RealTargetLoc, CamLoc, CamDir, AutoTargetAngleCurve) )
|
|
{
|
|
`log("ApplyAutoTarget target lost"@CurrentAutoTarget, bDebugAutoTarget);
|
|
return;
|
|
}
|
|
|
|
RotToTarget = Rotator(RealTargetLoc - CamLoc);
|
|
DeltaRot.Yaw = RotToTarget.Yaw - CamRot.Yaw;
|
|
DeltaRot.Pitch = RotToTarget.Pitch - CamRot.Pitch;
|
|
DeltaRot = Normalize( DeltaRot );
|
|
|
|
// Lateral adhesion
|
|
if( DeltaRot.Yaw != 0 )
|
|
{
|
|
// Amount we want to change by.
|
|
if ( BlendTimeToGo > DeltaTime )
|
|
{
|
|
AdjustY = (DeltaRot.Yaw / BlendTimeToGo) * DeltaTime;
|
|
}
|
|
else
|
|
{
|
|
AdjustY = DeltaRot.Yaw;
|
|
}
|
|
|
|
// Apply the adhesion
|
|
out_YawRot += AdjustY;
|
|
}
|
|
|
|
// Vertical adhesion
|
|
if( DeltaRot.Pitch != 0 )
|
|
{
|
|
// Amount we want to change by.
|
|
if ( BlendTimeToGo > DeltaTime )
|
|
{
|
|
AdjustZ = (DeltaRot.Pitch / BlendTimeToGo) * DeltaTime;
|
|
}
|
|
else
|
|
{
|
|
AdjustZ = DeltaRot.Pitch;
|
|
}
|
|
|
|
// Apply the adhesion
|
|
out_PitchRot += AdjustZ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This will forcibly aim the player at the look at pawn target.
|
|
*
|
|
**/
|
|
function ApplyForceLookAtPawn( float DeltaTime, out int out_YawRot, out int out_PitchRot )
|
|
{
|
|
local Vector RealTargetLoc, CamLoc;
|
|
local Vector X, Y, Z;
|
|
local Rotator CamRot, DeltaRot, CamRotWithFreeAim;
|
|
local float AdhesionAmtY, AdhesionAmtZ, TargetRadius, TargetHeight;
|
|
local int AdjustY, AdjustZ;
|
|
local float DotDiffToTarget;
|
|
local float UsedRotationRate;
|
|
|
|
if( ForceLookAtPawn == None )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Setup some initial data
|
|
GetPlayerViewPoint( CamLoc, CamRot );
|
|
CamRotWithFreeAim = CamRot + WeaponBufferRotation;
|
|
GetAxes( CamRotWithFreeAim, X, Y, Z );
|
|
|
|
// If the target is still alive
|
|
if( ForceLookAtPawn != none && ForceLookAtPawn.Health > 0 )
|
|
{
|
|
// Get the location from the pawn we're targeting if we can
|
|
RealTargetLoc = ForceLookAtPawn.GetAutoLookAtLocation(CamLoc, Pawn);
|
|
|
|
if( bDebugAutoTarget )
|
|
{
|
|
// Grab collision info from target
|
|
ForceLookAtPawn.GetBoundingCylinder( TargetRadius, TargetHeight );
|
|
|
|
DrawDebugCylinder(RealTargetLoc+vect(0,0,5), RealTargetLoc-vect(0,0,5), 10, 12, 255, 0, 0);
|
|
DrawDebugCylinder(ForceLookAtPawn.Location+vect(0,0,1)*TargetHeight, ForceLookAtPawn.Location-vect(0,0,1)*TargetHeight, TargetRadius, 12, 0, 255, 0);
|
|
}
|
|
|
|
// Figure out the angle to the target
|
|
DotDiffToTarget = Normal(RealTargetLoc - CamLoc) dot Normal(Vector(CamRotWithFreeAim));
|
|
|
|
// Select the rotation rate, fast if we are far away, slower if we are close to target
|
|
if( DotDiffToTarget < ForceLookAtPawnMinAngle )
|
|
{
|
|
UsedRotationRate = ForceLookAtPawnRotationRate;
|
|
}
|
|
else
|
|
{
|
|
UsedRotationRate = ForceLookAtPawnDampenedRotationRate;
|
|
}
|
|
|
|
// Rotate toward look at target
|
|
DeltaRot.Yaw = Rotator(RealTargetLoc - CamLoc).Yaw - CamRotWithFreeAim.Yaw;
|
|
DeltaRot.Pitch = Rotator(RealTargetLoc - CamLoc).Pitch - CamRotWithFreeAim.Pitch;
|
|
DeltaRot = Normalize( DeltaRot );
|
|
|
|
// Lateral adhesion
|
|
if( DeltaRot.Yaw != 0 )
|
|
{
|
|
AdhesionAmtY = UsedRotationRate;
|
|
|
|
// Apply the adhesion
|
|
AdjustY = DeltaRot.Yaw * (AdhesionAmtY * DeltaTime);
|
|
out_YawRot += AdjustY;
|
|
}
|
|
|
|
// Vertical adhesion
|
|
if( DeltaRot.Pitch != 0 )
|
|
{
|
|
AdhesionAmtZ = UsedRotationRate;
|
|
|
|
// Apply the adhesion
|
|
AdjustZ = DeltaRot.Pitch * (AdhesionAmtZ * DeltaTime);
|
|
out_PitchRot += AdjustZ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Returns true if our local player pawn is currently sprinting */
|
|
function bool IsPawnSprinting()
|
|
{
|
|
if ( Pawn == None )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check bRun first as an early out, but we should cast the KFP to make sure
|
|
return (bRun > 0 && KFPawn(Pawn).bIsSprinting);
|
|
}
|
|
|
|
/**
|
|
* This will attempt to keep the player aiming at the target. It will forcibly aim the player at the target.
|
|
*
|
|
* TODO: move this to c++
|
|
**/
|
|
function ApplyTargetAdhesion( float DeltaTime, KFWeapon W, out int out_YawRot, out int out_PitchRot )
|
|
{
|
|
local Vector CamLoc, X, Y, Z;
|
|
local Rotator CamRot;
|
|
local float DistToTarget, TargetRadius, TargetHeight, AdhesionScale;
|
|
local Pawn AdhesionTarget;
|
|
local vector AdhesionTargetVel, AdhesionPawnVel, AdhesionTargetRelVel, AdhesionViewOffset;
|
|
|
|
// No adhesion without a gamepad!
|
|
if ( !bUsingGamepad )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( W == None || !W.bTargetAdhesionEnabled )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( IsPawnSprinting() )
|
|
{
|
|
// keep track of player location either way
|
|
AdhesionPawnLastLoc = Pawn.Location;
|
|
LastAdhesionTarget = none;
|
|
return;
|
|
}
|
|
|
|
GetPlayerViewPoint( CamLoc, CamRot );
|
|
GetAxes( CamRot, X, Y, Z );
|
|
|
|
// attempt to use the friction target if available
|
|
AdhesionTarget = LastFrictionTarget;
|
|
if( AdhesionTarget == None )
|
|
{
|
|
// otherwise look for a new target
|
|
AdhesionTarget = GetTargetAdhesionFrictionTarget( W.TargetAdhesionDistanceMax, CamLoc, CamRot, AdhesionAngleCurve );
|
|
}
|
|
|
|
if( AdhesionTarget == none )
|
|
{
|
|
// keep track of player location either way
|
|
AdhesionPawnLastLoc = Pawn.Location;
|
|
LastAdhesionTarget = none;
|
|
return;
|
|
}
|
|
else if (AdhesionTarget != LastAdhesionTarget)
|
|
{
|
|
// reset locations when we switch adhesion targets
|
|
AdhesionPawnLastLoc = Pawn.Location;
|
|
AdhesionTargetLastLoc = AdhesionTarget.Location;
|
|
LastAdhesionTarget = AdhesionTarget;
|
|
return;
|
|
}
|
|
|
|
if( AdhesionTarget.Health > 0 && KFPawn_Monster(AdhesionTarget) != none && !KFPawn_Monster(AdhesionTarget).bIsHeadless )
|
|
{
|
|
// Get yaw and pitch view offset from adhesiontarget
|
|
GetAimAssistViewOffsetFromTarget(CamLoc, CamRot, AdhesionTarget, AdhesionViewOffset, DistToTarget);
|
|
|
|
AdhesionTarget.GetBoundingCylinder( TargetRadius, TargetHeight );
|
|
|
|
// If the player's view is "on" the target, apply adhesion.
|
|
// Adhesion gently rotates the player away from the relative velocity direction of player and target
|
|
if( AdhesionViewOffset.Y <= TargetRadius && AdhesionViewOffset.Z <= TargetHeight )
|
|
{
|
|
// Get target's velocity based on current and previous position (Velocity Actor member variable doesn't include Z). If current target is a new target, use 0 velocity
|
|
AdhesionTargetVel = (AdhesionTarget != LastAdhesionTarget) ? vect(0,0,0) : ((AdhesionTarget.Location - AdhesionTargetLastLoc) / DeltaTime);
|
|
|
|
// Get player's velocity based on current and previous position
|
|
AdhesionPawnVel = IsZero(AdhesionPawnLastLoc) ? vect(0,0,0) : ((Pawn.Location - AdhesionPawnLastLoc) / DeltaTime);
|
|
|
|
// re-add a percentage of the speed taken away due to "walking" (iron sights)
|
|
if( Pawn != none && Pawn.bIsWalking )
|
|
{
|
|
AdhesionPawnVel.X = Lerp(AdhesionPawnVel.X, AdhesionPawnVel.X / Pawn.WalkingPct, 0.20f);
|
|
AdhesionPawnVel.Y = Lerp(AdhesionPawnVel.Y, AdhesionPawnVel.Y / Pawn.WalkingPct, 0.20f);
|
|
}
|
|
|
|
// Calculate relative velocity between player and target and scale by distance scale curve
|
|
AdhesionTargetRelVel = (AdhesionTargetVel - AdhesionPawnVel) * EvalInterpCurveFloat(W.TargetAdhesionDistanceScaleCurve, DistToTarget / W.TargetAdhesionDistanceMax);
|
|
|
|
AdhesionScale = AdhesionFactor * DeltaTime;
|
|
|
|
// Delta rotation is based on velocity, distance, and angle to target
|
|
out_YawRot += (AdhesionTargetRelVel Dot Y) * AdhesionScale * EvalInterpCurveFloat(W.TargetAdhesionOffsetScaleCurve, AdhesionViewOffset.Y / TargetRadius);
|
|
out_PitchRot += (AdhesionTargetRelVel Dot Z) * AdhesionScale * EvalInterpCurveFloat(W.TargetAdhesionOffsetScaleCurve, AdhesionViewOffset.Z / TargetHeight);
|
|
}
|
|
}
|
|
|
|
// keep track of player and target locations
|
|
AdhesionTargetLastLoc = AdhesionTarget.Location;
|
|
AdhesionPawnLastLoc = Pawn.Location;
|
|
|
|
LastAdhesionTarget = AdhesionTarget;
|
|
}
|
|
|
|
/**
|
|
* This will slow down the player's aiming when they are on "top" of a valid Target. So when you whip around
|
|
* there will be a slight slow down when you are directly aiming at a target.
|
|
*
|
|
* TODO: Combine Friction and Adhesion, to use the same "area"
|
|
**/
|
|
function ApplyTargetFriction( float DeltaTime, KFWeapon W )
|
|
{
|
|
local Pawn FrictionTarget;
|
|
local Vector CamLoc, X, Y, Z, FrictionViewOffset;
|
|
local Rotator CamRot;
|
|
local float DistToTarget, TargetRadius, TargetHeight, FrictionMultiplier;
|
|
|
|
if( Pawn == None || !W.bTargetFrictionEnabled || IsPawnSprinting() )
|
|
{
|
|
//`log( "ApplyTargetFriction returning: W.bTargetFrictionEnabled: " $ W.bTargetFrictionEnabled $ " Pawn: " $ Pawn $ " W: " $ W );
|
|
return;
|
|
}
|
|
|
|
GetPlayerViewPoint( CamLoc, CamRot );
|
|
GetAxes( CamRot, X, Y, Z );
|
|
|
|
// Look for a friction target
|
|
FrictionTarget = GetTargetAdhesionFrictionTarget( W.TargetFrictionDistanceMax, CamLoc, CamRot, FrictionAngleCurve );
|
|
|
|
if( FrictionTarget != none )
|
|
{
|
|
// Get yaw and pitch view offset from FrictionTarget
|
|
GetAimAssistViewOffsetFromTarget(CamLoc, CamRot, FrictionTarget, FrictionViewOffset, DistToTarget);
|
|
|
|
FrictionTarget.GetBoundingCylinder( TargetRadius, TargetHeight );
|
|
|
|
// If the player's view is "on" the target, apply friction.
|
|
// Friction slows the player's rotation when player's view is "on" the target
|
|
if( FrictionViewOffset.Y <= TargetRadius && FrictionViewOffset.Z <= TargetHeight )
|
|
{
|
|
FrictionMultiplier = FrictionScale;
|
|
|
|
// scale friction by distance and yaw offset to target
|
|
FrictionMultiplier *= EvalInterpCurveFloat(W.TargetFrictionDistanceScaleCurve, DistToTarget / W.TargetFrictionDistanceMax);
|
|
FrictionMultiplier *= EvalInterpCurveFloat(W.TargetFrictionOffsetScaleCurve, FrictionViewOffset.Y / TargetRadius);
|
|
|
|
// reduce friction if player is currently using view acceleration (turn assist)
|
|
if( ApplyingViewAcceleration() )
|
|
{
|
|
FrictionMultiplier *= ViewAccelerationFrictionScale;
|
|
}
|
|
|
|
// Apply the friction
|
|
CurrTurn *= 1.f - FrictionMultiplier;
|
|
CurrLookUp *= 1.f - FrictionMultiplier;
|
|
|
|
// Keep the friction target for possible use with adhesion
|
|
LastFrictionTargetTime = WorldInfo.TimeSeconds;
|
|
LastFrictionTarget = FrictionTarget;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Returns yaw and pitch offsets for a given location/orientation to a given actor's location. Also can return distance. */
|
|
function GetAimAssistViewOffsetFromTarget( vector ViewLoc, rotator ViewRot, Actor Target, out vector Offset, optional out float Distance )
|
|
{
|
|
local Vector TargetLoc, CamToTarget, AimLoc;
|
|
|
|
CamToTarget = Target.Location - ViewLoc;
|
|
Distance = VSize(CamToTarget);
|
|
AimLoc = ViewLoc + vector(ViewRot) * Distance;
|
|
|
|
// Calculate the aim friction multiplier
|
|
// Y component
|
|
TargetLoc = Target.Location;
|
|
TargetLoc.Z = AimLoc.Z;
|
|
Offset.Y = PointDistToLine( AimLoc, (TargetLoc - ViewLoc), ViewLoc );
|
|
|
|
// Z component
|
|
TargetLoc = Target.Location;
|
|
TargetLoc.X = AimLoc.X;
|
|
TargetLoc.Y = AimLoc.Y;
|
|
Offset.Z = PointDistToLine( AimLoc, (TargetLoc - ViewLoc), ViewLoc );
|
|
}
|
|
|
|
/** Do custom FOV scaling, especially for weapons with 3D scopes */
|
|
function float GetSensitivityByFOV( float ZoomSensitivityScale )
|
|
{
|
|
local KFPawn_Monster KFPM;
|
|
local float UsedFOVAngle;
|
|
local bool bUsingSights;
|
|
|
|
// Take FOV into account (lower FOV == less sensitivity).
|
|
if ( bEnableFOVScaling )
|
|
{
|
|
if( Pawn != none && Pawn.Weapon != none )
|
|
{
|
|
bUsingSights = Pawn.bIsWalking;
|
|
UsedFOVAngle = Pawn.Weapon.GetModifiedFOVAngle();
|
|
}
|
|
else
|
|
{
|
|
UsedFOVAngle = GetFOVAngle();
|
|
}
|
|
|
|
// Allow monster pawns to do sensitivity adjustments first
|
|
if( bVersusInput && !bUsingSights )
|
|
{
|
|
KFPM = KFPawn_Monster( Pawn );
|
|
if( KFPM != none && KFPM.UseAdjustedControllerSensitivity() )
|
|
{
|
|
bUsingSights = true;
|
|
}
|
|
}
|
|
|
|
if( UsedFOVAngle != DefaultFOV && bUsingSights )
|
|
{
|
|
return UsedFOVAngle * 0.01333 * ZoomSensitivityScale; // 0.01333 = 1 / 75.0 - the default FOV
|
|
}
|
|
}
|
|
|
|
return 1.f;
|
|
}
|
|
|
|
/** Toggle debug display for view acceleration **/
|
|
exec function DebugViewAcceleration()
|
|
{
|
|
if ( WorldInfo.NetMode == NM_StandAlone )
|
|
{
|
|
bDebugViewAcceleration = !bDebugViewAcceleration;
|
|
ClientMessage( "bDebugViewAcceleration is now: " $ bDebugViewAcceleration );
|
|
}
|
|
}
|
|
|
|
/** Toggle debug display for target adhesion **/
|
|
exec function DebugTargetAdhesion()
|
|
{
|
|
if ( WorldInfo.NetMode == NM_StandAlone )
|
|
{
|
|
bDebugTargetAdhesion = !bDebugTargetAdhesion;
|
|
ClientMessage( "bDebugTargetAdhesion is now: " $ bDebugTargetAdhesion );
|
|
}
|
|
}
|
|
|
|
/** Toggle debug display for auto target **/
|
|
exec function DebugAutoTarget()
|
|
{
|
|
if ( WorldInfo.NetMode == NM_StandAlone )
|
|
{
|
|
bDebugAutoTarget = !bDebugAutoTarget;
|
|
ClientMessage( "bDebugAutoTarget is now: " $ bDebugAutoTarget );
|
|
}
|
|
}
|
|
|
|
/** Toggle debug display for target friction **/
|
|
exec function DebugTargetFriction()
|
|
{
|
|
if ( WorldInfo.NetMode == NM_StandAlone )
|
|
{
|
|
bDebugTargetFriction = !bDebugTargetFriction;
|
|
ClientMessage( "bDebugTargetFriction is now: " $ bDebugTargetFriction );
|
|
}
|
|
}
|
|
|
|
function bool IsAimAssistFrictionEnabled()
|
|
{
|
|
return bAimAssistEnabled && bTargetFrictionEnabled;
|
|
}
|
|
|
|
function bool IsAimAssistAdhesionEnabled()
|
|
{
|
|
return bAimAssistEnabled && bTargetAdhesionEnabled;
|
|
}
|
|
|
|
function bool IsAimAssistAutoTargetEnabled()
|
|
{
|
|
return bAimAssistEnabled && bAutoTargetEnabled;
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* Versus
|
|
********************************************************************************************* */
|
|
|
|
/** Returns special move associated with a firemode input */
|
|
simulated function ESpecialMove GetVersusZedMoveType(KFPawn_Monster P, byte FireModeNum)
|
|
{
|
|
if ( bUsingGamepad )
|
|
{
|
|
return GetVersusZedMoveTypeGamepad(P, FireModeNum);
|
|
}
|
|
|
|
switch (FireModeNum)
|
|
{
|
|
case class'KFWeapon'.const.DEFAULT_FIREMODE:
|
|
return SM_PlayerZedMove_LMB;
|
|
|
|
case 5: // Ironsights Key
|
|
return SM_PlayerZedMove_RMB;
|
|
|
|
case class'KFWeapon'.const.BASH_FIREMODE:
|
|
return SM_PlayerZedMove_V;
|
|
|
|
case class'KFWeapon'.const.ALTFIRE_FIREMODE:
|
|
return SM_PlayerZedMove_MMB;
|
|
|
|
case 8: // Quick heal key
|
|
return SM_PlayerZedMove_Q;
|
|
|
|
case class'KFWeapon'.const.GRENADE_FIREMODE:
|
|
return SM_PlayerZedMove_G;
|
|
|
|
case class'KFWeapon'.const.RELOAD_FIREMODE:
|
|
return SM_Taunt;
|
|
}
|
|
|
|
return SM_None;
|
|
}
|
|
|
|
/** Returns special move associated with a firemode input */
|
|
simulated function ESpecialMove GetVersusZedMoveTypeGamepad(KFPawn_Monster P, byte FireModeNum)
|
|
{
|
|
if ( FireModeNum < P.MoveListGamepadScheme.Length )
|
|
{
|
|
if ( P.MoveListGamepadScheme[FireModeNum] != SM_None )
|
|
{
|
|
return P.MoveListGamepadScheme[FireModeNum];
|
|
}
|
|
// else, fall-through to switch statement
|
|
}
|
|
else if ( FireModeNum == 7 )
|
|
{
|
|
return SM_Taunt;
|
|
}
|
|
|
|
// certain buttons can be used as secondary options when empty
|
|
switch ( FireModeNum )
|
|
{
|
|
case class'KFWeapon'.const.DEFAULT_FIREMODE:
|
|
return P.MoveListGamepadScheme[ZGM_Melee_Square];
|
|
|
|
case 5: // Ironsights Key
|
|
return P.MoveListGamepadScheme[ZGM_Melee_Triangle];
|
|
|
|
case 6: // Weapon Select Key
|
|
return P.MoveListGamepadScheme[ZGM_Melee_Square];
|
|
|
|
default:
|
|
}
|
|
|
|
return SM_None;
|
|
}
|
|
|
|
function bool CustomStartFireVersus( optional Byte FireModeNum )
|
|
{
|
|
local KFPawn_Monster P;
|
|
|
|
if( Pawn != none && (Pawn.Weapon == none || Pawn.Weapon.ShouldWeaponIgnoreStartFire()) )
|
|
{
|
|
P = KFPawn_Monster(Pawn);
|
|
if ( P != None )
|
|
{
|
|
bUsingVersusGamepadScheme = bUsingGamepad;
|
|
P.StartPlayerZedMove(GetVersusZedMoveType(P, FireModeNum));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool CustomStopFireVersus( optional Byte FireModeNum )
|
|
{
|
|
local KFPawn_Monster P;
|
|
|
|
if( Pawn != none && (Pawn.Weapon == none || Pawn.Weapon.ShouldWeaponIgnoreStartFire()) )
|
|
{
|
|
P = KFPawn_Monster(Pawn);
|
|
if ( P != None )
|
|
{
|
|
// bUsingGamepad uses a custom scheme, so when it's toggled we mayis toggled it
|
|
if ( bUsingVersusGamepadScheme != bUsingGamepad )
|
|
{
|
|
if( P != None && P.IsDoingSpecialMove() )
|
|
{
|
|
P.SpecialMoves[P.SpecialMove].SpecialMoveButtonReleased();
|
|
}
|
|
}
|
|
P.StopPlayerZedMove(GetVersusZedMoveType(P, FireModeNum));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool QuickHealVersus()
|
|
{
|
|
if( CustomStartFireVersus(8) )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function JumpVersus()
|
|
{
|
|
local KFPawn_Monster KFPM;
|
|
|
|
if ( WorldInfo.Pauser == PlayerReplicationInfo )
|
|
{
|
|
SetPause( False );
|
|
}
|
|
else
|
|
{
|
|
if( Pawn != none )
|
|
{
|
|
KFPM = KFPawn_Monster(Pawn);
|
|
if( KFPM != none )
|
|
{
|
|
if( KFPM.GetSpecialMoveCooldownTimeRemaining(SM_Jump) > 0.f )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bPressedJump = true;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* Debugging - These are not cheats! Some commands, such as SETNOPEC are disabled in ShippingPC
|
|
* We use 'config' vars for log groups so they can always be set from ini.
|
|
********************************************************************************************* */
|
|
|
|
exec function SuppressTakeDamage(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogTakeDamage false");
|
|
}
|
|
|
|
exec function UnsuppressTakeDamage(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogTakeDamage true");
|
|
}
|
|
|
|
exec function SuppressPhysicsBodyImpact(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogPhysicsBodyImpact false");
|
|
}
|
|
|
|
exec function UnsuppressPhysicsBodyImpact(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogPhysicsBodyImpact true");
|
|
}
|
|
|
|
exec function SuppressSpecialMove(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogSpecialMove false");
|
|
}
|
|
|
|
exec function UnsuppressSpecialMove(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogSpecialMove true");
|
|
}
|
|
|
|
exec function SuppressPawnAnim(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogCustomAnim false");
|
|
}
|
|
|
|
exec function UnsuppressPawnAnim(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFPawn';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogCustomAnim true");
|
|
}
|
|
|
|
exec function SuppressWeaponAttach(optional name ClassName)
|
|
{
|
|
ConsoleCommand("SETNOPEC KFWeaponAttachment bDebug false");
|
|
}
|
|
|
|
exec function UnsuppressWeaponAttach(optional name ClassName)
|
|
{
|
|
ConsoleCommand("SETNOPEC KFWeaponAttachment bDebug true");
|
|
}
|
|
|
|
exec function SuppressAffliction(optional name ClassName)
|
|
{
|
|
ConsoleCommand("SETNOPEC KFAfflictionBase bDebug false");
|
|
ConsoleCommand("SETNOPEC KFAfflictionManager bDebugLog false");
|
|
}
|
|
|
|
exec function UnsuppressAffliction(optional name ClassName)
|
|
{
|
|
ConsoleCommand("SETNOPEC KFAfflictionBase bDebug true");
|
|
ConsoleCommand("SETNOPEC KFAfflictionManager bDebugLog true");
|
|
}
|
|
|
|
exec function SuppressWeaponAnim(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFWeapon';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogAnimation false");
|
|
}
|
|
|
|
exec function UnsuppressWeaponAnim(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName!='') ? ClassName : 'KFWeapon';
|
|
ConsoleCommand("SETNOPEC"@ClassName@"bLogAnimation true");
|
|
}
|
|
|
|
exec function SuppressMelee()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFMeleeHelperBase bLogMelee false");
|
|
}
|
|
|
|
exec function UnsuppressMelee()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFMeleeHelperBase bLogMelee true");
|
|
}
|
|
|
|
exec function SuppressPerk()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFPerk bLogPerk false");
|
|
}
|
|
|
|
exec function UnsuppressPerk()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFPerk bLogPerk true");
|
|
}
|
|
|
|
exec function SuppressAISpawnLogging()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFAISpawnManager bLogAISpawning false");
|
|
}
|
|
|
|
exec function UnsuppressAISpawnLogging()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFAISpawnManager bLogAISpawning true");
|
|
}
|
|
|
|
exec function SuppressWaveSpawnLogging()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFAISpawnManager bLogWaveSpawnTiming false");
|
|
}
|
|
|
|
exec function UnsuppressWaveSpawnLogging()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFAISpawnManager bLogWaveSpawnTiming true");
|
|
}
|
|
|
|
exec function SuppressScoring()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFGameInfo bLogScoring false");
|
|
}
|
|
|
|
exec function UnsuppressScoring()
|
|
{
|
|
ConsoleCommand("SETNOPEC KFGameInfo bLogScoring true");
|
|
}
|
|
|
|
exec function SuppressDialog(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName != '') ? ClassName : 'KFDialogManager';
|
|
ConsoleCommand("SETNOPEC" @ ClassName @ "bLogDialog false");
|
|
}
|
|
|
|
exec function UnsuppressDialog(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName != '') ? ClassName : 'KFDialogManager';
|
|
ConsoleCommand("SETNOPEC" @ ClassName @ "bLogDialog true");
|
|
}
|
|
|
|
exec function SuppressTrader(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName != '') ? ClassName : 'KFTraderTrigger';
|
|
ConsoleCommand("SETNOPEC" @ ClassName @ "bLogTrader false");
|
|
}
|
|
|
|
exec function UnsuppressTrader(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName != '') ? ClassName : 'KFTraderTrigger';
|
|
ConsoleCommand("SETNOPEC" @ ClassName @ "bLogTrader true");
|
|
}
|
|
|
|
exec function SuppressWeaponUpgrade(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName != '') ? ClassName : 'KFWeapon';
|
|
ConsoleCommand("SETNOPEC" @ ClassName @ "bLogWeaponUpgrade false");
|
|
}
|
|
|
|
exec function UnsuppressWeaponUpgrade(optional name ClassName)
|
|
{
|
|
ClassName = (ClassName != '') ? ClassName : 'KFWeapon';
|
|
ConsoleCommand("SETNOPEC" @ ClassName @ "bLogWeaponUpgrade true");
|
|
}
|
|
|
|
`if(`notdefined(ShippingPC))
|
|
/** QA Logging for Players Selected Perk */
|
|
exec function LogPerkInfo()
|
|
{
|
|
if ( CurrentPerk != none )
|
|
{
|
|
CurrentPerk.ServerLogPerks();
|
|
}
|
|
}
|
|
`endif
|
|
|
|
defaultproperties
|
|
{
|
|
bEnableFOVScaling=true
|
|
|
|
LookSensitivityScaleCurve=(Points=((InVal=0.000000,OutVal=0.000000,ArriveTangent=0.500000,LeaveTangent=0.500000,InterpMode=CIM_CurveAuto),(InVal=0.800000,OutVal=0.600000,ArriveTangent=2.000000,LeaveTangent=2.000000,InterpMode=CIM_CurveAuto),(InVal=1.000000,OutVal=1.300000,ArriveTangent=8.000000,LeaveTangent=8.000000,InterpMode=CIM_CurveAuto)),InterpMethod=IMT_UseFixedTangentEvalAndNewAutoTangents)
|
|
MoveSensitivityScaleCurve=(Points=((InVal=0.000000,OutVal=0.300000,ArriveTangent=0.000000,LeaveTangent=0.000000,InterpMode=CIM_Constant),(InVal=0.300000,OutVal=0.300000,ArriveTangent=0.000000,LeaveTangent=0.000000,InterpMode=CIM_Linear),(InVal=0.900000,OutVal=1.000000,ArriveTangent=0.000000,LeaveTangent=0.000000,InterpMode=CIM_Linear)),InterpMethod=IMT_UseFixedTangentEvalAndNewAutoTangents)
|
|
|
|
DoubleTapDelay=0.25f
|
|
bShowGamepadWeaponSelectHint=TRUE
|
|
ZedAutoSprintAnalogThreshold=0.75f
|
|
|
|
// ---------------------------------------------
|
|
// Aim assists
|
|
|
|
// View Sensitivity
|
|
SprintingSensitivityScale=0.675f
|
|
|
|
// View Smoothing
|
|
ViewSmoothing_MaxAccel=25 // joystick per second?
|
|
ViewSmoothing_MaxDecel=50
|
|
bViewSmoothingEnabled=true
|
|
|
|
// Auto target / Zoom lock-on
|
|
AutoTargetTimeLeft=0.1
|
|
AutoTargetCooldown=0.75
|
|
|
|
// [0m:45d] [3m:20d] [15m:10d] [40m:5d]
|
|
AutoTargetAngleCurve=(Points=((InVal=0.f,OutVal=0.707), (InVal=300.f,OutVal=0.9397f),(InVal=1500.0f, OutVal=0.9848f),(InVal=4000.0f, OutVal=0.9962f),(InVal=6000.0f, OutVal=1.f)))
|
|
// [0m:5d], [10m:2d]
|
|
AutoTargetWeakspotCurve=(Points=((InVal=0.f,OutVal=0.9962f),(InVal=1000.0f, OutVal=0.9998f),(InVal=2000.0f, OutVal=1.f)))
|
|
|
|
// Adhesion / auto rotate
|
|
AdhesionAngleCurve=(Points=((InVal=0.f,OutVal=0.95f),(InVal=2000.0f, OutVal=0.98f)))
|
|
AdhesionFactor=16.f
|
|
|
|
// Friction / rotation slow down
|
|
// For now, friction to anything in front of us initially. The code that handles friction will pare this down to a smaller area around the pawn
|
|
FrictionAngleCurve=(Points=((InVal=0.f,OutVal=0.f),(InVal=2500.0f, OutVal=0.0f)))
|
|
FrictionScale=0.5f
|
|
ViewAccelerationFrictionScale=0.85f
|
|
|
|
// View Acceleration
|
|
ViewAccel_JoyMagThreshold=0.97
|
|
ViewAccel_JoyPitchThreshold=0.4
|
|
ViewAccel_BlendTime=0.25
|
|
ViewAccel_MaxTurnSpeed=4.5
|
|
ViewAccel_MinTurnSpeed=1.0
|
|
|
|
ForceLookAtPawnMinAngle=0.9f
|
|
ForceLookAtPawnRotationRate=22
|
|
ForceLookAtPawnDampenedRotationRate=8
|
|
}
|