// 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 #include #include "vgui_TeamFortressViewport.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_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 ) { if (gViewPort) return gViewPort->KeyInput(down, keynum, 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);} void IN_ForwardUp(void) {KeyUp(&in_forward);} void IN_BackDown(void) {KeyDown(&in_back);} void IN_BackUp(void) {KeyUp(&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);} void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} void IN_MoverightDown(void) {KeyDown(&in_moveright);} void IN_MoverightUp(void) {KeyUp(&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); __CmdFunc_InputPlayerSpecial(); } void IN_Attack2Up(void) {KeyUp(&in_attack2);} void IN_UseDown (void) {KeyDown(&in_use);} void IN_UseUp (void) {KeyUp(&in_use);} void IN_JumpDown (void) {KeyDown(&in_jump);} void IN_JumpUp (void) {KeyUp(&in_jump);} void IN_DuckDown(void) {KeyDown(&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 ); } 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); if ( gViewPort ) { gViewPort->ShowScoreBoard(); } } void IN_ScoreUp(void) { KeyUp(&in_score); if ( gViewPort ) { gViewPort->HideScoreBoard(); } } void IN_MLookUp (void) { KeyUp( &in_mlook ); if ( !( in_mlook.state & 1 ) && lookspring->value ) { V_StartPitchDrift(); } } /* =============== 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) { V_StopPitchDrift (); 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 (up || down) V_StopPitchDrift (); 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 ( gHUD.m_Health.m_iHealth > 0 ) { 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 ) { 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 ("+score", IN_ScoreDown); gEngfuncs.pfnAddCommand ("-score", IN_ScoreUp); gEngfuncs.pfnAddCommand ("+showscores", IN_ScoreDown); gEngfuncs.pfnAddCommand ("-showscores", IN_ScoreUp); 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(); }