//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================

// cl.input.c  -- builds an intended movement command to send to the server

//xxxxxx Move bob and pitch drifting code here and other stuff from view if needed

// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
// rights reserved.

#include "hud.h"
#include "cl_util.h"
#include "camera.h"
extern "C"
{
#include "kbutton.h"
}
#include "cvardef.h"
#include "usercmd.h"
#include "const.h"
#include "camera.h"
#include "in_defs.h"
//#include "view.h"
#include <string.h>
#include <ctype.h>

extern "C" 
{
	struct kbutton_s DLLEXPORT *KB_Find( const char *name );
	void DLLEXPORT CL_CreateMove( float frametime, struct usercmd_s *cmd, int active );
	void DLLEXPORT HUD_Shutdown( void );
	int DLLEXPORT HUD_Key_Event( int eventcode, int keynum, const char *pszCurrentBinding );
}

extern int g_iAlive;

extern int g_weaponselect;
extern cl_enginefunc_t gEngfuncs;

// Defined in pm_math.c
extern "C" float anglemod( float a );

void IN_Init( void );
void IN_Move( float frametime, usercmd_t *cmd );
void IN_Shutdown( void );
void V_Init( void );
void VectorAngles( const float *forward, float *angles );
int CL_ButtonBits( int );

// xxx need client dll function to get and clear impuse
extern cvar_t *in_joystick;

int	in_impulse = 0;
int	in_cancel = 0;

cvar_t	*m_pitch;
cvar_t	*m_yaw;
cvar_t	*m_forward;
cvar_t	*m_side;

cvar_t	*lookstrafe;
cvar_t	*lookspring;
cvar_t	*cl_pitchup;
cvar_t	*cl_pitchdown;
cvar_t	*cl_upspeed;
cvar_t	*cl_forwardspeed;
cvar_t	*cl_backspeed;
cvar_t	*cl_sidespeed;
cvar_t	*cl_movespeedkey;
cvar_t	*cl_yawspeed;
cvar_t	*cl_pitchspeed;
cvar_t	*cl_anglespeedkey;
cvar_t	*cl_vsmoothing;

/*
===============================================================================

KEY BUTTONS

Continuous button event tracking is complicated by the fact that two different
input sources (say, mouse button 1 and the control key) can both press the
same button, but the button should only be released when both of the
pressing key have been released.

When a key event issues a button command (+forward, +attack, etc), it appends
its key number as a parameter to the command so it can be matched up with
the release.

state bit 0 is the current state of the key
state bit 1 is edge triggered on the up to down transition
state bit 2 is edge triggered on the down to up transition

===============================================================================
*/

kbutton_t	in_mlook;
kbutton_t	in_klook;
kbutton_t	in_jlook;
kbutton_t	in_left;
kbutton_t	in_right;
kbutton_t	in_forward;
kbutton_t	in_back;
kbutton_t	in_lookup;
kbutton_t	in_lookdown;
kbutton_t	in_moveleft;
kbutton_t	in_moveright;
kbutton_t	in_strafe;
kbutton_t	in_speed;
kbutton_t	in_use;
kbutton_t	in_jump;
kbutton_t	in_attack;
kbutton_t	in_attack2;
kbutton_t	in_up;
kbutton_t	in_down;
kbutton_t	in_duck;
kbutton_t	in_reload;
kbutton_t	in_alt1;
kbutton_t	in_score;
kbutton_t	in_break;
kbutton_t	in_graph;  // Display the netgraph

typedef struct kblist_s
{
	struct kblist_s *next;
	kbutton_t *pkey;
	char name[32];
} kblist_t;

kblist_t *g_kbkeys = NULL;

/*
============
KB_ConvertString

Removes references to +use and replaces them with the keyname in the output string.  If
 a binding is unfound, then the original text is retained.
NOTE:  Only works for text with +word in it.
============
*/
int KB_ConvertString( char *in, char **ppout )
{
	char sz[4096];
	char binding[64];
	char *p;
	char *pOut;
	char *pEnd;
	const char *pBinding;

	if( !ppout )
		return 0;

	*ppout = NULL;
	p = in;
	pOut = sz;
	while( *p )
	{
		if( *p == '+' )
		{
			pEnd = binding;
			while( *p && ( isalnum( *p ) || ( pEnd == binding ) ) && ( ( pEnd - binding ) < 63 ) )
			{
				*pEnd++ = *p++;
			}

			*pEnd = '\0';

			pBinding = NULL;
			if( strlen( binding + 1 ) > 0 )
			{
				// See if there is a binding for binding?
				pBinding = gEngfuncs.Key_LookupBinding( binding + 1 );
			}

			if( pBinding )
			{
				*pOut++ = '[';
				pEnd = (char *)pBinding;
			}
			else
			{
				pEnd = binding;
			}

			while( *pEnd )
			{
				*pOut++ = *pEnd++;
			}

			if( pBinding )
			{
				*pOut++ = ']';
			}
		}
		else
		{
			*pOut++ = *p++;
		}
	}

	*pOut = '\0';

	pOut = (char *)malloc( strlen( sz ) + 1 );
	strcpy( pOut, sz );
	*ppout = pOut;

	return 1;
}

/*
============
KB_Find

Allows the engine to get a kbutton_t directly ( so it can check +mlook state, etc ) for saving out to .cfg files
============
*/
struct kbutton_s DLLEXPORT *KB_Find( const char *name )
{
	kblist_t *p;
	p = g_kbkeys;
	while( p )
	{
		if( !stricmp( name, p->name ) )
			return p->pkey;

		p = p->next;
	}
	return NULL;
}

/*
============
KB_Add

Add a kbutton_t * to the list of pointers the engine can retrieve via KB_Find
============
*/
void KB_Add( const char *name, kbutton_t *pkb )
{
	kblist_t *p;	
	kbutton_t *kb;

	kb = KB_Find( name );

	if( kb )
		return;

	p = (kblist_t *)malloc( sizeof(kblist_t) );
	memset( p, 0, sizeof(*p) );

	strcpy( p->name, name );
	p->pkey = pkb;

	p->next = g_kbkeys;
	g_kbkeys = p;
}

/*
============
KB_Init

Add kbutton_t definitions that the engine can query if needed
============
*/
void KB_Init( void )
{
	g_kbkeys = NULL;

	KB_Add( "in_graph", &in_graph );
	KB_Add( "in_mlook", &in_mlook );
	KB_Add( "in_jlook", &in_jlook );
}

/*
============
KB_Shutdown

Clear kblist
============
*/
void KB_Shutdown( void )
{
	kblist_t *p, *n;
	p = g_kbkeys;
	while( p )
	{
		n = p->next;
		free( p );
		p = n;
	}
	g_kbkeys = NULL;
}

/*
============
KeyDown
============
*/
void KeyDown( kbutton_t *b )
{
	int	k;
	char	*c;

	c = gEngfuncs.Cmd_Argv( 1 );
	if( c[0] )
		k = atoi( c );
	else
		k = -1;		// typed manually at the console for continuous down

	if( k == b->down[0] || k == b->down[1] )
		return;		// repeating key
	
	if( !b->down[0] )
		b->down[0] = k;
	else if( !b->down[1] )
		b->down[1] = k;
	else
	{
		gEngfuncs.Con_DPrintf( "Three keys down for a button '%c' '%c' '%c'!\n", b->down[0], b->down[1], c );
		return;
	}
	
	if( b->state & 1 )
		return;		// still down
	b->state |= 1 + 2;	// down + impulse down
}

/*
============
KeyUp
============
*/
void KeyUp( kbutton_t *b )
{
	int	k;
	char	*c;
	
	c = gEngfuncs.Cmd_Argv( 1 );
	if( c[0] )
		k = atoi(c);
	else
	{
		// typed manually at the console, assume for unsticking, so clear all
		b->down[0] = b->down[1] = 0;
		b->state = 4;	// impulse up
		return;
	}

	if( b->down[0] == k )
		b->down[0] = 0;
	else if( b->down[1] == k )
		b->down[1] = 0;
	else
		return;		// key up without coresponding down (menu pass through)
	if( b->down[0] || b->down[1] )
	{
		//Con_Printf ( "Keys down for button: '%c' '%c' '%c' (%d,%d,%d)!\n", b->down[0], b->down[1], c, b->down[0], b->down[1], c );
		return;		// some other key is still holding it down
	}

	if( !( b->state & 1 ) )
		return;		// still up (this should not happen)

	b->state &= ~1;		// now up
	b->state |= 4; 		// impulse up
}

/*
============
HUD_Key_Event

Return 1 to allow engine to process the key, otherwise, act on it as needed
============
*/
int DLLEXPORT HUD_Key_Event( int down, int keynum, const char *pszCurrentBinding )
{	
	return 1;
}

void IN_BreakDown( void )
{
	KeyDown( &in_break );
}

void IN_BreakUp( void )
{
	KeyUp( &in_break );
}

void IN_KLookDown( void )
{
	KeyDown( &in_klook );
}

void IN_KLookUp( void )
{
	KeyUp( &in_klook );
}

void IN_JLookDown( void )
{
	KeyDown( &in_jlook );
}

void IN_JLookUp( void )
{
	KeyUp( &in_jlook );
}

void IN_MLookDown( void )
{
	KeyDown( &in_mlook );
}

void IN_UpDown( void )
{
	KeyDown( &in_up );
}

void IN_UpUp( void )
{
	KeyUp( &in_up );
}

void IN_DownDown( void )
{
	KeyDown( &in_down );
}

void IN_DownUp( void )
{
	KeyUp( &in_down );
}

void IN_LeftDown( void )
{
	KeyDown( &in_left );
}

void IN_LeftUp( void )
{
	KeyUp( &in_left );
}

void IN_RightDown( void )
{
	KeyDown( &in_right );
}

void IN_RightUp( void )
{
	KeyUp( &in_right );
}

void IN_ForwardDown( void )
{
	KeyDown( &in_forward );
	gHUD.m_Spectator.HandleButtonsDown( IN_FORWARD );
}

void IN_ForwardUp( void )
{
	KeyUp( &in_forward );
	gHUD.m_Spectator.HandleButtonsUp( IN_FORWARD );
}

void IN_BackDown( void )
{
	KeyDown( &in_back );
	gHUD.m_Spectator.HandleButtonsDown( IN_BACK );
}

void IN_BackUp( void )
{
	KeyUp( &in_back );
	gHUD.m_Spectator.HandleButtonsUp( IN_BACK );
}

void IN_LookupDown( void )
{
	KeyDown( &in_lookup );
}

void IN_LookupUp( void )
{
	KeyUp( &in_lookup );
}

void IN_LookdownDown( void )
{
	KeyDown( &in_lookdown );
}

void IN_LookdownUp( void )
{
	KeyUp( &in_lookdown );
}

void IN_MoveleftDown( void )
{
	KeyDown( &in_moveleft );
	gHUD.m_Spectator.HandleButtonsDown( IN_MOVELEFT );
}

void IN_MoveleftUp( void )
{
	KeyUp( &in_moveleft );
	gHUD.m_Spectator.HandleButtonsUp( IN_MOVELEFT );
}

void IN_MoverightDown( void )
{
	KeyDown( &in_moveright );
	gHUD.m_Spectator.HandleButtonsDown( IN_MOVERIGHT );
}

void IN_MoverightUp( void )
{
	KeyUp( &in_moveright );
	gHUD.m_Spectator.HandleButtonsUp( IN_MOVERIGHT );
}

void IN_SpeedDown( void )
{
	KeyDown( &in_speed );
}

void IN_SpeedUp( void )
{
	KeyUp( &in_speed );
}

void IN_StrafeDown( void )
{
	KeyDown( &in_strafe );
}

void IN_StrafeUp( void )
{
	KeyUp( &in_strafe );
}

// needs capture by hud/vgui also
extern void __CmdFunc_InputPlayerSpecial( void );

void IN_Attack2Down( void )
{
	KeyDown( &in_attack2 );

	gHUD.m_Spectator.HandleButtonsDown( IN_ATTACK2 );
}

void IN_Attack2Up( void )
{
	KeyUp( &in_attack2 );
}

void IN_UseDown( void )
{
	KeyDown( &in_use );
	gHUD.m_Spectator.HandleButtonsDown( IN_USE );
}
void IN_UseUp( void )
{
	KeyUp( &in_use );
}
void IN_JumpDown( void )
{
	KeyDown( &in_jump );
	gHUD.m_Spectator.HandleButtonsDown( IN_JUMP );
}

void IN_JumpUp( void )
{
	KeyUp( &in_jump );
}

void IN_DuckDown( void )
{
	KeyDown( &in_duck );
	gHUD.m_Spectator.HandleButtonsDown( IN_DUCK );
}

void IN_DuckUp( void )
{
	KeyUp( &in_duck );
}

void IN_ReloadDown( void )
{
	KeyDown( &in_reload );
}

void IN_ReloadUp( void )
{
	KeyUp( &in_reload );
}

void IN_Alt1Down( void )
{
	KeyDown( &in_alt1 );
}

void IN_Alt1Up( void )
{
	KeyUp( &in_alt1 );
}

void IN_GraphDown( void )
{
	KeyDown( &in_graph );
}

void IN_GraphUp( void )
{
	KeyUp( &in_graph );
}

void IN_AttackDown( void )
{
	KeyDown( &in_attack );
	gHUD.m_Spectator.HandleButtonsDown( IN_ATTACK );
}

void IN_AttackUp( void )
{
	KeyUp( &in_attack );
	in_cancel = 0;
}

// Special handling
void IN_Cancel( void )
{
	in_cancel = 1;
}

void IN_Impulse( void )
{
	in_impulse = atoi( gEngfuncs.Cmd_Argv( 1 ) );
}

void IN_ScoreDown( void )
{
	KeyDown( &in_score );
}

void IN_ScoreUp( void )
{
	KeyUp( &in_score );
}

void IN_MLookUp( void )
{
	KeyUp( &in_mlook );
}

/*
===============
CL_KeyState

Returns 0.25 if a key was pressed and released during the frame,
0.5 if it was pressed and held
0 if held then released, and
1.0 if held for the entire time
===============
*/
float CL_KeyState( kbutton_t *key )
{
	float		val = 0.0;
	int		impulsedown, impulseup, down;

	impulsedown = key->state & 2;
	impulseup = key->state & 4;
	down = key->state & 1;

	if( impulsedown && !impulseup )
	{
		// pressed and held this frame?
		val = down ? 0.5 : 0.0;
	}

	if( impulseup && !impulsedown )
	{
		// released this frame?
		val = down ? 0.0 : 0.0;
	}

	if( !impulsedown && !impulseup )
	{
		// held the entire frame?
		val = down ? 1.0 : 0.0;
	}

	if( impulsedown && impulseup )
	{
		if( down )
		{
			// released and re-pressed this frame
			val = 0.75;	
		}
		else
		{
			// pressed and released this frame
			val = 0.25;	
		}
	}

	// clear impulses
	key->state &= 1;		
	return val;
}

/*
================
CL_AdjustAngles

Moves the local angle positions
================
*/
void CL_AdjustAngles( float frametime, float *viewangles )
{
	float speed;
	float up, down;

	if( in_speed.state & 1 )
	{
		speed = frametime * cl_anglespeedkey->value;
	}
	else
	{
		speed = frametime;
	}

	if( !( in_strafe.state & 1 ) )
	{
		viewangles[YAW] -= speed * cl_yawspeed->value * CL_KeyState( &in_right );
		viewangles[YAW] += speed * cl_yawspeed->value * CL_KeyState( &in_left );
		viewangles[YAW] = anglemod( viewangles[YAW] );
	}

	if( in_klook.state & 1 )
	{
		viewangles[PITCH] -= speed * cl_pitchspeed->value * CL_KeyState( &in_forward );
		viewangles[PITCH] += speed * cl_pitchspeed->value * CL_KeyState( &in_back );
	}

	up = CL_KeyState( &in_lookup );
	down = CL_KeyState( &in_lookdown );

	viewangles[PITCH] -= speed * cl_pitchspeed->value * up;
	viewangles[PITCH] += speed * cl_pitchspeed->value * down;

	if( viewangles[PITCH] > cl_pitchdown->value )
		viewangles[PITCH] = cl_pitchdown->value;
	if( viewangles[PITCH] < -cl_pitchup->value )
		viewangles[PITCH] = -cl_pitchup->value;

	if( viewangles[ROLL] > 50 )
		viewangles[ROLL] = 50;
	if( viewangles[ROLL] < -50 )
		viewangles[ROLL] = -50;
}

/*
================
CL_CreateMove

Send the intended movement message to the server
if active == 1 then we are 1) not playing back demos ( where our commands are ignored ) and
2 ) we have finished signing on to server
================
*/
void DLLEXPORT CL_CreateMove( float frametime, struct usercmd_s *cmd, int active )
{
	float spd;
	vec3_t viewangles;
	static vec3_t oldangles;

	if( active )
	{
		//memset( viewangles, 0, sizeof(vec3_t) );
		//viewangles[0] = viewangles[1] = viewangles[2] = 0.0;
		gEngfuncs.GetViewAngles( (float *)viewangles );

		CL_AdjustAngles( frametime, viewangles );

		memset( cmd, 0, sizeof(*cmd) );

		gEngfuncs.SetViewAngles( (float *)viewangles );

		if( in_strafe.state & 1 )
		{
			cmd->sidemove += cl_sidespeed->value * CL_KeyState( &in_right );
			cmd->sidemove -= cl_sidespeed->value * CL_KeyState( &in_left );
		}

		cmd->sidemove += cl_sidespeed->value * CL_KeyState( &in_moveright );
		cmd->sidemove -= cl_sidespeed->value * CL_KeyState( &in_moveleft );

		cmd->upmove += cl_upspeed->value * CL_KeyState( &in_up );
		cmd->upmove -= cl_upspeed->value * CL_KeyState( &in_down );

		if( !(in_klook.state & 1 ) )
		{	
			cmd->forwardmove += cl_forwardspeed->value * CL_KeyState( &in_forward );
			cmd->forwardmove -= cl_backspeed->value * CL_KeyState( &in_back );
		}

		// adjust for speed key
		if( in_speed.state & 1 )
		{
			cmd->forwardmove *= cl_movespeedkey->value;
			cmd->sidemove *= cl_movespeedkey->value;
			cmd->upmove *= cl_movespeedkey->value;
		}

		// clip to maxspeed
		spd = gEngfuncs.GetClientMaxspeed();
		if( spd != 0.0 )
		{
			// scale the 3 speeds so that the total velocity is not > cl.maxspeed
			float fmov = sqrt( ( cmd->forwardmove * cmd->forwardmove ) + ( cmd->sidemove * cmd->sidemove ) + ( cmd->upmove * cmd->upmove ) );

			if( fmov > spd )
			{
				float fratio = spd / fmov;
				cmd->forwardmove *= fratio;
				cmd->sidemove *= fratio;
				cmd->upmove *= fratio;
			}
		}

		// Allow mice and other controllers to add their inputs
		IN_Move( frametime, cmd );
	}

	cmd->impulse = in_impulse;
	in_impulse = 0;

	cmd->weaponselect = g_weaponselect;
	g_weaponselect = 0;
	//
	// set button and flag bits
	//
	cmd->buttons = CL_ButtonBits( 1 );

	// Using joystick?
	if( in_joystick->value )
	{
		if( cmd->forwardmove > 0 )
		{
			cmd->buttons |= IN_FORWARD;
		}
		else if( cmd->forwardmove < 0 )
		{
			cmd->buttons |= IN_BACK;
		}
	}

	gEngfuncs.GetViewAngles( (float *)viewangles );
	// Set current view angles.

	if( g_iAlive )
	{
		VectorCopy( viewangles, cmd->viewangles );
		VectorCopy( viewangles, oldangles );
	}
	else
	{
		VectorCopy( oldangles, cmd->viewangles );
	}

}

/*
============
CL_IsDead

Returns 1 if health is <= 0
============
*/
int CL_IsDead( void )
{
	return ( gHUD.m_Health.m_iHealth <= 0 ) ? 1 : 0;
}

/*
============
CL_ButtonBits

Returns appropriate button info for keyboard and mouse state
Set bResetState to 1 to clear old state info
============
*/
int CL_ButtonBits( int bResetState )
{
	int bits = 0;

	if( in_attack.state & 3 )
	{
		if( gHUD.m_MOTD.m_bShow )
			gHUD.m_MOTD.Reset();
		else
			bits |= IN_ATTACK;
	}

	if( in_duck.state & 3 )
	{
		bits |= IN_DUCK;
	}

	if( in_jump.state & 3 )
	{
		bits |= IN_JUMP;
	}

	if( in_forward.state & 3 )
	{
		bits |= IN_FORWARD;
	}

	if( in_back.state & 3 )
	{
		bits |= IN_BACK;
	}

	if( in_use.state & 3 )
	{
		bits |= IN_USE;
	}

	if( in_cancel )
	{
		bits |= IN_CANCEL;
	}

	if( in_left.state & 3 )
	{
		bits |= IN_LEFT;
	}
	
	if( in_right.state & 3 )
	{
		bits |= IN_RIGHT;
	}

	if( in_moveleft.state & 3 )
	{
		bits |= IN_MOVELEFT;
	}
	
	if( in_moveright.state & 3 )
	{
		bits |= IN_MOVERIGHT;
	}

	if( in_attack2.state & 3 )
	{
		bits |= IN_ATTACK2;
	}

	if( in_reload.state & 3 )
	{
		bits |= IN_RELOAD;
	}

	if( in_alt1.state & 3 )
	{
		bits |= IN_ALT1;
	}

	if( in_score.state & 3 )
	{
		bits |= IN_SCORE;
	}

	// Dead or in intermission? Shore scoreboard, too
	if( CL_IsDead() || gHUD.m_iIntermission )
	{
		bits |= IN_SCORE;
	}

	if( bResetState )
	{
		in_attack.state &= ~2;
		in_duck.state &= ~2;
		in_jump.state &= ~2;
		in_forward.state &= ~2;
		in_back.state &= ~2;
		in_use.state &= ~2;
		in_left.state &= ~2;
		in_right.state &= ~2;
		in_moveleft.state &= ~2;
		in_moveright.state &= ~2;
		in_attack2.state &= ~2;
		in_reload.state &= ~2;
		in_alt1.state &= ~2;
		in_score.state &= ~2;
	}

	return bits;
}

/*
============
CL_ResetButtonBits

============
*/
void CL_ResetButtonBits( int bits )
{
	int bitsNew = CL_ButtonBits( 0 ) ^ bits;

	// Has the attack button been changed
	if( bitsNew & IN_ATTACK )
	{
		// Was it pressed? or let go?
		if( bits & IN_ATTACK )
		{
			KeyDown( &in_attack );
		}
		else
		{
			// totally clear state
			in_attack.state &= ~7;
		}
	}
}

/*
============
InitInput
============
*/
void InitInput( void )
{
	gEngfuncs.pfnAddCommand( "+moveup", IN_UpDown );
	gEngfuncs.pfnAddCommand( "-moveup", IN_UpUp );
	gEngfuncs.pfnAddCommand( "+movedown", IN_DownDown );
	gEngfuncs.pfnAddCommand( "-movedown", IN_DownUp );
	gEngfuncs.pfnAddCommand( "+left", IN_LeftDown );
	gEngfuncs.pfnAddCommand( "-left", IN_LeftUp );
	gEngfuncs.pfnAddCommand( "+right", IN_RightDown );
	gEngfuncs.pfnAddCommand( "-right", IN_RightUp );
	gEngfuncs.pfnAddCommand( "+forward", IN_ForwardDown );
	gEngfuncs.pfnAddCommand( "-forward", IN_ForwardUp );
	gEngfuncs.pfnAddCommand( "+back", IN_BackDown );
	gEngfuncs.pfnAddCommand( "-back", IN_BackUp );
	gEngfuncs.pfnAddCommand( "+lookup", IN_LookupDown );
	gEngfuncs.pfnAddCommand( "-lookup", IN_LookupUp );
	gEngfuncs.pfnAddCommand( "+lookdown", IN_LookdownDown );
	gEngfuncs.pfnAddCommand( "-lookdown", IN_LookdownUp );
	gEngfuncs.pfnAddCommand( "+strafe", IN_StrafeDown );
	gEngfuncs.pfnAddCommand( "-strafe", IN_StrafeUp );
	gEngfuncs.pfnAddCommand( "+moveleft", IN_MoveleftDown );
	gEngfuncs.pfnAddCommand( "-moveleft", IN_MoveleftUp );
	gEngfuncs.pfnAddCommand( "+moveright", IN_MoverightDown );
	gEngfuncs.pfnAddCommand( "-moveright", IN_MoverightUp );
	gEngfuncs.pfnAddCommand( "+speed", IN_SpeedDown );
	gEngfuncs.pfnAddCommand( "-speed", IN_SpeedUp );
	gEngfuncs.pfnAddCommand( "+attack", IN_AttackDown );
	gEngfuncs.pfnAddCommand( "-attack", IN_AttackUp );
	gEngfuncs.pfnAddCommand( "+attack2", IN_Attack2Down );
	gEngfuncs.pfnAddCommand( "-attack2", IN_Attack2Up );
	gEngfuncs.pfnAddCommand( "+use", IN_UseDown );
	gEngfuncs.pfnAddCommand( "-use", IN_UseUp );
	gEngfuncs.pfnAddCommand( "+jump", IN_JumpDown );
	gEngfuncs.pfnAddCommand( "-jump", IN_JumpUp );
	gEngfuncs.pfnAddCommand( "impulse", IN_Impulse );
	gEngfuncs.pfnAddCommand( "+klook", IN_KLookDown );
	gEngfuncs.pfnAddCommand( "-klook", IN_KLookUp );
	gEngfuncs.pfnAddCommand( "+mlook", IN_MLookDown );
	gEngfuncs.pfnAddCommand( "-mlook", IN_MLookUp );
	gEngfuncs.pfnAddCommand( "+jlook", IN_JLookDown );
	gEngfuncs.pfnAddCommand( "-jlook", IN_JLookUp );
	gEngfuncs.pfnAddCommand( "+duck", IN_DuckDown );
	gEngfuncs.pfnAddCommand( "-duck", IN_DuckUp );
	gEngfuncs.pfnAddCommand( "+reload", IN_ReloadDown );
	gEngfuncs.pfnAddCommand( "-reload", IN_ReloadUp );
	gEngfuncs.pfnAddCommand( "+alt1", IN_Alt1Down );
	gEngfuncs.pfnAddCommand( "-alt1", IN_Alt1Up );
	gEngfuncs.pfnAddCommand( "+graph", IN_GraphDown );
	gEngfuncs.pfnAddCommand( "-graph", IN_GraphUp );
	gEngfuncs.pfnAddCommand( "+break", IN_BreakDown );
	gEngfuncs.pfnAddCommand( "-break", IN_BreakUp );

	lookstrafe		= gEngfuncs.pfnRegisterVariable( "lookstrafe", "0", FCVAR_ARCHIVE );
	lookspring		= gEngfuncs.pfnRegisterVariable( "lookspring", "0", FCVAR_ARCHIVE );
	cl_anglespeedkey	= gEngfuncs.pfnRegisterVariable( "cl_anglespeedkey", "0.67", 0 );
	cl_yawspeed		= gEngfuncs.pfnRegisterVariable( "cl_yawspeed", "210", 0 );
	cl_pitchspeed		= gEngfuncs.pfnRegisterVariable( "cl_pitchspeed", "225", 0 );
	cl_upspeed		= gEngfuncs.pfnRegisterVariable( "cl_upspeed", "320", 0 );
	cl_forwardspeed		= gEngfuncs.pfnRegisterVariable( "cl_forwardspeed", "400", FCVAR_ARCHIVE );
	cl_backspeed		= gEngfuncs.pfnRegisterVariable( "cl_backspeed", "400", FCVAR_ARCHIVE );
	cl_sidespeed		= gEngfuncs.pfnRegisterVariable( "cl_sidespeed", "400", 0 );
	cl_movespeedkey		= gEngfuncs.pfnRegisterVariable( "cl_movespeedkey", "0.3", 0 );
	cl_pitchup		= gEngfuncs.pfnRegisterVariable( "cl_pitchup", "89", 0 );
	cl_pitchdown		= gEngfuncs.pfnRegisterVariable( "cl_pitchdown", "89", 0 );

	cl_vsmoothing		= gEngfuncs.pfnRegisterVariable( "cl_vsmoothing", "0.05", FCVAR_ARCHIVE );

	m_pitch			= gEngfuncs.pfnRegisterVariable( "m_pitch","0.022", FCVAR_ARCHIVE );
	m_yaw			= gEngfuncs.pfnRegisterVariable( "m_yaw","0.022", FCVAR_ARCHIVE );
	m_forward		= gEngfuncs.pfnRegisterVariable( "m_forward","1", FCVAR_ARCHIVE );
	m_side			= gEngfuncs.pfnRegisterVariable( "m_side","0.8", FCVAR_ARCHIVE );

	// Initialize third person camera controls.
	CAM_Init();
	// Initialize inputs
	IN_Init();
	// Initialize keyboard
	KB_Init();
	// Initialize view system
	V_Init();
}

/*
============
ShutdownInput
============
*/
void ShutdownInput( void )
{
	IN_Shutdown();
	KB_Shutdown();
}

void DLLEXPORT HUD_Shutdown( void )
{
	ShutdownInput();
}