1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // cl_input.c
22 // Builds an intended movement command to send to the server
23 //
24 
25 #include "cl_local.h"
26 
27 cVar_t	*cl_nodelta;
28 
29 cVar_t	*cl_upspeed;
30 cVar_t	*cl_forwardspeed;
31 cVar_t	*cl_sidespeed;
32 
33 cVar_t	*cl_yawspeed;
34 cVar_t	*cl_pitchspeed;
35 
36 static cVar_t	*autosensitivity;
37 static cVar_t	*cl_anglespeedkey;
38 static cVar_t	*cl_run;
39 static cVar_t	*m_filter;
40 
41 static uint32	in_frameTime;
42 static uint32	in_lastFrameTime;
43 static uint32	in_frameMSec;
44 
45 static ivec2_t	in_mouseMove;
46 static ivec2_t	in_lastMouseMove;
47 static qBool	in_mLooking;
48 
49 /*
50 ============
51 IN_CenterView_f
52 ============
53 */
IN_CenterView_f(void)54 static void IN_CenterView_f (void)
55 {
56 	cl.viewAngles[PITCH] = -SHORT2ANGLE (cl.frame.playerState.pMove.deltaAngles[PITCH]);
57 }
58 
59 /*
60 =============================================================================
61 
62 	KEY BUTTONS
63 
64 	Continuous button event tracking is complicated by the fact that two
65 	different input sources (say, mouse button 1 and the control key) can both
66 	press the same button, but the button should only be released when both of
67 	the pressing key have been released.
68 
69 	When a key event issues a button command (+forward, +attack, etc), it
70 	appends its key number as a parameter to the command so it can be matched
71 	up with the release.
72 
73 	state bit 0 is the current state of the key
74 	state bit 1 is edge triggered on the up to down transition
75 	state bit 2 is edge triggered on the down to up transition
76 
77 =============================================================================
78 */
79 
80 typedef struct kButton_s {
81 	int			down[2];		// key nums holding it down
82 	uint32		downTime;		// msec timestamp
83 	uint32		msec;			// msec down this frame
84 	int			state;
85 } kButton_t;
86 
87 static kButton_t		btn_moveUp;
88 static kButton_t		btn_moveDown;
89 static kButton_t		btn_lookLeft;
90 static kButton_t		btn_lookRight;
91 static kButton_t		btn_moveForward;
92 static kButton_t		btn_moveBack;
93 static kButton_t		btn_lookUp;
94 static kButton_t		btn_lookDown;
95 static kButton_t		btn_moveLeft;
96 static kButton_t		btn_moveRight;
97 
98 static kButton_t		btn_speed;
99 static kButton_t		btn_strafe;
100 
101 static kButton_t		btn_attack;
102 static kButton_t		btn_use;
103 
104 static kButton_t		btn_keyLook;
105 
106 static int				btn_impulse;
107 
108 /*
109 ====================
110 CL_KeyDown
111 ====================
112 */
CL_KeyDown(kButton_t * b)113 static void CL_KeyDown (kButton_t *b)
114 {
115 	int		k;
116 	char	*c;
117 
118 	c = Cmd_Argv (1);
119 	if (c[0])
120 		k = atoi (c);
121 	else
122 		k = -1;	// Typed manually at the console for continuous down
123 
124 	if (k == b->down[0] || k == b->down[1])
125 		return;	// Repeating key
126 
127 	if (!b->down[0])
128 		b->down[0] = k;
129 	else if (!b->down[1])
130 		b->down[1] = k;
131 	else {
132 		Com_Printf (PRNT_WARNING, "Three keys down for a button!\n");
133 		return;
134 	}
135 
136 	if (b->state & 1)
137 		return;	// still down
138 
139 	// Save timestamp
140 	c = Cmd_Argv (2);
141 	b->downTime = atoi(c);
142 	if (!b->downTime)
143 		b->downTime = in_frameTime - 100;
144 
145 	b->state |= 1 + 2;	// down + impulse down
146 }
147 
148 
149 /*
150 ====================
151 CL_KeyUp
152 ====================
153 */
CL_KeyUp(kButton_t * b)154 static void CL_KeyUp (kButton_t *b)
155 {
156 	int		k;
157 	char	*c;
158 	uint32	uptime;
159 
160 	c = Cmd_Argv (1);
161 	if (c[0])
162 		k = atoi(c);
163 	else {
164 		// Typed manually at the console, assume for unsticking, so clear all
165 		b->down[0] = b->down[1] = 0;
166 		b->state = 4;	// Impulse up
167 		return;
168 	}
169 
170 	if (b->down[0] == k)
171 		b->down[0] = 0;
172 	else if (b->down[1] == k)
173 		b->down[1] = 0;
174 	else
175 		return;		// Key up without coresponding down (menu pass through)
176 	if (b->down[0] || b->down[1])
177 		return;		// Some other key is still holding it down
178 
179 	if (!(b->state & 1))
180 		return;		// Still up (this should not happen)
181 
182 	// Save timestamp
183 	c = Cmd_Argv (2);
184 	uptime = atoi(c);
185 	if (uptime)
186 		b->msec += uptime - b->downTime;
187 	else
188 		b->msec += 10;
189 
190 	b->state &= ~1;		// Now up
191 	b->state |= 4;		// Impulse up
192 }
193 
194 
195 /*
196 ===============
197 CL_KeyState
198 
199 Returns the fraction of the frame that the key was down
200 ===============
201 */
CL_KeyState(kButton_t * key)202 static float CL_KeyState (kButton_t *key)
203 {
204 	int			msec;
205 
206 	key->state &= 1;		// clear impulses
207 
208 	msec = key->msec;
209 	key->msec = 0;
210 
211 	if (key->state) {
212 		// Still down
213 		msec += in_frameTime - key->downTime;
214 		key->downTime = in_frameTime;
215 	}
216 
217 	return clamp ((float)msec / (float)in_frameMSec, 0, 1);
218 }
219 
220 
IN_UpDown_f(void)221 static void IN_UpDown_f (void)			{ CL_KeyDown (&btn_moveUp); }
IN_UpUp_f(void)222 static void IN_UpUp_f (void)			{ CL_KeyUp (&btn_moveUp); }
IN_DownDown_f(void)223 static void IN_DownDown_f (void)		{ CL_KeyDown (&btn_moveDown); }
IN_DownUp_f(void)224 static void IN_DownUp_f (void)			{ CL_KeyUp (&btn_moveDown); }
IN_LeftDown_f(void)225 static void IN_LeftDown_f (void)		{ CL_KeyDown (&btn_lookLeft); }
IN_LeftUp_f(void)226 static void IN_LeftUp_f (void)			{ CL_KeyUp (&btn_lookLeft); }
IN_RightDown_f(void)227 static void IN_RightDown_f (void)		{ CL_KeyDown (&btn_lookRight); }
IN_RightUp_f(void)228 static void IN_RightUp_f (void)			{ CL_KeyUp (&btn_lookRight); }
IN_ForwardDown_f(void)229 static void IN_ForwardDown_f (void)		{ CL_KeyDown (&btn_moveForward); }
IN_ForwardUp_f(void)230 static void IN_ForwardUp_f (void)		{ CL_KeyUp (&btn_moveForward); }
IN_BackDown_f(void)231 static void IN_BackDown_f (void)		{ CL_KeyDown (&btn_moveBack); }
IN_BackUp_f(void)232 static void IN_BackUp_f (void)			{ CL_KeyUp (&btn_moveBack); }
IN_LookupDown_f(void)233 static void IN_LookupDown_f (void)		{ CL_KeyDown (&btn_lookUp); }
IN_LookupUp_f(void)234 static void IN_LookupUp_f (void)		{ CL_KeyUp (&btn_lookUp); }
IN_LookdownDown_f(void)235 static void IN_LookdownDown_f (void)	{ CL_KeyDown (&btn_lookDown); }
IN_LookdownUp_f(void)236 static void IN_LookdownUp_f (void)		{ CL_KeyUp (&btn_lookDown); }
237 
238 // FIXME: Treat like the other keys here?
239 // Only like this because it was done like this in win32 input...
IN_MLookDown_f(void)240 static void IN_MLookDown_f (void)
241 {
242 	in_mLooking = qTrue;
243 }
IN_MLookUp_f(void)244 static void IN_MLookUp_f (void)
245 {
246 	in_mLooking = qFalse;
247 	if (!freelook->intVal && lookspring->intVal)
248 		IN_CenterView_f ();
249 }
250 
IN_MoveleftDown_f(void)251 static void IN_MoveleftDown_f (void)	{ CL_KeyDown (&btn_moveLeft); }
IN_MoveleftUp_f(void)252 static void IN_MoveleftUp_f (void)		{ CL_KeyUp (&btn_moveLeft); }
IN_MoverightDown_f(void)253 static void IN_MoverightDown_f (void)	{ CL_KeyDown (&btn_moveRight); }
IN_MoverightUp_f(void)254 static void IN_MoverightUp_f (void)		{ CL_KeyUp (&btn_moveRight); }
255 
IN_SpeedDown_f(void)256 static void IN_SpeedDown_f (void)		{ CL_KeyDown (&btn_speed); }
IN_SpeedUp_f(void)257 static void IN_SpeedUp_f (void)			{ CL_KeyUp (&btn_speed); }
IN_StrafeDown_f(void)258 static void IN_StrafeDown_f (void)		{ CL_KeyDown (&btn_strafe); }
IN_StrafeUp_f(void)259 static void IN_StrafeUp_f (void)		{ CL_KeyUp (&btn_strafe); }
260 
IN_AttackDown_f(void)261 static void IN_AttackDown_f (void)		{ CL_KeyDown (&btn_attack); }
IN_AttackUp_f(void)262 static void IN_AttackUp_f (void)		{ CL_KeyUp (&btn_attack); }
263 
IN_UseDown_f(void)264 static void IN_UseDown_f (void)			{ CL_KeyDown (&btn_use); }
IN_UseUp_f(void)265 static void IN_UseUp_f (void)			{ CL_KeyUp (&btn_use); }
266 
IN_Impulse_f(void)267 static void IN_Impulse_f (void)			{ btn_impulse=atoi (Cmd_Argv (1)); }
268 
IN_KLookDown_f(void)269 static void IN_KLookDown_f (void)		{ CL_KeyDown (&btn_keyLook); }
IN_KLookUp_f(void)270 static void IN_KLookUp_f (void)			{ CL_KeyUp (&btn_keyLook); }
271 
272 //==========================================================================
273 
274 /*
275 ================
276 CL_GetRunState
277 
278 For Win32 joystick input.
279 ================
280 */
CL_GetRunState(void)281 qBool CL_GetRunState (void)
282 {
283 	return ((btn_speed.state & 1) ^ cl_run->intVal);
284 }
285 
286 
287 /*
288 ================
289 CL_GetStrafeState
290 
291 For Win32 joystick input.
292 ================
293 */
CL_GetStrafeState(void)294 qBool CL_GetStrafeState (void)
295 {
296 	return (btn_strafe.state & 1);
297 }
298 
299 
300 /*
301 ================
302 CL_GetMLookState
303 
304 For Win32 joystick input.
305 ================
306 */
CL_GetMLookState(void)307 qBool CL_GetMLookState (void)
308 {
309 	return in_mLooking;
310 }
311 
312 /*
313 =============================================================================
314 
315 	MOUSE MOVEMENT
316 
317 	This takes mouse moves from the operating system and queues up the moves
318 	to later factor into the transmitted move command.
319 
320 =============================================================================
321 */
322 
323 /*
324 ================
325 CL_MoveMouse
326 
327 Queue cursor movement.
328 ================
329 */
CL_MoveMouse(int xMove,int yMove)330 void CL_MoveMouse (int xMove, int yMove)
331 {
332 	// Update GUI
333 	if (Key_GetDest () == KD_MENU) {
334 		CL_CGModule_MoveMouse (xMove, yMove);
335 		GUI_MoveMouse (xMove, yMove);
336 
337 		Vec2Clear (in_mouseMove);
338 		Vec2Clear (in_lastMouseMove);
339 		return;
340 	}
341 
342 	// Queue movement
343 	in_mouseMove[0] += xMove;
344 	in_mouseMove[1] += yMove;
345 }
346 
347 /*
348 =============================================================================
349 
350 	MOVE USER COMMAND
351 
352 =============================================================================
353 */
354 
355 /*
356 ================
357 CL_BaseMove
358 
359 Send the intended movement message to the server
360 ================
361 */
CL_BaseMove(userCmd_t * cmd)362 static void CL_BaseMove (userCmd_t *cmd)
363 {
364 	float	turnSpeed;
365 	float	moveSpeed;
366 
367 	// Adjust for turning speed
368 	if (btn_speed.state & 1)
369 		turnSpeed = cls.netFrameTime * cl_anglespeedkey->floatVal;
370 	else
371 		turnSpeed = cls.netFrameTime;
372 
373 	// Adjust for running speed
374 	if (CL_GetRunState ())
375 		moveSpeed = 2;
376 	else
377 		moveSpeed = 1;
378 
379 	// Handle left/right on keyboard
380 	cl.cmdNum = cls.netChan.outgoingSequence & CMD_MASK;
381 	cmd = &cl.cmds[cl.cmdNum];
382 	cl.cmdTime[cl.cmdNum] = cls.realTime;	// for netgraph ping calculation
383 
384 	if (CL_GetStrafeState ()) {
385 		// Keyboard strafe
386 		cmd->sideMove += cl_sidespeed->floatVal * CL_KeyState (&btn_lookRight);
387 		cmd->sideMove -= cl_sidespeed->floatVal * CL_KeyState (&btn_lookLeft);
388 	}
389 	else {
390 		// Keyboard turn
391 		cl.viewAngles[YAW] -= turnSpeed * cl_yawspeed->floatVal * CL_KeyState (&btn_lookRight);
392 		cl.viewAngles[YAW] += turnSpeed * cl_yawspeed->floatVal * CL_KeyState (&btn_lookLeft);
393 	}
394 
395 	if (btn_keyLook.state & 1) {
396 		// Keyboard look
397 		cl.viewAngles[PITCH] -= turnSpeed * cl_pitchspeed->floatVal * CL_KeyState (&btn_moveForward);
398 		cl.viewAngles[PITCH] += turnSpeed * cl_pitchspeed->floatVal * CL_KeyState (&btn_moveBack);
399 	}
400 	else {
401 		// Keyboard move front/back
402 		cmd->forwardMove += cl_forwardspeed->floatVal * CL_KeyState (&btn_moveForward);
403 		cmd->forwardMove -= cl_forwardspeed->floatVal * CL_KeyState (&btn_moveBack);
404 	}
405 
406 	// Keyboard look up/down
407 	cl.viewAngles[PITCH] -= turnSpeed * cl_pitchspeed->floatVal * CL_KeyState (&btn_lookUp);
408 	cl.viewAngles[PITCH] += turnSpeed * cl_pitchspeed->floatVal * CL_KeyState (&btn_lookDown);
409 
410 	// Keyboard strafe left/right
411 	cmd->sideMove += cl_sidespeed->floatVal * CL_KeyState (&btn_moveRight);
412 	cmd->sideMove -= cl_sidespeed->floatVal * CL_KeyState (&btn_moveLeft);
413 
414 	// Keyboard jump/crouch
415 	cmd->upMove += cl_upspeed->floatVal * CL_KeyState (&btn_moveUp);
416 	cmd->upMove -= cl_upspeed->floatVal * CL_KeyState (&btn_moveDown);
417 
418 	// Cap to max allowed ranges
419 	if (cmd->forwardMove > cl_forwardspeed->floatVal * moveSpeed)
420 		cmd->forwardMove = cl_forwardspeed->floatVal * moveSpeed;
421 	else if (cmd->forwardMove < -cl_forwardspeed->floatVal * moveSpeed)
422 		cmd->forwardMove = -cl_forwardspeed->floatVal * moveSpeed;
423 
424 	if (cmd->sideMove > cl_sidespeed->floatVal * moveSpeed)
425 		cmd->sideMove = cl_sidespeed->floatVal * moveSpeed;
426 	else if (cmd->sideMove < -cl_sidespeed->floatVal * moveSpeed)
427 		cmd->sideMove = -cl_sidespeed->floatVal * moveSpeed;
428 
429 	if (cmd->upMove > cl_upspeed->floatVal * moveSpeed)
430 		cmd->upMove = cl_upspeed->floatVal * moveSpeed;
431 	else if (cmd->upMove < -cl_upspeed->floatVal * moveSpeed)
432 		cmd->upMove = -cl_upspeed->floatVal * moveSpeed;
433 }
434 
435 
436 /*
437 ================
438 CL_MouseMove
439 
440 Add mouse X/Y movement to cmd
441 ================
442 */
CL_MouseMove(userCmd_t * cmd)443 static void CL_MouseMove (userCmd_t *cmd)
444 {
445 	ivec2_t			move;
446 
447 	// Movement filtering
448 	if (m_filter->intVal) {
449 		move[0] = (in_mouseMove[0] + in_lastMouseMove[0]) * 0.5f;
450 		move[1] = (in_mouseMove[1] + in_lastMouseMove[1]) * 0.5f;
451 	}
452 	else {
453 		move[0] = in_mouseMove[0];
454 		move[1] = in_mouseMove[1];
455 	}
456 	in_lastMouseMove[0] = in_mouseMove[0];
457 	in_lastMouseMove[1] = in_mouseMove[1];
458 
459 	// Zooming in preserves sensitivity
460 	if (autosensitivity->intVal) {
461 		move[0] *= sensitivity->floatVal * (cl.refDef.fovX/90.0f);
462 		move[1] *= sensitivity->floatVal * (cl.refDef.fovY/90.0f);
463 	}
464 	else {
465 		move[0] *= sensitivity->floatVal;
466 		move[1] *= sensitivity->floatVal;
467 	}
468 
469 	// Side/yaw movement
470 	if (CL_GetStrafeState () || (lookstrafe->intVal && in_mLooking))
471 		cmd->sideMove += m_side->floatVal * move[0];
472 	else
473 		cl.viewAngles[YAW] -= m_yaw->floatVal * move[0];
474 
475 	// Forward/pitch movement
476 	if (!CL_GetStrafeState () && (freelook->intVal || in_mLooking))
477 		cl.viewAngles[PITCH] += m_pitch->floatVal * move[1];
478 	else
479 		cmd->forwardMove -= m_forward->floatVal * move[1];
480 
481 	// Clear
482 	Vec2Clear (in_mouseMove);
483 }
484 
485 
486 /*
487 ==============
488 CL_ClampPitch
489 ==============
490 */
CL_ClampPitch(void)491 static void CL_ClampPitch (void)
492 {
493 	float	pitch;
494 
495 	pitch = SHORT2ANGLE (cl.frame.playerState.pMove.deltaAngles[PITCH]);
496 	if (pitch > 180)
497 		pitch -= 360;
498 
499 	if (cl.viewAngles[PITCH] + pitch < -360)
500 		cl.viewAngles[PITCH] += 360; // wrapped
501 	if (cl.viewAngles[PITCH] + pitch > 360)
502 		cl.viewAngles[PITCH] -= 360; // wrapped
503 
504 	if (cl.viewAngles[PITCH] + pitch > 89)
505 		cl.viewAngles[PITCH] = 89 - pitch;
506 	if (cl.viewAngles[PITCH] + pitch < -89)
507 		cl.viewAngles[PITCH] = -89 - pitch;
508 }
509 
510 
511 /*
512 =================
513 CL_RefreshCmd
514 =================
515 */
CL_RefreshCmd(void)516 void CL_RefreshCmd (void)
517 {
518 	int			ms;
519 	userCmd_t	*cmd = &cl.cmds[cls.netChan.outgoingSequence & CMD_MASK];
520 
521 	// Get delta for this sample.
522 	in_frameMSec = in_frameTime - in_lastFrameTime;
523 	if (in_frameMSec < 1)
524 		return;
525 	else if (in_frameMSec > 200)
526 		in_frameMSec = 200;
527 
528 	// Get basic movement from keyboard
529 	CL_BaseMove (cmd);
530 
531 	// Allow mice or other external controllers to add to the move
532 	IN_Move (cmd);
533 
534 	// Add mouse movement
535 	CL_MouseMove (cmd);
536 
537 	// Update cmd viewangles for CL_PredictMove
538 	CL_ClampPitch ();
539 
540 	// Transmit data
541 	cmd->angles[0] = ANGLE2SHORT(cl.viewAngles[0]);
542 	cmd->angles[1] = ANGLE2SHORT(cl.viewAngles[1]);
543 	cmd->angles[2] = ANGLE2SHORT(cl.viewAngles[2]);
544 
545 	// Update cmd->msec for CL_PredictMove
546 	ms = (int)(cls.netFrameTime * 1000);
547 	if (ms > 250)
548 		ms = 100;
549 	cmd->msec = ms;
550 
551 	// Update counter
552 	in_lastFrameTime = in_frameTime;
553 
554 	// Send packet immediately on important events
555 	if (btn_attack.state & 2 || btn_use.state & 2)
556 		cls.forcePacket = qTrue;
557 }
558 
559 
560 /*
561 =================
562 CL_FinalizeCmd
563 =================
564 */
CL_FinalizeCmd(void)565 static void CL_FinalizeCmd (void)
566 {
567 	userCmd_t *cmd = &cl.cmds[cls.netChan.outgoingSequence & CMD_MASK];
568 
569 	// Set any button hits that occured since last frame
570 	if (btn_attack.state & 3)
571 		cmd->buttons |= BUTTON_ATTACK;
572 	btn_attack.state &= ~2;
573 
574 	if (btn_use.state & 3)
575 		cmd->buttons |= BUTTON_USE;
576 	btn_use.state &= ~2;
577 
578 	if (key_anyKeyDown && Key_GetDest () == KD_GAME)
579 		cmd->buttons |= BUTTON_ANY;
580 
581 	// ...
582 	cmd->impulse = btn_impulse;
583 	btn_impulse = 0;
584 
585 	// Set the ambient light level at the player's current position
586 	cmd->lightLevel = (byte)cl_lightlevel->floatVal;
587 }
588 
589 
590 /*
591 =================
592 CL_SendCmd
593 =================
594 */
CL_SendCmd(void)595 void CL_SendCmd (void)
596 {
597 	byte		data[128];
598 	netMsg_t	buf;
599 	userCmd_t	*cmd, *oldCmd;
600 	userCmd_t	nullCmd;
601 	int			checkSumIndex;
602 
603 	switch (Com_ClientState ()) {
604 	case CA_CONNECTED:
605 		if (cls.netChan.gotReliable || cls.netChan.message.curSize || Sys_Milliseconds()-cls.netChan.lastSent > 100)
606 			Netchan_Transmit (&cls.netChan, 0, NULL);
607 		return;
608 
609 	case CA_DISCONNECTED:
610 	case CA_CONNECTING:
611 		// Wait until active
612 		return;
613 	}
614 
615 	cl.cmdNum = cls.netChan.outgoingSequence & CMD_MASK;
616 	cmd = &cl.cmds[cl.cmdNum];
617 	cl.cmdTime[cl.cmdNum] = cls.realTime;	// for ping calculation
618 
619 	CL_FinalizeCmd ();
620 
621 	cl.cmd = *cmd;
622 
623 	// Send a userinfo update if needed
624 	if (com_userInfoModified) {
625 		com_userInfoModified = qFalse;
626 		MSG_WriteByte (&cls.netChan.message, CLC_USERINFO);
627 		MSG_WriteString (&cls.netChan.message, Cvar_BitInfo (CVAR_USERINFO));
628 	}
629 
630 	MSG_Init (&buf, data, sizeof (data));
631 
632 	if (cmd->buttons && cl.cin.time > 0 && !cl.attractLoop && cls.realTime-cl.cin.time > 1000) {
633 		// Skip the rest of the cinematic
634 		CIN_FinishCinematic ();
635 	}
636 
637 	// Begin a client move command
638 	MSG_WriteByte (&buf, CLC_MOVE);
639 
640 	// Save the position for a checksum byte
641 	if (cls.serverProtocol != ENHANCED_PROTOCOL_VERSION) {
642 		checkSumIndex = buf.curSize;
643 		MSG_WriteByte (&buf, 0);
644 	}
645 	else
646 		checkSumIndex = 0;
647 
648 	/*
649 	** Let the server know what the last frame we got was,
650 	** so the next message can be delta compressed
651 	*/
652 	if (cl_nodelta->intVal || !cl.frame.valid || cls.demoWaiting)
653 		MSG_WriteLong (&buf, -1);	// no compression
654 	else
655 		MSG_WriteLong (&buf, cl.frame.serverFrame);
656 
657 	/*
658 	** Send this and the previous cmds in the message,
659 	** so if the last packet was dropped, it can be recovered
660 	*/
661 	cmd = &cl.cmds[(cls.netChan.outgoingSequence-2)&CMD_MASK];
662 	memset (&nullCmd, 0, sizeof (nullCmd));
663 	MSG_WriteDeltaUsercmd (&buf, &nullCmd, cmd);
664 	oldCmd = cmd;
665 
666 	cmd = &cl.cmds[(cls.netChan.outgoingSequence-1)&CMD_MASK];
667 	MSG_WriteDeltaUsercmd (&buf, oldCmd, cmd);
668 	oldCmd = cmd;
669 
670 	cmd = &cl.cmds[(cls.netChan.outgoingSequence)&CMD_MASK];
671 	MSG_WriteDeltaUsercmd (&buf, oldCmd, cmd);
672 
673 	// Calculate a checksum over the move commands
674 	if (cls.serverProtocol != ENHANCED_PROTOCOL_VERSION) {
675 		buf.data[checkSumIndex] = Com_BlockSequenceCRCByte (
676 			buf.data + checkSumIndex + 1, buf.curSize - checkSumIndex - 1,
677 			cls.netChan.outgoingSequence);
678 	}
679 
680 	// Deliver the message
681 	Netchan_Transmit (&cls.netChan, buf.curSize, buf.data);
682 
683 	// Init the current cmd buffer and clear it
684 	cmd = &cl.cmds[cls.netChan.outgoingSequence&CMD_MASK];
685 	memset (cmd, 0, sizeof (userCmd_t));
686 }
687 
688 
689 /*
690 ============
691 CL_UpdateFrameTime
692 ============
693 */
CL_UpdateFrameTime(uint32 time)694 void CL_UpdateFrameTime (uint32 time)
695 {
696 	in_frameTime = time;
697 }
698 
699 
700 /*
701 ============
702 CL_InputInit
703 ============
704 */
CL_InputInit(void)705 void CL_InputInit (void)
706 {
707 	// Cvars
708 	autosensitivity		= Cvar_Register ("autosensitivity",		"0",		CVAR_ARCHIVE);
709 
710 	cl_nodelta			= Cvar_Register ("cl_nodelta",			"0",		0);
711 
712 	cl_upspeed			= Cvar_Register ("cl_upspeed",			"200",		0);
713 	cl_forwardspeed		= Cvar_Register ("cl_forwardspeed",		"200",		0);
714 	cl_sidespeed		= Cvar_Register ("cl_sidespeed",		"200",		0);
715 	cl_yawspeed			= Cvar_Register ("cl_yawspeed",			"140",		0);
716 	cl_pitchspeed		= Cvar_Register ("cl_pitchspeed",		"150",		0);
717 
718 	cl_run				= Cvar_Register ("cl_run",				"0",		CVAR_ARCHIVE);
719 
720 	cl_anglespeedkey	= Cvar_Register ("cl_anglespeedkey",	"1.5",		0);
721 
722 	m_filter			= Cvar_Register ("m_filter",			"0",		0);
723 
724 	// Commands
725 	Cmd_AddCommand ("centerview",	IN_CenterView_f,	"Centers the view");
726 
727 	Cmd_AddCommand ("+moveup",		IN_UpDown_f,		"");
728 	Cmd_AddCommand ("-moveup",		IN_UpUp_f,			"");
729 	Cmd_AddCommand ("+movedown",	IN_DownDown_f,		"");
730 	Cmd_AddCommand ("-movedown",	IN_DownUp_f,		"");
731 	Cmd_AddCommand ("+left",		IN_LeftDown_f,		"");
732 	Cmd_AddCommand ("-left",		IN_LeftUp_f,		"");
733 	Cmd_AddCommand ("+right",		IN_RightDown_f,		"");
734 	Cmd_AddCommand ("-right",		IN_RightUp_f,		"");
735 	Cmd_AddCommand ("+forward",		IN_ForwardDown_f,	"");
736 	Cmd_AddCommand ("-forward",		IN_ForwardUp_f,		"");
737 	Cmd_AddCommand ("+back",		IN_BackDown_f,		"");
738 	Cmd_AddCommand ("-back",		IN_BackUp_f,		"");
739 	Cmd_AddCommand ("+lookup",		IN_LookupDown_f,	"");
740 	Cmd_AddCommand ("-lookup",		IN_LookupUp_f,		"");
741 	Cmd_AddCommand ("+lookdown",	IN_LookdownDown_f,	"");
742 	Cmd_AddCommand ("-lookdown",	IN_LookdownUp_f,	"");
743 	Cmd_AddCommand ("+strafe",		IN_StrafeDown_f,	"");
744 	Cmd_AddCommand ("-strafe",		IN_StrafeUp_f,		"");
745 	Cmd_AddCommand ("+mlook",		IN_MLookDown_f,		"");
746 	Cmd_AddCommand ("-mlook",		IN_MLookUp_f,		"");
747 	Cmd_AddCommand ("+moveleft",	IN_MoveleftDown_f,	"");
748 	Cmd_AddCommand ("-moveleft",	IN_MoveleftUp_f,	"");
749 	Cmd_AddCommand ("+moveright",	IN_MoverightDown_f,	"");
750 	Cmd_AddCommand ("-moveright",	IN_MoverightUp_f,	"");
751 	Cmd_AddCommand ("+speed",		IN_SpeedDown_f,		"");
752 	Cmd_AddCommand ("-speed",		IN_SpeedUp_f,		"");
753 	Cmd_AddCommand ("+attack",		IN_AttackDown_f,	"");
754 	Cmd_AddCommand ("-attack",		IN_AttackUp_f,		"");
755 	Cmd_AddCommand ("+use",			IN_UseDown_f,		"");
756 	Cmd_AddCommand ("-use",			IN_UseUp_f,			"");
757 
758 	Cmd_AddCommand ("impulse",		IN_Impulse_f,		"");
759 
760 	Cmd_AddCommand ("+klook",		IN_KLookDown_f,		"");
761 	Cmd_AddCommand ("-klook",		IN_KLookUp_f,		"");
762 }
763