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 }