/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // in_win.c -- windows 95 mouse and joystick code // 02/21/97 JCB Added extended DirectInput code to support external controllers. #include #include "quakedef.h" #include "winquake.h" #include "dosisms.h" #define DINPUT_BUFFERSIZE 16 #define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); // mouse variables cvar_t *m_filter; extern float thestandstill; // leilei - standstill hack int mouse_buttons; int mouse_oldbuttonstate; POINT current_pos; int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; static qboolean restore_spi; static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; unsigned int uiWheelMessage; qboolean mouseactive; qboolean mouseinitialized; static qboolean mouseparmsvalid, mouseactivatetoggle; static qboolean mouseshowtoggle = 1; static qboolean dinput_acquired; extern int deathcam_yesiamdead; // leilei - deathcam static unsigned int mstate_di; extern float amouse_x; amouse_y; // joystick defines and variables // where should defines be moved? #define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick #define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball #define JOY_MAX_AXES 6 // X, Y, Z, R, U, V #define JOY_AXIS_X 0 #define JOY_AXIS_Y 1 #define JOY_AXIS_Z 2 #define JOY_AXIS_R 3 #define JOY_AXIS_U 4 #define JOY_AXIS_V 5 enum _ControlList { AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn }; DWORD dwAxisFlags[JOY_MAX_AXES] = { JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV }; DWORD dwAxisMap[JOY_MAX_AXES]; DWORD dwControlMap[JOY_MAX_AXES]; PDWORD pdwRawValue[JOY_MAX_AXES]; // none of these cvars are saved over a session // this means that advanced controller configuration needs to be executed // each time. this avoids any problems with getting back to a default usage // or when changing from one controller to another. this way at least something // works. cvar_t *in_joystick; cvar_t *joy_name; cvar_t *joy_advanced; cvar_t *joy_advaxisx; cvar_t *joy_advaxisy; cvar_t *joy_advaxisz; cvar_t *joy_advaxisr; cvar_t *joy_advaxisu; cvar_t *joy_advaxisv; cvar_t *joy_forwardthreshold; cvar_t *joy_sidethreshold; cvar_t *joy_pitchthreshold; cvar_t *joy_yawthreshold; cvar_t *joy_forwardsensitivity; cvar_t *joy_sidesensitivity; cvar_t *joy_pitchsensitivity; cvar_t *joy_yawsensitivity; cvar_t *joy_wwhack1; cvar_t *joy_wwhack2; qboolean joy_avail, joy_advancedinit, joy_haspov; DWORD joy_oldbuttonstate, joy_oldpovstate; int joy_id; DWORD joy_flags; DWORD joy_numbuttons; static LPDIRECTINPUT g_pdi; static LPDIRECTINPUTDEVICE g_pMouse; static JOYINFOEX ji; static HINSTANCE hInstDI; static qboolean dinput; typedef struct MYDATA { LONG lX; // X axis goes here LONG lY; // Y axis goes here LONG lZ; // Z axis goes here BYTE bButtonA; // One button goes here BYTE bButtonB; // Another button goes here BYTE bButtonC; // Another button goes here BYTE bButtonD; // Another button goes here } MYDATA; static DIOBJECTDATAFORMAT rgodf[] = { { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, }; #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) static DIDATAFORMAT df = { sizeof(DIDATAFORMAT), // this structure sizeof(DIOBJECTDATAFORMAT), // size of object data format DIDF_RELAXIS, // absolute axis coordinates sizeof(MYDATA), // device data size NUM_OBJECTS, // number of objects rgodf, // and here they are }; // forward-referenced functions void IN_StartupJoystick (void); void Joy_AdvancedUpdate_f (void); void IN_JoyMove (usercmd_t *cmd); /* =========== Force_CenterView_f =========== */ void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } /* =========== IN_UpdateClipCursor =========== */ void IN_UpdateClipCursor (void) { if (mouseinitialized && mouseactive && !dinput) { ClipCursor (&window_rect); // leilei - why the hell did i comment this line out...? } } /* =========== IN_ShowMouse =========== */ void IN_ShowMouse (void) { if (!mouseshowtoggle) { ShowCursor (TRUE); mouseshowtoggle = 1; } } /* =========== IN_HideMouse =========== */ void IN_HideMouse (void) { if (mouseshowtoggle) { ShowCursor (FALSE); mouseshowtoggle = 0; } } /* =========== IN_ActivateMouse =========== */ void IN_ActivateMouse (void) { mouseactivatetoggle = true; if (mouseinitialized) { if (dinput) { if (g_pMouse) { if (!dinput_acquired) { IDirectInputDevice_Acquire(g_pMouse); dinput_acquired = true; } } else { return; } } else { #ifndef EFFINGMOUSE if (mouseparmsvalid) restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); #endif SetCursorPos (window_center_x, window_center_y); SetCapture (mainwindow); ClipCursor (&window_rect); } mouseactive = true; } } /* =========== IN_SetQuakeMouseState =========== */ void IN_SetQuakeMouseState (void) { if (mouseactivatetoggle) IN_ActivateMouse (); } /* =========== IN_DeactivateMouse =========== */ void IN_DeactivateMouse (void) { mouseactivatetoggle = false; if (mouseinitialized) { if (dinput) { if (g_pMouse) { if (dinput_acquired) { IDirectInputDevice_Unacquire(g_pMouse); dinput_acquired = false; } } } else { #ifndef EFFINGMOUSE if (restore_spi) SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); #endif ClipCursor (NULL); ReleaseCapture (); } mouseactive = false; } } /* =========== IN_RestoreOriginalMouseState =========== */ void IN_RestoreOriginalMouseState (void) { if (mouseactivatetoggle) { IN_DeactivateMouse (); mouseactivatetoggle = true; } // try to redraw the cursor so it gets reinitialized, because sometimes it // has garbage after the mode switch ShowCursor (TRUE); ShowCursor (FALSE); } /* =========== IN_InitDInput =========== */ qboolean IN_InitDInput (void) { HRESULT hr; DIPROPDWORD dipdw = { { sizeof(DIPROPDWORD), // diph.dwSize sizeof(DIPROPHEADER), // diph.dwHeaderSize 0, // diph.dwObj DIPH_DEVICE, // diph.dwHow }, DINPUT_BUFFERSIZE, // dwData }; if (!hInstDI) { hInstDI = LoadLibrary("dinput.dll"); if (hInstDI == NULL) { Con_SafePrintf ("Couldn't load dinput.dll\n"); return false; } } if (!pDirectInputCreate) { pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA"); if (!pDirectInputCreate) { Con_SafePrintf ("Couldn't get DI proc addr\n"); return false; } } // register with DirectInput and get an IDirectInput to play with. hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); if (FAILED(hr)) { return false; } // obtain an interface to the system mouse device. hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); if (FAILED(hr)) { Con_SafePrintf ("Couldn't open DI mouse device\n"); return false; } // set the data format to "mouse format". hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df); if (FAILED(hr)) { Con_SafePrintf ("Couldn't set DI mouse format\n"); return false; } // set the cooperativity level. hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, DISCL_EXCLUSIVE | DISCL_FOREGROUND); if (FAILED(hr)) { Con_SafePrintf ("Couldn't set DI coop level\n"); return false; } // set the buffer size to DINPUT_BUFFERSIZE elements. // the buffer size is a DWORD property associated with the device hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); if (FAILED(hr)) { Con_SafePrintf ("Couldn't set DI buffersize\n"); return false; } return true; } /* =========== IN_StartupMouse =========== */ void IN_StartupMouse (void) { HDC hdc; if ( COM_CheckParm ("-nomouse") ) return; mouseinitialized = true; if (COM_CheckParm ("-dinput")) { dinput = IN_InitDInput (); if (dinput) { Con_SafePrintf ("DirectInput initialized\n"); } else { Con_SafePrintf ("DirectInput not initialized\n"); } } if (!dinput) { #ifndef EFFINGMOUSE mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); if (mouseparmsvalid) { if ( COM_CheckParm ("-noforcemspd") ) newmouseparms[2] = originalmouseparms[2]; if ( COM_CheckParm ("-noforcemaccel") ) { newmouseparms[0] = originalmouseparms[0]; newmouseparms[1] = originalmouseparms[1]; } if ( COM_CheckParm ("-noforcemparms") ) { newmouseparms[0] = originalmouseparms[0]; newmouseparms[1] = originalmouseparms[1]; newmouseparms[2] = originalmouseparms[2]; } } #endif } mouse_buttons = 7; // if a fullscreen video mode was set before the mouse was initialized, // set the mouse state appropriately if (mouseactivatetoggle) IN_ActivateMouse (); } // 2001-09-18 New cvar system by Maddes (Init) start /* =========== IN_Init_Cvars =========== */ void IN_Init_Cvars (void) { // mouse variables m_filter = Cvar_Get ("m_filter", "0", CVAR_ORIGINAL); // joystick variables in_joystick = Cvar_Get ("joystick", "1", CVAR_ARCHIVE|CVAR_ORIGINAL); joy_name = Cvar_Get ("joyname", "joystick", CVAR_ORIGINAL); // leilei - xb defaults, apologies if gamepad defaults offend anybody :) // // xinput standard: preset // // joyadvaxisx 3 // left stick strafe // joyadvaxisy 1 // left stick move fwd/back // joyadvaxisz 0 // pressure buttons disabled // joyadvaxisr 2 // right stick look up/down // joy_advanced = Cvar_Get ("joyadvanced", "1", CVAR_ORIGINAL); joy_advaxisx = Cvar_Get ("joyadvaxisx", "3", CVAR_ORIGINAL); joy_advaxisy = Cvar_Get ("joyadvaxisy", "1", CVAR_ORIGINAL); joy_advaxisz = Cvar_Get ("joyadvaxisz", "0", CVAR_ORIGINAL); joy_advaxisr = Cvar_Get ("joyadvaxisr", "2", CVAR_ORIGINAL); joy_advaxisu = Cvar_Get ("joyadvaxisu", "4", CVAR_ORIGINAL); joy_advaxisv = Cvar_Get ("joyadvaxisv", "0", CVAR_ORIGINAL); joy_forwardthreshold = Cvar_Get ("joyforwardthreshold", "0.15", CVAR_ORIGINAL); joy_sidethreshold = Cvar_Get ("joysidethreshold", "0.15", CVAR_ORIGINAL); joy_pitchthreshold = Cvar_Get ("joypitchthreshold", "0.15", CVAR_ORIGINAL); joy_yawthreshold = Cvar_Get ("joyyawthreshold", "0.15", CVAR_ORIGINAL); joy_forwardsensitivity = Cvar_Get ("joyforwardsensitivity", "-1", CVAR_ORIGINAL); joy_sidesensitivity = Cvar_Get ("joysidesensitivity", "1", CVAR_ORIGINAL); // leilei - was -1 joy_pitchsensitivity = Cvar_Get ("joypitchsensitivity", "1", CVAR_ORIGINAL); joy_yawsensitivity = Cvar_Get ("joyyawsensitivity", "-1", CVAR_ORIGINAL); joy_wwhack1 = Cvar_Get ("joywwhack1", "0", CVAR_ORIGINAL); joy_wwhack2 = Cvar_Get ("joywwhack2", "0", CVAR_ORIGINAL); } // 2001-09-18 New cvar system by Maddes (Init) end /* =========== IN_Init =========== */ void IN_Init (void) { // 2001-09-18 New cvar system by Maddes (Init) start Cmd_AddCommand ("force_centerview", Force_CenterView_f); Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" ); IN_StartupMouse (); IN_StartupJoystick (); } /* =========== IN_Shutdown =========== */ void IN_Shutdown (void) { IN_DeactivateMouse (); IN_ShowMouse (); if (g_pMouse) { IDirectInputDevice_Release(g_pMouse); g_pMouse = NULL; } if (g_pdi) { IDirectInput_Release(g_pdi); g_pdi = NULL; } } /* =========== IN_MouseEvent =========== */ void IN_MouseEvent (int mstate) { int i; if (mouseactive && !dinput) { // perform button actions for (i=0 ; ivalue) { mouse_x = (mx + old_mouse_x) * 0.5; mouse_y = (my + old_mouse_y) * 0.5; } else { mouse_x = mx; mouse_y = my; } old_mouse_x = mx; old_mouse_y = my; mouse_x *= sensitivity->value; mouse_y *= sensitivity->value; if (deathcam_yesiamdead){ tangles[0] = deathcam_angles[0]; tangles[1] = deathcam_angles[1]; tangles[2] = deathcam_angles[2]; } else{ tangles[0] = cl.viewangles[0]; tangles[1] = cl.viewangles[1]; tangles[2] = cl.viewangles[2]; } // add mouse X/Y movement to cmd if ( (in_strafe.state & 1) || (lookstrafe->value && ((in_mlook.state & 1) ^ ((int)m_look->value & 1)) )) // 2001-12-16 M_LOOK cvar by Heffo/Maddes cmd->sidemove += m_side->value * mouse_x; else tangles[YAW] -= m_yaw->value * mouse_x; if ((in_mlook.state & 1) ^ ((int)m_look->value & 1)) // 2001-12-16 M_LOOK cvar by Heffo/Maddes V_StopPitchDrift (); if ( ((in_mlook.state & 1) ^ ((int)m_look->value & 1)) && !(in_strafe.state & 1)) // 2001-12-16 M_LOOK cvar by Heffo/Maddes { tangles[PITCH] += m_pitch->value * mouse_y; if (!deathcam_yesiamdead){ if (tangles[PITCH] > 80) tangles[PITCH] = 80; if (tangles[PITCH] < -70) tangles[PITCH] = -70; } } else { if ((in_strafe.state & 1) && noclip_anglehack) cmd->upmove -= m_forward->value * mouse_y; else cmd->forwardmove -= m_forward->value * mouse_y; } if (deathcam_yesiamdead){ deathcam_angles[0] = tangles[0]; deathcam_angles[1] = tangles[1]; deathcam_angles[2] = tangles[2]; } else{ cl.viewangles[0] = tangles[0]; cl.viewangles[1] = tangles[1]; cl.viewangles[2] = tangles[2]; } // if the mouse has moved, force it to the center, so there's room to move if (mx || my) { SetCursorPos (window_center_x, window_center_y); } // leilei - thestandstill hack amouse_x = mouse_x; amouse_y = mouse_y; } /* =========== IN_Move =========== */ void IN_Move (usercmd_t *cmd) { if (ActiveApp && !Minimized) { IN_MouseMove (cmd); IN_JoyMove (cmd); } } /* =========== IN_Accumulate =========== */ void IN_Accumulate (void) { int mx, my; HDC hdc; if (mouseactive) { if (!dinput) { GetCursorPos (¤t_pos); mx_accum += current_pos.x - window_center_x; my_accum += current_pos.y - window_center_y; // force the mouse to the center, so there's room to move SetCursorPos (window_center_x, window_center_y); } } } /* =================== IN_ClearStates =================== */ void IN_ClearStates (void) { if (mouseactive) { mx_accum = 0; my_accum = 0; mouse_oldbuttonstate = 0; } } /* =============== IN_StartupJoystick =============== */ void IN_StartupJoystick (void) { int i, numdevs; JOYCAPS jc; MMRESULT mmr; // assume no joystick joy_avail = false; #ifdef WINDOWS31 return; #endif // abort startup if user requests no joystick if ( COM_CheckParm ("-nojoy") ) return; #ifndef WINDOWS31 // verify joystick driver is present if ((numdevs = joyGetNumDevs ()) == 0) { Con_Printf ("\njoystick not found -- driver not present\n\n"); return; } // cycle through the joystick ids for the first valid one for (joy_id=0 ; joy_idvalue == 0.0) { // default joystick initialization // 2 axes only with joystick control dwAxisMap[JOY_AXIS_X] = AxisTurn; // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; dwAxisMap[JOY_AXIS_Y] = AxisForward; // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; } else { if (strcmp (joy_name->string, "joystick") != 0) { // notify user of advanced controller Con_Printf ("\n%s configured\n\n", joy_name->string); } // advanced initialization here // data supplied by user via joy_axisn cvars dwTemp = (DWORD) joy_advaxisx->value; dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisy->value; dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisz->value; dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisr->value; dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisu->value; dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisv->value; dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; } // compute the axes to collect from DirectInput joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; for (i = 0; i < JOY_MAX_AXES; i++) { if (dwAxisMap[i] != AxisNada) { joy_flags |= dwAxisFlags[i]; } } } /* =========== IN_Commands =========== */ void IN_Commands (void) { int i, key_index; DWORD buttonstate, povstate; if (!joy_avail) { return; } // loop through the joystick buttons // key a joystick event or auxillary event for higher number buttons for each state change buttonstate = ji.dwButtons; for (i=0 ; i < joy_numbuttons ; i++) { if ( (buttonstate & (1<value != 0.0) { ji.dwUpos += 100; } return true; } else { // read error occurred // turning off the joystick seems too harsh for 1 read error,\ // but what should be done? // Con_Printf ("IN_ReadJoystick: no response\n"); // joy_avail = false; return false; } #endif } /* =========== IN_JoyMove =========== */ void IN_JoyMove (usercmd_t *cmd) { float speed, aspeed; float fAxisValue, fTemp; int i; vec3_t tangles; // leilei - deathcam // complete initialization if first time in // this is needed as cvars are not available at initialization time if( joy_advancedinit != true ) { Joy_AdvancedUpdate_f(); joy_advancedinit = true; } // verify joystick is available and that the user wants to use it if (!joy_avail || !in_joystick->value) { return; } // collect the joystick data, if possible if (IN_ReadJoystick () != true) { return; } if (in_speed.state & 1) speed = cl_movespeedkey->value; else speed = 1; // 2001-10-20 TIMESCALE extension by Tomaz/Maddes start // aspeed = speed * host_frametime; aspeed = speed * host_org_frametime; // 2001-10-20 TIMESCALE extension by Tomaz/Maddes end // loop through the axes for (i = 0; i < JOY_MAX_AXES; i++) { // get the floating point zero-centered, potentially-inverted data for the current axis fAxisValue = (float) *pdwRawValue[i]; // move centerpoint to zero fAxisValue -= 32768.0; if (joy_wwhack2->value != 0.0) { if (dwAxisMap[i] == AxisTurn) { // 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 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; } } // convert range from -32768..32767 to -1..1 fAxisValue /= 32768.0; if (deathcam_yesiamdead){ tangles[0] = deathcam_angles[0]; tangles[1] = deathcam_angles[1]; tangles[2] = deathcam_angles[2]; } else{ tangles[0] = cl.viewangles[0]; tangles[1] = cl.viewangles[1]; tangles[2] = cl.viewangles[2]; } switch (dwAxisMap[i]) { case AxisForward: if ((joy_advanced->value == 0.0) && ((in_mlook.state & 1) ^ ((int)m_look->value & 1))) // 2001-12-16 M_LOOK cvar by Heffo/Maddes { // user wants forward control to become look control if (fabs(fAxisValue) > joy_pitchthreshold->value) { // if mouse invert is on, invert the joystick pitch value // only absolute control support here (joy_advanced is false) if (m_pitch->value < 0.0) { tangles[PITCH] -= (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; } else { tangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; } V_StopPitchDrift(); } else { // 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 if(lookspring->value == 0.0) V_StopPitchDrift(); } } else { // user wants forward control to be forward control if (fabs(fAxisValue) > joy_forwardthreshold->value) { cmd->forwardmove += (fAxisValue * joy_forwardsensitivity->value) * speed * cl_forwardspeed->value; } } break; case AxisSide: if (fabs(fAxisValue) > joy_sidethreshold->value) { cmd->sidemove += (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; } break; case AxisTurn: if ((in_strafe.state & 1) || (lookstrafe->value && ((in_mlook.state & 1) ^ ((int)m_look->value & 1)))) // 2001-12-16 M_LOOK cvar by Heffo/Maddes { // user wants turn control to become side control if (fabs(fAxisValue) > joy_sidethreshold->value) { cmd->sidemove -= (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; } } else { // user wants turn control to be turn control if (fabs(fAxisValue) > joy_yawthreshold->value) { if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) { tangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * aspeed * cl_yawspeed->value; } else { tangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * speed * 180.0; } } } break; case AxisLook: if ((in_mlook.state & 1) ^ ((int)m_look->value & 1)) // 2001-12-16 M_LOOK cvar by Heffo/Maddes { if (fabs(fAxisValue) > joy_pitchthreshold->value) { // pitch movement detected and pitch movement desired by user if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) { tangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; } else { tangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * speed * 180.0; } V_StopPitchDrift(); } else { // 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 if(lookspring->value == 0.0) V_StopPitchDrift(); } } break; default: break; } if (deathcam_yesiamdead){ deathcam_angles[0] = tangles[0]; deathcam_angles[1] = tangles[1]; deathcam_angles[2] = tangles[2]; } else{ cl.viewangles[0] = tangles[0]; cl.viewangles[1] = tangles[1]; cl.viewangles[2] = tangles[2]; } } // bounds check pitch if (!deathcam_yesiamdead){ if (tangles[PITCH] > 80.0) tangles[PITCH] = 80.0; if (tangles[PITCH] < -70.0) tangles[PITCH] = -70.0; } }