UsercmdGen.cpp 37 KB

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