You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
914 lines
29 KiB
914 lines
29 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Joystick handling function |
|
// |
|
// $Workfile: $ |
|
// $Date: $ |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
|
|
#include "cbase.h" |
|
#include "basehandle.h" |
|
#include "utlvector.h" |
|
#include "cdll_client_int.h" |
|
#include "cdll_util.h" |
|
#include "kbutton.h" |
|
#include "usercmd.h" |
|
#include "iclientvehicle.h" |
|
#include "input.h" |
|
#include "iviewrender.h" |
|
#include "convar.h" |
|
#include "hud.h" |
|
#include "vgui/ISurface.h" |
|
#include "vgui_controls/Controls.h" |
|
#include "vgui/Cursor.h" |
|
#include "tier0/icommandline.h" |
|
#include "inputsystem/iinputsystem.h" |
|
#include "inputsystem/ButtonCode.h" |
|
#include "math.h" |
|
#include "tier1/convar_serverbounded.h" |
|
#include "cam_thirdperson.h" |
|
|
|
#if defined( _X360 ) |
|
#include "xbox/xbox_win32stubs.h" |
|
#else |
|
#include "../common/xbox/xboxstubs.h" |
|
#endif |
|
|
|
#ifdef HL2_CLIENT_DLL |
|
// FIXME: Autoaim support needs to be moved from HL2_DLL to the client dll, so this include should be c_baseplayer.h |
|
#include "c_basehlplayer.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// Control like a joystick |
|
#define JOY_ABSOLUTE_AXIS 0x00000000 |
|
// Control like a mouse, spinner, trackball |
|
#define JOY_RELATIVE_AXIS 0x00000010 |
|
|
|
// Axis mapping |
|
static ConVar joy_name( "joy_name", "joystick", FCVAR_ARCHIVE ); |
|
static ConVar joy_advanced( "joy_advanced", "1", FCVAR_ARCHIVE ); |
|
static ConVar joy_advaxisx( "joy_advaxisx", "4", FCVAR_ARCHIVE ); |
|
static ConVar joy_advaxisy( "joy_advaxisy", "2", FCVAR_ARCHIVE ); |
|
static ConVar joy_advaxisz( "joy_advaxisz", "0", FCVAR_ARCHIVE ); |
|
static ConVar joy_advaxisr( "joy_advaxisr", "1", FCVAR_ARCHIVE ); |
|
static ConVar joy_advaxisu( "joy_advaxisu", "3", FCVAR_ARCHIVE ); |
|
static ConVar joy_advaxisv( "joy_advaxisv", "0", FCVAR_ARCHIVE ); |
|
|
|
// Basic "dead zone" and sensitivity |
|
static ConVar joy_forwardthreshold( "joy_forwardthreshold", "0.15", FCVAR_ARCHIVE ); |
|
static ConVar joy_sidethreshold( "joy_sidethreshold", "0.15", FCVAR_ARCHIVE ); |
|
static ConVar joy_pitchthreshold( "joy_pitchthreshold", "0.15", FCVAR_ARCHIVE ); |
|
static ConVar joy_yawthreshold( "joy_yawthreshold", "0.15", FCVAR_ARCHIVE ); |
|
static ConVar joy_forwardsensitivity( "joy_forwardsensitivity", "-1", FCVAR_ARCHIVE ); |
|
static ConVar joy_sidesensitivity( "joy_sidesensitivity", "1", FCVAR_ARCHIVE ); |
|
static ConVar joy_pitchsensitivity( "joy_pitchsensitivity", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX ); |
|
static ConVar joy_yawsensitivity( "joy_yawsensitivity", "-1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX ); |
|
|
|
// Advanced sensitivity and response |
|
static ConVar joy_response_move( "joy_response_move", "1", FCVAR_ARCHIVE, "'Movement' stick response mode: 0=Linear, 1=quadratic, 2=cubic, 3=quadratic extreme, 4=power function(i.e., pow(x,1/sensitivity)), 5=two-stage" ); |
|
ConVar joy_response_move_vehicle("joy_response_move_vehicle", "6"); |
|
static ConVar joy_response_look( "joy_response_look", "0", FCVAR_ARCHIVE, "'Look' stick response mode: 0=Default, 1=Acceleration Promotion" ); |
|
static ConVar joy_lowend( "joy_lowend", "1", FCVAR_ARCHIVE ); |
|
static ConVar joy_lowmap( "joy_lowmap", "1", FCVAR_ARCHIVE ); |
|
static ConVar joy_accelscale( "joy_accelscale", "0.6", FCVAR_ARCHIVE); |
|
static ConVar joy_accelmax( "joy_accelmax", "1.0", FCVAR_ARCHIVE); |
|
static ConVar joy_autoaimdampenrange( "joy_autoaimdampenrange", "0", FCVAR_ARCHIVE, "The stick range where autoaim dampening is applied. 0 = off" ); |
|
static ConVar joy_autoaimdampen( "joy_autoaimdampen", "0", FCVAR_ARCHIVE, "How much to scale user stick input when the gun is pointing at a valid target." ); |
|
|
|
static ConVar joy_vehicle_turn_lowend("joy_vehicle_turn_lowend", "0.7"); |
|
static ConVar joy_vehicle_turn_lowmap("joy_vehicle_turn_lowmap", "0.4"); |
|
|
|
|
|
// Misc |
|
static ConVar joy_diagonalpov( "joy_diagonalpov", "0", FCVAR_ARCHIVE, "POV manipulator operates on diagonal axes, too." ); |
|
static ConVar joy_display_input("joy_display_input", "0", FCVAR_ARCHIVE); |
|
static ConVar joy_wwhack2( "joy_wingmanwarrior_turnhack", "0", FCVAR_ARCHIVE, "Wingman warrior hack related to turn axes." ); |
|
ConVar joy_autosprint("joy_autosprint", "0", 0, "Automatically sprint when moving with an analog joystick" ); |
|
|
|
static ConVar joy_inverty("joy_inverty", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Whether to invert the Y axis of the joystick for looking." ); |
|
|
|
// XBox Defaults |
|
static ConVar joy_yawsensitivity_default( "joy_yawsensitivity_default", "-1.25", FCVAR_NONE ); |
|
static ConVar joy_pitchsensitivity_default( "joy_pitchsensitivity_default", "-1.0", FCVAR_NONE ); |
|
static ConVar option_duck_method_default( "option_duck_method_default", "1.0", FCVAR_NONE ); |
|
static ConVar joy_inverty_default( "joy_inverty_default", "0", FCVAR_ARCHIVE_XBOX ); // Extracted & saved from profile |
|
static ConVar joy_movement_stick_default( "joy_movement_stick_default", "0", FCVAR_ARCHIVE_XBOX ); // Extracted & saved from profile |
|
static ConVar sv_stickysprint_default( "sv_stickysprint_default", "0", FCVAR_NONE ); |
|
|
|
void joy_movement_stick_Callback( IConVar *var, const char *pOldString, float flOldValue ) |
|
{ |
|
engine->ClientCmd( "joyadvancedupdate" ); |
|
} |
|
static ConVar joy_movement_stick("joy_movement_stick", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Which stick controls movement (0 is left stick)", joy_movement_stick_Callback ); |
|
|
|
static ConVar joy_xcontroller_cfg_loaded( "joy_xcontroller_cfg_loaded", "0", FCVAR_ARCHIVE, "If 0, the 360controller.cfg file will be executed on startup & option changes." ); |
|
|
|
extern ConVar lookspring; |
|
extern ConVar cl_forwardspeed; |
|
extern ConVar lookstrafe; |
|
extern ConVar in_joystick; |
|
extern ConVar_ServerBounded *m_pitch; |
|
extern ConVar l_pitchspeed; |
|
extern ConVar cl_sidespeed; |
|
extern ConVar cl_yawspeed; |
|
extern ConVar cl_pitchdown; |
|
extern ConVar cl_pitchup; |
|
extern ConVar cl_pitchspeed; |
|
|
|
extern ConVar cam_idealpitch; |
|
extern ConVar cam_idealyaw; |
|
extern ConVar thirdperson_platformer; |
|
extern ConVar thirdperson_screenspace; |
|
|
|
//----------------------------------------------------------------- |
|
// Purpose: Returns true if there's an active joystick connected. |
|
//----------------------------------------------------------------- |
|
bool CInput::EnableJoystickMode() |
|
{ |
|
return IsConsole() || in_joystick.GetBool(); |
|
} |
|
|
|
|
|
//----------------------------------------------- |
|
// Response curve function for the move axes |
|
//----------------------------------------------- |
|
static float ResponseCurve( int curve, float x, int axis, float sensitivity ) |
|
{ |
|
switch ( curve ) |
|
{ |
|
case 1: |
|
// quadratic |
|
if ( x < 0 ) |
|
return -(x*x) * sensitivity; |
|
return x*x * sensitivity; |
|
|
|
case 2: |
|
// cubic |
|
return x*x*x*sensitivity; |
|
|
|
case 3: |
|
{ |
|
// quadratic extreme |
|
float extreme = 1.0f; |
|
if ( fabs( x ) >= 0.95f ) |
|
{ |
|
extreme = 1.5f; |
|
} |
|
if ( x < 0 ) |
|
return -extreme * x*x*sensitivity; |
|
return extreme * x*x*sensitivity; |
|
} |
|
case 4: |
|
{ |
|
float flScale = sensitivity < 0.0f ? -1.0f : 1.0f; |
|
|
|
sensitivity = clamp( fabs( sensitivity ), 1.0e-8f, 1000.0f ); |
|
|
|
float oneOverSens = 1.0f / sensitivity; |
|
|
|
if ( x < 0.0f ) |
|
{ |
|
flScale = -flScale; |
|
} |
|
|
|
float retval = clamp( powf( fabs( x ), oneOverSens ), 0.0f, 1.0f ); |
|
return retval * flScale; |
|
} |
|
break; |
|
case 5: |
|
{ |
|
float out = x; |
|
|
|
if( fabs(out) <= 0.6f ) |
|
{ |
|
out *= 0.5f; |
|
} |
|
|
|
out = out * sensitivity; |
|
return out; |
|
} |
|
break; |
|
case 6: // Custom for driving a vehicle! |
|
{ |
|
if( axis == YAW ) |
|
{ |
|
// This code only wants to affect YAW axis (the left and right axis), which |
|
// is used for turning in the car. We fall-through and use a linear curve on |
|
// the PITCH axis, which is the vehicle's throttle. REALLY, these are the 'forward' |
|
// and 'side' axes, but we don't have constants for those, so we re-use the same |
|
// axis convention as the look stick. (sjb) |
|
float sign = 1; |
|
|
|
if( x < 0.0 ) |
|
sign = -1; |
|
|
|
x = fabs(x); |
|
|
|
if( x <= joy_vehicle_turn_lowend.GetFloat() ) |
|
x = RemapVal( x, 0.0f, joy_vehicle_turn_lowend.GetFloat(), 0.0f, joy_vehicle_turn_lowmap.GetFloat() ); |
|
else |
|
x = RemapVal( x, joy_vehicle_turn_lowend.GetFloat(), 1.0f, joy_vehicle_turn_lowmap.GetFloat(), 1.0f ); |
|
|
|
return x * sensitivity * sign; |
|
} |
|
//else |
|
// fall through and just return x*sensitivity below (as if using default curve) |
|
} |
|
} |
|
|
|
// linear |
|
return x*sensitivity; |
|
} |
|
|
|
|
|
//----------------------------------------------- |
|
// If we have a valid autoaim target, dampen the |
|
// player's stick input if it is moving away from |
|
// the target. |
|
// |
|
// This assists the player staying on target. |
|
//----------------------------------------------- |
|
float AutoAimDampening( float x, int axis, float dist ) |
|
{ |
|
// FIXME: Autoaim support needs to be moved from HL2_DLL to the client dll, so all games can use it. |
|
#ifdef HL2_CLIENT_DLL |
|
// Help the user stay on target if the feature is enabled and the user |
|
// is not making a gross stick movement. |
|
if( joy_autoaimdampen.GetFloat() > 0.0f && fabs(x) < joy_autoaimdampenrange.GetFloat() ) |
|
{ |
|
// Get the HL2 player |
|
C_BaseHLPlayer *pLocalPlayer = (C_BaseHLPlayer *)C_BasePlayer::GetLocalPlayer(); |
|
|
|
if( pLocalPlayer ) |
|
{ |
|
// Get the autoaim target |
|
if( pLocalPlayer->m_HL2Local.m_bAutoAimTarget ) |
|
{ |
|
return joy_autoaimdampen.GetFloat(); |
|
} |
|
} |
|
} |
|
#endif |
|
return 1.0f;// No dampening. |
|
} |
|
|
|
|
|
//----------------------------------------------- |
|
// This structure holds persistent information used |
|
// to make decisions about how to modulate analog |
|
// stick input. |
|
//----------------------------------------------- |
|
typedef struct |
|
{ |
|
float envelopeScale[2]; |
|
bool peggedAxis[2]; |
|
bool axisPeggedDir[2]; |
|
} envelope_t; |
|
|
|
envelope_t controlEnvelope; |
|
|
|
//----------------------------------------------- |
|
// Response curve function specifically for the |
|
// 'look' analog stick. |
|
// |
|
// when AXIS == YAW, otherAxisValue contains the |
|
// value for the pitch of the control stick, and |
|
// vice-versa. |
|
//----------------------------------------------- |
|
ConVar joy_pegged("joy_pegged", "0.75");// Once the stick is pushed this far, it's assumed pegged. |
|
ConVar joy_virtual_peg("joy_virtual_peg", "0"); |
|
static float ResponseCurveLookDefault( float x, int axis, float otherAxis, float dist, float frametime ) |
|
{ |
|
float input = x; |
|
|
|
bool bStickIsPhysicallyPegged = ( dist >= joy_pegged.GetFloat() ); |
|
|
|
// Make X positive to make things easier, just remember whether we have to flip it back! |
|
bool negative = false; |
|
if( x < 0.0f ) |
|
{ |
|
negative = true; |
|
x *= -1; |
|
} |
|
|
|
if( axis == YAW && joy_virtual_peg.GetBool() ) |
|
{ |
|
if( x >= 0.95f ) |
|
{ |
|
// User has pegged the stick |
|
controlEnvelope.peggedAxis[axis] = true; |
|
controlEnvelope.axisPeggedDir[axis] = negative; |
|
} |
|
|
|
if( controlEnvelope.peggedAxis[axis] == true ) |
|
{ |
|
// User doesn't have the stick pegged on this axis, but they used to. |
|
// If the stick is physically pegged, pretend this axis is still pegged. |
|
if( bStickIsPhysicallyPegged && negative == controlEnvelope.axisPeggedDir[axis] ) |
|
{ |
|
// If the user still has the stick physically pegged and hasn't changed direction on |
|
// this axis, keep pretending they have the stick pegged on this axis. |
|
x = 1.0f; |
|
} |
|
else |
|
{ |
|
controlEnvelope.peggedAxis[axis] = false; |
|
} |
|
} |
|
} |
|
|
|
// Perform the two-stage mapping. |
|
if( x > joy_lowend.GetFloat() ) |
|
{ |
|
float highmap = 1.0f - joy_lowmap.GetFloat(); |
|
float xNormal = x - joy_lowend.GetFloat(); |
|
|
|
float factor = xNormal / ( 1.0f - joy_lowend.GetFloat() ); |
|
x = joy_lowmap.GetFloat() + (highmap * factor); |
|
|
|
// Accelerate. |
|
if( controlEnvelope.envelopeScale[axis] < 1.0f ) |
|
{ |
|
controlEnvelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() ); |
|
if( controlEnvelope.envelopeScale[axis] > 1.0f ) |
|
{ |
|
controlEnvelope.envelopeScale[axis] = 1.0f; |
|
} |
|
} |
|
|
|
float delta = x - joy_lowmap.GetFloat(); |
|
x = joy_lowmap.GetFloat() + (delta * controlEnvelope.envelopeScale[axis]); |
|
} |
|
else |
|
{ |
|
// Shut off acceleration |
|
controlEnvelope.envelopeScale[axis] = 0.0f; |
|
float factor = x / joy_lowend.GetFloat(); |
|
x = joy_lowmap.GetFloat() * factor; |
|
} |
|
|
|
x *= AutoAimDampening( input, axis, dist ); |
|
|
|
if( axis == YAW && x > 0.0f && joy_display_input.GetBool() ) |
|
{ |
|
Msg("In:%f Out:%f Frametime:%f\n", input, x, frametime ); |
|
} |
|
|
|
if( negative ) |
|
{ |
|
x *= -1; |
|
} |
|
|
|
return x; |
|
} |
|
|
|
ConVar joy_accel_filter("joy_accel_filter", "0.2");// If the non-accelerated axis is pushed farther than this, then accelerate it, too. |
|
static float ResponseCurveLookAccelerated( float x, int axis, float otherAxis, float dist, float frametime ) |
|
{ |
|
float input = x; |
|
|
|
float flJoyDist = ( sqrt(x*x + otherAxis * otherAxis) ); |
|
bool bIsPegged = ( flJoyDist>= joy_pegged.GetFloat() ); |
|
|
|
// Make X positive to make arithmetic easier for the rest of this function, and |
|
// remember whether we have to flip it back! |
|
bool negative = false; |
|
if( x < 0.0f ) |
|
{ |
|
negative = true; |
|
x *= -1; |
|
} |
|
|
|
// Perform the two-stage mapping. |
|
bool bDoAcceleration = false;// Assume we won't accelerate the input |
|
|
|
if( bIsPegged && x > joy_accel_filter.GetFloat() ) |
|
{ |
|
// Accelerate this axis, since the stick is pegged and |
|
// this axis is pressed farther than the acceleration filter |
|
// Take the lowmap value, or the input, whichever is higher, since |
|
// we don't necesarily know whether this is the axis which is pegged |
|
x = MAX( joy_lowmap.GetFloat(), x ); |
|
bDoAcceleration = true; |
|
} |
|
else |
|
{ |
|
// Joystick is languishing in the low-end, turn off acceleration. |
|
controlEnvelope.envelopeScale[axis] = 0.0f; |
|
float factor = x / joy_lowend.GetFloat(); |
|
x = joy_lowmap.GetFloat() * factor; |
|
} |
|
|
|
if( bDoAcceleration ) |
|
{ |
|
float flMax = joy_accelmax.GetFloat(); |
|
if( controlEnvelope.envelopeScale[axis] < flMax ) |
|
{ |
|
float delta = x - joy_lowmap.GetFloat(); |
|
x = joy_lowmap.GetFloat() + (delta * controlEnvelope.envelopeScale[axis]); |
|
controlEnvelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() ); |
|
|
|
if( controlEnvelope.envelopeScale[axis] > flMax ) |
|
{ |
|
controlEnvelope.envelopeScale[axis] = flMax; |
|
} |
|
} |
|
} |
|
|
|
x *= AutoAimDampening( input, axis, dist ); |
|
|
|
if( axis == YAW && input != 0.0f && joy_display_input.GetBool() ) |
|
{ |
|
Msg("In:%f Out:%f Frametime:%f\n", input, x, frametime ); |
|
} |
|
|
|
if( negative ) |
|
{ |
|
x *= -1; |
|
} |
|
|
|
return x; |
|
} |
|
|
|
//----------------------------------------------- |
|
//----------------------------------------------- |
|
static float ResponseCurveLook( int curve, float x, int axis, float otherAxis, float dist, float frametime ) |
|
{ |
|
switch( curve ) |
|
{ |
|
case 1://Promotion of acceleration |
|
return ResponseCurveLookAccelerated( x, axis, otherAxis, dist, frametime ); |
|
break; |
|
|
|
default: |
|
return ResponseCurveLookDefault( x, axis, otherAxis, dist, frametime ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Advanced joystick setup |
|
//----------------------------------------------------------------------------- |
|
void CInput::Joystick_Advanced(void) |
|
{ |
|
// called whenever an update is needed |
|
int i; |
|
DWORD dwTemp; |
|
|
|
if ( IsX360() ) |
|
{ |
|
// Xbox always uses a joystick |
|
in_joystick.SetValue( 1 ); |
|
} |
|
|
|
// Initialize all the maps |
|
for ( i = 0; i < MAX_JOYSTICK_AXES; i++ ) |
|
{ |
|
m_rgAxes[i].AxisMap = GAME_AXIS_NONE; |
|
m_rgAxes[i].ControlMap = JOY_ABSOLUTE_AXIS; |
|
} |
|
|
|
if ( !joy_advanced.GetBool() ) |
|
{ |
|
// default joystick initialization |
|
// 2 axes only with joystick control |
|
m_rgAxes[JOY_AXIS_X].AxisMap = GAME_AXIS_YAW; |
|
m_rgAxes[JOY_AXIS_Y].AxisMap = GAME_AXIS_FORWARD; |
|
} |
|
else |
|
{ |
|
if ( Q_stricmp( joy_name.GetString(), "joystick") != 0 ) |
|
{ |
|
// notify user of advanced controller |
|
Msg( "Using joystick '%s' configuration\n", joy_name.GetString() ); |
|
} |
|
|
|
// advanced initialization here |
|
// data supplied by user via joy_axisn cvars |
|
dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisu.GetInt() : (DWORD)joy_advaxisx.GetInt(); |
|
m_rgAxes[JOY_AXIS_X].AxisMap = dwTemp & 0x0000000f; |
|
m_rgAxes[JOY_AXIS_X].ControlMap = dwTemp & JOY_RELATIVE_AXIS; |
|
|
|
DescribeJoystickAxis( "JOY_AXIS_X", &m_rgAxes[JOY_AXIS_X] ); |
|
|
|
dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisr.GetInt() : (DWORD)joy_advaxisy.GetInt(); |
|
m_rgAxes[JOY_AXIS_Y].AxisMap = dwTemp & 0x0000000f; |
|
m_rgAxes[JOY_AXIS_Y].ControlMap = dwTemp & JOY_RELATIVE_AXIS; |
|
|
|
DescribeJoystickAxis( "JOY_AXIS_Y", &m_rgAxes[JOY_AXIS_Y] ); |
|
|
|
dwTemp = (DWORD)joy_advaxisz.GetInt(); |
|
m_rgAxes[JOY_AXIS_Z].AxisMap = dwTemp & 0x0000000f; |
|
m_rgAxes[JOY_AXIS_Z].ControlMap = dwTemp & JOY_RELATIVE_AXIS; |
|
|
|
DescribeJoystickAxis( "JOY_AXIS_Z", &m_rgAxes[JOY_AXIS_Z] ); |
|
|
|
dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisy.GetInt() : (DWORD)joy_advaxisr.GetInt(); |
|
m_rgAxes[JOY_AXIS_R].AxisMap = dwTemp & 0x0000000f; |
|
m_rgAxes[JOY_AXIS_R].ControlMap = dwTemp & JOY_RELATIVE_AXIS; |
|
|
|
DescribeJoystickAxis( "JOY_AXIS_R", &m_rgAxes[JOY_AXIS_R] ); |
|
|
|
dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisx.GetInt() : (DWORD)joy_advaxisu.GetInt(); |
|
m_rgAxes[JOY_AXIS_U].AxisMap = dwTemp & 0x0000000f; |
|
m_rgAxes[JOY_AXIS_U].ControlMap = dwTemp & JOY_RELATIVE_AXIS; |
|
|
|
DescribeJoystickAxis( "JOY_AXIS_U", &m_rgAxes[JOY_AXIS_U] ); |
|
|
|
dwTemp = (DWORD)joy_advaxisv.GetInt(); |
|
m_rgAxes[JOY_AXIS_V].AxisMap = dwTemp & 0x0000000f; |
|
m_rgAxes[JOY_AXIS_V].ControlMap = dwTemp & JOY_RELATIVE_AXIS; |
|
|
|
DescribeJoystickAxis( "JOY_AXIS_V", &m_rgAxes[JOY_AXIS_V] ); |
|
|
|
Msg( "Advanced Joystick settings initialized\n" ); |
|
} |
|
|
|
// If we have an xcontroller, load the cfg file if it hasn't been loaded. |
|
static ConVarRef var( "joy_xcontroller_found" ); |
|
if ( var.IsValid() && var.GetBool() && in_joystick.GetBool() ) |
|
{ |
|
if ( joy_xcontroller_cfg_loaded.GetInt() < 2 ) |
|
{ |
|
engine->ClientCmd_Unrestricted( "exec 360controller.cfg" ); |
|
if ( IsLinux () ) |
|
{ |
|
engine->ClientCmd_Unrestricted( "exec 360controller-linux.cfg" ); |
|
} |
|
joy_xcontroller_cfg_loaded.SetValue( 2 ); |
|
} |
|
} |
|
else if ( joy_xcontroller_cfg_loaded.GetInt() > 0 ) |
|
{ |
|
engine->ClientCmd_Unrestricted( "exec undo360controller.cfg" ); |
|
joy_xcontroller_cfg_loaded.SetValue( 0 ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : index - |
|
// Output : char const |
|
//----------------------------------------------------------------------------- |
|
char const *CInput::DescribeAxis( int index ) |
|
{ |
|
switch ( index ) |
|
{ |
|
case GAME_AXIS_FORWARD: |
|
return "Forward"; |
|
case GAME_AXIS_PITCH: |
|
return "Look"; |
|
case GAME_AXIS_SIDE: |
|
return "Side"; |
|
case GAME_AXIS_YAW: |
|
return "Turn"; |
|
case GAME_AXIS_NONE: |
|
default: |
|
return "Unknown"; |
|
} |
|
|
|
return "Unknown"; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *axis - |
|
// *mapping - |
|
//----------------------------------------------------------------------------- |
|
void CInput::DescribeJoystickAxis( char const *axis, joy_axis_t *mapping ) |
|
{ |
|
if ( !mapping->AxisMap ) |
|
{ |
|
Msg( "%s: unmapped\n", axis ); |
|
} |
|
else |
|
{ |
|
Msg( "%s: mapped to %s (%s)\n", |
|
axis, |
|
DescribeAxis( mapping->AxisMap ), |
|
mapping->ControlMap != 0 ? "relative" : "absolute" ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Allow joystick to issue key events |
|
// Not currently used - controller button events are pumped through the windprocs. KWD |
|
//----------------------------------------------------------------------------- |
|
void CInput::ControllerCommands( void ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Scales the raw analog value to lie withing the axis range (full range - deadzone ) |
|
//----------------------------------------------------------------------------- |
|
float CInput::ScaleAxisValue( const float axisValue, const float axisThreshold ) |
|
{ |
|
// Xbox scales the range of all axes in the inputsystem. PC can't do that because each axis mapping |
|
// has a (potentially) unique threshold value. If all axes were restricted to a single threshold |
|
// as they are on the Xbox, this function could move to inputsystem and be slightly more optimal. |
|
float result = 0.f; |
|
if ( IsPC() ) |
|
{ |
|
if ( axisValue < -axisThreshold ) |
|
{ |
|
result = ( axisValue + axisThreshold ) / ( MAX_BUTTONSAMPLE - axisThreshold ); |
|
} |
|
else if ( axisValue > axisThreshold ) |
|
{ |
|
result = ( axisValue - axisThreshold ) / ( MAX_BUTTONSAMPLE - axisThreshold ); |
|
} |
|
} |
|
else |
|
{ |
|
// IsXbox |
|
result = axisValue * ( 1.f / MAX_BUTTONSAMPLE ); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
void CInput::Joystick_SetSampleTime(float frametime) |
|
{ |
|
m_flRemainingJoystickSampleTime = frametime; |
|
} |
|
|
|
float CInput::Joystick_GetForward( void ) |
|
{ |
|
return m_flPreviousJoystickForward; |
|
} |
|
|
|
float CInput::Joystick_GetSide( void ) |
|
{ |
|
return m_flPreviousJoystickSide; |
|
} |
|
|
|
float CInput::Joystick_GetPitch( void ) |
|
{ |
|
return m_flPreviousJoystickPitch; |
|
} |
|
|
|
float CInput::Joystick_GetYaw( void ) |
|
{ |
|
return m_flPreviousJoystickYaw; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply joystick to CUserCmd creation |
|
// Input : frametime - |
|
// *cmd - |
|
//----------------------------------------------------------------------------- |
|
void CInput::JoyStickMove( float frametime, CUserCmd *cmd ) |
|
{ |
|
// complete initialization if first time in ( needed as cvars are not available at initialization time ) |
|
if ( !m_fJoystickAdvancedInit ) |
|
{ |
|
Joystick_Advanced(); |
|
m_fJoystickAdvancedInit = true; |
|
} |
|
|
|
// Verify that the user wants to use the joystick |
|
if ( !in_joystick.GetInt() ) |
|
return; |
|
|
|
// Reinitialize the 'advanced joystick' system if hotplugging has caused us toggle between some/none joysticks. |
|
bool haveJoysticks = ( inputsystem->GetJoystickCount() > 0 ); |
|
if ( haveJoysticks != m_fHadJoysticks ) |
|
{ |
|
Joystick_Advanced(); |
|
m_fHadJoysticks = haveJoysticks; |
|
} |
|
|
|
// Verify that a joystick is available |
|
if ( !haveJoysticks ) |
|
return; |
|
|
|
if ( m_flRemainingJoystickSampleTime <= 0 ) |
|
return; |
|
frametime = MIN(m_flRemainingJoystickSampleTime, frametime); |
|
m_flRemainingJoystickSampleTime -= frametime; |
|
|
|
QAngle viewangles; |
|
|
|
// Get starting angles |
|
engine->GetViewAngles( viewangles ); |
|
|
|
struct axis_t |
|
{ |
|
float value; |
|
int controlType; |
|
}; |
|
axis_t gameAxes[ MAX_GAME_AXES ]; |
|
memset( &gameAxes, 0, sizeof(gameAxes) ); |
|
|
|
// Get each joystick axis value, and normalize the range |
|
for ( int i = 0; i < MAX_JOYSTICK_AXES; ++i ) |
|
{ |
|
if ( GAME_AXIS_NONE == m_rgAxes[i].AxisMap ) |
|
continue; |
|
|
|
float fAxisValue = inputsystem->GetAnalogValue( (AnalogCode_t)JOYSTICK_AXIS( 0, i ) ); |
|
|
|
if (joy_wwhack2.GetInt() != 0 ) |
|
{ |
|
// this is a special formula for the Logitech WingMan Warrior |
|
// y=ax^b; where a = 300 and b = 1.3 |
|
// also x values are in increments of 800 (so this is factored out) |
|
// then bounds check result to level out excessively high spin rates |
|
float fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3); |
|
if (fTemp > 14000.0) |
|
fTemp = 14000.0; |
|
// restore direction information |
|
fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; |
|
} |
|
|
|
unsigned int idx = m_rgAxes[i].AxisMap; |
|
gameAxes[idx].value = fAxisValue; |
|
gameAxes[idx].controlType = m_rgAxes[i].ControlMap; |
|
} |
|
|
|
// Re-map the axis values if necessary, based on the joystick configuration |
|
if ( (joy_advanced.GetInt() == 0) && (in_jlook.state & 1) ) |
|
{ |
|
// user wants forward control to become pitch control |
|
gameAxes[GAME_AXIS_PITCH] = gameAxes[GAME_AXIS_FORWARD]; |
|
gameAxes[GAME_AXIS_FORWARD].value = 0; |
|
|
|
// if mouse invert is on, invert the joystick pitch value |
|
// Note: only absolute control support here - joy_advanced = 0 |
|
if ( m_pitch->GetFloat() < 0.0 ) |
|
{ |
|
gameAxes[GAME_AXIS_PITCH].value *= -1; |
|
} |
|
} |
|
|
|
if ( (in_strafe.state & 1) || ( lookstrafe.GetFloat() && (in_jlook.state & 1) ) ) |
|
{ |
|
// user wants yaw control to become side control |
|
gameAxes[GAME_AXIS_SIDE] = gameAxes[GAME_AXIS_YAW]; |
|
gameAxes[GAME_AXIS_YAW].value = 0; |
|
} |
|
|
|
m_flPreviousJoystickForward = ScaleAxisValue( gameAxes[GAME_AXIS_FORWARD].value, MAX_BUTTONSAMPLE * joy_forwardthreshold.GetFloat() ); |
|
m_flPreviousJoystickSide = ScaleAxisValue( gameAxes[GAME_AXIS_SIDE].value, MAX_BUTTONSAMPLE * joy_sidethreshold.GetFloat() ); |
|
m_flPreviousJoystickPitch = ScaleAxisValue( gameAxes[GAME_AXIS_PITCH].value, MAX_BUTTONSAMPLE * joy_pitchthreshold.GetFloat() ); |
|
m_flPreviousJoystickYaw = ScaleAxisValue( gameAxes[GAME_AXIS_YAW].value, MAX_BUTTONSAMPLE * joy_yawthreshold.GetFloat() ); |
|
|
|
// Skip out if vgui is active |
|
if ( vgui::surface()->IsCursorVisible() ) |
|
return; |
|
|
|
// If we're inverting our joystick, do so |
|
if ( joy_inverty.GetBool() ) |
|
{ |
|
m_flPreviousJoystickPitch *= -1.0f; |
|
} |
|
|
|
// drive yaw, pitch and move like a screen relative platformer game |
|
if ( CAM_IsThirdPerson() && thirdperson_platformer.GetInt() ) |
|
{ |
|
if ( m_flPreviousJoystickForward || m_flPreviousJoystickSide ) |
|
{ |
|
// apply turn control [ YAW ] |
|
// factor in the camera offset, so that the move direction is relative to the thirdperson camera |
|
viewangles[ YAW ] = RAD2DEG(atan2(-m_flPreviousJoystickSide, -m_flPreviousJoystickForward)) + g_ThirdPersonManager.GetCameraOffsetAngles()[ YAW ]; |
|
engine->SetViewAngles( viewangles ); |
|
|
|
// apply movement |
|
Vector2D moveDir( m_flPreviousJoystickForward, m_flPreviousJoystickSide ); |
|
cmd->forwardmove += moveDir.Length() * cl_forwardspeed.GetFloat(); |
|
} |
|
|
|
if ( m_flPreviousJoystickPitch || m_flPreviousJoystickYaw ) |
|
{ |
|
Vector vTempOffset = g_ThirdPersonManager.GetCameraOffsetAngles(); |
|
|
|
// look around with the camera |
|
vTempOffset[ PITCH ] += m_flPreviousJoystickPitch * joy_pitchsensitivity.GetFloat(); |
|
vTempOffset[ YAW ] += m_flPreviousJoystickYaw * joy_yawsensitivity.GetFloat(); |
|
|
|
g_ThirdPersonManager.SetCameraOffsetAngles( vTempOffset ); |
|
} |
|
|
|
if ( m_flPreviousJoystickForward || m_flPreviousJoystickSide || m_flPreviousJoystickPitch || m_flPreviousJoystickYaw ) |
|
{ |
|
Vector vTempOffset = g_ThirdPersonManager.GetCameraOffsetAngles(); |
|
|
|
// update the ideal pitch and yaw |
|
cam_idealpitch.SetValue( vTempOffset[ PITCH ] - viewangles[ PITCH ] ); |
|
cam_idealyaw.SetValue( vTempOffset[ YAW ] - viewangles[ YAW ] ); |
|
} |
|
return; |
|
} |
|
|
|
float joySideMove = 0.f; |
|
float joyForwardMove = 0.f; |
|
float aspeed = frametime * gHUD.GetFOVSensitivityAdjust(); |
|
|
|
// apply forward and side control |
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); |
|
|
|
int iResponseCurve = 0; |
|
if ( pLocalPlayer && pLocalPlayer->IsInAVehicle() ) |
|
{ |
|
iResponseCurve = pLocalPlayer->GetVehicle() ? pLocalPlayer->GetVehicle()->GetJoystickResponseCurve() : joy_response_move_vehicle.GetInt(); |
|
} |
|
else |
|
{ |
|
iResponseCurve = joy_response_move.GetInt(); |
|
} |
|
|
|
float val = ResponseCurve( iResponseCurve, m_flPreviousJoystickForward, PITCH, joy_forwardsensitivity.GetFloat() ); |
|
joyForwardMove += val * cl_forwardspeed.GetFloat(); |
|
val = ResponseCurve( iResponseCurve, m_flPreviousJoystickSide, YAW, joy_sidesensitivity.GetFloat() ); |
|
joySideMove += val * cl_sidespeed.GetFloat(); |
|
|
|
Vector2D move( m_flPreviousJoystickYaw, m_flPreviousJoystickPitch ); |
|
float dist = move.Length(); |
|
|
|
// apply turn control |
|
float angle = 0.f; |
|
|
|
if ( JOY_ABSOLUTE_AXIS == gameAxes[GAME_AXIS_YAW].controlType ) |
|
{ |
|
float fAxisValue = ResponseCurveLook( joy_response_look.GetInt(), m_flPreviousJoystickYaw, YAW, m_flPreviousJoystickPitch, dist, frametime ); |
|
angle = fAxisValue * joy_yawsensitivity.GetFloat() * aspeed * cl_yawspeed.GetFloat(); |
|
} |
|
else |
|
{ |
|
angle = m_flPreviousJoystickYaw * joy_yawsensitivity.GetFloat() * aspeed * 180.0; |
|
} |
|
|
|
angle = JoyStickAdjustYaw( angle ); |
|
viewangles[YAW] += angle; |
|
cmd->mousedx = angle; |
|
|
|
// apply look control |
|
if ( IsX360() || in_jlook.state & 1 ) |
|
{ |
|
float angle = 0; |
|
if ( JOY_ABSOLUTE_AXIS == gameAxes[GAME_AXIS_PITCH].controlType ) |
|
{ |
|
float fAxisValue = ResponseCurveLook( joy_response_look.GetInt(), m_flPreviousJoystickPitch, PITCH, m_flPreviousJoystickYaw, dist, frametime ); |
|
angle = fAxisValue * joy_pitchsensitivity.GetFloat() * aspeed * cl_pitchspeed.GetFloat(); |
|
} |
|
else |
|
{ |
|
angle = m_flPreviousJoystickPitch * joy_pitchsensitivity.GetFloat() * aspeed * 180.0; |
|
} |
|
viewangles[PITCH] += angle; |
|
cmd->mousedy = angle; |
|
view->StopPitchDrift(); |
|
if( m_flPreviousJoystickPitch == 0.f && lookspring.GetFloat() == 0.f ) |
|
{ |
|
// no pitch movement |
|
// disable pitch return-to-center unless requested by user |
|
// *** this code can be removed when the lookspring bug is fixed |
|
// *** the bug always has the lookspring feature on |
|
view->StopPitchDrift(); |
|
} |
|
} |
|
|
|
// apply player motion relative to screen space |
|
if ( CAM_IsThirdPerson() && thirdperson_screenspace.GetInt() ) |
|
{ |
|
float ideal_yaw = cam_idealyaw.GetFloat(); |
|
float ideal_sin = sin(DEG2RAD(ideal_yaw)); |
|
float ideal_cos = cos(DEG2RAD(ideal_yaw)); |
|
float side_movement = ideal_cos*joySideMove - ideal_sin*joyForwardMove; |
|
float forward_movement = ideal_cos*joyForwardMove + ideal_sin*joySideMove; |
|
cmd->forwardmove += forward_movement; |
|
cmd->sidemove += side_movement; |
|
} |
|
else |
|
{ |
|
cmd->forwardmove += joyForwardMove; |
|
cmd->sidemove += joySideMove; |
|
} |
|
|
|
if ( IsPC() ) |
|
{ |
|
CCommand tmp; |
|
if ( FloatMakePositive(joyForwardMove) >= joy_autosprint.GetFloat() || FloatMakePositive(joySideMove) >= joy_autosprint.GetFloat() ) |
|
{ |
|
KeyDown( &in_joyspeed, NULL ); |
|
} |
|
else |
|
{ |
|
KeyUp( &in_joyspeed, NULL ); |
|
} |
|
} |
|
|
|
// Bound pitch |
|
viewangles[PITCH] = clamp( viewangles[ PITCH ], -cl_pitchup.GetFloat(), cl_pitchdown.GetFloat() ); |
|
|
|
engine->SetViewAngles( viewangles ); |
|
}
|
|
|