1
0
KF2-Dev-Scripts/KFGame/Classes/KFPlayerInput.uc
2020-12-13 18:09:05 +03:00

2981 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 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
}