Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Joystick handling function
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "cbase.h"
  10. #include "basehandle.h"
  11. #include "utlvector.h"
  12. #include "cdll_client_int.h"
  13. #include "cdll_util.h"
  14. #include "kbutton.h"
  15. #include "usercmd.h"
  16. #include "iclientvehicle.h"
  17. #include "input.h"
  18. #include "iviewrender.h"
  19. #include "convar.h"
  20. #include "hud.h"
  21. #include "vgui/ISurface.h"
  22. #include "vgui_controls/Controls.h"
  23. #include "vgui/Cursor.h"
  24. #include "tier0/icommandline.h"
  25. #include "inputsystem/iinputsystem.h"
  26. #include "inputsystem/ButtonCode.h"
  27. #include "math.h"
  28. #include "tier1/convar_serverbounded.h"
  29. #include "cam_thirdperson.h"
  30. #if defined( _X360 )
  31. #include "xbox/xbox_win32stubs.h"
  32. #else
  33. #include "../common/xbox/xboxstubs.h"
  34. #endif
  35. #ifdef HL2_CLIENT_DLL
  36. // FIXME: Autoaim support needs to be moved from HL2_DLL to the client dll, so this include should be c_baseplayer.h
  37. #include "c_basehlplayer.h"
  38. #endif
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. // Control like a joystick
  42. #define JOY_ABSOLUTE_AXIS 0x00000000
  43. // Control like a mouse, spinner, trackball
  44. #define JOY_RELATIVE_AXIS 0x00000010
  45. // Axis mapping
  46. static ConVar joy_name( "joy_name", "joystick", FCVAR_ARCHIVE );
  47. static ConVar joy_advanced( "joy_advanced", "1", FCVAR_ARCHIVE );
  48. static ConVar joy_advaxisx( "joy_advaxisx", "4", FCVAR_ARCHIVE );
  49. static ConVar joy_advaxisy( "joy_advaxisy", "2", FCVAR_ARCHIVE );
  50. static ConVar joy_advaxisz( "joy_advaxisz", "0", FCVAR_ARCHIVE );
  51. static ConVar joy_advaxisr( "joy_advaxisr", "1", FCVAR_ARCHIVE );
  52. static ConVar joy_advaxisu( "joy_advaxisu", "3", FCVAR_ARCHIVE );
  53. static ConVar joy_advaxisv( "joy_advaxisv", "0", FCVAR_ARCHIVE );
  54. // Basic "dead zone" and sensitivity
  55. static ConVar joy_forwardthreshold( "joy_forwardthreshold", "0.15", FCVAR_ARCHIVE );
  56. static ConVar joy_sidethreshold( "joy_sidethreshold", "0.15", FCVAR_ARCHIVE );
  57. static ConVar joy_pitchthreshold( "joy_pitchthreshold", "0.15", FCVAR_ARCHIVE );
  58. static ConVar joy_yawthreshold( "joy_yawthreshold", "0.15", FCVAR_ARCHIVE );
  59. static ConVar joy_forwardsensitivity( "joy_forwardsensitivity", "-1", FCVAR_ARCHIVE );
  60. static ConVar joy_sidesensitivity( "joy_sidesensitivity", "1", FCVAR_ARCHIVE );
  61. static ConVar joy_pitchsensitivity( "joy_pitchsensitivity", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX );
  62. static ConVar joy_yawsensitivity( "joy_yawsensitivity", "-1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX );
  63. // Advanced sensitivity and response
  64. 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" );
  65. ConVar joy_response_move_vehicle("joy_response_move_vehicle", "6");
  66. static ConVar joy_response_look( "joy_response_look", "0", FCVAR_ARCHIVE, "'Look' stick response mode: 0=Default, 1=Acceleration Promotion" );
  67. static ConVar joy_lowend( "joy_lowend", "1", FCVAR_ARCHIVE );
  68. static ConVar joy_lowmap( "joy_lowmap", "1", FCVAR_ARCHIVE );
  69. static ConVar joy_accelscale( "joy_accelscale", "0.6", FCVAR_ARCHIVE);
  70. static ConVar joy_accelmax( "joy_accelmax", "1.0", FCVAR_ARCHIVE);
  71. static ConVar joy_autoaimdampenrange( "joy_autoaimdampenrange", "0", FCVAR_ARCHIVE, "The stick range where autoaim dampening is applied. 0 = off" );
  72. 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." );
  73. static ConVar joy_vehicle_turn_lowend("joy_vehicle_turn_lowend", "0.7");
  74. static ConVar joy_vehicle_turn_lowmap("joy_vehicle_turn_lowmap", "0.4");
  75. // Misc
  76. static ConVar joy_diagonalpov( "joy_diagonalpov", "0", FCVAR_ARCHIVE, "POV manipulator operates on diagonal axes, too." );
  77. static ConVar joy_display_input("joy_display_input", "0", FCVAR_ARCHIVE);
  78. static ConVar joy_wwhack2( "joy_wingmanwarrior_turnhack", "0", FCVAR_ARCHIVE, "Wingman warrior hack related to turn axes." );
  79. ConVar joy_autosprint("joy_autosprint", "0", 0, "Automatically sprint when moving with an analog joystick" );
  80. static ConVar joy_inverty("joy_inverty", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Whether to invert the Y axis of the joystick for looking." );
  81. // XBox Defaults
  82. static ConVar joy_yawsensitivity_default( "joy_yawsensitivity_default", "-1.25", FCVAR_NONE );
  83. static ConVar joy_pitchsensitivity_default( "joy_pitchsensitivity_default", "-1.0", FCVAR_NONE );
  84. static ConVar option_duck_method_default( "option_duck_method_default", "1.0", FCVAR_NONE );
  85. static ConVar joy_inverty_default( "joy_inverty_default", "0", FCVAR_ARCHIVE_XBOX ); // Extracted & saved from profile
  86. static ConVar joy_movement_stick_default( "joy_movement_stick_default", "0", FCVAR_ARCHIVE_XBOX ); // Extracted & saved from profile
  87. static ConVar sv_stickysprint_default( "sv_stickysprint_default", "0", FCVAR_NONE );
  88. void joy_movement_stick_Callback( IConVar *var, const char *pOldString, float flOldValue )
  89. {
  90. engine->ClientCmd( "joyadvancedupdate" );
  91. }
  92. 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 );
  93. 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." );
  94. extern ConVar lookspring;
  95. extern ConVar cl_forwardspeed;
  96. extern ConVar lookstrafe;
  97. extern ConVar in_joystick;
  98. extern ConVar_ServerBounded *m_pitch;
  99. extern ConVar l_pitchspeed;
  100. extern ConVar cl_sidespeed;
  101. extern ConVar cl_yawspeed;
  102. extern ConVar cl_pitchdown;
  103. extern ConVar cl_pitchup;
  104. extern ConVar cl_pitchspeed;
  105. extern ConVar cam_idealpitch;
  106. extern ConVar cam_idealyaw;
  107. extern ConVar thirdperson_platformer;
  108. extern ConVar thirdperson_screenspace;
  109. //-----------------------------------------------------------------
  110. // Purpose: Returns true if there's an active joystick connected.
  111. //-----------------------------------------------------------------
  112. bool CInput::EnableJoystickMode()
  113. {
  114. return IsConsole() || in_joystick.GetBool();
  115. }
  116. //-----------------------------------------------
  117. // Response curve function for the move axes
  118. //-----------------------------------------------
  119. static float ResponseCurve( int curve, float x, int axis, float sensitivity )
  120. {
  121. switch ( curve )
  122. {
  123. case 1:
  124. // quadratic
  125. if ( x < 0 )
  126. return -(x*x) * sensitivity;
  127. return x*x * sensitivity;
  128. case 2:
  129. // cubic
  130. return x*x*x*sensitivity;
  131. case 3:
  132. {
  133. // quadratic extreme
  134. float extreme = 1.0f;
  135. if ( fabs( x ) >= 0.95f )
  136. {
  137. extreme = 1.5f;
  138. }
  139. if ( x < 0 )
  140. return -extreme * x*x*sensitivity;
  141. return extreme * x*x*sensitivity;
  142. }
  143. case 4:
  144. {
  145. float flScale = sensitivity < 0.0f ? -1.0f : 1.0f;
  146. sensitivity = clamp( fabs( sensitivity ), 1.0e-8f, 1000.0f );
  147. float oneOverSens = 1.0f / sensitivity;
  148. if ( x < 0.0f )
  149. {
  150. flScale = -flScale;
  151. }
  152. float retval = clamp( powf( fabs( x ), oneOverSens ), 0.0f, 1.0f );
  153. return retval * flScale;
  154. }
  155. break;
  156. case 5:
  157. {
  158. float out = x;
  159. if( fabs(out) <= 0.6f )
  160. {
  161. out *= 0.5f;
  162. }
  163. out = out * sensitivity;
  164. return out;
  165. }
  166. break;
  167. case 6: // Custom for driving a vehicle!
  168. {
  169. if( axis == YAW )
  170. {
  171. // This code only wants to affect YAW axis (the left and right axis), which
  172. // is used for turning in the car. We fall-through and use a linear curve on
  173. // the PITCH axis, which is the vehicle's throttle. REALLY, these are the 'forward'
  174. // and 'side' axes, but we don't have constants for those, so we re-use the same
  175. // axis convention as the look stick. (sjb)
  176. float sign = 1;
  177. if( x < 0.0 )
  178. sign = -1;
  179. x = fabs(x);
  180. if( x <= joy_vehicle_turn_lowend.GetFloat() )
  181. x = RemapVal( x, 0.0f, joy_vehicle_turn_lowend.GetFloat(), 0.0f, joy_vehicle_turn_lowmap.GetFloat() );
  182. else
  183. x = RemapVal( x, joy_vehicle_turn_lowend.GetFloat(), 1.0f, joy_vehicle_turn_lowmap.GetFloat(), 1.0f );
  184. return x * sensitivity * sign;
  185. }
  186. //else
  187. // fall through and just return x*sensitivity below (as if using default curve)
  188. }
  189. }
  190. // linear
  191. return x*sensitivity;
  192. }
  193. //-----------------------------------------------
  194. // If we have a valid autoaim target, dampen the
  195. // player's stick input if it is moving away from
  196. // the target.
  197. //
  198. // This assists the player staying on target.
  199. //-----------------------------------------------
  200. float AutoAimDampening( float x, int axis, float dist )
  201. {
  202. // FIXME: Autoaim support needs to be moved from HL2_DLL to the client dll, so all games can use it.
  203. #ifdef HL2_CLIENT_DLL
  204. // Help the user stay on target if the feature is enabled and the user
  205. // is not making a gross stick movement.
  206. if( joy_autoaimdampen.GetFloat() > 0.0f && fabs(x) < joy_autoaimdampenrange.GetFloat() )
  207. {
  208. // Get the HL2 player
  209. C_BaseHLPlayer *pLocalPlayer = (C_BaseHLPlayer *)C_BasePlayer::GetLocalPlayer();
  210. if( pLocalPlayer )
  211. {
  212. // Get the autoaim target
  213. if( pLocalPlayer->m_HL2Local.m_bAutoAimTarget )
  214. {
  215. return joy_autoaimdampen.GetFloat();
  216. }
  217. }
  218. }
  219. #endif
  220. return 1.0f;// No dampening.
  221. }
  222. //-----------------------------------------------
  223. // This structure holds persistent information used
  224. // to make decisions about how to modulate analog
  225. // stick input.
  226. //-----------------------------------------------
  227. typedef struct
  228. {
  229. float envelopeScale[2];
  230. bool peggedAxis[2];
  231. bool axisPeggedDir[2];
  232. } envelope_t;
  233. envelope_t controlEnvelope;
  234. //-----------------------------------------------
  235. // Response curve function specifically for the
  236. // 'look' analog stick.
  237. //
  238. // when AXIS == YAW, otherAxisValue contains the
  239. // value for the pitch of the control stick, and
  240. // vice-versa.
  241. //-----------------------------------------------
  242. ConVar joy_pegged("joy_pegged", "0.75");// Once the stick is pushed this far, it's assumed pegged.
  243. ConVar joy_virtual_peg("joy_virtual_peg", "0");
  244. static float ResponseCurveLookDefault( float x, int axis, float otherAxis, float dist, float frametime )
  245. {
  246. float inputX = x;
  247. bool bStickIsPhysicallyPegged = ( dist >= joy_pegged.GetFloat() );
  248. // Make X positive to make things easier, just remember whether we have to flip it back!
  249. bool negative = false;
  250. if( x < 0.0f )
  251. {
  252. negative = true;
  253. x *= -1;
  254. }
  255. if( axis == YAW && joy_virtual_peg.GetBool() )
  256. {
  257. if( x >= 0.95f )
  258. {
  259. // User has pegged the stick
  260. controlEnvelope.peggedAxis[axis] = true;
  261. controlEnvelope.axisPeggedDir[axis] = negative;
  262. }
  263. if( controlEnvelope.peggedAxis[axis] == true )
  264. {
  265. // User doesn't have the stick pegged on this axis, but they used to.
  266. // If the stick is physically pegged, pretend this axis is still pegged.
  267. if( bStickIsPhysicallyPegged && negative == controlEnvelope.axisPeggedDir[axis] )
  268. {
  269. // If the user still has the stick physically pegged and hasn't changed direction on
  270. // this axis, keep pretending they have the stick pegged on this axis.
  271. x = 1.0f;
  272. }
  273. else
  274. {
  275. controlEnvelope.peggedAxis[axis] = false;
  276. }
  277. }
  278. }
  279. // Perform the two-stage mapping.
  280. if( x > joy_lowend.GetFloat() )
  281. {
  282. float highmap = 1.0f - joy_lowmap.GetFloat();
  283. float xNormal = x - joy_lowend.GetFloat();
  284. float factor = xNormal / ( 1.0f - joy_lowend.GetFloat() );
  285. x = joy_lowmap.GetFloat() + (highmap * factor);
  286. // Accelerate.
  287. if( controlEnvelope.envelopeScale[axis] < 1.0f )
  288. {
  289. controlEnvelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() );
  290. if( controlEnvelope.envelopeScale[axis] > 1.0f )
  291. {
  292. controlEnvelope.envelopeScale[axis] = 1.0f;
  293. }
  294. }
  295. float delta = x - joy_lowmap.GetFloat();
  296. x = joy_lowmap.GetFloat() + (delta * controlEnvelope.envelopeScale[axis]);
  297. }
  298. else
  299. {
  300. // Shut off acceleration
  301. controlEnvelope.envelopeScale[axis] = 0.0f;
  302. float factor = x / joy_lowend.GetFloat();
  303. x = joy_lowmap.GetFloat() * factor;
  304. }
  305. x *= AutoAimDampening( inputX, axis, dist );
  306. if( axis == YAW && x > 0.0f && joy_display_input.GetBool() )
  307. {
  308. Msg("In:%f Out:%f Frametime:%f\n", inputX, x, frametime );
  309. }
  310. if( negative )
  311. {
  312. x *= -1;
  313. }
  314. return x;
  315. }
  316. ConVar joy_accel_filter("joy_accel_filter", "0.2");// If the non-accelerated axis is pushed farther than this, then accelerate it, too.
  317. static float ResponseCurveLookAccelerated( float x, int axis, float otherAxis, float dist, float frametime )
  318. {
  319. float inputX = x;
  320. float flJoyDist = ( sqrt(x*x + otherAxis * otherAxis) );
  321. bool bIsPegged = ( flJoyDist>= joy_pegged.GetFloat() );
  322. // Make X positive to make arithmetic easier for the rest of this function, and
  323. // remember whether we have to flip it back!
  324. bool negative = false;
  325. if( x < 0.0f )
  326. {
  327. negative = true;
  328. x *= -1;
  329. }
  330. // Perform the two-stage mapping.
  331. bool bDoAcceleration = false;// Assume we won't accelerate the input
  332. if( bIsPegged && x > joy_accel_filter.GetFloat() )
  333. {
  334. // Accelerate this axis, since the stick is pegged and
  335. // this axis is pressed farther than the acceleration filter
  336. // Take the lowmap value, or the input, whichever is higher, since
  337. // we don't necesarily know whether this is the axis which is pegged
  338. x = MAX( joy_lowmap.GetFloat(), x );
  339. bDoAcceleration = true;
  340. }
  341. else
  342. {
  343. // Joystick is languishing in the low-end, turn off acceleration.
  344. controlEnvelope.envelopeScale[axis] = 0.0f;
  345. float factor = x / joy_lowend.GetFloat();
  346. x = joy_lowmap.GetFloat() * factor;
  347. }
  348. if( bDoAcceleration )
  349. {
  350. float flMax = joy_accelmax.GetFloat();
  351. if( controlEnvelope.envelopeScale[axis] < flMax )
  352. {
  353. float delta = x - joy_lowmap.GetFloat();
  354. x = joy_lowmap.GetFloat() + (delta * controlEnvelope.envelopeScale[axis]);
  355. controlEnvelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() );
  356. if( controlEnvelope.envelopeScale[axis] > flMax )
  357. {
  358. controlEnvelope.envelopeScale[axis] = flMax;
  359. }
  360. }
  361. }
  362. x *= AutoAimDampening( inputX, axis, dist );
  363. if( axis == YAW && inputX != 0.0f && joy_display_input.GetBool() )
  364. {
  365. Msg("In:%f Out:%f Frametime:%f\n", inputX, x, frametime );
  366. }
  367. if( negative )
  368. {
  369. x *= -1;
  370. }
  371. return x;
  372. }
  373. //-----------------------------------------------
  374. //-----------------------------------------------
  375. static float ResponseCurveLook( int curve, float x, int axis, float otherAxis, float dist, float frametime )
  376. {
  377. switch( curve )
  378. {
  379. case 1://Promotion of acceleration
  380. return ResponseCurveLookAccelerated( x, axis, otherAxis, dist, frametime );
  381. break;
  382. default:
  383. return ResponseCurveLookDefault( x, axis, otherAxis, dist, frametime );
  384. break;
  385. }
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Purpose: Advanced joystick setup
  389. //-----------------------------------------------------------------------------
  390. void CInput::Joystick_Advanced(void)
  391. {
  392. // called whenever an update is needed
  393. int i;
  394. DWORD dwTemp;
  395. if ( IsX360() )
  396. {
  397. // Xbox always uses a joystick
  398. in_joystick.SetValue( 1 );
  399. }
  400. // Initialize all the maps
  401. for ( i = 0; i < MAX_JOYSTICK_AXES; i++ )
  402. {
  403. m_rgAxes[i].AxisMap = GAME_AXIS_NONE;
  404. m_rgAxes[i].ControlMap = JOY_ABSOLUTE_AXIS;
  405. }
  406. if ( !joy_advanced.GetBool() )
  407. {
  408. // default joystick initialization
  409. // 2 axes only with joystick control
  410. m_rgAxes[JOY_AXIS_X].AxisMap = GAME_AXIS_YAW;
  411. m_rgAxes[JOY_AXIS_Y].AxisMap = GAME_AXIS_FORWARD;
  412. }
  413. else
  414. {
  415. if ( Q_stricmp( joy_name.GetString(), "joystick") != 0 )
  416. {
  417. // notify user of advanced controller
  418. Msg( "Using joystick '%s' configuration\n", joy_name.GetString() );
  419. }
  420. // advanced initialization here
  421. // data supplied by user via joy_axisn cvars
  422. dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisu.GetInt() : (DWORD)joy_advaxisx.GetInt();
  423. m_rgAxes[JOY_AXIS_X].AxisMap = dwTemp & 0x0000000f;
  424. m_rgAxes[JOY_AXIS_X].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
  425. DescribeJoystickAxis( "JOY_AXIS_X", &m_rgAxes[JOY_AXIS_X] );
  426. dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisr.GetInt() : (DWORD)joy_advaxisy.GetInt();
  427. m_rgAxes[JOY_AXIS_Y].AxisMap = dwTemp & 0x0000000f;
  428. m_rgAxes[JOY_AXIS_Y].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
  429. DescribeJoystickAxis( "JOY_AXIS_Y", &m_rgAxes[JOY_AXIS_Y] );
  430. dwTemp = (DWORD)joy_advaxisz.GetInt();
  431. m_rgAxes[JOY_AXIS_Z].AxisMap = dwTemp & 0x0000000f;
  432. m_rgAxes[JOY_AXIS_Z].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
  433. DescribeJoystickAxis( "JOY_AXIS_Z", &m_rgAxes[JOY_AXIS_Z] );
  434. dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisy.GetInt() : (DWORD)joy_advaxisr.GetInt();
  435. m_rgAxes[JOY_AXIS_R].AxisMap = dwTemp & 0x0000000f;
  436. m_rgAxes[JOY_AXIS_R].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
  437. DescribeJoystickAxis( "JOY_AXIS_R", &m_rgAxes[JOY_AXIS_R] );
  438. dwTemp = ( joy_movement_stick.GetBool() ) ? (DWORD)joy_advaxisx.GetInt() : (DWORD)joy_advaxisu.GetInt();
  439. m_rgAxes[JOY_AXIS_U].AxisMap = dwTemp & 0x0000000f;
  440. m_rgAxes[JOY_AXIS_U].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
  441. DescribeJoystickAxis( "JOY_AXIS_U", &m_rgAxes[JOY_AXIS_U] );
  442. dwTemp = (DWORD)joy_advaxisv.GetInt();
  443. m_rgAxes[JOY_AXIS_V].AxisMap = dwTemp & 0x0000000f;
  444. m_rgAxes[JOY_AXIS_V].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
  445. DescribeJoystickAxis( "JOY_AXIS_V", &m_rgAxes[JOY_AXIS_V] );
  446. Msg( "Advanced Joystick settings initialized\n" );
  447. }
  448. // If we have an xcontroller, load the cfg file if it hasn't been loaded.
  449. static ConVarRef var( "joy_xcontroller_found" );
  450. if ( var.IsValid() && var.GetBool() && in_joystick.GetBool() )
  451. {
  452. if ( joy_xcontroller_cfg_loaded.GetInt() < 2 )
  453. {
  454. engine->ClientCmd_Unrestricted( "exec 360controller.cfg" );
  455. if ( IsLinux () )
  456. {
  457. engine->ClientCmd_Unrestricted( "exec 360controller-linux.cfg" );
  458. }
  459. joy_xcontroller_cfg_loaded.SetValue( 2 );
  460. }
  461. }
  462. else if ( joy_xcontroller_cfg_loaded.GetInt() > 0 )
  463. {
  464. engine->ClientCmd_Unrestricted( "exec undo360controller.cfg" );
  465. joy_xcontroller_cfg_loaded.SetValue( 0 );
  466. }
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Purpose:
  470. // Input : index -
  471. // Output : char const
  472. //-----------------------------------------------------------------------------
  473. char const *CInput::DescribeAxis( int index )
  474. {
  475. switch ( index )
  476. {
  477. case GAME_AXIS_FORWARD:
  478. return "Forward";
  479. case GAME_AXIS_PITCH:
  480. return "Look";
  481. case GAME_AXIS_SIDE:
  482. return "Side";
  483. case GAME_AXIS_YAW:
  484. return "Turn";
  485. case GAME_AXIS_NONE:
  486. default:
  487. return "Unknown";
  488. }
  489. return "Unknown";
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose:
  493. // Input : *axis -
  494. // *mapping -
  495. //-----------------------------------------------------------------------------
  496. void CInput::DescribeJoystickAxis( char const *axis, joy_axis_t *mapping )
  497. {
  498. if ( !mapping->AxisMap )
  499. {
  500. Msg( "%s: unmapped\n", axis );
  501. }
  502. else
  503. {
  504. Msg( "%s: mapped to %s (%s)\n",
  505. axis,
  506. DescribeAxis( mapping->AxisMap ),
  507. mapping->ControlMap != 0 ? "relative" : "absolute" );
  508. }
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose: Allow joystick to issue key events
  512. // Not currently used - controller button events are pumped through the windprocs. KWD
  513. //-----------------------------------------------------------------------------
  514. void CInput::ControllerCommands( void )
  515. {
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Purpose: Scales the raw analog value to lie withing the axis range (full range - deadzone )
  519. //-----------------------------------------------------------------------------
  520. float CInput::ScaleAxisValue( const float axisValue, const float axisThreshold )
  521. {
  522. // Xbox scales the range of all axes in the inputsystem. PC can't do that because each axis mapping
  523. // has a (potentially) unique threshold value. If all axes were restricted to a single threshold
  524. // as they are on the Xbox, this function could move to inputsystem and be slightly more optimal.
  525. float result = 0.f;
  526. if ( IsPC() )
  527. {
  528. if ( axisValue < -axisThreshold )
  529. {
  530. result = ( axisValue + axisThreshold ) / ( MAX_BUTTONSAMPLE - axisThreshold );
  531. }
  532. else if ( axisValue > axisThreshold )
  533. {
  534. result = ( axisValue - axisThreshold ) / ( MAX_BUTTONSAMPLE - axisThreshold );
  535. }
  536. }
  537. else
  538. {
  539. // IsXbox
  540. result = axisValue * ( 1.f / MAX_BUTTONSAMPLE );
  541. }
  542. return result;
  543. }
  544. void CInput::Joystick_SetSampleTime(float frametime)
  545. {
  546. m_flRemainingJoystickSampleTime = frametime;
  547. }
  548. float CInput::Joystick_GetForward( void )
  549. {
  550. return m_flPreviousJoystickForward;
  551. }
  552. float CInput::Joystick_GetSide( void )
  553. {
  554. return m_flPreviousJoystickSide;
  555. }
  556. float CInput::Joystick_GetPitch( void )
  557. {
  558. return m_flPreviousJoystickPitch;
  559. }
  560. float CInput::Joystick_GetYaw( void )
  561. {
  562. return m_flPreviousJoystickYaw;
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Purpose: Apply joystick to CUserCmd creation
  566. // Input : frametime -
  567. // *cmd -
  568. //-----------------------------------------------------------------------------
  569. void CInput::JoyStickMove( float frametime, CUserCmd *cmd )
  570. {
  571. // complete initialization if first time in ( needed as cvars are not available at initialization time )
  572. if ( !m_fJoystickAdvancedInit )
  573. {
  574. Joystick_Advanced();
  575. m_fJoystickAdvancedInit = true;
  576. }
  577. // Verify that the user wants to use the joystick
  578. if ( !in_joystick.GetInt() )
  579. return;
  580. // Reinitialize the 'advanced joystick' system if hotplugging has caused us toggle between some/none joysticks.
  581. bool haveJoysticks = ( inputsystem->GetJoystickCount() > 0 );
  582. if ( haveJoysticks != m_fHadJoysticks )
  583. {
  584. Joystick_Advanced();
  585. m_fHadJoysticks = haveJoysticks;
  586. }
  587. // Verify that a joystick is available
  588. if ( !haveJoysticks )
  589. return;
  590. if ( m_flRemainingJoystickSampleTime <= 0 )
  591. return;
  592. frametime = MIN(m_flRemainingJoystickSampleTime, frametime);
  593. m_flRemainingJoystickSampleTime -= frametime;
  594. QAngle viewangles;
  595. // Get starting angles
  596. engine->GetViewAngles( viewangles );
  597. struct axis_t
  598. {
  599. float value;
  600. int controlType;
  601. };
  602. axis_t gameAxes[ MAX_GAME_AXES ];
  603. memset( &gameAxes, 0, sizeof(gameAxes) );
  604. // Get each joystick axis value, and normalize the range
  605. for ( int i = 0; i < MAX_JOYSTICK_AXES; ++i )
  606. {
  607. if ( GAME_AXIS_NONE == m_rgAxes[i].AxisMap )
  608. continue;
  609. float fAxisValue = inputsystem->GetAnalogValue( (AnalogCode_t)JOYSTICK_AXIS( 0, i ) );
  610. if (joy_wwhack2.GetInt() != 0 )
  611. {
  612. // this is a special formula for the Logitech WingMan Warrior
  613. // y=ax^b; where a = 300 and b = 1.3
  614. // also x values are in increments of 800 (so this is factored out)
  615. // then bounds check result to level out excessively high spin rates
  616. float fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
  617. if (fTemp > 14000.0)
  618. fTemp = 14000.0;
  619. // restore direction information
  620. fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
  621. }
  622. unsigned int idx = m_rgAxes[i].AxisMap;
  623. gameAxes[idx].value = fAxisValue;
  624. gameAxes[idx].controlType = m_rgAxes[i].ControlMap;
  625. }
  626. // Re-map the axis values if necessary, based on the joystick configuration
  627. if ( (joy_advanced.GetInt() == 0) && (in_jlook.state & 1) )
  628. {
  629. // user wants forward control to become pitch control
  630. gameAxes[GAME_AXIS_PITCH] = gameAxes[GAME_AXIS_FORWARD];
  631. gameAxes[GAME_AXIS_FORWARD].value = 0;
  632. // if mouse invert is on, invert the joystick pitch value
  633. // Note: only absolute control support here - joy_advanced = 0
  634. if ( m_pitch->GetFloat() < 0.0 )
  635. {
  636. gameAxes[GAME_AXIS_PITCH].value *= -1;
  637. }
  638. }
  639. if ( (in_strafe.state & 1) || ( lookstrafe.GetFloat() && (in_jlook.state & 1) ) )
  640. {
  641. // user wants yaw control to become side control
  642. gameAxes[GAME_AXIS_SIDE] = gameAxes[GAME_AXIS_YAW];
  643. gameAxes[GAME_AXIS_YAW].value = 0;
  644. }
  645. m_flPreviousJoystickForward = ScaleAxisValue( gameAxes[GAME_AXIS_FORWARD].value, MAX_BUTTONSAMPLE * joy_forwardthreshold.GetFloat() );
  646. m_flPreviousJoystickSide = ScaleAxisValue( gameAxes[GAME_AXIS_SIDE].value, MAX_BUTTONSAMPLE * joy_sidethreshold.GetFloat() );
  647. m_flPreviousJoystickPitch = ScaleAxisValue( gameAxes[GAME_AXIS_PITCH].value, MAX_BUTTONSAMPLE * joy_pitchthreshold.GetFloat() );
  648. m_flPreviousJoystickYaw = ScaleAxisValue( gameAxes[GAME_AXIS_YAW].value, MAX_BUTTONSAMPLE * joy_yawthreshold.GetFloat() );
  649. // Skip out if vgui is active
  650. if ( vgui::surface()->IsCursorVisible() )
  651. return;
  652. // If we're inverting our joystick, do so
  653. if ( joy_inverty.GetBool() )
  654. {
  655. m_flPreviousJoystickPitch *= -1.0f;
  656. }
  657. // drive yaw, pitch and move like a screen relative platformer game
  658. if ( CAM_IsThirdPerson() && thirdperson_platformer.GetInt() )
  659. {
  660. if ( m_flPreviousJoystickForward || m_flPreviousJoystickSide )
  661. {
  662. // apply turn control [ YAW ]
  663. // factor in the camera offset, so that the move direction is relative to the thirdperson camera
  664. viewangles[ YAW ] = RAD2DEG(atan2(-m_flPreviousJoystickSide, -m_flPreviousJoystickForward)) + g_ThirdPersonManager.GetCameraOffsetAngles()[ YAW ];
  665. engine->SetViewAngles( viewangles );
  666. // apply movement
  667. Vector2D moveDir( m_flPreviousJoystickForward, m_flPreviousJoystickSide );
  668. cmd->forwardmove += moveDir.Length() * cl_forwardspeed.GetFloat();
  669. }
  670. if ( m_flPreviousJoystickPitch || m_flPreviousJoystickYaw )
  671. {
  672. Vector vTempOffset = g_ThirdPersonManager.GetCameraOffsetAngles();
  673. // look around with the camera
  674. vTempOffset[ PITCH ] += m_flPreviousJoystickPitch * joy_pitchsensitivity.GetFloat();
  675. vTempOffset[ YAW ] += m_flPreviousJoystickYaw * joy_yawsensitivity.GetFloat();
  676. g_ThirdPersonManager.SetCameraOffsetAngles( vTempOffset );
  677. }
  678. if ( m_flPreviousJoystickForward || m_flPreviousJoystickSide || m_flPreviousJoystickPitch || m_flPreviousJoystickYaw )
  679. {
  680. const Vector& vTempOffset = g_ThirdPersonManager.GetCameraOffsetAngles();
  681. // update the ideal pitch and yaw
  682. cam_idealpitch.SetValue( vTempOffset[ PITCH ] - viewangles[ PITCH ] );
  683. cam_idealyaw.SetValue( vTempOffset[ YAW ] - viewangles[ YAW ] );
  684. }
  685. return;
  686. }
  687. float joySideMove = 0.f;
  688. float joyForwardMove = 0.f;
  689. float aspeed = frametime * gHUD.GetFOVSensitivityAdjust();
  690. // apply forward and side control
  691. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  692. int iResponseCurve = 0;
  693. if ( pLocalPlayer && pLocalPlayer->IsInAVehicle() )
  694. {
  695. iResponseCurve = pLocalPlayer->GetVehicle() ? pLocalPlayer->GetVehicle()->GetJoystickResponseCurve() : joy_response_move_vehicle.GetInt();
  696. }
  697. else
  698. {
  699. iResponseCurve = joy_response_move.GetInt();
  700. }
  701. float val = ResponseCurve( iResponseCurve, m_flPreviousJoystickForward, PITCH, joy_forwardsensitivity.GetFloat() );
  702. joyForwardMove += val * cl_forwardspeed.GetFloat();
  703. val = ResponseCurve( iResponseCurve, m_flPreviousJoystickSide, YAW, joy_sidesensitivity.GetFloat() );
  704. joySideMove += val * cl_sidespeed.GetFloat();
  705. Vector2D move( m_flPreviousJoystickYaw, m_flPreviousJoystickPitch );
  706. float dist = move.Length();
  707. // apply turn control
  708. float angle = 0.f;
  709. if ( JOY_ABSOLUTE_AXIS == gameAxes[GAME_AXIS_YAW].controlType )
  710. {
  711. float fAxisValue = ResponseCurveLook( joy_response_look.GetInt(), m_flPreviousJoystickYaw, YAW, m_flPreviousJoystickPitch, dist, frametime );
  712. angle = fAxisValue * joy_yawsensitivity.GetFloat() * aspeed * cl_yawspeed.GetFloat();
  713. }
  714. else
  715. {
  716. angle = m_flPreviousJoystickYaw * joy_yawsensitivity.GetFloat() * aspeed * 180.0;
  717. }
  718. angle = JoyStickAdjustYaw( angle );
  719. viewangles[YAW] += angle;
  720. cmd->mousedx = angle;
  721. // apply look control
  722. if ( IsX360() || in_jlook.state & 1 )
  723. {
  724. angle = 0;
  725. if ( JOY_ABSOLUTE_AXIS == gameAxes[GAME_AXIS_PITCH].controlType )
  726. {
  727. float fAxisValue = ResponseCurveLook( joy_response_look.GetInt(), m_flPreviousJoystickPitch, PITCH, m_flPreviousJoystickYaw, dist, frametime );
  728. angle = fAxisValue * joy_pitchsensitivity.GetFloat() * aspeed * cl_pitchspeed.GetFloat();
  729. }
  730. else
  731. {
  732. angle = m_flPreviousJoystickPitch * joy_pitchsensitivity.GetFloat() * aspeed * 180.0;
  733. }
  734. viewangles[PITCH] += angle;
  735. cmd->mousedy = angle;
  736. view->StopPitchDrift();
  737. if( m_flPreviousJoystickPitch == 0.f && lookspring.GetFloat() == 0.f )
  738. {
  739. // no pitch movement
  740. // disable pitch return-to-center unless requested by user
  741. // *** this code can be removed when the lookspring bug is fixed
  742. // *** the bug always has the lookspring feature on
  743. view->StopPitchDrift();
  744. }
  745. }
  746. // apply player motion relative to screen space
  747. if ( CAM_IsThirdPerson() && thirdperson_screenspace.GetInt() )
  748. {
  749. float ideal_yaw = cam_idealyaw.GetFloat();
  750. float ideal_sin = sin(DEG2RAD(ideal_yaw));
  751. float ideal_cos = cos(DEG2RAD(ideal_yaw));
  752. float side_movement = ideal_cos*joySideMove - ideal_sin*joyForwardMove;
  753. float forward_movement = ideal_cos*joyForwardMove + ideal_sin*joySideMove;
  754. cmd->forwardmove += forward_movement;
  755. cmd->sidemove += side_movement;
  756. }
  757. else
  758. {
  759. cmd->forwardmove += joyForwardMove;
  760. cmd->sidemove += joySideMove;
  761. }
  762. if ( IsPC() )
  763. {
  764. CCommand tmp;
  765. if ( FloatMakePositive(joyForwardMove) >= joy_autosprint.GetFloat() || FloatMakePositive(joySideMove) >= joy_autosprint.GetFloat() )
  766. {
  767. KeyDown( &in_joyspeed, NULL );
  768. }
  769. else
  770. {
  771. KeyUp( &in_joyspeed, NULL );
  772. }
  773. }
  774. // Bound pitch
  775. viewangles[PITCH] = clamp( viewangles[ PITCH ], -cl_pitchup.GetFloat(), cl_pitchdown.GetFloat() );
  776. engine->SetViewAngles( viewangles );
  777. }