//===== Copyright 1996-2005, 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 "iclientmode.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 "c_baseplayer.h"
#include "ienginevgui.h"
#include "inputsystem/iinputstacksystem.h"


#if defined (CSTRIKE_DLL)
#include "c_cs_player.h"
#endif

#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#elif defined( _PS3 )
#include "ps3/ps3_core.h"
#include "ps3/ps3_win32stubs.h"
#else
#include "../common/xbox/xboxstubs.h"
#endif

#ifdef PORTAL2
#include "radialmenu.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		

// Set the joystick being force disabled just as we write the config
// This allows us to chose this option in the menu with a controller without accidentally disabling our only mode of input
void JoystickForceDisabled_ChangeCallback( IConVar *pConVar, char const *pOldString, float flOldValue );
static ConVar joystick_force_disabled_set_from_options( "joystick_force_disabled_set_from_options", "1", FCVAR_ARCHIVE, "Sets controllers enabled/disabled just before the config is written.", JoystickForceDisabled_ChangeCallback );
static ConVar joystick_force_disabled( "joystick_force_disabled", "1", FCVAR_ARCHIVE, "Prevents any and all joystick input for cases where a piece of hardware is incorrectly identified as a joystick an sends bad signals." );
void JoystickForceDisabled_ChangeCallback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
	ConVarRef var( pConVar );
	if ( var.IsValid() && !var.GetBool() )
	{
		// Enabling joystick happens immediately, rather than being delayed
		if ( joystick_force_disabled.GetBool() )
		{
			joystick_force_disabled.SetValue( false );
		}
	}
}

static ConVar joy_variable_frametime( "joy_variable_frametime", IsGameConsole() ? "0" : "1", 0 );

// Axis mapping
static ConVar joy_name( "joy_name", "joystick", FCVAR_ARCHIVE );
static ConVar joy_advanced( "joy_advanced", "0", FCVAR_ARCHIVE );
static ConVar joy_advaxisx( "joy_advaxisx", "0", FCVAR_ARCHIVE );
static ConVar joy_advaxisy( "joy_advaxisy", "0", FCVAR_ARCHIVE );
static ConVar joy_advaxisz( "joy_advaxisz", "0", FCVAR_ARCHIVE );
static ConVar joy_advaxisr( "joy_advaxisr", "0", FCVAR_ARCHIVE );
static ConVar joy_advaxisu( "joy_advaxisu", "0", 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_GAMECONSOLE | FCVAR_SS, "joystick pitch sensitivity", true, -5.0f, true, -0.1f );
static ConVar joy_yawsensitivity( "joy_yawsensitivity", "-1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS, "joystick yaw sensitivity", true, -5.0f, true, -0.1f );

// Advanced sensitivity and response
#ifdef _X360 //tmuaer
static ConVar joy_response_move( "joy_response_move", "9", 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" );
#else
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" );
#endif

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_response_look_pitch( "joy_response_look_pitch", "1", FCVAR_ARCHIVE, "'Look' stick response mode for pitch: 0=Default, 1=Acceleration Promotion" );

static ConVar joy_lowend( "joy_lowend", "1", FCVAR_ARCHIVE );
static ConVar joy_lowend_linear( "joy_lowend_linear", "0.55", FCVAR_ARCHIVE );
static ConVar joy_lowmap( "joy_lowmap", "1", FCVAR_ARCHIVE );
static ConVar joy_gamma( "joy_gamma", "0.2", FCVAR_ARCHIVE );
static ConVar joy_accelscale( "joy_accelscale", "3.5", FCVAR_ARCHIVE);
static ConVar joy_accelscalepoly( "joy_accelscalepoly", "0.4", FCVAR_ARCHIVE);
static ConVar joy_accelmax( "joy_accelmax", "1.0", FCVAR_ARCHIVE);
static ConVar joy_autoAimDampenMethod( "joy_autoAimDampenMethod", "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." );
// smooth out of the auto-aim at this amount per second
static ConVar joy_autoaim_dampen_smoothout_speed( "joy_autoaim_dampen_smoothout_speed", "0.25" ); // percentage per second.  0.5 == 50 percentage points per second

static ConVar joy_curvepoint_1( "joy_curvepoint_1", "0.001", FCVAR_ARCHIVE, "", true, 0.001, true, 5 );
static ConVar joy_curvepoint_2( "joy_curvepoint_2", "0.4", FCVAR_ARCHIVE, "", true, 0.001, true, 5 );
static ConVar joy_curvepoint_3( "joy_curvepoint_3", "0.75", FCVAR_ARCHIVE, "", true, 0.001, true, 5 );
static ConVar joy_curvepoint_4( "joy_curvepoint_4", "1", FCVAR_ARCHIVE, "", true, 0.001, true, 5 );
static ConVar joy_curvepoint_end( "joy_curvepoint_end", "2", FCVAR_ARCHIVE, "", true, 0.001, true, 5 );

static ConVar joy_vehicle_turn_lowend("joy_vehicle_turn_lowend", "0.7");
static ConVar joy_vehicle_turn_lowmap("joy_vehicle_turn_lowmap", "0.4");

static ConVar joy_sensitive_step0( "joy_sensitive_step0", "0.1", FCVAR_ARCHIVE);
static ConVar joy_sensitive_step1( "joy_sensitive_step1", "0.4", FCVAR_ARCHIVE);
static ConVar joy_sensitive_step2( "joy_sensitive_step2", "0.90", FCVAR_ARCHIVE);
static ConVar joy_circle_correct( "joy_circle_correct", "1", FCVAR_ARCHIVE);

// 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_GAMECONSOLE | FCVAR_SS, "Whether to invert the Y axis of the joystick for looking." );

#if !defined ( CSTRIKE15 )
static ConVar joy_inverty_default( "joy_inverty_default", "0", FCVAR_ARCHIVE_GAMECONSOLE );				// Extracted & saved from profile
static ConVar joy_movement_stick_default( "joy_movement_stick_default", "0", FCVAR_ARCHIVE_GAMECONSOLE );	// Extracted & saved from profile
#endif

// XBox Defaults
static ConVar joy_yawsensitivity_default( "joy_yawsensitivity_default", "-1.0", FCVAR_NONE );
static ConVar joy_pitchsensitivity_default( "joy_pitchsensitivity_default", "-1.0", FCVAR_NONE );
static ConVar sv_stickysprint_default( "sv_stickysprint_default", "0", FCVAR_NONE );
static ConVar joy_lookspin_default( "joy_lookspin_default", "0.35", FCVAR_NONE );

static ConVar joy_cfg_preset( "joy_cfg_preset", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS );

void joy_movement_stick_Callback( IConVar *var, const char *pOldString, float flOldValue )
{
	if ( engine )
	{
		engine->ClientCmd_Unrestricted( "joyadvancedupdate silent\n" );
	}
}

static ConVar joy_movement_stick("joy_movement_stick", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS, "Which stick controls movement : 0 = left stick, 1 = right stick, 2 = legacy controls", joy_movement_stick_Callback );

static ConVar joy_xcontroller_cfg_loaded( "joy_xcontroller_cfg_loaded", "0", 0, "If 0, the 360controller.cfg file will be executed on startup & option changes." );

ConVar joy_no_accel_jump( "joy_no_accel_jump", "0", FCVAR_ARCHIVE );

// Motion controller
static ConVar mc_dead_zone_radius( "mc_dead_zone_radius", "0.06", FCVAR_ARCHIVE, "0 to 0.9. 0 being just around the center of the screen and 1 being the edges of the screen.", true, 0.0f, true, 0.9f );
static ConVar mc_accel_band_size( "mc_accel_band_size", "0.5", FCVAR_ARCHIVE, "Percentage of half the screen width or height.", true, 0.01f, true, 2.0f );
static ConVar mc_max_yawrate( "mc_max_yawrate", "230.0", FCVAR_ARCHIVE, "(degrees/sec)", true, 10.0,true, 720.0 );
static ConVar mc_max_pitchrate( "mc_max_pitchrate", "100.0", FCVAR_ARCHIVE, "(degrees/sec)", true, 10.0,true, 720.0 );
static ConVar mc_turnPctPegged("mc_turnPctPegged", "1.0", FCVAR_DEVELOPMENTONLY, "pegged at above this amount" );
static ConVar mc_turnPctPeggedMultiplier("mc_turnPctPeggedMultiplier", "1.0", FCVAR_DEVELOPMENTONLY, "speed multiplier when pegged" );
static ConVar mc_turn_curve("mc_turn_curve", "0", FCVAR_DEVELOPMENTONLY, "What type of acceleration curve to use for turning.");
static ConVar mc_screen_clamp( "mc_screen_clamp", "0.8f", FCVAR_DEVELOPMENTONLY, "Clamps the cursor to this much of the screen.");
static ConVar mc_force_aim_x("mc_force_aim_x", "0", FCVAR_DEVELOPMENTONLY, "debug for testing player's aim");
static ConVar mc_force_aim_y("mc_force_aim_y", "0", FCVAR_DEVELOPMENTONLY, "debug for testing player's aim");
static ConVar mc_always_lock_ret_on_zoom( "mc_always_lock_ret_on_zoom", "1", FCVAR_DEVELOPMENTONLY, "Always lock the reticle when zoomed (even for partial zoom weapons)");
static ConVar mc_max_dampening("mc_max_dampening", "0.9", FCVAR_DEVELOPMENTONLY, "dampening player's aim");
static ConVar mc_dampening_blend_amount("mc_dampening_blend_amount", "0.0", FCVAR_DEVELOPMENTONLY, "dampening player's aim");

static ConVar mc_max_turn_dampening("mc_max_turn_dampening", "0.8", FCVAR_DEVELOPMENTONLY, "dampening player's aim while scoped");
static ConVar mc_turn_dampening_blend_amount("mc_turn_dampening_blend_amount", "0.02", FCVAR_DEVELOPMENTONLY, "dampening player's aim while scoped");

static ConVar mc_zoom_out_cursor_offset_blend("mc_zoom_out_cursor_offset_blend", "0.05", FCVAR_DEVELOPMENTONLY, "0.0 means snap to the new amount.");

static ConVar mc_zoomed_out_dead_zone_radius( "mc_zoomed_out_dead_zone_radius", "0.1", FCVAR_DEVELOPMENTONLY, "0 to 0.9. 0 being just around the center of the screen and 1 being the edges of the screen.", true, 0.0f, true, 0.9f );
static ConVar mc_zoomed_aim_style("mc_zoomed_aim_style", "1", FCVAR_DEVELOPMENTONLY, "0-analog stick style. 1-pointer style.");

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;
#ifdef INFESTED_DLL
extern ConVar asw_cam_marine_yaw;
#endif
extern ConVar cam_idealpitch;
extern ConVar cam_idealyaw;
extern ConVar thirdperson_platformer;
extern ConVar thirdperson_screenspace;

//-----------------------------------------------
// 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)
		}
		//The idea is to create a max large walk zone surrounded by a max run zone.
	case 7:
		{
			float xAbs = fabs(x);
			if(xAbs < joy_sensitive_step0.GetFloat())
			{
				return 0;
			}
			else if (xAbs < joy_sensitive_step2.GetFloat())
			{
				return (85.0f/cl_forwardspeed.GetFloat()) * ((x < 0)? -1.0f : 1.0f);
			}
			else
			{
				return ((x < 0)? -1.0f : 1.0f);
			}
		}
		break;
	case 8: //same concept as above but with smooth speeds
		{
			float xAbs = fabs(x);
			if(xAbs < joy_sensitive_step0.GetFloat())
			{
				return 0;
			}
			else if (xAbs < joy_sensitive_step2.GetFloat())
			{
				float maxSpeed = (85.0f/cl_forwardspeed.GetFloat());
				float t = (xAbs-joy_sensitive_step0.GetFloat())
					/ (joy_sensitive_step2.GetFloat()-joy_sensitive_step0.GetFloat());
				float speed = t*maxSpeed;
				return speed * ((x < 0)? -1.0f : 1.0f);
			}
			else
			{
				float maxSpeed = 1.0f;
				float minSpeed = (85.0f/cl_forwardspeed.GetFloat());
				float t = (xAbs-joy_sensitive_step2.GetFloat())
					/ (1.0f-joy_sensitive_step2.GetFloat());
				float speed = t*(maxSpeed-minSpeed) + minSpeed;
				return speed * ((x < 0)? -1.0f : 1.0f);
			}
		}
		break;
	case 9: //same concept as above but with smooth speeds for walking and a hard speed for running
		{
			float xAbs = fabs(x);
			if(xAbs < joy_sensitive_step0.GetFloat())
			{
				return 0;
			}
			else if (xAbs < joy_sensitive_step1.GetFloat())
			{
				float maxSpeed = (85.0f/cl_forwardspeed.GetFloat());
				float t = (xAbs-joy_sensitive_step0.GetFloat())
					/ (joy_sensitive_step1.GetFloat()-joy_sensitive_step0.GetFloat());
				float speed = t*maxSpeed;
				return speed * ((x < 0)? -1.0f : 1.0f);
			}
			else if (xAbs < joy_sensitive_step2.GetFloat())
			{
				return (85.0f/cl_forwardspeed.GetFloat()) * ((x < 0)? -1.0f : 1.0f);
			}
			else
			{
				return ((x < 0)? -1.0f : 1.0f);
			}
		}
		break;
	}

	// 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 CInput::AutoAimDampening( float x, int axis, float dist )
{
	if ( joy_autoAimDampenMethod.GetInt() == 1 )	
	{
// disabled 6/29/15 -mtw
/*
		// $FIXME(hpe) Split screen

		// 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() )
		{
			C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();

			if ( pLocalPlayer && pLocalPlayer->IsCursorOnAutoAimTarget() )
			{
				m_lastAutoAimValue = joy_autoaimdampen.GetFloat();
				return m_lastAutoAimValue;
			}
		}

		if ( m_lastAutoAimValue < 1.0f )
		{
			m_lastAutoAimValue += joy_autoaim_dampen_smoothout_speed.GetFloat() * gpGlobals->frametime;
			if ( m_lastAutoAimValue >= 1.0f )
			{
				m_lastAutoAimValue = 1.0f;
			}
		}
*/
		return m_lastAutoAimValue;// No dampening.
	}
	else
	{
		// 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 player
			C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
			if ( pLocalPlayer )
			{
				// Get the autoaim target
				if ( pLocalPlayer->m_Local.m_bAutoAimTarget )
				{
					return joy_autoaimdampen.GetFloat();
				}
			}
		}
	}

	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[ MAX_SPLITSCREEN_PLAYERS ];

static bool IsJoystickPegged( float input, float otherAxis )
{

#if defined( _X360 )
	static float fPower = 1.25f;
#elif defined( _PS3 )
	static float fPower = 0.9f;
#else
	static float fPower = 0.9f; // pc
#endif


	float fMinimumVal = 0.01f; // accomodate dead zone
	float algorythmX = abs(input); 
	float algorythmY = MAX( abs(otherAxis),fMinimumVal ); 

#if defined( _PS3 )
	float fltempAlgorythmSample = MAX( algorythmX, algorythmY );
#else
	float fltempAlgorythmSample = pow(  pow(algorythmX,fPower)+pow(algorythmY,fPower),fPower); 
#endif

#if defined( _X360 )
	float flJoyAddititiveDistComparison = 0.98f;
#elif defined( _PS3 )
	float flJoyAddititiveDistComparison = 0.91f;
#else
	float flJoyAddititiveDistComparison = 0.94f;
#endif
	bool result = fltempAlgorythmSample >= flJoyAddititiveDistComparison;

	return result;
}

//-----------------------------------------------
// 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");
float CInput::ResponseCurveLookDefault( int nSlot, float x, int axis, float otherAxis, float dist, float frametime )
{
	envelope_t &envelope = controlEnvelope[ MAX( nSlot, 0 ) ];
	float input = x;
	// float maxX = 1.0; used in prev algorithm 
	float flJoyDist = dist;//MAX( dist, MIN( joy_pegged.GetFloat(), sqrt(x*x + otherAxis*otherAxis) ) );

	bool bPegged = ( flJoyDist >= joy_pegged.GetFloat() ) || IsJoystickPegged( x, otherAxis );

	// 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 ( otherAxis < 0.0f )
	{
		otherAxis *= -1;
	}

	if( axis == YAW && joy_virtual_peg.GetBool() )
	{
		if( x >= 0.95f )
		{
			// User has pegged the stick
			envelope.peggedAxis[axis] = true;
			envelope.axisPeggedDir[axis] = negative;
		}
		
		if( envelope.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( bPegged && negative == envelope.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
			{
				envelope.peggedAxis[axis] = false;
			}
		}
	}

	// Perform the two-stage mapping.
	float tmpDist = dist;
	if( tmpDist > joy_lowend.GetFloat() || x > joy_lowend_linear.GetFloat() )
	{
		tmpDist = RemapValClamped( tmpDist, joy_lowend.GetFloat(), 1.0f, joy_lowmap.GetFloat(), 1.0f );

		// Accelerate.
		if( envelope.envelopeScale[axis] < 1.0f )
		{
			envelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() );
			if( envelope.envelopeScale[axis] > 1.0f )
			{
				envelope.envelopeScale[axis] = 1.0f;
			}
		}

		float delta = tmpDist - joy_lowmap.GetFloat();
		tmpDist = joy_lowmap.GetFloat() + (delta * envelope.envelopeScale[axis]);
	}
	else
	{
		// Shut off acceleration
		envelope.envelopeScale[axis] = 0.0f;

		tmpDist = RemapValClamped( tmpDist, 0.0f, joy_lowend.GetFloat(), 0.0f, joy_lowmap.GetFloat() ); 

		if( tmpDist > 0.0f && joy_display_input.GetBool() )
		{
			Msg("AXIS == %d : 2:  x = :%f, otherAxis = %f\n", axis, x, otherAxis );
		}
	}

	if ( dist > 0.01f )
	{
		float newX = x;
		if ( axis == YAW )
		{
			float input = x / dist;
			input = clamp( input, -1.0f, 1.0f );
			float theta = acos( input );
			newX = cos( theta ) * tmpDist;
			//Msg( "input: %f, theta:%f, x: %f\n", input, theta, newX );
		}
		else
		{
			float input = x / dist;
			input = clamp( input, -1.0f, 1.0f );
			float theta = asin( input );
			newX = sin( theta ) * tmpDist;
			//Msg( "input: %f, theta:%f, x: %f\n", input, theta, newX );
		}
		x = newX;
	}
	/*(
	// Perform the two-stage mapping.
	if( x > joy_lowend.GetFloat() )
	{
		float highmap = 1.0f - joy_lowmap.GetFloat();
		float xNormal = x - joy_lowend.GetFloat();
		float xNormalMax = maxX - joy_lowend.GetFloat();

		float factor = xNormal / ( 1.0f - joy_lowend.GetFloat() );
		float factorMax = xNormalMax / ( 1.0f - joy_lowend.GetFloat() );
		x = joy_lowmap.GetFloat() + (highmap * factor);
		maxX = joy_lowmap.GetFloat() + (highmap * factorMax);

		//if( x > 0.0f && joy_display_input.GetBool() )
		//{
		//	Msg("AXIS == %d : 1a:  x = :%f\n", axis, x );
		//}

		// Accelerate.
		if( envelope.envelopeScale[axis] < 1.0f )
		{
			envelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() );
			if( envelope.envelopeScale[axis] > 1.0f )
			{
				envelope.envelopeScale[axis] = 1.0f;
			}
		}

		float delta = x - joy_lowmap.GetFloat();
		float deltaMax = maxX - joy_lowmap.GetFloat();
		x = joy_lowmap.GetFloat() + (delta * envelope.envelopeScale[axis]);
		maxX = joy_lowmap.GetFloat() + (deltaMax * envelope.envelopeScale[axis]);

		if( x > 0.0f && joy_display_input.GetBool() )
		{
			Msg("AXIS == %d : 1b:  x = :%f, otherAxis = %f\n", axis, x, otherAxis );
		}
	}
	else
	{
		// Shut off acceleration
		envelope.envelopeScale[axis] = 0.0f;
		float factor = x / joy_lowend.GetFloat();

		x = (joy_lowmap.GetFloat() * factor);

		if( x > 0.0f && joy_display_input.GetBool() )
		{
			Msg("AXIS == %d : 2:  x = :%f, otherAxis = %f\n", axis, x, otherAxis );
		}
	}
	*/
	x *= AutoAimDampening( input, axis, dist );

	//float flDiagDiff = abs(x - otherAxis);
	//x *= MIN( maxX, 1+(otherAxis) );

	if( x > 0.0f && joy_display_input.GetBool() )
	{
		Msg("AXIS == %d : In:%f Out:%f Frametime:%f\n", axis, 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.
ConVar joy_useNewAcecelMethod("joy_useNewAcecelMethod","1");
ConVar joy_useNewJoystickPegged( "joy_useNewJoystickPeggedTest", "0" );
float CInput::ResponseCurveLookAccelerated( int nSlot, float x, int axis, float otherAxis, float dist, float frametime )
{
	envelope_t &envelope = controlEnvelope[ MAX( nSlot, 0 ) ];

	float input = x;

	float flJoyDist = ( sqrt(x*x + otherAxis * otherAxis) );
	bool bIsPegged = ( flJoyDist>= joy_pegged.GetFloat() );
	if ( joy_useNewAcecelMethod.GetBool() || joy_useNewJoystickPegged.GetBool() )
	{
		bIsPegged = IsJoystickPegged( input, otherAxis );
	}

	float curvParam = joy_gamma.GetFloat() * 2.0f - 1.0f;

	// 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
		if( !joy_no_accel_jump.GetBool() )
		{
			x = MAX( joy_lowmap.GetFloat(), x );
		}

		bDoAcceleration = true;
	}
	else
	{
		// Joystick is languishing in the low-end, turn off acceleration.
		envelope.envelopeScale[axis] = 0.0f;
		float factor = x / joy_lowend.GetFloat();
		if ( joy_useNewAcecelMethod.GetBool() )
		{
			float divisor = factor * curvParam + 1;
			//ReleaseAssert(divisor);
			if (divisor != 0.0f)
				x = joy_lowmap.GetFloat() * ( factor * ( curvParam + 1 ) / divisor );		
		}
		else
		{
			x = joy_lowmap.GetFloat() * factor;
		}
	}

	if( bDoAcceleration )
	{
		float flMax = joy_accelmax.GetFloat();
		if( envelope.envelopeScale[axis] < flMax && !joy_useNewAcecelMethod.GetBool() )
		{
			envelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() );
			if( envelope.envelopeScale[axis] > flMax )
			{
				envelope.envelopeScale[axis] = flMax;
			}
		}
		float delta = x - joy_lowmap.GetFloat();
		x = joy_lowmap.GetFloat() + (delta * envelope.envelopeScale[axis]);

		if ( joy_useNewAcecelMethod.GetBool() )
		{
			float factor = x / joy_lowend.GetFloat();
			float divisor = factor * curvParam + 1;
			//ReleaseAssert(divisor);
			float minx = 0.0f;
			if (divisor != 0.0f)
				minx = joy_lowmap.GetFloat() * ( factor * ( curvParam + 1 ) / divisor );

			x = MAX( x, minx );		
		}
	}

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

//-----------------------------------------------
//-----------------------------------------------
float CInput::ResponseCurveLookPolynomial( int nSlot, float x, int axis, float otherAxis, float dist, float frametime )
{
	// 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 ( otherAxis < 0.0f )
	{
		otherAxis *= -1;
	}

	envelope_t &envelope = controlEnvelope[ MAX( nSlot, 0 ) ];
	float input = x;
	float scale = MIN( 1.0f, sqrt(x*x+otherAxis*otherAxis) );
	bool bPegged = ( scale >= joy_pegged.GetFloat() ) || IsJoystickPegged( x, otherAxis );

	if( axis == YAW && joy_virtual_peg.GetBool() )
	{
		if( x >= 0.95f )
		{
			// User has pegged the stick
			envelope.peggedAxis[axis] = true;
			envelope.axisPeggedDir[axis] = negative;
		}

		if( envelope.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( bPegged && negative == envelope.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
			{
				envelope.peggedAxis[axis] = false;
			}
		}
	}

	//if ( bPegged )
	//{
	//	x = 1.0f;
	//}

	float flMaxOutput = 1;
	//float flMaxAccel = joy_accelmax.GetFloat();
	if( envelope.envelopeScale[axis] < 1.0f )
	{
		envelope.envelopeScale[axis] += ( frametime * joy_accelscale.GetFloat() );
		if( envelope.envelopeScale[axis] > 1.0f )
		{
			envelope.envelopeScale[axis] = 1.0f;
		}
	}
	
	//x = joy_curvepoint_3.GetFloat() + (delta * envelope.envelopeScale[axis]);

	if ( x > 0 && scale > 0 )
	{
		x = (joy_curvepoint_end.GetFloat()*sqrt(x*x*x*x*x*x) + joy_curvepoint_4.GetFloat()*sqrt(x*x*x*x*x) + joy_curvepoint_3.GetFloat()*sqrt(x*x*x*x) + joy_curvepoint_2.GetFloat()*sqrt(x*x*x) + joy_curvepoint_1.GetFloat()*sqrt(x*x) + 0.00001f*sqrt(x)) * joy_accelscalepoly.GetFloat();
		flMaxOutput = (joy_curvepoint_end.GetFloat() + joy_curvepoint_4.GetFloat() + joy_curvepoint_3.GetFloat() + joy_curvepoint_2.GetFloat() + joy_curvepoint_1.GetFloat() + 0.00001f) * joy_accelscalepoly.GetFloat();

		if( x > 0.0f && joy_display_input.GetBool() )
		{
			//Msg("scale = %f........\n", scale );
			Msg("AXIS == %d : 1b:  x = :%f, scale = %f\n", axis, x, scale );
		}

		x *= 1+((otherAxis+scale)*0.75);
	}
	else
	{
		envelope.envelopeScale[axis] = 0.0f;
	}


	// account for pushing diagonally
	float flDiagonal = 0;
	/*
	if ( x > otherAxis )
		flDiagonal = (scale-otherAxis) * (otherAxis/x);
	else if ( otherAxis > x )
		flDiagonal = (scale-x) * (x/otherAxis);

	x = MIN( flMaxOutput, x + (flDiagonal*scale) );
	*/
	//if ( bPegged )
	//	x = flMaxOutput;

	x *= envelope.envelopeScale[axis];
	x = MIN( flMaxOutput, x );

	if( x > 0.0f && joy_display_input.GetBool() )
		Msg("flDiagonal == %f : otherAxis = :%f : x = :%f, flMaxOutput = %f\n", flDiagonal, otherAxis, x, flMaxOutput );

	x *= AutoAimDampening( input, axis, dist );

	if( x > 0.0f && joy_display_input.GetBool() )
	{
		Msg("AXIS == %d : flJoyDist:%f In:%f Out:%f Frametime:%f\n", axis, scale, input, x, frametime );
	}

	if( negative )
	{
		x *= -1;
	}

	return x;
}

//-----------------------------------------------
//-----------------------------------------------
float CInput::ResponseCurveLook( int nSlot, int curve, float x, int axis, float otherAxis, float dist, float frametime )
{
	switch( curve )
	{
	case 1://Promotion of acceleration
		return ResponseCurveLookAccelerated( nSlot, x, axis, otherAxis, dist, frametime );
		break;

	case 2://Modern
		return ResponseCurveLookPolynomial( nSlot, x, axis, otherAxis, dist, frametime );
		break;

	default:
		return ResponseCurveLookDefault( nSlot, x, axis, otherAxis, dist, frametime );
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Advanced joystick setup
//-----------------------------------------------------------------------------
void CInput::Joystick_Advanced( bool bSilent )
{
	m_fJoystickAdvancedInit = true;

	// called whenever an update is needed
	int	i;
	DWORD dwTemp;

	if ( IsGameConsole() )
	{
		// Xbox always uses a joystick
		in_joystick.SetValue( 1 );
	}

	for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
	{
		ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );

		PerUserInput_t &user = GetPerUser();

		// Initialize all the maps
		for ( i = 0; i < MAX_JOYSTICK_AXES; i++ )
		{
			user.m_rgAxes[i].AxisMap = GAME_AXIS_NONE;
			user.m_rgAxes[i].ControlMap = JOY_ABSOLUTE_AXIS;
		}

		if ( !joy_advanced.GetBool() )
		{
			// default joystick initialization
			// 2 axes only with joystick control
			user.m_rgAxes[JOY_AXIS_X].AxisMap = GAME_AXIS_YAW;
			user.m_rgAxes[JOY_AXIS_Y].AxisMap = GAME_AXIS_FORWARD;
		}
		else
		{
			if ( !bSilent && 
				hh == 0 && Q_stricmp( joy_name.GetString(), "joystick") )
			{
				// notify user of advanced controller
				Msg( "Using joystick '%s' configuration\n", joy_name.GetString() );
			}

			static SplitScreenConVarRef s_joy_movement_stick( "joy_movement_stick" );

			bool bJoyMovementStick = s_joy_movement_stick.GetBool( hh );

			// advanced initialization here
			// data supplied by user via joy_axisn cvars
			dwTemp = ( bJoyMovementStick ) ? (DWORD)joy_advaxisu.GetInt() : (DWORD)joy_advaxisx.GetInt();
			user.m_rgAxes[JOY_AXIS_X].AxisMap = dwTemp & 0x0000000f;
			user.m_rgAxes[JOY_AXIS_X].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
	
			dwTemp = ( bJoyMovementStick ) ? (DWORD)joy_advaxisr.GetInt() : (DWORD)joy_advaxisy.GetInt();
			user.m_rgAxes[JOY_AXIS_Y].AxisMap = dwTemp & 0x0000000f;
			user.m_rgAxes[JOY_AXIS_Y].ControlMap = dwTemp & JOY_RELATIVE_AXIS;

			dwTemp = (DWORD)joy_advaxisz.GetInt();
			user.m_rgAxes[JOY_AXIS_Z].AxisMap = dwTemp & 0x0000000f;
			user.m_rgAxes[JOY_AXIS_Z].ControlMap = dwTemp & JOY_RELATIVE_AXIS;

			dwTemp = ( bJoyMovementStick ) ? (DWORD)joy_advaxisy.GetInt() : (DWORD)joy_advaxisr.GetInt();
			user.m_rgAxes[JOY_AXIS_R].AxisMap = dwTemp & 0x0000000f;
			user.m_rgAxes[JOY_AXIS_R].ControlMap = dwTemp & JOY_RELATIVE_AXIS;

			dwTemp = ( bJoyMovementStick ) ? (DWORD)joy_advaxisx.GetInt() : (DWORD)joy_advaxisu.GetInt();
			user.m_rgAxes[JOY_AXIS_U].AxisMap = dwTemp & 0x0000000f;
			user.m_rgAxes[JOY_AXIS_U].ControlMap = dwTemp & JOY_RELATIVE_AXIS;
	
			dwTemp = (DWORD)joy_advaxisv.GetInt();
			user.m_rgAxes[JOY_AXIS_V].AxisMap = dwTemp & 0x0000000f;
			user.m_rgAxes[JOY_AXIS_V].ControlMap = dwTemp & JOY_RELATIVE_AXIS;

			if ( !bSilent )
			{
				Msg( "Advanced joystick settings initialized for joystick %d\n------------\n", hh + 1 );
				DescribeJoystickAxis( hh, "x axis", &user.m_rgAxes[JOY_AXIS_X] );
				DescribeJoystickAxis( hh, "y axis", &user.m_rgAxes[JOY_AXIS_Y] );
				DescribeJoystickAxis( hh, "z axis", &user.m_rgAxes[JOY_AXIS_Z] );
				DescribeJoystickAxis( hh, "r axis", &user.m_rgAxes[JOY_AXIS_R] );
				DescribeJoystickAxis( hh, "u axis", &user.m_rgAxes[JOY_AXIS_U] );
				DescribeJoystickAxis( hh, "v axis", &user.m_rgAxes[JOY_AXIS_V] );
			}
		}
	}

#if defined( SWARM_DLL ) || defined( PORTAL )
	// Load the xbox controller cfg file if it hasn't been loaded.
	if ( in_joystick.GetBool() )
	{
		if ( joy_xcontroller_cfg_loaded.GetBool() == false )
		{
			engine->ClientCmd( "exec joy_configuration" PLATFORM_EXT ".cfg" );
			joy_xcontroller_cfg_loaded.SetValue( 1 );
		}
	}
	else if ( joy_xcontroller_cfg_loaded.GetBool() )
	{
		engine->ClientCmd( "exec undo360controller.cfg" );
		joy_xcontroller_cfg_loaded.SetValue( 0 );
	}
#else // !SWARM_DLL && !PORTAL

#if !defined( _PS3 )

	// [Forrest] For CStrike 1.5 we want to load 360controller.cfg on Xbox as well as PC.
	// If we have an xcontroller on the PC, load the cfg file if it hasn't been loaded.
	// [Forrest] engine->ClientCmd didn't go through (FCVAR_CLIENTCMD_CAN_EXECUTE prevented running command).
	// Changed it to engine->ClientCmd_Unrestricted.
	ConVarRef var( "joy_xcontroller_found" );
	if ( var.IsValid() && var.GetBool() && in_joystick.GetBool() )
	{
		if ( joy_xcontroller_cfg_loaded.GetBool() == false )
		{
			for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
			{
				engine->ClientCmd_Unrestricted( "exec controller" PLATFORM_EXT ".cfg", false, i, false );
			}
			joy_xcontroller_cfg_loaded.SetValue( 1 );
		}
	}
	else if ( joy_xcontroller_cfg_loaded.GetBool() )
	{
		for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
		{
			engine->ClientCmd_Unrestricted( "exec undo360controller.cfg", false, i, false );
		}
		joy_xcontroller_cfg_loaded.SetValue( 0 );
	}

#endif

#endif // SWARM_DLL
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : index - 
// Output : char const
//-----------------------------------------------------------------------------
char const *CInput::DescribeAxis( int index )
{
// $FIXME(hpe) the string command syntax differs from xbla; need to verify 
	switch ( index )
	{
	case GAME_AXIS_FORWARD:
		return "forward";
	case GAME_AXIS_PITCH:
		return "pitch";
	case GAME_AXIS_SIDE:
		return "strafe";
	case GAME_AXIS_YAW:
		return "yaw";
	case GAME_AXIS_NONE:
	default:
		return "n/a";
	}

	return "n/a";
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *axis - 
//			*mapping - 
//-----------------------------------------------------------------------------
void CInput::DescribeJoystickAxis( int nJoystick, char const *axis, joy_axis_t *mapping )
{
	if ( !mapping->AxisMap )
	{
		Msg( "joy%d %s:  unmapped\n", nJoystick + 1, axis );
	}
	else
	{
		Msg( "joy%d %s:  %s (%s)\n",
			nJoystick + 1,
			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
	{
		result =  axisValue * ( 1.f / MAX_BUTTONSAMPLE );
	}

	return result;
}


void CInput::Joystick_SetSampleTime(float frametime)
{
	FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
	{
		m_PerUser[ i ].m_flRemainingJoystickSampleTime = frametime;
	}
}

float CInput::Joystick_GetPitch( void )
{
	if ( !ControllerModeActive() )
		return 0.0f;

	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	PerUserInput_t &user = GetPerUser( nSlot );

	return user.m_flPreviousJoystickPitch;
}

float CInput::Joystick_GetYaw( void )
{
	if ( !ControllerModeActive() )
		return 0.0f;

	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	PerUserInput_t &user = GetPerUser( nSlot );

	return user.m_flPreviousJoystickYaw;
}

void CInput::Joystick_Querry( float &forward, float &side, float &pitch, float &yaw )
{
	bool bAbsoluteYaw, bAbsolutePitch;
	JoyStickSampleAxes( forward, side, pitch, yaw, bAbsoluteYaw, bAbsolutePitch );
}

void CInput::Joystick_ForceRecentering( int nStick, bool bSet /*= true*/ )
{
	if ( nStick < 0 || nStick > 1 )
		return;

	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	PerUserInput_t &user = GetPerUser( nSlot );

	user.m_bForceJoystickRecentering[ nStick ] = bSet;
}

extern void IN_ForceSpeedUp( );
extern void IN_ForceSpeedDown( );


bool CInput::ControllerModeActive( void )
{
	return ( in_joystick.GetInt() != 0 && m_bControllerMode );
}

//--------------------------------------------------------------------
// See if we want to use the joystick
//--------------------------------------------------------------------
bool CInput::JoyStickActive()
{
	// verify joystick is available and that the user wants to use it
	if ( !in_joystick.GetInt() || 0 == inputsystem->GetJoystickCount() )
		return false; 

	// Skip out if vgui or gameui is active
	if ( !g_pInputStackSystem->IsTopmostEnabledContext( m_hInputContext ) )
		return false;

	return true;
}

//--------------------------------------------------------------------
// Reads joystick values
//--------------------------------------------------------------------
void CInput::JoyStickSampleAxes( float &forward, float &side, float &pitch, float &yaw, bool &bAbsoluteYaw, bool &bAbsolutePitch )
{
	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	PerUserInput_t &user = GetPerUser( nSlot );

	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 == user.m_rgAxes[i].AxisMap )
			continue;

		float fAxisValue = inputsystem->GetAnalogValue( (AnalogCode_t)JOYSTICK_AXIS( GET_ACTIVE_SPLITSCREEN_SLOT(), 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 = user.m_rgAxes[i].AxisMap;
		gameAxes[idx].value = fAxisValue;
		gameAxes[idx].controlType = user.m_rgAxes[i].ControlMap;
	}

	// Before these axes are allowed to return values must bring them back to mostly centered
	if ( user.m_bForceJoystickRecentering[ 0 ] )
	{
		if ( fabsf( gameAxes[GAME_AXIS_FORWARD].value ) < 0.1f && fabsf( gameAxes[GAME_AXIS_SIDE].value ) < 0.1f )
		{
			user.m_bForceJoystickRecentering[ 0 ] = false;
		}

		gameAxes[GAME_AXIS_FORWARD].value = 0.0f;
		gameAxes[GAME_AXIS_SIDE].value = 0.0f;
	}

	// Before these axes are allowed to return values must bring them back to mostly centered
	if ( user.m_bForceJoystickRecentering[ 1 ] )
	{
		if ( fabsf( gameAxes[GAME_AXIS_PITCH].value ) < 0.1f && fabsf( gameAxes[GAME_AXIS_YAW].value ) < 0.1f )
		{
			user.m_bForceJoystickRecentering[ 1 ] = false;
		}

		gameAxes[GAME_AXIS_PITCH].value = 0.0f;
		gameAxes[GAME_AXIS_YAW].value = 0.0f;
	}

	// Re-map the axis values if necessary, based on the joystick configuration
	if ( (joy_advanced.GetInt() == 0) && (in_jlook.GetPerUser( nSlot ).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.GetPerUser( nSlot ).state & 1) || lookstrafe.GetFloat() && (in_jlook.GetPerUser( nSlot ).state & 1) )
	{
		// user wants yaw control to become side control
		gameAxes[GAME_AXIS_SIDE] = gameAxes[GAME_AXIS_YAW];
		gameAxes[GAME_AXIS_YAW].value = 0;
	}

	static SplitScreenConVarRef joy_movement_stick("joy_movement_stick");
	if( joy_movement_stick.IsValid() && joy_movement_stick.GetInt( nSlot ) == 2 )
	{
		axis_t swap = gameAxes[GAME_AXIS_YAW];
		gameAxes[GAME_AXIS_YAW] = gameAxes[GAME_AXIS_SIDE];
		gameAxes[GAME_AXIS_SIDE] = swap;
	}

	forward	= ScaleAxisValue( gameAxes[GAME_AXIS_FORWARD].value, MAX_BUTTONSAMPLE * joy_forwardthreshold.GetFloat() );
	side	= ScaleAxisValue( gameAxes[GAME_AXIS_SIDE].value, MAX_BUTTONSAMPLE * joy_sidethreshold.GetFloat()  );
	pitch	= ScaleAxisValue( gameAxes[GAME_AXIS_PITCH].value, MAX_BUTTONSAMPLE * joy_pitchthreshold.GetFloat()  );
	yaw		= ScaleAxisValue( gameAxes[GAME_AXIS_YAW].value, MAX_BUTTONSAMPLE * joy_yawthreshold.GetFloat()  );

	bAbsoluteYaw = ( JOY_ABSOLUTE_AXIS == gameAxes[GAME_AXIS_YAW].controlType );
	bAbsolutePitch = ( JOY_ABSOLUTE_AXIS == gameAxes[GAME_AXIS_PITCH].controlType );

	// If we're inverting our joystick, do so
	static SplitScreenConVarRef s_joy_inverty( "joy_inverty" );
	bool isInverted = s_joy_inverty.IsValid() && s_joy_inverty.GetBool( nSlot );
	if ( !isInverted )
	{
		pitch *= -1.0f;
	}
}


//--------------------------------------------------------------------
// drive yaw, pitch and move like a screen relative platformer game
//--------------------------------------------------------------------
void CInput::JoyStickThirdPersonPlatformer( CUserCmd *cmd, float &forward, float &side, float &pitch, float &yaw )
{
	// Get starting angles
	QAngle viewangles;
	engine->GetViewAngles( viewangles );

	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	PerUserInput_t &user = GetPerUser( nSlot );

	if ( forward || side )
	{
		// apply turn control [ YAW ]
		// factor in the camera offset, so that the move direction is relative to the thirdperson camera
		viewangles[ YAW ] = RAD2DEG(atan2(-side, -forward)) + user.m_vecCameraOffset[ YAW ];
		engine->SetViewAngles( viewangles );

		// apply movement
		Vector2D moveDir( forward, side );
		cmd->forwardmove += moveDir.Length() * cl_forwardspeed.GetFloat();
	}

	if ( pitch || yaw )
	{
		static SplitScreenConVarRef s_joy_yawsensitivity( "joy_yawsensitivity" );
		static SplitScreenConVarRef s_joy_pitchsensitivity( "joy_pitchsensitivity" );

		// look around with the camera
		user.m_vecCameraOffset[ PITCH ] += pitch * s_joy_pitchsensitivity.GetFloat( nSlot );
		user.m_vecCameraOffset[ YAW ]   += yaw * s_joy_yawsensitivity.GetFloat( nSlot );
	}

	if ( forward || side || pitch || yaw )
	{
		// update the ideal pitch and yaw
		cam_idealpitch.SetValue( user.m_vecCameraOffset[ PITCH ] - viewangles[ PITCH ] );
		cam_idealyaw.SetValue( user.m_vecCameraOffset[ YAW ] - viewangles[ YAW ] );
	}
}

//-----------------------------------------------
// Turns viewangles based on sampled joystick
//-----------------------------------------------
void CInput::JoyStickTurn( CUserCmd *cmd, float &yaw, float &pitch, float frametime, bool bAbsoluteYaw, bool bAbsolutePitch )
{
	// Get starting angles
	QAngle viewangles;
	engine->GetViewAngles( viewangles );

	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	// PerUserInput_t &user = GetPerUser( nSlot );

	static SplitScreenConVarRef s_joy_yawsensitivity( "joy_yawsensitivity" );
	static SplitScreenConVarRef s_joy_pitchsensitivity( "joy_pitchsensitivity" );

	C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
	bool bSpecThird = pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE;

	Vector2D move( yaw, pitch );
	float dist = move.Length();
	bool	bVariableFrametime = joy_variable_frametime.GetBool();
	float	lookFrametime = bVariableFrametime ? frametime : gpGlobals->frametime;
	float   aspeed = lookFrametime * GetHud().GetFOVSensitivityAdjust();
	if ( bSpecThird )
		aspeed *= 1.5;

	// No quick turn so just sample the joystick
	float angle = 0.0f;

	// Sample the joystick.
	if ( bVariableFrametime || frametime != gpGlobals->frametime )
	{
		if ( bAbsoluteYaw )
		{
			float fAxisValue = ResponseCurveLook( nSlot, joy_response_look.GetInt(), yaw, YAW, pitch, dist, lookFrametime );
			angle = fAxisValue * s_joy_yawsensitivity.GetFloat( nSlot ) * aspeed * cl_yawspeed.GetFloat();
		}
		else
		{
			angle = yaw * s_joy_yawsensitivity.GetFloat( nSlot ) * aspeed * 180.0;
		}
	}

	// Update and apply turn control.  This may produce a new angle if we're doing a quick turn.
	angle = UpdateAndGetQuickTurnYaw( nSlot, lookFrametime, angle );

	viewangles[YAW] += angle;
	cmd->mousedx = angle;

	// apply look control
	if ( in_jlook.GetPerUser( nSlot ).state & 1 )
	{
		float angle = 0;
		if ( bVariableFrametime || frametime != gpGlobals->frametime )
		{
			if ( bAbsolutePitch )
			{
				float fAxisValue = ResponseCurveLook( nSlot, joy_response_look_pitch.GetInt(), pitch, PITCH, yaw, dist, lookFrametime );
				angle = fAxisValue * s_joy_pitchsensitivity.GetFloat( nSlot ) * aspeed * cl_pitchspeed.GetFloat();
			}
			else
			{
				angle = pitch * s_joy_pitchsensitivity.GetFloat( nSlot ) * aspeed * 180.0;
			}
		}
		viewangles[PITCH] += angle;
		cmd->mousedy = angle;
		view->StopPitchDrift();
		if ( pitch == 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();
		}
	}

	viewangles[PITCH] = clamp( viewangles[ PITCH ], -cl_pitchup.GetFloat(), cl_pitchdown.GetFloat() );
	engine->SetViewAngles( viewangles );
}


//---------------------------------------------------------------------
// Calculates strafe and forward/back motion based on sampled joystick
//---------------------------------------------------------------------
void CInput::JoyStickForwardSideControl( float forward, float side, float &joyForwardMove, float &joySideMove )
{
	joyForwardMove = joySideMove = 0.0f;

	// apply forward and side control
	if ( joy_response_move.GetInt() > 6 && joy_circle_correct.GetBool() )
	{
		// ok the 360 controller is scaled to a circular area.  our movement is scaled to the square two axis, 
		// so diagonal needs to be scaled properly to full speed.

		bool bInWalk = true;
		float scale = MIN(1.0f,sqrt(forward*forward+side*side));
		if ( scale > 0.01f )
		{
			float val;
			if ( scale > joy_sensitive_step2.GetFloat() )
			{
				bInWalk = false;
			}
			float scaledVal = ResponseCurve( joy_response_move.GetInt(), scale, PITCH, fabsf( joy_forwardsensitivity.GetFloat() ) );
			val = scaledVal * ( ( forward * Sign( joy_forwardsensitivity.GetFloat() ) ) / scale );
			joyForwardMove += val * cl_forwardspeed.GetFloat();

			scaledVal = ResponseCurve( joy_response_move.GetInt(), scale, PITCH, fabsf( joy_sidesensitivity.GetFloat() ) );
			val = scaledVal  * ( ( side * Sign( joy_sidesensitivity.GetFloat() ) ) / scale );
			joySideMove += val * cl_sidespeed.GetFloat();

			// big hack here, if we are not moving past the joy_sensitive_step2 thresh hold then walk.
			if ( bInWalk )
			{
				IN_ForceSpeedDown();
			}
			else
			{
				IN_ForceSpeedUp();
			}
		}
		else
		{
			IN_ForceSpeedUp();
		}
	}
	else
	{
		// 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, forward, PITCH, joy_forwardsensitivity.GetFloat() );
		joyForwardMove	+= val * cl_forwardspeed.GetFloat();
		val = ResponseCurve( iResponseCurve, side, YAW, joy_sidesensitivity.GetFloat() );
		joySideMove		+= val * cl_sidespeed.GetFloat();
	}
}

// expects a -1.0 - 1.0 value
// returns a 0.0 - 1.2 value, signed to match the input
float CInput::HandleMotionControllerInputSmoothing( float flDeadZonePct, float val )
{	
	bool isPositive = val > 0.0f;
	float absVal = abs(val);
	if ( absVal <= flDeadZonePct )
		return 0.0f;

	// Allow player to point off the screen if they've made the dead zone the size of the screen.
	float flBandSize = mc_accel_band_size.GetFloat();
	float normalizedAfterDeadzone = (absVal - flDeadZonePct) / flBandSize;
	
	float result = 0.0f;
	if ( normalizedAfterDeadzone > mc_turnPctPegged.GetFloat() )
	{
		// in our high acceleration zone, bump up the value
		result = mc_turnPctPeggedMultiplier.GetFloat();
	}
	else
	{
		// low acceleration
		// X*X method
		switch (mc_turn_curve.GetInt() )
		{
		case 0:
			result = normalizedAfterDeadzone;
			break;
		case 1:
			result = normalizedAfterDeadzone * normalizedAfterDeadzone;
			break;
		case 2:
			result = normalizedAfterDeadzone * normalizedAfterDeadzone * normalizedAfterDeadzone;
			break;

		}
	}	

	result = isPositive ? result : result * -1.0f;
	return result;
}

//-----------------------------------------------------------------------------
// Purpose: Apply motion controller to CUserCmd creation
// Input  : frametime - 
//			*cmd - 
//-----------------------------------------------------------------------------
void CInput::MotionControllerMove( float frametime, CUserCmd *cmd )
{
	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();

	C_CSPlayer* pPlayer = C_CSPlayer::GetLocalCSPlayer();
	Assert( pPlayer );

	// [dkorus] make sure our max turn rate is based on our zoom level sensitivity
	float flDeadZonePct = mc_dead_zone_radius.GetFloat();
	float flScreenClamp = mc_screen_clamp.GetFloat();

	int width=0, height=0;
	materials->GetBackBufferDimensions( width, height );

	float fX = inputsystem->GetMotionControllerPosX();
	float fY = inputsystem->GetMotionControllerPosY();

	static float s_lastCursorValueX = fX;
	static float s_lastCursorValueY = fY;

	static QAngle s_referenceDirection;
	static float s_FOVSensitivityAdjust = 1.0f;
	static float s_FOVOffsetX = 0.0f;
	static float s_FOVOffsetY = 0.0f;
	static float s_TargetFOVOffsetX = 0.0f;
	static float s_TargetFOVOffsetY = 0.0f;

	// Don't consider pointer input unless we're actively in the game.  Otherwise our view will change outside of when we want it to.
	int iObserverMode = pPlayer->GetObserverMode();
	bool ignorePointerInput = ( iObserverMode == OBS_MODE_DEATHCAM || iObserverMode == OBS_MODE_FREEZECAM );

#if defined( INCLUDE_SCALEFORM )

	// If we're in the pause menu, then lock the cursor to the screen.
	if ( g_pScaleformUI->SlotDeniesInputToGame( SF_SS_SLOT( nSlot ) )  )
	{
		ignorePointerInput = true;
	}

#endif
	
	// If we're in the pause menu, then lock the cursor to the screen.
	if ( ignorePointerInput  )
	{
		// Move this to center screen.
		fX = s_lastCursorValueX * 0.85f;
		fY = s_lastCursorValueY * 0.85f;
	}

	bool bLookingAtTarget = pPlayer->IsCursorOnAutoAimTarget();

	// Free moving cursor dampening code.
	static float s_fDampeningValue = 0.0f;  // s_fDampeningValue: 0.0 don't dampen, 1.0 fully locked in place.
	float fTargetDampening = 0.0f;
	if ( bLookingAtTarget )
	{
		fTargetDampening = mc_max_dampening.GetFloat();
	}
	// This little bit of code gives us a frame rate independent blend value.
	float blend_t = 1.0f - pow( mc_dampening_blend_amount.GetFloat(), frametime );
	s_fDampeningValue += ( fTargetDampening - s_fDampeningValue )*blend_t;


	// Turn Dampening code.
	static float s_fTurnDampeningValue = 0.0f;
	float fTargetTurnDampening = 0.0f;
	if ( bLookingAtTarget )
	{
		fTargetTurnDampening = mc_max_turn_dampening.GetFloat();
	}
	// This little bit of code gives us a frame rate independent blend value.
	blend_t = 1.0f - pow( mc_turn_dampening_blend_amount.GetFloat(), frametime );
	s_fTurnDampeningValue += ( fTargetTurnDampening - s_fTurnDampeningValue )*blend_t;

	// handle turning the view with the motion controller
	QAngle currentViewAngles;
	engine->GetViewAngles( currentViewAngles );

	if ( mc_zoomed_aim_style.GetInt() == 1 )
	{
		if ( !pPlayer->m_bIsScoped )
		{
			// Only set the reference direction to the current view when we are not zoomed in.
			s_referenceDirection = currentViewAngles;
		}
	}
	else
	{
		s_referenceDirection = currentViewAngles;
	}

	CWeaponCSBase *pWeapon = ( CWeaponCSBase* )pPlayer->GetActiveWeapon();

	float fTurnDampeningMultiplier = 1.0f;
	if ( pWeapon )
	{
		if ( mc_zoomed_aim_style.GetInt() == 1 )
		{
			// For non scoped weapons like the bomb and knife, only do clamping if we're not scoped.
			if ( !pWeapon->WantReticleShown() && !pPlayer->m_bIsScoped )
			{
				flScreenClamp = 0.0f;
				flDeadZonePct = mc_zoomed_out_dead_zone_radius.GetFloat();
			}
		}
		else
		{
			// Lock reticle if we're using a scoped weapon with our motion controller.
			// We do this by setting the screen clamp to zero so that we move like with an analog stick.
			// We also want to lock when we have a sniper rifle because the reticule will not show up.
			if ( !pWeapon->WantReticleShown() || 
				( mc_always_lock_ret_on_zoom.GetBool() && pPlayer->m_bIsScoped ) )
			{
				flScreenClamp = 0.0f;

				// If in addition to not showing the reticule we are also zoomed in, apply dampening to turning.
				if ( pPlayer->m_bIsScoped )
				{
					// If we're zoomed in, we want to apply the dampening to turning to help lock onto the enemies.
					fTurnDampeningMultiplier = (1.0f - s_fTurnDampeningValue);
					// Zoomed in weapons get a zero dead zone to make aiming smoother.
					flDeadZonePct = 0.0f;
				}
				else
				{
					// We add a bit of deadzone when zoomed out so that world navigation is easier.
					// It also helps to keep your aim on your target after firing if the weapon pops back to zoomed out.
					flDeadZonePct = mc_zoomed_out_dead_zone_radius.GetFloat();
				}
			}

		}

	}


	// We increase the deadzone size for pitch by 1.5
	const float flPitchDeadZoneScale = 1.5f;
	float pitchDeadZone = clamp( flDeadZonePct * flPitchDeadZoneScale, 0.0f, 0.9f );

	// Calculate the pitch and yaw deltas.
	const float flCurrFOVScale = GetHud().GetFOVSensitivityAdjust();

	if ( s_FOVSensitivityAdjust != flCurrFOVScale )
	{
		if ( flCurrFOVScale == 1.0f )
		{
			// Set the offset to put the cursor in the middle of the screen.
			s_FOVOffsetX = -s_lastCursorValueX * flCurrFOVScale;
			s_FOVOffsetY = -s_lastCursorValueY * flCurrFOVScale;

			// Now we want to zero the offset over time.
			s_TargetFOVOffsetX = 0.0f;
			s_TargetFOVOffsetY = 0.0f;
		}
		else
		{
			// We're zooming in on an offset position, so we need to an offset compensation here.
			// save off the offsets from the last cursor value.
			// The last cursor position on the screen was
			float lastAdjustedX = s_lastCursorValueX * s_FOVSensitivityAdjust + s_FOVOffsetX;
			float lastAdjustedY = s_lastCursorValueY * s_FOVSensitivityAdjust + s_FOVOffsetY;

			// With the new FOV scale, the offsets to give us the same position is:
			s_TargetFOVOffsetX = lastAdjustedX - s_lastCursorValueX * flCurrFOVScale;
			s_TargetFOVOffsetY = lastAdjustedY - s_lastCursorValueY * flCurrFOVScale;

			// Set the offset immediately since we don't need or want to blend to this.
			// The blending is for zooming out when the cursor will not be pointing where we were looking.
			s_FOVOffsetX = s_TargetFOVOffsetX;
			s_FOVOffsetY = s_TargetFOVOffsetY;
		}
		s_FOVSensitivityAdjust = flCurrFOVScale;
	}

	blend_t = 1.0f - pow( mc_zoom_out_cursor_offset_blend.GetFloat(), frametime );
	s_FOVOffsetX += ( s_TargetFOVOffsetX - s_FOVOffsetX ) * blend_t;
	s_FOVOffsetY += ( s_TargetFOVOffsetY - s_FOVOffsetY ) * blend_t;



	float flMaxTurnRate = mc_max_yawrate.GetFloat() * s_FOVSensitivityAdjust; 
	float flMaxPitchRate = mc_max_pitchrate.GetFloat() * s_FOVSensitivityAdjust;
	float smoothedX = HandleMotionControllerInputSmoothing( flDeadZonePct, fX );
	float smoothedY = HandleMotionControllerInputSmoothing( pitchDeadZone, fY );
	float deltaYaw = -smoothedX * flMaxTurnRate * frametime * fTurnDampeningMultiplier;
	float deltaPitch = -smoothedY * flMaxPitchRate * frametime * fTurnDampeningMultiplier;

	// Update and apply turn control.  This may produce a new angle if we're doing a quick turn.
	deltaYaw = UpdateAndGetQuickTurnYaw( nSlot, frametime, deltaYaw );

	s_referenceDirection[YAW] += deltaYaw;
	s_referenceDirection[PITCH] += deltaPitch;

	if ( joy_autoAimDampenMethod.GetInt() == 1 )
	{
		// If we are dampening, we reduce the amount we update towards our target vector.
		fX = s_lastCursorValueX + (fX - s_lastCursorValueX) * (1.0f - s_fDampeningValue);
		fY = s_lastCursorValueY + (fY - s_lastCursorValueY) * (1.0f - s_fDampeningValue);
	}

	fX = clamp( fX, -flScreenClamp, flScreenClamp );
	fY = clamp( fY, -flScreenClamp, flScreenClamp );

	float adjustedX = fX * s_FOVSensitivityAdjust + s_FOVOffsetX;
	float adjustedY = fY * s_FOVSensitivityAdjust + s_FOVOffsetY;

	Vector forward, right, up;
	// Get the orientation matrix for the camera.
	AngleVectors (s_referenceDirection, &forward, &right, &up );

	// Forward project the cursor position into the world.
	// dist is the distance from the camera to the screen.
	// NOTE!  GAME_FOV_YAW needs to be the same value returned from CCSGameRules::DefaultFOV().
	const float GAME_FOV_YAW = 90.0f;
	float dist = ( width * 0.5f ) / tanf( GAME_FOV_YAW*0.5f );
	Vector aimDirection = forward*dist + up*height*0.5f*adjustedY + right*width*0.5f*adjustedX;
	aimDirection.NormalizeInPlace();
	pPlayer->SetAimDirection( aimDirection );

	Vector PureForward( 1.0f, 0.0f, 0.0f );
	Vector PureUp( 0.0f, 0.0f, 1.0f);
	Vector PureRight( 0.0f, -1.0f, 0.0f );
	QAngle viewOffset;

	Vector pureOffset = PureForward*dist + PureUp*height*0.5f*adjustedY + PureRight*width*0.5f*adjustedX;
	VectorAngles( pureOffset, viewOffset );
	
	if ( mc_force_aim_x.GetFloat() != 0.0f )
	{
		Vector eyePos = pPlayer->EyePosition();
		Vector forcedAimPoint = forward*dist + up*height*0.5f*mc_force_aim_y.GetFloat() + right*width*0.5f*mc_force_aim_x.GetFloat();

		const float DURATION = 0.15f;
		DebugDrawLine( eyePos + forcedAimPoint, eyePos + forcedAimPoint + up*height*0.05f, 0,255,0, true, DURATION );
		DebugDrawLine( eyePos + forcedAimPoint, eyePos + forcedAimPoint + right*width*0.05f, 255, 0, 0, true, DURATION );
		DebugDrawLine( eyePos + forcedAimPoint, eyePos + forcedAimPoint - up*height*0.05f, 255, 255, 0, true, DURATION );
		DebugDrawLine( eyePos + forcedAimPoint, eyePos + forcedAimPoint - right*width*0.05f, 255,0,255, true, DURATION );

		forcedAimPoint.NormalizeInPlace();
		pPlayer->SetAimDirection( forcedAimPoint );
		
		Vector pureOffset2 = PureForward*dist + PureUp*height*0.5f*mc_force_aim_y.GetFloat() + PureRight*width*0.5f*mc_force_aim_x.GetFloat();
		VectorAngles( pureOffset2, viewOffset );
	}

	pPlayer->SetEyeAngleOffset( viewOffset );

	// Update the camera's angle.
	if ( mc_zoomed_aim_style.GetInt() == 1 && pPlayer->m_bIsScoped )
	{
		QAngle aimDirectionAngles;
		VectorAngles(aimDirection, aimDirectionAngles);
		engine->SetViewAngles( aimDirectionAngles );
	}
	else
	{
		engine->SetViewAngles( s_referenceDirection );
	}

	s_lastCursorValueX = fX;
	s_lastCursorValueY = fY;

	cmd->aimdirection = pPlayer->GetAimDirection();
	cmd->mousedx = deltaYaw;
	cmd->mousedy = deltaPitch;
}

//-----------------------------------------------------------------------------
// 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( false );
	}

	// verify joystick is available and that the user wants to use it
	if ( !in_joystick.GetInt() || 0 == inputsystem->GetJoystickCount() )
		return; 

	// Skip out if vgui is active
	if ( vgui::surface()->IsCursorVisible() )
		return;

	// Don't move if GameUI is visible
	if ( enginevgui->IsGameUIVisible() )
		return;

#ifdef PORTAL2
	if ( IsRadialMenuOpen() )
		return;
#endif

	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();

#if defined( INCLUDE_SCALEFORM )
	if ( g_pScaleformUI->SlotDeniesInputToGame( SF_SS_SLOT( nSlot ) ) )
		return;
#endif

	PerUserInput_t &user = GetPerUser( nSlot );

	// Sample the axes, apply the input, and consume sample time.
	if ( user.m_flRemainingJoystickSampleTime > 0 )
	{
		frametime = MIN(user.m_flRemainingJoystickSampleTime, frametime);
		user.m_flRemainingJoystickSampleTime -= frametime;

		float forward, side, pitch, yaw;
		bool bAbsoluteYaw, bAbsolutePitch;

		JoyStickSampleAxes( forward, side, pitch, yaw, bAbsoluteYaw, bAbsolutePitch );
		
		if ( !m_bControllerMode )
		{
			if ( fabsf(forward) > 0.1f || fabsf(side) > 0.1f || fabsf(pitch) > 0.1f || fabsf(yaw) > 0.1f )
			{
				m_bControllerMode = true;
			}
		}
			
		if ( CAM_IsThirdPerson() && thirdperson_platformer.GetInt() )
		{
			JoyStickThirdPersonPlatformer( cmd, forward, side, pitch, yaw );
			return;
		}

		float	joyForwardMove, joySideMove;
		JoyStickForwardSideControl( forward, side, joyForwardMove, joySideMove );

		// Cache off the input sample values in case we run out of sample time.
		user.m_flPreviousJoystickForwardMove = joyForwardMove;
		user.m_flPreviousJoystickSideMove = joySideMove;
		user.m_flPreviousJoystickYaw = yaw;
		user.m_flPreviousJoystickPitch = pitch;
		user.m_bPreviousJoystickUseAbsoluteYaw = bAbsoluteYaw;
		user.m_bPreviousJoystickUseAbsolutePitch = bAbsolutePitch;
	}

	if ( JoyStickActive() )
	{
		// If we are using a motion controller, then we use the pointing device for updating the look direction.
		if( inputsystem->MotionControllerActive())
		{
			MotionControllerMove( frametime, cmd );
		}
		else
		{
			JoyStickTurn( cmd,
				user.m_flPreviousJoystickYaw,
				user.m_flPreviousJoystickPitch,
				frametime,
				user.m_bPreviousJoystickUseAbsoluteYaw,
				user.m_bPreviousJoystickUseAbsolutePitch );
		}

		JoyStickApplyMovement( cmd,
			user.m_flPreviousJoystickForwardMove,
			user.m_flPreviousJoystickSideMove );
	}
}

//--------------------------------------------------------------
// Applies the calculated forward/side movement to the UserCmd
//--------------------------------------------------------------
void CInput::JoyStickApplyMovement( CUserCmd *cmd, float joyForwardMove, float joySideMove )
{
	// apply player motion relative to screen space
	if ( CAM_IsThirdPerson() && thirdperson_screenspace.GetInt() )
	{
#ifdef INFESTED_DLL
		float ideal_yaw = asw_cam_marine_yaw.GetFloat();
#else
		float ideal_yaw = cam_idealyaw.GetFloat();
#endif
		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 );
		}
	}
}


float CInput::UpdateAndGetQuickTurnYaw( int nSlot, float frametime, float angle )
{
	PerUserInput_t &user = GetPerUser( nSlot );

	if ( user.m_flSpinFrameTime )
	{
		// apply specified yaw velocity until duration expires
		float delta = frametime;
		if ( user.m_flSpinFrameTime - delta <= 0 )
		{
			// effect expired, avoid floating point creep
			delta = user.m_flSpinFrameTime;
			user.m_flSpinFrameTime = 0;
		}
		else
		{
			user.m_flSpinFrameTime -= delta;
		}

		// Modify the angle if we're doing a quick turn.
		angle = user.m_flSpinRate * delta;
	}

	// Update the spin rate
	C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( nSlot );
	if ( ( in_lookspin.GetPerUser( nSlot ).state & 2 ) && !user.m_flSpinFrameTime && pLocalPlayer && !pLocalPlayer->IsObserver() )
	{
		// user has actuated a new spin boost
		float spinFrameTime = joy_lookspin_default.GetFloat();
		user.m_flSpinFrameTime = spinFrameTime;
		// yaw velocity is in last known direction
		if ( user.m_flLastYawAngle >= 0 )
		{ 
			user.m_flSpinRate = 180.0f/spinFrameTime;
		}
		else
		{
			user.m_flSpinRate = -180.0f/spinFrameTime;
		}
	}

	// Save off the last angle if non zero.
	if ( angle != 0.0f )
	{
		// track angular direction
		user.m_flLastYawAngle = angle;
	}

	return angle;
}