DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

UsercmdGen.cpp (37588B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 idCVar joy_mergedThreshold( "joy_mergedThreshold", "1", CVAR_BOOL | CVAR_ARCHIVE, "If the thresholds aren't merged, you drift more off center" );
     33 idCVar joy_newCode( "joy_newCode", "1", CVAR_BOOL | CVAR_ARCHIVE, "Use the new codepath" );
     34 idCVar joy_triggerThreshold( "joy_triggerThreshold", "0.05", CVAR_FLOAT | CVAR_ARCHIVE, "how far the joystick triggers have to be pressed before they register as down" );
     35 idCVar joy_deadZone( "joy_deadZone", "0.2", CVAR_FLOAT | CVAR_ARCHIVE, "specifies how large the dead-zone is on the joystick" );
     36 idCVar joy_range( "joy_range", "1.0", CVAR_FLOAT | CVAR_ARCHIVE, "allow full range to be mapped to a smaller offset" );
     37 idCVar joy_gammaLook( "joy_gammaLook", "1", CVAR_INTEGER | CVAR_ARCHIVE, "use a log curve instead of a power curve for movement" );
     38 idCVar joy_powerScale( "joy_powerScale", "2", CVAR_FLOAT | CVAR_ARCHIVE, "Raise joystick values to this power" );
     39 idCVar joy_pitchSpeed( "joy_pitchSpeed", "100",	CVAR_ARCHIVE | CVAR_FLOAT, "pitch speed when pressing up or down on the joystick", 60, 600 );
     40 idCVar joy_yawSpeed( "joy_yawSpeed", "240",	CVAR_ARCHIVE | CVAR_FLOAT, "pitch speed when pressing left or right on the joystick", 60, 600 );
     41 
     42 // these were a bad idea!
     43 idCVar joy_dampenLook( "joy_dampenLook", "1", CVAR_BOOL | CVAR_ARCHIVE, "Do not allow full acceleration on look" );
     44 idCVar joy_deltaPerMSLook( "joy_deltaPerMSLook", "0.003", CVAR_FLOAT | CVAR_ARCHIVE, "Max amount to be added on look per MS" );
     45 
     46 idCVar in_mouseSpeed( "in_mouseSpeed", "1",	CVAR_ARCHIVE | CVAR_FLOAT, "speed at which the mouse moves", 0.25f, 4.0f );
     47 idCVar in_alwaysRun( "in_alwaysRun", "1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_BOOL, "always run (reverse _speed button) - only in MP" );
     48 
     49 idCVar in_useJoystick( "in_useJoystick", "0", CVAR_ARCHIVE | CVAR_BOOL, "enables/disables the gamepad for PC use" );
     50 idCVar in_joystickRumble( "in_joystickRumble", "1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_BOOL, "enable joystick rumble" );
     51 idCVar in_invertLook( "in_invertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
     52 idCVar in_mouseInvertLook( "in_mouseInvertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
     53 
     54 /*
     55 ================
     56 usercmd_t::ByteSwap
     57 ================
     58 */
     59 void usercmd_t::ByteSwap() {
     60 	angles[0] = LittleShort( angles[0] );
     61 	angles[1] = LittleShort( angles[1] );
     62 	angles[2] = LittleShort( angles[2] );
     63 }
     64 
     65 /*
     66 ================
     67 usercmd_t::Serialize
     68 ================
     69 */
     70 void usercmd_t::Serialize( idSerializer & ser, const usercmd_t & base ) {
     71 	ser.SerializeDelta( buttons, base.buttons );
     72 	ser.SerializeDelta( forwardmove, base.forwardmove );
     73 	ser.SerializeDelta( rightmove, base.rightmove );
     74 	ser.SerializeDelta( angles[0], base.angles[0] );
     75 	ser.SerializeDelta( angles[1], base.angles[1] );
     76 	ser.SerializeDelta( angles[2], base.angles[2] );
     77 	ser.SerializeDelta( pos.x, base.pos.x );
     78 	ser.SerializeDelta( pos.y, base.pos.y );
     79 	ser.SerializeDelta( pos.z, base.pos.z );
     80 	ser.SerializeDelta( clientGameMilliseconds, base.clientGameMilliseconds );
     81 	ser.SerializeDelta( serverGameMilliseconds, base.serverGameMilliseconds );
     82 	ser.SerializeDelta( fireCount, base.fireCount );
     83 	ser.SerializeDelta( speedSquared, base.speedSquared );
     84 	ser.SerializeDelta( impulse, base.impulse );
     85 	ser.SerializeDelta( impulseSequence, base.impulseSequence );
     86 }
     87 
     88 /*
     89 ================
     90 usercmd_t::operator==
     91 ================
     92 */
     93 bool usercmd_t::operator==( const usercmd_t &rhs ) const { 
     94 	return ( buttons == rhs.buttons &&
     95 			forwardmove == rhs.forwardmove &&
     96 			rightmove == rhs.rightmove &&
     97 			angles[0] == rhs.angles[0] &&
     98 			angles[1] == rhs.angles[1] &&
     99 			angles[2] == rhs.angles[2] &&
    100 			impulse == rhs.impulse &&
    101 			impulseSequence == rhs.impulseSequence &&
    102 			mx == rhs.mx &&
    103 			my == rhs.my &&
    104 			fireCount == rhs.fireCount &&
    105 			speedSquared == speedSquared );
    106 }
    107 
    108 
    109 const int KEY_MOVESPEED	= 127;
    110 
    111 userCmdString_t	userCmdStrings[] = {
    112 	{ "_moveUp",		UB_MOVEUP },
    113 	{ "_moveDown",		UB_MOVEDOWN },
    114 	{ "_left",			UB_LOOKLEFT },
    115 	{ "_right",			UB_LOOKRIGHT },
    116 	{ "_forward",		UB_MOVEFORWARD },
    117 	{ "_back",			UB_MOVEBACK },
    118 	{ "_lookUp",		UB_LOOKUP },
    119 	{ "_lookDown",		UB_LOOKDOWN },
    120 	{ "_moveLeft",		UB_MOVELEFT },
    121 	{ "_moveRight",		UB_MOVERIGHT },
    122 
    123 	{ "_attack",		UB_ATTACK },
    124 	{ "_speed",			UB_SPEED },
    125 	{ "_zoom",			UB_ZOOM },
    126 	{ "_showScores",	UB_SHOWSCORES },
    127 	{ "_use",			UB_USE },
    128 
    129 	{ "_impulse0",		UB_IMPULSE0 },
    130 	{ "_impulse1",		UB_IMPULSE1 },
    131 	{ "_impulse2",		UB_IMPULSE2 },
    132 	{ "_impulse3",		UB_IMPULSE3 },
    133 	{ "_impulse4",		UB_IMPULSE4 },
    134 	{ "_impulse5",		UB_IMPULSE5 },
    135 	{ "_impulse6",		UB_IMPULSE6 },
    136 	{ "_impulse7",		UB_IMPULSE7 },
    137 	{ "_impulse8",		UB_IMPULSE8 },
    138 	{ "_impulse9",		UB_IMPULSE9 },
    139 	{ "_impulse10",		UB_IMPULSE10 },
    140 	{ "_impulse11",		UB_IMPULSE11 },
    141 	{ "_impulse12",		UB_IMPULSE12 },
    142 	{ "_impulse13",		UB_IMPULSE13 },
    143 	{ "_impulse14",		UB_IMPULSE14 },
    144 	{ "_impulse15",		UB_IMPULSE15 },
    145 	{ "_impulse16",		UB_IMPULSE16 },
    146 	{ "_impulse17",		UB_IMPULSE17 },
    147 	{ "_impulse18",		UB_IMPULSE18 },
    148 	{ "_impulse19",		UB_IMPULSE19 },
    149 	{ "_impulse20",		UB_IMPULSE20 },
    150 	{ "_impulse21",		UB_IMPULSE21 },
    151 	{ "_impulse22",		UB_IMPULSE22 },
    152 	{ "_impulse23",		UB_IMPULSE23 },
    153 	{ "_impulse24",		UB_IMPULSE24 },
    154 	{ "_impulse25",		UB_IMPULSE25 },
    155 	{ "_impulse26",		UB_IMPULSE26 },
    156 	{ "_impulse27",		UB_IMPULSE27 },
    157 	{ "_impulse28",		UB_IMPULSE28 },
    158 	{ "_impulse29",		UB_IMPULSE29 },
    159 	{ "_impulse30",		UB_IMPULSE30 },
    160 	{ "_impulse31",		UB_IMPULSE31 },
    161 
    162 	{ NULL,				UB_NONE },
    163 };
    164 
    165  class buttonState_t {
    166  public:
    167 	int		on;
    168 	bool	held;
    169 
    170 			buttonState_t() { Clear(); };
    171 	void	Clear();
    172 	void	SetKeyState( int keystate, bool toggle );
    173 };
    174 
    175 /*
    176 ================
    177 buttonState_t::Clear
    178 ================
    179 */
    180 void buttonState_t::Clear() {
    181 	held = false;
    182 	on = 0;
    183 }
    184 
    185 /*
    186 ================
    187 buttonState_t::SetKeyState
    188 ================
    189 */
    190 void buttonState_t::SetKeyState( int keystate, bool toggle ) {
    191 	if ( !toggle ) {
    192 		held = false;
    193 		on = keystate;
    194 	} else if ( !keystate ) {
    195 		held = false;
    196 	} else if ( !held ) {
    197 		held = true;
    198 		on ^= 1;
    199 	}
    200 }
    201 
    202 
    203 const int NUM_USER_COMMANDS = sizeof(userCmdStrings) / sizeof(userCmdString_t);
    204 
    205 const int MAX_CHAT_BUFFER = 127;
    206 
    207 class idUsercmdGenLocal : public idUsercmdGen {
    208 public:
    209 					idUsercmdGenLocal();
    210 	
    211 	void			Init();
    212 
    213 	void			InitForNewMap();
    214 
    215 	void			Shutdown();
    216 
    217 	void			Clear();
    218 
    219 	void			ClearAngles();
    220 
    221 	void			InhibitUsercmd( inhibit_t subsystem, bool inhibit );
    222 
    223 	int				CommandStringUsercmdData( const char *cmdString );
    224 
    225 	void			BuildCurrentUsercmd( int deviceNum );
    226 
    227 	usercmd_t		GetCurrentUsercmd() { return cmd; };
    228 
    229 	void			MouseState( int *x, int *y, int *button, bool *down );
    230 
    231 	int				ButtonState( int key );
    232 	int				KeyState( int key );
    233 
    234 private:
    235 	void			MakeCurrent();
    236 	void			InitCurrent();
    237 
    238 	bool			Inhibited();
    239 	void			AdjustAngles();
    240 	void			KeyMove();
    241 	void			CircleToSquare( float & axis_x, float & axis_y ) const;
    242 	void			HandleJoystickAxis( int keyNum, float unclampedValue, float threshold, bool positive );
    243 	void			JoystickMove();
    244 	void			JoystickMove2();
    245 	void			MouseMove();
    246 	void			CmdButtons();
    247 
    248 	void			AimAssist();
    249 
    250 	void			Mouse();
    251 	void			Keyboard();
    252 	void			Joystick( int deviceNum );
    253 
    254 	void			Key( int keyNum, bool down );
    255 
    256 	idVec3			viewangles;
    257 	int				impulseSequence;
    258 	int				impulse;
    259 
    260 	buttonState_t	toggled_crouch;
    261 	buttonState_t	toggled_run;
    262 	buttonState_t	toggled_zoom;
    263 
    264 	int				buttonState[UB_MAX_BUTTONS];
    265 	bool			keyState[K_LAST_KEY];
    266 
    267 	int				inhibitCommands;	// true when in console or menu locally
    268 
    269 	bool			initialized;
    270 
    271 	usercmd_t		cmd;		// the current cmd being built
    272 
    273 	int				continuousMouseX, continuousMouseY;	// for gui event generatioin, never zerod
    274 	int				mouseButton;						// for gui event generatioin
    275 	bool			mouseDown;
    276 
    277 	int				mouseDx, mouseDy;	// added to by mouse events
    278 	float			joystickAxis[MAX_JOYSTICK_AXIS];	// set by joystick events
    279 
    280 	int				pollTime;
    281 	int				lastPollTime;
    282 	float			lastLookValuePitch;
    283 	float			lastLookValueYaw;
    284 
    285 	static idCVar	in_yawSpeed;
    286 	static idCVar	in_pitchSpeed;
    287 	static idCVar	in_angleSpeedKey;
    288 	static idCVar	in_toggleRun;
    289 	static idCVar	in_toggleCrouch;
    290 	static idCVar	in_toggleZoom;
    291 	static idCVar	sensitivity;
    292 	static idCVar	m_pitch;
    293 	static idCVar	m_yaw;
    294 	static idCVar	m_smooth;
    295 	static idCVar	m_showMouseRate;
    296 };
    297 
    298 idCVar idUsercmdGenLocal::in_yawSpeed( "in_yawspeed", "140", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "yaw change speed when holding down _left or _right button" );
    299 idCVar idUsercmdGenLocal::in_pitchSpeed( "in_pitchspeed", "140", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "pitch change speed when holding down look _lookUp or _lookDown button" );
    300 idCVar idUsercmdGenLocal::in_angleSpeedKey( "in_anglespeedkey", "1.5", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "angle change scale when holding down _speed button" );
    301 idCVar idUsercmdGenLocal::in_toggleRun( "in_toggleRun", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_BOOL, "pressing _speed button toggles run on/off - only in MP" );
    302 idCVar idUsercmdGenLocal::in_toggleCrouch( "in_toggleCrouch", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_BOOL, "pressing _movedown button toggles player crouching/standing" );
    303 idCVar idUsercmdGenLocal::in_toggleZoom( "in_toggleZoom", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_BOOL, "pressing _zoom button toggles zoom on/off" );
    304 idCVar idUsercmdGenLocal::sensitivity( "sensitivity", "5", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "mouse view sensitivity" );
    305 idCVar idUsercmdGenLocal::m_pitch( "m_pitch", "0.022", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "mouse pitch scale" );
    306 idCVar idUsercmdGenLocal::m_yaw( "m_yaw", "0.022", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "mouse yaw scale" );
    307 idCVar idUsercmdGenLocal::m_smooth( "m_smooth", "1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "number of samples blended for mouse viewing", 1, 8, idCmdSystem::ArgCompletion_Integer<1,8> );
    308 idCVar idUsercmdGenLocal::m_showMouseRate( "m_showMouseRate", "0", CVAR_SYSTEM | CVAR_BOOL, "shows mouse movement" );
    309 
    310 static idUsercmdGenLocal localUsercmdGen;
    311 idUsercmdGen	*usercmdGen = &localUsercmdGen;
    312 
    313 /*
    314 ================
    315 idUsercmdGenLocal::idUsercmdGenLocal
    316 ================
    317 */
    318 idUsercmdGenLocal::idUsercmdGenLocal() {
    319 	initialized = false;
    320 
    321 	pollTime = 0;
    322 	lastPollTime = 0;
    323 	lastLookValuePitch = 0.0f;
    324 	lastLookValueYaw = 0.0f;
    325 
    326 	impulseSequence = 0;
    327 	impulse = 0;
    328 
    329 	toggled_crouch.Clear();
    330 	toggled_run.Clear();
    331 	toggled_zoom.Clear();
    332 	toggled_run.on = false;
    333 
    334 	ClearAngles();
    335 	Clear();
    336 }
    337 
    338 /*
    339 ================
    340 idUsercmdGenLocal::InhibitUsercmd
    341 ================
    342 */
    343 void idUsercmdGenLocal::InhibitUsercmd( inhibit_t subsystem, bool inhibit ) {
    344 	if ( inhibit ) {
    345 		inhibitCommands |= 1 << subsystem;
    346 	} else {
    347 		inhibitCommands &= ( 0xffffffff ^ ( 1 << subsystem ) );
    348 	}
    349 }
    350 
    351 /*
    352 ===============
    353 idUsercmdGenLocal::ButtonState
    354 
    355 Returns (the fraction of the frame) that the key was down
    356 ===============
    357 */
    358 int	idUsercmdGenLocal::ButtonState( int key ) {
    359 	if ( key<0 || key>=UB_MAX_BUTTONS ) {
    360 		return -1;
    361 	}
    362 	return ( buttonState[key] > 0 ) ? 1 : 0;
    363 }
    364 
    365 /*
    366 ===============
    367 idUsercmdGenLocal::KeyState
    368 
    369 Returns (the fraction of the frame) that the key was down
    370 bk20060111
    371 ===============
    372 */
    373 int	idUsercmdGenLocal::KeyState( int key ) {
    374 	if ( key<0 || key>=K_LAST_KEY ) {
    375 		return -1;
    376 	}
    377 	return ( keyState[key] ) ? 1 : 0;
    378 }
    379 
    380 
    381 //=====================================================================
    382 
    383 /*
    384 ================
    385 idUsercmdGenLocal::Inhibited
    386 
    387 is user cmd generation inhibited
    388 ================
    389 */
    390 bool idUsercmdGenLocal::Inhibited() {
    391 	return ( inhibitCommands != 0);
    392 }
    393 
    394 /*
    395 ================
    396 idUsercmdGenLocal::AdjustAngles
    397 
    398 Moves the local angle positions
    399 ================
    400 */
    401 void idUsercmdGenLocal::AdjustAngles() {
    402 	float speed = MS2SEC( 16 );
    403 	
    404 	if ( toggled_run.on || ( in_alwaysRun.GetBool() && common->IsMultiplayer() ) ) {
    405 		speed *= in_angleSpeedKey.GetFloat();
    406 	}
    407 
    408 	viewangles[YAW] -= speed * in_yawSpeed.GetFloat() * ButtonState( UB_LOOKRIGHT );
    409 	viewangles[YAW] += speed * in_yawSpeed.GetFloat() * ButtonState( UB_LOOKLEFT );
    410 
    411 	viewangles[PITCH] -= speed * in_pitchSpeed.GetFloat() * ButtonState( UB_LOOKUP );
    412 	viewangles[PITCH] += speed * in_pitchSpeed.GetFloat() * ButtonState( UB_LOOKDOWN );
    413 }
    414 
    415 /*
    416 ================
    417 idUsercmdGenLocal::KeyMove
    418 
    419 Sets the usercmd_t based on key states
    420 ================
    421 */
    422 void idUsercmdGenLocal::KeyMove() {
    423 	int forward = 0;
    424 	int side = 0;
    425 
    426 	side += KEY_MOVESPEED * ButtonState( UB_MOVERIGHT );
    427 	side -= KEY_MOVESPEED * ButtonState( UB_MOVELEFT );
    428 
    429 	forward += KEY_MOVESPEED * ButtonState( UB_MOVEFORWARD );
    430 	forward -= KEY_MOVESPEED * ButtonState( UB_MOVEBACK );
    431 
    432 	cmd.forwardmove += idMath::ClampChar( forward );
    433 	cmd.rightmove += idMath::ClampChar( side );
    434 }
    435 
    436 /*
    437 =================
    438 idUsercmdGenLocal::MouseMove
    439 =================
    440 */
    441 void idUsercmdGenLocal::MouseMove() {
    442 	float		mx, my;
    443 	static int	history[8][2];
    444 	static int	historyCounter;
    445 	int			i;
    446 
    447 	history[historyCounter&7][0] = mouseDx;
    448 	history[historyCounter&7][1] = mouseDy;
    449 	
    450 	// allow mouse movement to be smoothed together
    451 	int smooth = m_smooth.GetInteger();
    452 	if ( smooth < 1 ) {
    453 		smooth = 1;
    454 	}
    455 	if ( smooth > 8 ) {
    456 		smooth = 8;
    457 	}
    458 	mx = 0;
    459 	my = 0;
    460 	for ( i = 0 ; i < smooth ; i++ ) {
    461 		mx += history[ ( historyCounter - i + 8 ) & 7 ][0];
    462 		my += history[ ( historyCounter - i + 8 ) & 7 ][1];
    463 	}
    464 	mx /= smooth;
    465 	my /= smooth;
    466 
    467 	historyCounter++;
    468 
    469 	if ( idMath::Fabs( mx ) > 1000 || idMath::Fabs( my ) > 1000 ) {
    470 		Sys_DebugPrintf( "idUsercmdGenLocal::MouseMove: Ignoring ridiculous mouse delta.\n" );
    471 		mx = my = 0;
    472 	}
    473 
    474 	mx *= sensitivity.GetFloat();
    475 	my *= sensitivity.GetFloat();
    476 
    477 	if ( m_showMouseRate.GetBool() ) {
    478 		Sys_DebugPrintf( "[%3i %3i  = %5.1f %5.1f] ", mouseDx, mouseDy, mx, my );
    479 	}
    480 
    481 	mouseDx = 0;
    482 	mouseDy = 0;
    483 
    484 	viewangles[YAW] -= m_yaw.GetFloat() * mx * in_mouseSpeed.GetFloat();
    485 	viewangles[PITCH] += m_pitch.GetFloat() * in_mouseSpeed.GetFloat() * ( in_mouseInvertLook.GetBool() ? -my : my );
    486 }
    487 
    488 /*
    489 ========================
    490 idUsercmdGenLocal::CircleToSquare
    491 ========================
    492 */
    493 void idUsercmdGenLocal::CircleToSquare( float & axis_x, float & axis_y ) const {
    494 	// bring everything in the first quadrant
    495 	bool flip_x = false;
    496 	if ( axis_x < 0.0f ) {
    497 		flip_x = true;
    498 		axis_x *= -1.0f;
    499 	}
    500 	bool flip_y = false;
    501 	if ( axis_y < 0.0f ) {
    502 		flip_y = true;
    503 		axis_y *= -1.0f;
    504 	}
    505 
    506 	// swap the two axes so we project against the vertical line X = 1
    507 	bool swap = false;
    508 	if ( axis_y > axis_x ) {
    509 		float tmp = axis_x;
    510 		axis_x = axis_y;
    511 		axis_y = tmp;
    512 		swap = true;
    513 	}
    514 
    515 	if ( axis_x < 0.001f ) {
    516 		// on one of the axes where no correction is needed
    517 		return;
    518 	}
    519 
    520 	// length (max 1.0f at the unit circle)
    521 	float len = idMath::Sqrt( axis_x * axis_x + axis_y * axis_y );
    522 	if ( len > 1.0f ) {
    523 		len = 1.0f;
    524 	}
    525 	// thales
    526 	float axis_y_us = axis_y / axis_x;
    527 
    528 	// use a power curve to shift the correction to happen closer to the unit circle
    529 	float correctionRatio = Square( len );
    530 	axis_x += correctionRatio * ( len - axis_x );
    531 	axis_y += correctionRatio * ( axis_y_us - axis_y );
    532 
    533 	// go back through the symmetries
    534 	if ( swap ) {
    535 		float tmp = axis_x;
    536 		axis_x = axis_y;
    537 		axis_y = tmp;
    538 	}
    539 	if ( flip_x ) {
    540 		axis_x *= -1.0f;
    541 	}
    542 	if ( flip_y ) {
    543 		axis_y *= -1.0f;
    544 	}
    545 }
    546 
    547 /*
    548 ========================
    549 idUsercmdGenLocal::HandleJoystickAxis
    550 ========================
    551 */
    552 void idUsercmdGenLocal::HandleJoystickAxis( int keyNum, float unclampedValue, float threshold, bool positive ) {
    553 	if ( ( unclampedValue > 0.0f ) && !positive ) {
    554 		return;
    555 	}
    556 	if ( ( unclampedValue < 0.0f ) && positive ) {
    557 		return;
    558 	}
    559 	float value = 0.0f;
    560 	bool pressed = false;
    561 	if ( unclampedValue > threshold ) {
    562 		value = idMath::Fabs( ( unclampedValue - threshold ) / ( 1.0f - threshold ) );
    563 		pressed = true;
    564 	} else if ( unclampedValue < -threshold ) {
    565 		value = idMath::Fabs( ( unclampedValue + threshold ) / ( 1.0f - threshold ) );
    566 		pressed = true;
    567 	}
    568 
    569 	int action = idKeyInput::GetUsercmdAction( keyNum );
    570 	if ( action >= UB_ATTACK ) {
    571 		Key( keyNum, pressed );
    572 		return;
    573 	}
    574 	if ( !pressed ) {
    575 		return;
    576 	}
    577 
    578 	float lookValue = 0.0f;
    579 	if ( joy_gammaLook.GetBool() ) {
    580 		lookValue = idMath::Pow( 1.04712854805f, value * 100.0f ) * 0.01f;
    581 	} else {
    582 		lookValue = idMath::Pow( value, joy_powerScale.GetFloat() );
    583 	}
    584 
    585 	idGame * game = common->Game();
    586 	if ( game != NULL ) {
    587 		lookValue *= game->GetAimAssistSensitivity();
    588 	}
    589 
    590 	switch ( action ) {
    591 		case UB_MOVEFORWARD: {
    592 			float move = (float)cmd.forwardmove + ( KEY_MOVESPEED * value );
    593 			cmd.forwardmove = idMath::ClampChar( idMath::Ftoi( move ) );
    594 			break;
    595 		}
    596 		case UB_MOVEBACK: {
    597 			float move = (float)cmd.forwardmove - ( KEY_MOVESPEED * value );
    598 			cmd.forwardmove = idMath::ClampChar( idMath::Ftoi( move ) );
    599 			break;
    600 		}
    601 		case UB_MOVELEFT: {
    602 			float move = (float)cmd.rightmove - ( KEY_MOVESPEED * value );
    603 			cmd.rightmove = idMath::ClampChar( idMath::Ftoi( move ) );
    604 			break;
    605 		}
    606 		case UB_MOVERIGHT: {
    607 			float move = (float)cmd.rightmove + ( KEY_MOVESPEED * value );
    608 			cmd.rightmove = idMath::ClampChar( idMath::Ftoi( move ) );
    609 			break;
    610 		}
    611 		case UB_LOOKUP: {
    612 			if ( joy_dampenLook.GetBool() ) {
    613 				lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValuePitch );
    614 				lastLookValuePitch = lookValue;
    615 			}
    616 
    617 			float invertPitch = in_invertLook.GetBool() ? -1.0f : 1.0f;
    618 			viewangles[PITCH] -= MS2SEC( pollTime - lastPollTime ) * lookValue * joy_pitchSpeed.GetFloat() * invertPitch;
    619 			break;
    620 		}
    621 		case UB_LOOKDOWN: {
    622 			if ( joy_dampenLook.GetBool() ) {
    623 				lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValuePitch );
    624 				lastLookValuePitch = lookValue;
    625 			}
    626 
    627 			float invertPitch = in_invertLook.GetBool() ? -1.0f : 1.0f;
    628 			viewangles[PITCH] += MS2SEC( pollTime - lastPollTime ) * lookValue * joy_pitchSpeed.GetFloat() * invertPitch;
    629 			break;
    630 		}
    631 		case UB_LOOKLEFT: {
    632 			if ( joy_dampenLook.GetBool() ) {
    633 				lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValueYaw );
    634 				lastLookValueYaw = lookValue;
    635 			}
    636 			viewangles[YAW] += MS2SEC( pollTime - lastPollTime ) * lookValue * joy_yawSpeed.GetFloat();
    637 			break;
    638 		}
    639 		case UB_LOOKRIGHT: {
    640 			if ( joy_dampenLook.GetBool() ) {
    641 				lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValueYaw );
    642 				lastLookValueYaw = lookValue;
    643 			}
    644 			viewangles[YAW] -= MS2SEC( pollTime - lastPollTime ) * lookValue * joy_yawSpeed.GetFloat();
    645 			break;
    646 		}
    647 	}
    648 }
    649 
    650 /*
    651 =================
    652 idUsercmdGenLocal::JoystickMove
    653 =================
    654 */
    655 void idUsercmdGenLocal::JoystickMove() {
    656 	float threshold = joy_deadZone.GetFloat();
    657 	float triggerThreshold = joy_triggerThreshold.GetFloat();
    658 
    659 	float axis_y = joystickAxis[ AXIS_LEFT_Y ];
    660 	float axis_x = joystickAxis[ AXIS_LEFT_X ];
    661 	CircleToSquare( axis_x, axis_y );
    662 
    663 	HandleJoystickAxis( K_JOY_STICK1_UP, axis_y, threshold, false );
    664 	HandleJoystickAxis( K_JOY_STICK1_DOWN, axis_y, threshold, true );
    665 	HandleJoystickAxis( K_JOY_STICK1_LEFT, axis_x, threshold, false );
    666 	HandleJoystickAxis( K_JOY_STICK1_RIGHT, axis_x, threshold, true );
    667 
    668 	axis_y = joystickAxis[ AXIS_RIGHT_Y ];
    669 	axis_x = joystickAxis[ AXIS_RIGHT_X ];
    670 	CircleToSquare( axis_x, axis_y );
    671 
    672 	HandleJoystickAxis( K_JOY_STICK2_UP, axis_y, threshold, false );
    673 	HandleJoystickAxis( K_JOY_STICK2_DOWN, axis_y, threshold, true );
    674 	HandleJoystickAxis( K_JOY_STICK2_LEFT, axis_x, threshold, false );
    675 	HandleJoystickAxis( K_JOY_STICK2_RIGHT, axis_x, threshold, true );
    676 
    677 	HandleJoystickAxis( K_JOY_TRIGGER1, joystickAxis[ AXIS_LEFT_TRIG ], triggerThreshold, true );
    678 	HandleJoystickAxis( K_JOY_TRIGGER2, joystickAxis[ AXIS_RIGHT_TRIG ], triggerThreshold, true );
    679 }
    680 
    681 enum transferFunction_t {
    682 	FUNC_LINEAR,
    683 	FUNC_LOGARITHMIC,
    684 	FUNC_EXPONENTIAL
    685 };
    686 
    687 /*
    688 =================
    689 JoypadFunction
    690 =================
    691 */
    692 idVec2 JoypadFunction(
    693 	const idVec2 raw,
    694 	const float	aimAssistScale,
    695 	const float threshold,
    696 	const float range,
    697 	const transferFunction_t shape,
    698 	const bool	mergedThreshold ) {
    699 
    700 	if ( range <= threshold ) {
    701 		return idVec2( 0.0f, 0.0f );
    702 	}
    703 
    704 	idVec2	threshed;
    705 	if ( !mergedThreshold ) {
    706 		// if the thresholding is performed independently, you can more easily move
    707 		// or look in a pure axial direction without drifting
    708 		for ( int i = 0 ; i < 2 ; i++ ) {
    709 			const float v = raw[i];
    710 			float t;
    711 			if ( v > 0.0f ) {
    712 				t = Max( 0.0f, v - threshold );
    713 			} else {
    714 				t = Min( 0.0f, v + threshold );
    715 			}
    716 			threshed[i] = t;
    717 		}
    718 	} else {
    719 		// thresholding together is the most predictable in free-form movement,
    720 		// but you tend to slide off axis based on which side your thumb is
    721 		// on the pad
    722 		const float	rawLength = raw.Length();
    723 		const float	afterThreshold = Max( 0.0f, rawLength - threshold );
    724 
    725 		idVec2 rawDir = raw;
    726 		rawDir.Normalize();
    727 
    728 		threshed = rawDir * afterThreshold;
    729 	}
    730 
    731 	// threshold and range reduce the range of raw values, but we
    732 	// scale them back up to the full 0.0 - 1.0 range
    733 	const float rangeScale = 1.0f / ( range - threshold );
    734 	idVec2 reScaled = threshed * rangeScale;
    735 
    736 	const float rescaledLen = reScaled.Length();
    737 
    738 	// if inside the deadband area, return a solid 0,0
    739 	if ( rescaledLen <= 0.0f ) {
    740 		return idVec2( 0.0f, 0.0f );
    741 	}
    742 
    743 	reScaled.Normalize();
    744 
    745 	// apply the acceleration
    746 	float accelerated;
    747 	
    748 	if ( shape == FUNC_EXPONENTIAL ) {
    749 		accelerated = idMath::Pow( 1.04712854805f, rescaledLen * 100.0f ) * 0.01f;
    750 	} else if ( shape == FUNC_LOGARITHMIC ) {
    751 		const float power = 2.0f;
    752 		accelerated = idMath::Pow( rescaledLen, power );
    753 	} else {	// FUNC_LINEAR
    754 		accelerated = rescaledLen;
    755 	}
    756 
    757 	// optionally slow down for aim-assist
    758 	const float aimAssisted = accelerated * aimAssistScale;
    759 
    760 	const float clamped = ( aimAssisted > 1.0f ) ? 1.0f : aimAssisted;
    761 
    762 	return reScaled * clamped;
    763 }
    764 
    765 /*
    766 =================
    767 DrawJoypadTexture
    768 
    769 Draws axis and threshold / range rings into an RGBA image
    770 =================
    771 */
    772 void	DrawJoypadTexture(
    773 	const int	size,
    774 	byte	image[],
    775 
    776 	const idVec2 raw,
    777 
    778 	const float threshold,
    779 	const float range,
    780 	const transferFunction_t shape,
    781 	const bool	mergedThreshold ) {
    782 
    783 //	assert( raw.x >= -1.0f && raw.x <= 1.0f && raw.y >= -1.0f && raw.y <= 1.0f );
    784 	idVec2	clamped;
    785 	for ( int i = 0 ; i < 2 ; i++ ) {
    786 		clamped[i] = Max( -1.0f, Min( raw[i], 1.0f ) );
    787 	}
    788 
    789 	const int halfSize = size/2;
    790 
    791 	// find the offsets that will give certain values for
    792 	// the rings
    793 	static const int NUM_RINGS = 5;
    794 	float	ringSizes[NUM_RINGS] = {};
    795 	float	ringValue[NUM_RINGS] = { 0.0f, 0.25f, 0.5f, 0.75f, 0.99f };
    796 	int		ringNum = 0;
    797 	for ( int i = 1 ; i < size ; i++ ) {
    798 		const float	v = (float)i / (size-1);
    799 
    800 		const idVec2 mapped = JoypadFunction(
    801 			idVec2( v, 0.0f ), 1.0f, threshold, range, shape, mergedThreshold );
    802 		if ( mapped.x > ringValue[ ringNum ] ) {
    803 			ringSizes[ ringNum ] = v * halfSize;
    804 			ringNum++;
    805 			if ( ringNum == NUM_RINGS ) {
    806 				break;
    807 			}
    808 		}
    809 	}
    810 
    811 	memset( image, 0, size * size * 4 );
    812 #define PLOT(x,y) ((int *)image)[(int)(y)*size+(int)(x)]=0xffffffff
    813 #define CPLOT(x,y) ((int *)image)[(int)(halfSize+y)*size+(int)(halfSize+x)]=0xffffffff
    814 
    815 	int	clampedX = halfSize + Min( halfSize-1, (int)(halfSize * clamped.x) );
    816 	int	clampedY = halfSize + Min( halfSize-1, (int)(halfSize * clamped.y) );
    817 
    818 	// draw the box edge outline and center lines
    819 	for ( int i = 0 ; i < size ; i++ ) {
    820 		PLOT( i, 0 );
    821 		PLOT( i, size-1 );
    822 		PLOT( 0, i );
    823 		PLOT( size-1, i );
    824 		PLOT( i, clampedY );
    825 		PLOT( clampedX, i );
    826 	}
    827 	const int iThresh = size * threshold * 0.5f;
    828 	if ( !mergedThreshold ) {
    829 		const int open = size * 0.5f - iThresh;
    830 		for ( int i = 0 ; i < open ; i++ ) {
    831 			PLOT( i, halfSize - iThresh );
    832 			PLOT( i, halfSize + iThresh );
    833 			PLOT( size-1-i, halfSize - iThresh );
    834 			PLOT( size-1-i, halfSize + iThresh );
    835 
    836 			PLOT( halfSize - iThresh, i );
    837 			PLOT( halfSize + iThresh, i );
    838 			PLOT( halfSize - iThresh, size-1-i );
    839 			PLOT( halfSize + iThresh, size-1-i );
    840 		}
    841 	}
    842 
    843 	// I'm not going to bother writing a proper circle drawing algorithm...
    844 	const int octantPoints = size * 2;
    845 	float rad = 0.0f;
    846 	float radStep = idMath::PI / ( 4 * octantPoints );
    847 	for ( int point = 0 ; point < octantPoints ; point++, rad += radStep ) {
    848 		float	s, c;
    849 		idMath::SinCos( rad, s, c );
    850 		for ( int ringNum = 0 ; ringNum < NUM_RINGS ; ringNum++ ) {
    851 			const float ringSize = ringSizes[ ringNum ];
    852 			const int	ix = idMath::Floor( ringSize * c );
    853 			const int	iy = idMath::Floor( ringSize * s );
    854 #if 0
    855 			if ( !mergedThreshold && ( ix < iThresh || iy < iThresh ) ) {
    856 				continue;
    857 			}
    858 #endif
    859 			CPLOT( ix, iy );
    860 			CPLOT( iy, ix );
    861 			CPLOT( -ix, iy );
    862 			CPLOT( -iy, ix );
    863 			CPLOT( ix, -iy );
    864 			CPLOT( iy, -ix );
    865 			CPLOT( -ix, -iy );
    866 			CPLOT( -iy, -ix );
    867 		}
    868 	}
    869 
    870 #undef PLOT
    871 }
    872 
    873 static idVec2	lastLookJoypad;
    874 
    875 /*
    876 =================
    877 DrawJoypadTexture
    878 
    879 Can be called to fill in a scratch texture for visualization
    880 =================
    881 */
    882 void DrawJoypadTexture( const int size, byte image[] ) {
    883 	const float threshold =			joy_deadZone.GetFloat();
    884 	const float range =				joy_range.GetFloat();
    885 	const bool mergedThreshold =	joy_mergedThreshold.GetBool();
    886 	const transferFunction_t shape =(transferFunction_t)joy_gammaLook.GetInteger();
    887 
    888 	DrawJoypadTexture( size, image, lastLookJoypad, threshold, range, shape, mergedThreshold );
    889 }
    890 
    891 
    892 /*
    893 =================
    894 idUsercmdGenLocal::JoystickMove2
    895 =================
    896 */
    897 void idUsercmdGenLocal::JoystickMove2() {
    898 	const bool invertLook =			in_invertLook.GetBool();
    899 	const float threshold =			joy_deadZone.GetFloat();
    900 	const float range =				joy_range.GetFloat();
    901 	const transferFunction_t shape =(transferFunction_t)joy_gammaLook.GetInteger();
    902 	const bool mergedThreshold =	joy_mergedThreshold.GetBool();
    903 	const float pitchSpeed =		joy_pitchSpeed.GetFloat();
    904 	const float yawSpeed =			joy_yawSpeed.GetFloat();
    905 
    906 	idGame * game = common->Game();
    907 	const float aimAssist = game != NULL ? game->GetAimAssistSensitivity() : 1.0f;
    908 
    909 	idVec2 leftRaw( joystickAxis[ AXIS_LEFT_X ], joystickAxis[ AXIS_LEFT_Y ] );
    910 	idVec2 rightRaw( joystickAxis[ AXIS_RIGHT_X ], joystickAxis[ AXIS_RIGHT_Y ] );
    911 
    912 	// optional stick swap
    913 	if ( idKeyInput::GetUsercmdAction( K_JOY_STICK1_LEFT ) == UB_LOOKLEFT ) {
    914 		const idVec2	temp = leftRaw;
    915 		leftRaw = rightRaw;
    916 		rightRaw = temp;
    917 	}
    918 
    919 	// optional invert look by inverting the right Y axis
    920 	if ( invertLook ) {
    921 		rightRaw.y = -rightRaw.y;
    922 	}
    923 
    924 	// save for visualization
    925 	lastLookJoypad = rightRaw;
    926 
    927 	idVec2 leftMapped = JoypadFunction( leftRaw, 1.0f, threshold, range, shape, mergedThreshold );
    928 	idVec2 rightMapped = JoypadFunction( rightRaw, aimAssist, threshold, range, shape, mergedThreshold );
    929 
    930 	// because idPhysics_Player::CmdScale scales mvoement values down so that 1,1 = sqrt(2), sqrt(2),
    931 	// we need to expand our circular values out to a square
    932 	CircleToSquare( leftMapped.x, leftMapped.y );
    933 
    934 	// add on top of mouse / keyboard move values
    935 	cmd.forwardmove = idMath::ClampChar( cmd.forwardmove + KEY_MOVESPEED * -leftMapped.y );
    936 	cmd.rightmove = idMath::ClampChar( cmd.rightmove + KEY_MOVESPEED * leftMapped.x );
    937 
    938 	viewangles[PITCH] += MS2SEC( pollTime - lastPollTime ) * rightMapped.y * pitchSpeed;
    939 	viewangles[YAW] += MS2SEC( pollTime - lastPollTime ) * -rightMapped.x * yawSpeed;
    940 
    941 	const float triggerThreshold = joy_triggerThreshold.GetFloat();
    942 	HandleJoystickAxis( K_JOY_TRIGGER1, joystickAxis[ AXIS_LEFT_TRIG ], triggerThreshold, true );
    943 	HandleJoystickAxis( K_JOY_TRIGGER2, joystickAxis[ AXIS_RIGHT_TRIG ], triggerThreshold, true );
    944 }
    945 
    946 /*
    947 ==============
    948 idUsercmdGenLocal::CmdButtons
    949 ==============
    950 */
    951 void idUsercmdGenLocal::CmdButtons() {
    952 	cmd.buttons = 0;
    953 
    954 	// check the attack button
    955 	if ( ButtonState( UB_ATTACK ) ) {
    956 		cmd.buttons |= BUTTON_ATTACK;
    957 	}
    958 
    959 	// check the use button
    960 	if ( ButtonState( UB_USE ) ) {
    961 		cmd.buttons |= BUTTON_USE;
    962 	}
    963 
    964 	// check the run button
    965 	if ( toggled_run.on || ( in_alwaysRun.GetBool() && common->IsMultiplayer() ) ) {
    966 		cmd.buttons |= BUTTON_RUN;
    967 	}
    968 
    969 	// check the zoom button
    970 	if ( toggled_zoom.on ) {
    971 		cmd.buttons |= BUTTON_ZOOM;
    972 	}
    973 
    974 	if ( ButtonState( UB_MOVEUP ) ) {
    975 		cmd.buttons |= BUTTON_JUMP;
    976 	}
    977 	if ( toggled_crouch.on ) {
    978 		cmd.buttons |= BUTTON_CROUCH;
    979 	}
    980 }
    981 
    982 /*
    983 ================
    984 idUsercmdGenLocal::InitCurrent
    985 
    986 inits the current command for this frame
    987 ================
    988 */
    989 void idUsercmdGenLocal::InitCurrent() {
    990 	memset( &cmd, 0, sizeof( cmd ) );
    991 	cmd.impulseSequence = impulseSequence;
    992 	cmd.impulse = impulse;
    993 	cmd.buttons |= ( in_alwaysRun.GetBool() && common->IsMultiplayer() ) ? BUTTON_RUN : 0;
    994 }
    995 
    996 /*
    997 ================
    998 idUsercmdGenLocal::MakeCurrent
    999 
   1000 creates the current command for this frame
   1001 ================
   1002 */
   1003 void idUsercmdGenLocal::MakeCurrent() {
   1004 	idVec3 oldAngles = viewangles;
   1005 	
   1006 	if ( !Inhibited() ) {
   1007 		// update toggled key states
   1008 		toggled_crouch.SetKeyState( ButtonState( UB_MOVEDOWN ), in_toggleCrouch.GetBool() );
   1009 		toggled_run.SetKeyState( ButtonState( UB_SPEED ), in_toggleRun.GetBool() && common->IsMultiplayer() );
   1010 		toggled_zoom.SetKeyState( ButtonState( UB_ZOOM ), in_toggleZoom.GetBool() );
   1011 
   1012 		// get basic movement from mouse
   1013 		MouseMove();
   1014 
   1015 		// get basic movement from joystick and set key bits
   1016 		// must be done before CmdButtons!
   1017 		if ( joy_newCode.GetBool() ) {
   1018 			JoystickMove2();
   1019 		} else {
   1020 			JoystickMove();
   1021 		}
   1022 
   1023 		// keyboard angle adjustment
   1024 		AdjustAngles();
   1025 
   1026 		// set button bits
   1027 		CmdButtons();
   1028 
   1029 		// get basic movement from keyboard
   1030 		KeyMove();
   1031 
   1032 		// aim assist
   1033 		AimAssist();
   1034 
   1035 		// check to make sure the angles haven't wrapped
   1036 		if ( viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
   1037 			viewangles[PITCH] = oldAngles[PITCH] + 90;
   1038 		} else if ( oldAngles[PITCH] - viewangles[PITCH] > 90 ) {
   1039 			viewangles[PITCH] = oldAngles[PITCH] - 90;
   1040 		} 
   1041 	} else {
   1042 		mouseDx = 0;
   1043 		mouseDy = 0;
   1044 	}
   1045 
   1046 	for ( int i = 0; i < 3; i++ ) {
   1047 		cmd.angles[i] = ANGLE2SHORT( viewangles[i] );
   1048 	}
   1049 
   1050 	cmd.mx = continuousMouseX;
   1051 	cmd.my = continuousMouseY;
   1052 
   1053 	impulseSequence = cmd.impulseSequence;
   1054 	impulse = cmd.impulse;
   1055 
   1056 }
   1057 
   1058 /*
   1059 ================
   1060 idUsercmdGenLocal::AimAssist
   1061 ================
   1062 */
   1063 void idUsercmdGenLocal::AimAssist() {
   1064 	// callback to the game to update the aim assist for the current device
   1065 	idAngles aimAssistAngles( 0.0f, 0.0f, 0.0f );
   1066 
   1067 	idGame * game = common->Game();
   1068 	if ( game != NULL ) {
   1069 		game->GetAimAssistAngles( aimAssistAngles );
   1070 	}
   1071 
   1072 	viewangles[YAW] += aimAssistAngles.yaw;
   1073 	viewangles[PITCH] += aimAssistAngles.pitch;
   1074 	viewangles[ROLL] += aimAssistAngles.roll;
   1075 }
   1076 
   1077 //=====================================================================
   1078 
   1079 
   1080 /*
   1081 ================
   1082 idUsercmdGenLocal::CommandStringUsercmdData
   1083 
   1084 Returns the button if the command string is used by the usercmd generator.
   1085 ================
   1086 */
   1087 int	idUsercmdGenLocal::CommandStringUsercmdData( const char *cmdString ) {
   1088 	for ( userCmdString_t *ucs = userCmdStrings ; ucs->string ; ucs++ ) {
   1089 		if ( idStr::Icmp( cmdString, ucs->string ) == 0 ) {
   1090 			return ucs->button;
   1091 		}
   1092 	}
   1093 	return UB_NONE;
   1094 }
   1095 
   1096 /*
   1097 ================
   1098 idUsercmdGenLocal::Init
   1099 ================
   1100 */
   1101 void idUsercmdGenLocal::Init() {
   1102 	initialized = true;
   1103 }
   1104 
   1105 /*
   1106 ================
   1107 idUsercmdGenLocal::InitForNewMap
   1108 ================
   1109 */
   1110 void idUsercmdGenLocal::InitForNewMap() {
   1111 	impulseSequence = 0;
   1112 	impulse = 0;
   1113 
   1114 	toggled_crouch.Clear();
   1115 	toggled_run.Clear();
   1116 	toggled_zoom.Clear();
   1117 	toggled_run.on = false;
   1118 
   1119 	Clear();
   1120 	ClearAngles();
   1121 }
   1122 
   1123 /*
   1124 ================
   1125 idUsercmdGenLocal::Shutdown
   1126 ================
   1127 */
   1128 void idUsercmdGenLocal::Shutdown() {
   1129 	initialized = false;
   1130 }
   1131 
   1132 /*
   1133 ================
   1134 idUsercmdGenLocal::Clear
   1135 ================
   1136 */
   1137 void idUsercmdGenLocal::Clear() {
   1138 	// clears all key states 
   1139 	memset( buttonState, 0, sizeof( buttonState ) );
   1140 	memset( keyState, false, sizeof( keyState ) );
   1141 	memset( joystickAxis, 0, sizeof( joystickAxis ) );
   1142 
   1143 	inhibitCommands = false;
   1144 
   1145 	mouseDx = mouseDy = 0;
   1146 	mouseButton = 0;
   1147 	mouseDown = false;
   1148 }
   1149 
   1150 /*
   1151 ================
   1152 idUsercmdGenLocal::ClearAngles
   1153 ================
   1154 */
   1155 void idUsercmdGenLocal::ClearAngles() {
   1156 	viewangles.Zero();
   1157 }
   1158 
   1159 //======================================================================
   1160 
   1161 
   1162 /*
   1163 ===================
   1164 idUsercmdGenLocal::Key
   1165 
   1166 Handles mouse/keyboard button actions
   1167 ===================
   1168 */
   1169 void idUsercmdGenLocal::Key( int keyNum, bool down ) {
   1170 
   1171 	// Sanity check, sometimes we get double message :(
   1172 	if ( keyState[ keyNum ] == down ) {
   1173 		return;
   1174 	}
   1175 	keyState[ keyNum ] = down;
   1176 
   1177 	int action = idKeyInput::GetUsercmdAction( keyNum );
   1178 
   1179 	if ( down ) {
   1180 		buttonState[ action ]++;
   1181 		if ( !Inhibited()  ) {
   1182 			if ( action >= UB_IMPULSE0 && action <= UB_IMPULSE31 ) {
   1183 				cmd.impulse = action - UB_IMPULSE0;
   1184 				cmd.impulseSequence++;
   1185 			}
   1186 		}
   1187 	} else {
   1188 		buttonState[ action ]--;
   1189 		// we might have one held down across an app active transition
   1190 		if ( buttonState[ action ] < 0 ) {
   1191 			buttonState[ action ] = 0;
   1192 		}
   1193 	}
   1194 }
   1195 
   1196 /*
   1197 ===================
   1198 idUsercmdGenLocal::Mouse
   1199 ===================
   1200 */
   1201 void idUsercmdGenLocal::Mouse() {
   1202 	int	mouseEvents[MAX_MOUSE_EVENTS][2];
   1203 
   1204 	int numEvents = Sys_PollMouseInputEvents( mouseEvents );
   1205 
   1206 	// Study each of the buffer elements and process them.
   1207 	for ( int i = 0; i < numEvents; i++ ) {
   1208 		int action = mouseEvents[i][0];
   1209 		int value = mouseEvents[i][1];
   1210 		switch ( action ) {
   1211 		case M_ACTION1:
   1212 		case M_ACTION2:
   1213 		case M_ACTION3:
   1214 		case M_ACTION4:
   1215 		case M_ACTION5:
   1216 		case M_ACTION6:
   1217 		case M_ACTION7:
   1218 		case M_ACTION8:
   1219 			mouseButton = K_MOUSE1 + ( action - M_ACTION1 );
   1220 			mouseDown = ( value != 0 );
   1221 			Key( mouseButton, mouseDown );
   1222 			break;
   1223 		case M_DELTAX:
   1224 			mouseDx += value;
   1225 			continuousMouseX += value;
   1226 			break;
   1227 		case M_DELTAY:
   1228 			mouseDy += value;
   1229 			continuousMouseY += value;
   1230 			break;
   1231 		case M_DELTAZ:	// mouse wheel, may have multiple clicks
   1232 			{
   1233 				int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
   1234 				value = abs( value );
   1235 				while( value-- > 0 ) {
   1236 					Key( key, true );
   1237 					Key( key, false );
   1238 					mouseButton = key;
   1239 					mouseDown = true;
   1240 				}
   1241 			}
   1242 			break;
   1243 		default:	// some other undefined button
   1244 			break;
   1245 		}
   1246 	}
   1247 }
   1248 
   1249 /*
   1250 ===============
   1251 idUsercmdGenLocal::Keyboard
   1252 ===============
   1253 */
   1254 void idUsercmdGenLocal::Keyboard() {
   1255 
   1256 	int numEvents = Sys_PollKeyboardInputEvents();
   1257 
   1258     // Study each of the buffer elements and process them.
   1259 	for ( int i = 0; i < numEvents; i++ ) {
   1260 		int key;
   1261 		bool state;
   1262 		if ( Sys_ReturnKeyboardInputEvent( i, key, state ) ) {
   1263 			Key( key, state );
   1264 		}
   1265 	}
   1266 
   1267 	Sys_EndKeyboardInputEvents();
   1268 }
   1269 
   1270 /*
   1271 ===============
   1272 idUsercmdGenLocal::Joystick
   1273 ===============
   1274 */
   1275 void idUsercmdGenLocal::Joystick( int deviceNum ) {
   1276 	int numEvents = Sys_PollJoystickInputEvents( deviceNum );
   1277 
   1278 	// Study each of the buffer elements and process them.
   1279 	for ( int i = 0; i < numEvents; i++ ) {
   1280 		int action;
   1281 		int value;
   1282 		if ( Sys_ReturnJoystickInputEvent( i, action, value ) ) {
   1283 			if ( action >= J_ACTION1 && action <= J_ACTION_MAX ) {
   1284 				int joyButton = K_JOY1 + ( action - J_ACTION1 );
   1285 				Key( joyButton, ( value != 0 ) );
   1286 			} else if ( ( action >= J_AXIS_MIN ) && ( action <= J_AXIS_MAX ) ) {
   1287 				joystickAxis[ action - J_AXIS_MIN ] = static_cast<float>( value ) / 32767.0f;
   1288 			} else if ( action >= J_DPAD_UP && action <= J_DPAD_RIGHT ) {
   1289 				int joyButton = K_JOY_DPAD_UP + ( action - J_DPAD_UP );
   1290 				Key( joyButton, ( value != 0 ) );
   1291 			} else {
   1292 				assert( !"Unknown joystick event" );
   1293 			}
   1294 		}
   1295 	}
   1296 
   1297 	Sys_EndJoystickInputEvents();
   1298 }
   1299 
   1300 /*
   1301 ================
   1302 idUsercmdGenLocal::MouseState
   1303 ================
   1304 */
   1305 void idUsercmdGenLocal::MouseState( int *x, int *y, int *button, bool *down ) {
   1306 	*x = continuousMouseX;
   1307 	*y = continuousMouseY;
   1308 	*button = mouseButton;
   1309 	*down = mouseDown;
   1310 }
   1311 
   1312 /*
   1313 ================
   1314 idUsercmdGenLocal::BuildCurrentUsercmd
   1315 ================
   1316 */
   1317 void idUsercmdGenLocal::BuildCurrentUsercmd( int deviceNum ) {
   1318 
   1319 	pollTime = Sys_Milliseconds();
   1320 	if ( pollTime - lastPollTime > 100 ) {
   1321 		lastPollTime = pollTime - 100;
   1322 	}
   1323 
   1324 	// initialize current usercmd
   1325 	InitCurrent();
   1326 
   1327 	// process the system mouse events
   1328 	Mouse();
   1329 
   1330 	// process the system keyboard events
   1331 	Keyboard();
   1332 
   1333 	// process the system joystick events
   1334 	if ( deviceNum >= 0 && in_useJoystick.GetBool() ) {
   1335 		Joystick( deviceNum );
   1336 	}
   1337 
   1338 	// create the usercmd
   1339 	MakeCurrent();
   1340 
   1341 	lastPollTime = pollTime;
   1342 }