1 /*
2 Copyright (C) 1996-1997 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 // cl.input.c  -- builds an intended movement command to send to the server
21 
22 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
23 // rights reserved.
24 
25 #include "quakedef.h"
26 #include "csprogs.h"
27 #include "thread.h"
28 
29 /*
30 ===============================================================================
31 
32 KEY BUTTONS
33 
34 Continuous button event tracking is complicated by the fact that two different
35 input sources (say, mouse button 1 and the control key) can both press the
36 same button, but the button should only be released when both of the
37 pressing key have been released.
38 
39 When a key event issues a button command (+forward, +attack, etc), it appends
40 its key number as a parameter to the command so it can be matched up with
41 the release.
42 
43 state bit 0 is the current state of the key
44 state bit 1 is edge triggered on the up to down transition
45 state bit 2 is edge triggered on the down to up transition
46 
47 ===============================================================================
48 */
49 
50 
51 kbutton_t	in_mlook, in_klook;
52 kbutton_t	in_left, in_right, in_forward, in_back;
53 kbutton_t	in_lookup, in_lookdown, in_moveleft, in_moveright;
54 kbutton_t	in_strafe, in_speed, in_jump, in_attack, in_use;
55 kbutton_t	in_up, in_down;
56 // LordHavoc: added 6 new buttons
57 kbutton_t	in_button3, in_button4, in_button5, in_button6, in_button7, in_button8;
58 //even more
59 kbutton_t	in_button9, in_button10, in_button11, in_button12, in_button13, in_button14, in_button15, in_button16;
60 
61 int			in_impulse;
62 
63 
64 
KeyDown(kbutton_t * b)65 static void KeyDown (kbutton_t *b)
66 {
67 	int k;
68 	const char *c;
69 
70 	c = Cmd_Argv(1);
71 	if (c[0])
72 		k = atoi(c);
73 	else
74 		k = -1;		// typed manually at the console for continuous down
75 
76 	if (k == b->down[0] || k == b->down[1])
77 		return;		// repeating key
78 
79 	if (!b->down[0])
80 		b->down[0] = k;
81 	else if (!b->down[1])
82 		b->down[1] = k;
83 	else
84 	{
85 		Con_Print("Three keys down for a button!\n");
86 		return;
87 	}
88 
89 	if (b->state & 1)
90 		return;		// still down
91 	b->state |= 1 + 2;	// down + impulse down
92 }
93 
KeyUp(kbutton_t * b)94 static void KeyUp (kbutton_t *b)
95 {
96 	int k;
97 	const char *c;
98 
99 	c = Cmd_Argv(1);
100 	if (c[0])
101 		k = atoi(c);
102 	else
103 	{ // typed manually at the console, assume for unsticking, so clear all
104 		b->down[0] = b->down[1] = 0;
105 		b->state = 4;	// impulse up
106 		return;
107 	}
108 
109 	if (b->down[0] == k)
110 		b->down[0] = 0;
111 	else if (b->down[1] == k)
112 		b->down[1] = 0;
113 	else
114 		return;		// key up without coresponding down (menu pass through)
115 	if (b->down[0] || b->down[1])
116 		return;		// some other key is still holding it down
117 
118 	if (!(b->state & 1))
119 		return;		// still up (this should not happen)
120 	b->state &= ~1;		// now up
121 	b->state |= 4; 		// impulse up
122 }
123 
IN_KLookDown(void)124 static void IN_KLookDown (void) {KeyDown(&in_klook);}
IN_KLookUp(void)125 static void IN_KLookUp (void) {KeyUp(&in_klook);}
IN_MLookDown(void)126 static void IN_MLookDown (void) {KeyDown(&in_mlook);}
IN_MLookUp(void)127 static void IN_MLookUp (void)
128 {
129 	KeyUp(&in_mlook);
130 	if ( !(in_mlook.state&1) && lookspring.value)
131 		V_StartPitchDrift();
132 }
IN_UpDown(void)133 static void IN_UpDown(void) {KeyDown(&in_up);}
IN_UpUp(void)134 static void IN_UpUp(void) {KeyUp(&in_up);}
IN_DownDown(void)135 static void IN_DownDown(void) {KeyDown(&in_down);}
IN_DownUp(void)136 static void IN_DownUp(void) {KeyUp(&in_down);}
IN_LeftDown(void)137 static void IN_LeftDown(void) {KeyDown(&in_left);}
IN_LeftUp(void)138 static void IN_LeftUp(void) {KeyUp(&in_left);}
IN_RightDown(void)139 static void IN_RightDown(void) {KeyDown(&in_right);}
IN_RightUp(void)140 static void IN_RightUp(void) {KeyUp(&in_right);}
IN_ForwardDown(void)141 static void IN_ForwardDown(void) {KeyDown(&in_forward);}
IN_ForwardUp(void)142 static void IN_ForwardUp(void) {KeyUp(&in_forward);}
IN_BackDown(void)143 static void IN_BackDown(void) {KeyDown(&in_back);}
IN_BackUp(void)144 static void IN_BackUp(void) {KeyUp(&in_back);}
IN_LookupDown(void)145 static void IN_LookupDown(void) {KeyDown(&in_lookup);}
IN_LookupUp(void)146 static void IN_LookupUp(void) {KeyUp(&in_lookup);}
IN_LookdownDown(void)147 static void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
IN_LookdownUp(void)148 static void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
IN_MoveleftDown(void)149 static void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
IN_MoveleftUp(void)150 static void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
IN_MoverightDown(void)151 static void IN_MoverightDown(void) {KeyDown(&in_moveright);}
IN_MoverightUp(void)152 static void IN_MoverightUp(void) {KeyUp(&in_moveright);}
153 
IN_SpeedDown(void)154 static void IN_SpeedDown(void) {KeyDown(&in_speed);}
IN_SpeedUp(void)155 static void IN_SpeedUp(void) {KeyUp(&in_speed);}
IN_StrafeDown(void)156 static void IN_StrafeDown(void) {KeyDown(&in_strafe);}
IN_StrafeUp(void)157 static void IN_StrafeUp(void) {KeyUp(&in_strafe);}
158 
IN_AttackDown(void)159 static void IN_AttackDown(void) {KeyDown(&in_attack);}
IN_AttackUp(void)160 static void IN_AttackUp(void) {KeyUp(&in_attack);}
161 
IN_UseDown(void)162 static void IN_UseDown(void) {KeyDown(&in_use);}
IN_UseUp(void)163 static void IN_UseUp(void) {KeyUp(&in_use);}
164 
165 // LordHavoc: added 6 new buttons
IN_Button3Down(void)166 static void IN_Button3Down(void) {KeyDown(&in_button3);}
IN_Button3Up(void)167 static void IN_Button3Up(void) {KeyUp(&in_button3);}
IN_Button4Down(void)168 static void IN_Button4Down(void) {KeyDown(&in_button4);}
IN_Button4Up(void)169 static void IN_Button4Up(void) {KeyUp(&in_button4);}
IN_Button5Down(void)170 static void IN_Button5Down(void) {KeyDown(&in_button5);}
IN_Button5Up(void)171 static void IN_Button5Up(void) {KeyUp(&in_button5);}
IN_Button6Down(void)172 static void IN_Button6Down(void) {KeyDown(&in_button6);}
IN_Button6Up(void)173 static void IN_Button6Up(void) {KeyUp(&in_button6);}
IN_Button7Down(void)174 static void IN_Button7Down(void) {KeyDown(&in_button7);}
IN_Button7Up(void)175 static void IN_Button7Up(void) {KeyUp(&in_button7);}
IN_Button8Down(void)176 static void IN_Button8Down(void) {KeyDown(&in_button8);}
IN_Button8Up(void)177 static void IN_Button8Up(void) {KeyUp(&in_button8);}
178 
IN_Button9Down(void)179 static void IN_Button9Down(void) {KeyDown(&in_button9);}
IN_Button9Up(void)180 static void IN_Button9Up(void) {KeyUp(&in_button9);}
IN_Button10Down(void)181 static void IN_Button10Down(void) {KeyDown(&in_button10);}
IN_Button10Up(void)182 static void IN_Button10Up(void) {KeyUp(&in_button10);}
IN_Button11Down(void)183 static void IN_Button11Down(void) {KeyDown(&in_button11);}
IN_Button11Up(void)184 static void IN_Button11Up(void) {KeyUp(&in_button11);}
IN_Button12Down(void)185 static void IN_Button12Down(void) {KeyDown(&in_button12);}
IN_Button12Up(void)186 static void IN_Button12Up(void) {KeyUp(&in_button12);}
IN_Button13Down(void)187 static void IN_Button13Down(void) {KeyDown(&in_button13);}
IN_Button13Up(void)188 static void IN_Button13Up(void) {KeyUp(&in_button13);}
IN_Button14Down(void)189 static void IN_Button14Down(void) {KeyDown(&in_button14);}
IN_Button14Up(void)190 static void IN_Button14Up(void) {KeyUp(&in_button14);}
IN_Button15Down(void)191 static void IN_Button15Down(void) {KeyDown(&in_button15);}
IN_Button15Up(void)192 static void IN_Button15Up(void) {KeyUp(&in_button15);}
IN_Button16Down(void)193 static void IN_Button16Down(void) {KeyDown(&in_button16);}
IN_Button16Up(void)194 static void IN_Button16Up(void) {KeyUp(&in_button16);}
195 
IN_JumpDown(void)196 static void IN_JumpDown (void) {KeyDown(&in_jump);}
IN_JumpUp(void)197 static void IN_JumpUp (void) {KeyUp(&in_jump);}
198 
IN_Impulse(void)199 static void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
200 
201 in_bestweapon_info_t in_bestweapon_info[IN_BESTWEAPON_MAX];
202 
IN_BestWeapon_Register(const char * name,int impulse,int weaponbit,int activeweaponcode,int ammostat,int ammomin)203 static void IN_BestWeapon_Register(const char *name, int impulse, int weaponbit, int activeweaponcode, int ammostat, int ammomin)
204 {
205 	int i;
206 	for(i = 0; i < IN_BESTWEAPON_MAX && in_bestweapon_info[i].impulse; ++i)
207 		if(in_bestweapon_info[i].impulse == impulse)
208 			break;
209 	if(i >= IN_BESTWEAPON_MAX)
210 	{
211 		Con_Printf("no slot left for weapon definition; increase IN_BESTWEAPON_MAX\n");
212 		return; // sorry
213 	}
214 	strlcpy(in_bestweapon_info[i].name, name, sizeof(in_bestweapon_info[i].name));
215 	in_bestweapon_info[i].impulse = impulse;
216 	if(weaponbit != -1)
217 		in_bestweapon_info[i].weaponbit = weaponbit;
218 	if(activeweaponcode != -1)
219 		in_bestweapon_info[i].activeweaponcode = activeweaponcode;
220 	if(ammostat != -1)
221 		in_bestweapon_info[i].ammostat = ammostat;
222 	if(ammomin != -1)
223 		in_bestweapon_info[i].ammomin = ammomin;
224 }
225 
IN_BestWeapon_ResetData(void)226 void IN_BestWeapon_ResetData (void)
227 {
228 	memset(in_bestweapon_info, 0, sizeof(in_bestweapon_info));
229 	IN_BestWeapon_Register("1", 1, IT_AXE, IT_AXE, STAT_SHELLS, 0);
230 	IN_BestWeapon_Register("2", 2, IT_SHOTGUN, IT_SHOTGUN, STAT_SHELLS, 1);
231 	IN_BestWeapon_Register("3", 3, IT_SUPER_SHOTGUN, IT_SUPER_SHOTGUN, STAT_SHELLS, 1);
232 	IN_BestWeapon_Register("4", 4, IT_NAILGUN, IT_NAILGUN, STAT_NAILS, 1);
233 	IN_BestWeapon_Register("5", 5, IT_SUPER_NAILGUN, IT_SUPER_NAILGUN, STAT_NAILS, 1);
234 	IN_BestWeapon_Register("6", 6, IT_GRENADE_LAUNCHER, IT_GRENADE_LAUNCHER, STAT_ROCKETS, 1);
235 	IN_BestWeapon_Register("7", 7, IT_ROCKET_LAUNCHER, IT_ROCKET_LAUNCHER, STAT_ROCKETS, 1);
236 	IN_BestWeapon_Register("8", 8, IT_LIGHTNING, IT_LIGHTNING, STAT_CELLS, 1);
237 	IN_BestWeapon_Register("9", 9, 128, 128, STAT_CELLS, 1); // generic energy weapon for mods
238 	IN_BestWeapon_Register("p", 209, 128, 128, STAT_CELLS, 1); // dpmod plasma gun
239 	IN_BestWeapon_Register("w", 210, 8388608, 8388608, STAT_CELLS, 1); // dpmod plasma wave cannon
240 	IN_BestWeapon_Register("l", 225, HIT_LASER_CANNON, HIT_LASER_CANNON, STAT_CELLS, 1); // hipnotic laser cannon
241 	IN_BestWeapon_Register("h", 226, HIT_MJOLNIR, HIT_MJOLNIR, STAT_CELLS, 0); // hipnotic mjolnir hammer
242 }
243 
IN_BestWeapon_Register_f(void)244 static void IN_BestWeapon_Register_f (void)
245 {
246 	if(Cmd_Argc() == 7)
247 	{
248 		IN_BestWeapon_Register(
249 			Cmd_Argv(1),
250 			atoi(Cmd_Argv(2)),
251 			atoi(Cmd_Argv(3)),
252 			atoi(Cmd_Argv(4)),
253 			atoi(Cmd_Argv(5)),
254 			atoi(Cmd_Argv(6))
255 		);
256 	}
257 	else if(Cmd_Argc() == 2 && !strcmp(Cmd_Argv(1), "clear"))
258 	{
259 		memset(in_bestweapon_info, 0, sizeof(in_bestweapon_info));
260 	}
261 	else if(Cmd_Argc() == 2 && !strcmp(Cmd_Argv(1), "quake"))
262 	{
263 		IN_BestWeapon_ResetData();
264 	}
265 	else
266 	{
267 		Con_Printf("Usage: %s weaponshortname impulse itemcode activeweaponcode ammostat ammomin; %s clear; %s quake\n", Cmd_Argv(0), Cmd_Argv(0), Cmd_Argv(0));
268 	}
269 }
270 
IN_BestWeapon(void)271 static void IN_BestWeapon (void)
272 {
273 	int i, n;
274 	const char *t;
275 	if (Cmd_Argc() < 2)
276 	{
277 		Con_Printf("bestweapon requires 1 or more parameters\n");
278 		return;
279 	}
280 	for (i = 1;i < Cmd_Argc();i++)
281 	{
282 		t = Cmd_Argv(i);
283 		// figure out which weapon this character refers to
284 		for (n = 0;n < IN_BESTWEAPON_MAX && in_bestweapon_info[n].impulse;n++)
285 		{
286 			if (!strcmp(in_bestweapon_info[n].name, t))
287 			{
288 				// we found out what weapon this character refers to
289 				// check if the inventory contains the weapon and enough ammo
290 				if ((cl.stats[STAT_ITEMS] & in_bestweapon_info[n].weaponbit) && (cl.stats[in_bestweapon_info[n].ammostat] >= in_bestweapon_info[n].ammomin))
291 				{
292 					// we found one of the weapons the player wanted
293 					// send an impulse to switch to it
294 					in_impulse = in_bestweapon_info[n].impulse;
295 					return;
296 				}
297 				break;
298 			}
299 		}
300 		// if we couldn't identify the weapon we just ignore it and continue checking for other weapons
301 	}
302 	// if we couldn't find any of the weapons, there's nothing more we can do...
303 }
304 
305 #if 0
306 void IN_CycleWeapon (void)
307 {
308 	int i, n;
309 	int first = -1;
310 	qboolean found = false;
311 	const char *t;
312 	if (Cmd_Argc() < 2)
313 	{
314 		Con_Printf("bestweapon requires 1 or more parameters\n");
315 		return;
316 	}
317 	for (i = 1;i < Cmd_Argc();i++)
318 	{
319 		t = Cmd_Argv(i);
320 		// figure out which weapon this character refers to
321 		for (n = 0;n < IN_BESTWEAPON_MAX && in_bestweapon_info[n].impulse;n++)
322 		{
323 			if (!strcmp(in_bestweapon_info[n].name, t))
324 			{
325 				// we found out what weapon this character refers to
326 				// check if the inventory contains the weapon and enough ammo
327 				if ((cl.stats[STAT_ITEMS] & in_bestweapon_info[n].weaponbit) && (cl.stats[in_bestweapon_info[n].ammostat] >= in_bestweapon_info[n].ammomin))
328 				{
329 					// we found one of the weapons the player wanted
330 					if(first == -1)
331 						first = n;
332 					if(found)
333 					{
334 						in_impulse = in_bestweapon_info[n].impulse;
335 						return;
336 					}
337 					if(cl.stats[STAT_ACTIVEWEAPON] == in_bestweapon_info[n].activeweaponcode)
338 						found = true;
339 				}
340 				break;
341 			}
342 		}
343 		// if we couldn't identify the weapon we just ignore it and continue checking for other weapons
344 	}
345 	if(first != -1)
346 	{
347 		in_impulse = in_bestweapon_info[first].impulse;
348 		return;
349 	}
350 	// if we couldn't find any of the weapons, there's nothing more we can do...
351 }
352 #endif
353 
354 /*
355 ===============
356 CL_KeyState
357 
358 Returns 0.25 if a key was pressed and released during the frame,
359 0.5 if it was pressed and held
360 0 if held then released, and
361 1.0 if held for the entire time
362 ===============
363 */
CL_KeyState(kbutton_t * key)364 float CL_KeyState (kbutton_t *key)
365 {
366 	float		val;
367 	qboolean	impulsedown, impulseup, down;
368 
369 	impulsedown = (key->state & 2) != 0;
370 	impulseup = (key->state & 4) != 0;
371 	down = (key->state & 1) != 0;
372 	val = 0;
373 
374 	if (impulsedown && !impulseup)
375 	{
376 		if (down)
377 			val = 0.5;	// pressed and held this frame
378 		else
379 			val = 0;	//	I_Error ();
380 	}
381 	if (impulseup && !impulsedown)
382 	{
383 		if (down)
384 			val = 0;	//	I_Error ();
385 		else
386 			val = 0;	// released this frame
387 	}
388 	if (!impulsedown && !impulseup)
389 	{
390 		if (down)
391 			val = 1.0;	// held the entire frame
392 		else
393 			val = 0;	// up the entire frame
394 	}
395 	if (impulsedown && impulseup)
396 	{
397 		if (down)
398 			val = 0.75;	// released and re-pressed this frame
399 		else
400 			val = 0.25;	// pressed and released this frame
401 	}
402 
403 	key->state &= 1;		// clear impulses
404 
405 	return val;
406 }
407 
408 
409 
410 
411 //==========================================================================
412 
413 cvar_t cl_upspeed = {CVAR_SAVE, "cl_upspeed","400","vertical movement speed (while swimming or flying)"};
414 cvar_t cl_forwardspeed = {CVAR_SAVE, "cl_forwardspeed","400","forward movement speed"};
415 cvar_t cl_backspeed = {CVAR_SAVE, "cl_backspeed","400","backward movement speed"};
416 cvar_t cl_sidespeed = {CVAR_SAVE, "cl_sidespeed","350","strafe movement speed"};
417 
418 cvar_t cl_movespeedkey = {CVAR_SAVE, "cl_movespeedkey","2.0","how much +speed multiplies keyboard movement speed"};
419 cvar_t cl_movecliptokeyboard = {0, "cl_movecliptokeyboard", "0", "if set to 1, any move is clipped to the nine keyboard states; if set to 2, only the direction is clipped, not the amount"};
420 
421 cvar_t cl_yawspeed = {CVAR_SAVE, "cl_yawspeed","140","keyboard yaw turning speed"};
422 cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150","keyboard pitch turning speed"};
423 
424 cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5","how much +speed multiplies keyboard turning speed"};
425 
426 cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0", "enables clientside prediction of your player movement on DP servers (use cl_nopred for QWSV servers)"};
427 cvar_t cl_movement_replay = {0, "cl_movement_replay", "1", "use engine prediction"};
428 cvar_t cl_movement_nettimeout = {CVAR_SAVE, "cl_movement_nettimeout", "0.3", "stops predicting moves when server is lagging badly (avoids major performance problems), timeout in seconds"};
429 cvar_t cl_movement_minping = {CVAR_SAVE, "cl_movement_minping", "0", "whether to use prediction when ping is lower than this value in milliseconds"};
430 cvar_t cl_movement_track_canjump = {CVAR_SAVE, "cl_movement_track_canjump", "1", "track if the player released the jump key between two jumps to decide if he is able to jump or not; when off, this causes some \"sliding\" slightly above the floor when the jump key is held too long; if the mod allows repeated jumping by holding space all the time, this has to be set to zero too"};
431 cvar_t cl_movement_maxspeed = {0, "cl_movement_maxspeed", "320", "how fast you can move (should match sv_maxspeed)"};
432 cvar_t cl_movement_maxairspeed = {0, "cl_movement_maxairspeed", "30", "how fast you can move while in the air (should match sv_maxairspeed)"};
433 cvar_t cl_movement_stopspeed = {0, "cl_movement_stopspeed", "100", "speed below which you will be slowed rapidly to a stop rather than sliding endlessly (should match sv_stopspeed)"};
434 cvar_t cl_movement_friction = {0, "cl_movement_friction", "4", "how fast you slow down (should match sv_friction)"};
435 cvar_t cl_movement_wallfriction = {0, "cl_movement_wallfriction", "1", "how fast you slow down while sliding along a wall (should match sv_wallfriction)"};
436 cvar_t cl_movement_waterfriction = {0, "cl_movement_waterfriction", "-1", "how fast you slow down (should match sv_waterfriction), if less than 0 the cl_movement_friction variable is used instead"};
437 cvar_t cl_movement_edgefriction = {0, "cl_movement_edgefriction", "1", "how much to slow down when you may be about to fall off a ledge (should match edgefriction)"};
438 cvar_t cl_movement_stepheight = {0, "cl_movement_stepheight", "18", "how tall a step you can step in one instant (should match sv_stepheight)"};
439 cvar_t cl_movement_accelerate = {0, "cl_movement_accelerate", "10", "how fast you accelerate (should match sv_accelerate)"};
440 cvar_t cl_movement_airaccelerate = {0, "cl_movement_airaccelerate", "-1", "how fast you accelerate while in the air (should match sv_airaccelerate), if less than 0 the cl_movement_accelerate variable is used instead"};
441 cvar_t cl_movement_wateraccelerate = {0, "cl_movement_wateraccelerate", "-1", "how fast you accelerate while in water (should match sv_wateraccelerate), if less than 0 the cl_movement_accelerate variable is used instead"};
442 cvar_t cl_movement_jumpvelocity = {0, "cl_movement_jumpvelocity", "270", "how fast you move upward when you begin a jump (should match the quakec code)"};
443 cvar_t cl_movement_airaccel_qw = {0, "cl_movement_airaccel_qw", "1", "ratio of QW-style air control as opposed to simple acceleration (reduces speed gain when zigzagging) (should match sv_airaccel_qw); when < 0, the speed is clamped against the maximum allowed forward speed after the move"};
444 cvar_t cl_movement_airaccel_sideways_friction = {0, "cl_movement_airaccel_sideways_friction", "0", "anti-sideways movement stabilization (should match sv_airaccel_sideways_friction); when < 0, only so much friction is applied that braking (by accelerating backwards) cannot be stronger"};
445 cvar_t cl_nopred = {CVAR_SAVE, "cl_nopred", "0", "(QWSV only) disables player movement prediction when playing on QWSV servers (this setting is separate from cl_movement because player expectations are different when playing on DP vs QW servers)"};
446 
447 cvar_t in_pitch_min = {0, "in_pitch_min", "-90", "how far you can aim upward (quake used -70)"};
448 cvar_t in_pitch_max = {0, "in_pitch_max", "90", "how far you can aim downward (quake used 80)"};
449 
450 cvar_t m_filter = {CVAR_SAVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"};
451 cvar_t m_accelerate = {CVAR_SAVE, "m_accelerate","1", "mouse acceleration factor (try 2)"};
452 cvar_t m_accelerate_minspeed = {CVAR_SAVE, "m_accelerate_minspeed","5000", "below this speed, no acceleration is done"};
453 cvar_t m_accelerate_maxspeed = {CVAR_SAVE, "m_accelerate_maxspeed","10000", "above this speed, full acceleration is done"};
454 cvar_t m_accelerate_filter = {CVAR_SAVE, "m_accelerate_filter","0.1", "mouse acceleration factor filtering"};
455 
456 cvar_t cl_netfps = {CVAR_SAVE, "cl_netfps","72", "how many input packets to send to server each second"};
457 cvar_t cl_netrepeatinput = {CVAR_SAVE, "cl_netrepeatinput", "1", "how many packets in a row can be lost without movement issues when using cl_movement (technically how many input messages to repeat in each packet that have not yet been acknowledged by the server), only affects DP7 and later servers (Quake uses 0, QuakeWorld uses 2, and just for comparison Quake3 uses 1)"};
458 cvar_t cl_netimmediatebuttons = {CVAR_SAVE, "cl_netimmediatebuttons", "1", "sends extra packets whenever your buttons change or an impulse is used (basically: whenever you click fire or change weapon)"};
459 
460 cvar_t cl_nodelta = {0, "cl_nodelta", "0", "disables delta compression of non-player entities in QW network protocol"};
461 
462 cvar_t cl_csqc_generatemousemoveevents = {0, "cl_csqc_generatemousemoveevents", "1", "enables calls to CSQC_InputEvent with type 2, for compliance with EXT_CSQC spec"};
463 
464 extern cvar_t v_flipped;
465 
466 /*
467 ================
468 CL_AdjustAngles
469 
470 Moves the local angle positions
471 ================
472 */
CL_AdjustAngles(void)473 static void CL_AdjustAngles (void)
474 {
475 	float	speed;
476 	float	up, down;
477 
478 	if (in_speed.state & 1)
479 		speed = cl.realframetime * cl_anglespeedkey.value;
480 	else
481 		speed = cl.realframetime;
482 
483 	if (!(in_strafe.state & 1))
484 	{
485 		cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
486 		cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
487 	}
488 	if (in_klook.state & 1)
489 	{
490 		V_StopPitchDrift ();
491 		cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
492 		cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
493 	}
494 
495 	up = CL_KeyState (&in_lookup);
496 	down = CL_KeyState(&in_lookdown);
497 
498 	cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
499 	cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
500 
501 	if (up || down)
502 		V_StopPitchDrift ();
503 
504 	cl.viewangles[YAW] = ANGLEMOD(cl.viewangles[YAW]);
505 	cl.viewangles[PITCH] = ANGLEMOD(cl.viewangles[PITCH]);
506 	if (cl.viewangles[YAW] >= 180)
507 		cl.viewangles[YAW] -= 360;
508 	if (cl.viewangles[PITCH] >= 180)
509 		cl.viewangles[PITCH] -= 360;
510         // TODO: honor serverinfo minpitch and maxpitch values in PROTOCOL_QUAKEWORLD
511         // TODO: honor proquake pq_fullpitch cvar when playing on proquake server (server stuffcmd's this to 0 usually)
512 	cl.viewangles[PITCH] = bound(in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
513 	cl.viewangles[ROLL] = bound(-180, cl.viewangles[ROLL], 180);
514 }
515 
516 int cl_ignoremousemoves = 2;
517 
518 /*
519 ================
520 CL_Input
521 
522 Send the intended movement message to the server
523 ================
524 */
CL_Input(void)525 void CL_Input (void)
526 {
527 	float mx, my;
528 	static float old_mouse_x = 0, old_mouse_y = 0;
529 
530 	// clamp before the move to prevent starting with bad angles
531 	CL_AdjustAngles ();
532 
533 	if(v_flipped.integer)
534 		cl.viewangles[YAW] = -cl.viewangles[YAW];
535 
536 	// reset some of the command fields
537 	cl.cmd.forwardmove = 0;
538 	cl.cmd.sidemove = 0;
539 	cl.cmd.upmove = 0;
540 
541 	// get basic movement from keyboard
542 	if (in_strafe.state & 1)
543 	{
544 		cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
545 		cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
546 	}
547 
548 	cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
549 	cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
550 
551 	cl.cmd.upmove += cl_upspeed.value * CL_KeyState (&in_up);
552 	cl.cmd.upmove -= cl_upspeed.value * CL_KeyState (&in_down);
553 
554 	if (! (in_klook.state & 1) )
555 	{
556 		cl.cmd.forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
557 		cl.cmd.forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
558 	}
559 
560 	// adjust for speed key
561 	if (in_speed.state & 1)
562 	{
563 		cl.cmd.forwardmove *= cl_movespeedkey.value;
564 		cl.cmd.sidemove *= cl_movespeedkey.value;
565 		cl.cmd.upmove *= cl_movespeedkey.value;
566 	}
567 
568 	// allow mice or other external controllers to add to the move
569 	IN_Move ();
570 
571 	// send mouse move to csqc
572 	if (cl.csqc_loaded && cl_csqc_generatemousemoveevents.integer)
573 	{
574 		if (cl.csqc_wantsmousemove)
575 		{
576 			// event type 3 is a DP_CSQC thing
577 			static int oldwindowmouse[2];
578 			if (oldwindowmouse[0] != in_windowmouse_x || oldwindowmouse[1] != in_windowmouse_y)
579 			{
580 				CL_VM_InputEvent(3, in_windowmouse_x * vid_conwidth.value / vid.width, in_windowmouse_y * vid_conheight.value / vid.height);
581 				oldwindowmouse[0] = in_windowmouse_x;
582 				oldwindowmouse[1] = in_windowmouse_y;
583 			}
584 		}
585 		else
586 		{
587 			if (in_mouse_x || in_mouse_y)
588 				CL_VM_InputEvent(2, in_mouse_x, in_mouse_y);
589 		}
590 	}
591 
592 	// apply m_accelerate if it is on
593 	if(m_accelerate.value > 1)
594 	{
595 		static float averagespeed = 0;
596 		float speed, f, mi, ma;
597 
598 		speed = sqrt(in_mouse_x * in_mouse_x + in_mouse_y * in_mouse_y) / cl.realframetime;
599 		if(m_accelerate_filter.value > 0)
600 			f = bound(0, cl.realframetime / m_accelerate_filter.value, 1);
601 		else
602 			f = 1;
603 		averagespeed = speed * f + averagespeed * (1 - f);
604 
605 		mi = max(1, m_accelerate_minspeed.value);
606 		ma = max(m_accelerate_minspeed.value + 1, m_accelerate_maxspeed.value);
607 
608 		if(averagespeed <= mi)
609 		{
610 			f = 1;
611 		}
612 		else if(averagespeed >= ma)
613 		{
614 			f = m_accelerate.value;
615 		}
616 		else
617 		{
618 			f = averagespeed;
619 			f = (f - mi) / (ma - mi) * (m_accelerate.value - 1) + 1;
620 		}
621 
622 		in_mouse_x *= f;
623 		in_mouse_y *= f;
624 	}
625 
626 	// apply m_filter if it is on
627 	mx = in_mouse_x;
628 	my = in_mouse_y;
629 	if (m_filter.integer)
630 	{
631 		in_mouse_x = (mx + old_mouse_x) * 0.5;
632 		in_mouse_y = (my + old_mouse_y) * 0.5;
633 	}
634 	old_mouse_x = mx;
635 	old_mouse_y = my;
636 
637 	// ignore a mouse move if mouse was activated/deactivated this frame
638 	if (cl_ignoremousemoves)
639 	{
640 		cl_ignoremousemoves--;
641 		in_mouse_x = old_mouse_x = 0;
642 		in_mouse_y = old_mouse_y = 0;
643 	}
644 
645 	// if not in menu, apply mouse move to viewangles/movement
646 	if (!key_consoleactive && key_dest == key_game && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0)
647 	{
648 		float modulatedsensitivity = sensitivity.value * cl.sensitivityscale;
649 		if (in_strafe.state & 1)
650 		{
651 			// strafing mode, all looking is movement
652 			V_StopPitchDrift();
653 			cl.cmd.sidemove += m_side.value * in_mouse_x * modulatedsensitivity;
654 			if (noclip_anglehack)
655 				cl.cmd.upmove -= m_forward.value * in_mouse_y * modulatedsensitivity;
656 			else
657 				cl.cmd.forwardmove -= m_forward.value * in_mouse_y * modulatedsensitivity;
658 		}
659 		else if ((in_mlook.state & 1) || freelook.integer)
660 		{
661 			// mouselook, lookstrafe causes turning to become strafing
662 			V_StopPitchDrift();
663 			if (lookstrafe.integer)
664 				cl.cmd.sidemove += m_side.value * in_mouse_x * modulatedsensitivity;
665 			else
666 				cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * modulatedsensitivity * cl.viewzoom;
667 			cl.viewangles[PITCH] += m_pitch.value * in_mouse_y * modulatedsensitivity * cl.viewzoom;
668 		}
669 		else
670 		{
671 			// non-mouselook, yaw turning and forward/back movement
672 			cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * modulatedsensitivity * cl.viewzoom;
673 			cl.cmd.forwardmove -= m_forward.value * in_mouse_y * modulatedsensitivity;
674 		}
675 	}
676 	else // don't pitch drift when csqc is controlling the mouse
677 	{
678 		// mouse interacting with the scene, mostly stationary view
679 		V_StopPitchDrift();
680 		// update prydon cursor
681 		cl.cmd.cursor_screen[0] = in_windowmouse_x * 2.0 / vid.width - 1.0;
682 		cl.cmd.cursor_screen[1] = in_windowmouse_y * 2.0 / vid.height - 1.0;
683 	}
684 
685 	if(v_flipped.integer)
686 	{
687 		cl.viewangles[YAW] = -cl.viewangles[YAW];
688 		cl.cmd.sidemove = -cl.cmd.sidemove;
689 	}
690 
691 	// clamp after the move to prevent rendering with bad angles
692 	CL_AdjustAngles ();
693 
694 	if(cl_movecliptokeyboard.integer)
695 	{
696 		vec_t f = 1;
697 		if (in_speed.state & 1)
698 			f *= cl_movespeedkey.value;
699 		if(cl_movecliptokeyboard.integer == 2)
700 		{
701 			// digital direction, analog amount
702 			vec_t wishvel_x, wishvel_y;
703 			wishvel_x = fabs(cl.cmd.forwardmove);
704 			wishvel_y = fabs(cl.cmd.sidemove);
705 			if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y)
706 			{
707 				vec_t wishspeed = sqrt(wishvel_x * wishvel_x + wishvel_y * wishvel_y);
708 				if(wishvel_x >= 2 * wishvel_y)
709 				{
710 					// pure X motion
711 					if(cl.cmd.forwardmove > 0)
712 						cl.cmd.forwardmove = wishspeed;
713 					else
714 						cl.cmd.forwardmove = -wishspeed;
715 					cl.cmd.sidemove = 0;
716 				}
717 				else if(wishvel_y >= 2 * wishvel_x)
718 				{
719 					// pure Y motion
720 					cl.cmd.forwardmove = 0;
721 					if(cl.cmd.sidemove > 0)
722 						cl.cmd.sidemove = wishspeed;
723 					else
724 						cl.cmd.sidemove = -wishspeed;
725 				}
726 				else
727 				{
728 					// diagonal
729 					if(cl.cmd.forwardmove > 0)
730 						cl.cmd.forwardmove = 0.70710678118654752440 * wishspeed;
731 					else
732 						cl.cmd.forwardmove = -0.70710678118654752440 * wishspeed;
733 					if(cl.cmd.sidemove > 0)
734 						cl.cmd.sidemove = 0.70710678118654752440 * wishspeed;
735 					else
736 						cl.cmd.sidemove = -0.70710678118654752440 * wishspeed;
737 				}
738 			}
739 		}
740 		else if(cl_movecliptokeyboard.integer)
741 		{
742 			// digital direction, digital amount
743 			if(cl.cmd.sidemove >= cl_sidespeed.value * f * 0.5)
744 				cl.cmd.sidemove = cl_sidespeed.value * f;
745 			else if(cl.cmd.sidemove <= -cl_sidespeed.value * f * 0.5)
746 				cl.cmd.sidemove = -cl_sidespeed.value * f;
747 			else
748 				cl.cmd.sidemove = 0;
749 			if(cl.cmd.forwardmove >= cl_forwardspeed.value * f * 0.5)
750 				cl.cmd.forwardmove = cl_forwardspeed.value * f;
751 			else if(cl.cmd.forwardmove <= -cl_backspeed.value * f * 0.5)
752 				cl.cmd.forwardmove = -cl_backspeed.value * f;
753 			else
754 				cl.cmd.forwardmove = 0;
755 		}
756 	}
757 }
758 
759 #include "cl_collision.h"
760 
CL_UpdatePrydonCursor(void)761 static void CL_UpdatePrydonCursor(void)
762 {
763 	vec3_t temp;
764 
765 	if (cl_prydoncursor.integer <= 0)
766 		VectorClear(cl.cmd.cursor_screen);
767 
768 	/*
769 	if (cl.cmd.cursor_screen[0] < -1)
770 	{
771 		cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - -1) * vid.width * sensitivity.value * cl.viewzoom;
772 		cl.cmd.cursor_screen[0] = -1;
773 	}
774 	if (cl.cmd.cursor_screen[0] > 1)
775 	{
776 		cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - 1) * vid.width * sensitivity.value * cl.viewzoom;
777 		cl.cmd.cursor_screen[0] = 1;
778 	}
779 	if (cl.cmd.cursor_screen[1] < -1)
780 	{
781 		cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - -1) * vid.height * sensitivity.value * cl.viewzoom;
782 		cl.cmd.cursor_screen[1] = -1;
783 	}
784 	if (cl.cmd.cursor_screen[1] > 1)
785 	{
786 		cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - 1) * vid.height * sensitivity.value * cl.viewzoom;
787 		cl.cmd.cursor_screen[1] = 1;
788 	}
789 	*/
790 	cl.cmd.cursor_screen[0] = bound(-1, cl.cmd.cursor_screen[0], 1);
791 	cl.cmd.cursor_screen[1] = bound(-1, cl.cmd.cursor_screen[1], 1);
792 	cl.cmd.cursor_screen[2] = 1;
793 
794 	// calculate current view matrix
795 	Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, cl.cmd.cursor_start);
796 	// calculate direction vector of cursor in viewspace by using frustum slopes
797 	VectorSet(temp, cl.cmd.cursor_screen[2] * 1000000, (v_flipped.integer ? -1 : 1) * cl.cmd.cursor_screen[0] * -r_refdef.view.frustum_x * 1000000, cl.cmd.cursor_screen[1] * -r_refdef.view.frustum_y * 1000000);
798 	Matrix4x4_Transform(&r_refdef.view.matrix, temp, cl.cmd.cursor_end);
799 	// trace from view origin to the cursor
800 	if (cl_prydoncursor_notrace.integer)
801 	{
802 		cl.cmd.cursor_fraction = 1.0f;
803 		VectorCopy(cl.cmd.cursor_end, cl.cmd.cursor_impact);
804 		VectorClear(cl.cmd.cursor_normal);
805 		cl.cmd.cursor_entitynumber = 0;
806 	}
807 	else
808 		cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl.entities[cl.playerentity].render : NULL);
809 }
810 
811 #define NUMOFFSETS 27
812 static vec3_t offsets[NUMOFFSETS] =
813 {
814 // 1 no nudge (just return the original if this test passes)
815 	{ 0.000,  0.000,  0.000},
816 // 6 simple nudges
817 	{ 0.000,  0.000,  0.125}, { 0.000,  0.000, -0.125},
818 	{-0.125,  0.000,  0.000}, { 0.125,  0.000,  0.000},
819 	{ 0.000, -0.125,  0.000}, { 0.000,  0.125,  0.000},
820 // 4 diagonal flat nudges
821 	{-0.125, -0.125,  0.000}, { 0.125, -0.125,  0.000},
822 	{-0.125,  0.125,  0.000}, { 0.125,  0.125,  0.000},
823 // 8 diagonal upward nudges
824 	{-0.125,  0.000,  0.125}, { 0.125,  0.000,  0.125},
825 	{ 0.000, -0.125,  0.125}, { 0.000,  0.125,  0.125},
826 	{-0.125, -0.125,  0.125}, { 0.125, -0.125,  0.125},
827 	{-0.125,  0.125,  0.125}, { 0.125,  0.125,  0.125},
828 // 8 diagonal downward nudges
829 	{-0.125,  0.000, -0.125}, { 0.125,  0.000, -0.125},
830 	{ 0.000, -0.125, -0.125}, { 0.000,  0.125, -0.125},
831 	{-0.125, -0.125, -0.125}, { 0.125, -0.125, -0.125},
832 	{-0.125,  0.125, -0.125}, { 0.125,  0.125, -0.125},
833 };
834 
CL_ClientMovement_Unstick(cl_clientmovement_state_t * s)835 static qboolean CL_ClientMovement_Unstick(cl_clientmovement_state_t *s)
836 {
837 	int i;
838 	vec3_t neworigin;
839 	for (i = 0;i < NUMOFFSETS;i++)
840 	{
841 		VectorAdd(offsets[i], s->origin, neworigin);
842 		if (!CL_TraceBox(neworigin, cl.playercrouchmins, cl.playercrouchmaxs, neworigin, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true).startsolid)
843 		{
844 			VectorCopy(neworigin, s->origin);
845 			return true;
846 		}
847 	}
848 	// if all offsets failed, give up
849 	return false;
850 }
851 
CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t * s)852 static void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s)
853 {
854 	vec_t f;
855 	vec3_t origin1, origin2;
856 	trace_t trace;
857 
858 	// make sure player is not stuck
859 	CL_ClientMovement_Unstick(s);
860 
861 	// set crouched
862 	if (s->cmd.crouch)
863 	{
864 		// wants to crouch, this always works..
865 		if (!s->crouched)
866 			s->crouched = true;
867 	}
868 	else
869 	{
870 		// wants to stand, if currently crouching we need to check for a
871 		// low ceiling first
872 		if (s->crouched)
873 		{
874 			trace = CL_TraceBox(s->origin, cl.playerstandmins, cl.playerstandmaxs, s->origin, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true);
875 			if (!trace.startsolid)
876 				s->crouched = false;
877 		}
878 	}
879 	if (s->crouched)
880 	{
881 		VectorCopy(cl.playercrouchmins, s->mins);
882 		VectorCopy(cl.playercrouchmaxs, s->maxs);
883 	}
884 	else
885 	{
886 		VectorCopy(cl.playerstandmins, s->mins);
887 		VectorCopy(cl.playerstandmaxs, s->maxs);
888 	}
889 
890 	// set onground
891 	VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + 1);
892 	VectorSet(origin2, s->origin[0], s->origin[1], s->origin[2] - 1); // -2 causes clientside doublejump bug at above 150fps, raising that to 300fps :)
893 	trace = CL_TraceBox(origin1, s->mins, s->maxs, origin2, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true);
894 	if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
895 	{
896 		s->onground = true;
897 
898 		// this code actually "predicts" an impact; so let's clip velocity first
899 		f = DotProduct(s->velocity, trace.plane.normal);
900 		if(f < 0) // only if moving downwards actually
901 			VectorMA(s->velocity, -f, trace.plane.normal, s->velocity);
902 	}
903 	else
904 		s->onground = false;
905 
906 	// set watertype/waterlevel
907 	VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + s->mins[2] + 1);
908 	s->waterlevel = WATERLEVEL_NONE;
909 	s->watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, s->self, 0, 0, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
910 	if (s->watertype)
911 	{
912 		s->waterlevel = WATERLEVEL_WETFEET;
913 		origin1[2] = s->origin[2] + (s->mins[2] + s->maxs[2]) * 0.5f;
914 		if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s->self, 0, 0, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
915 		{
916 			s->waterlevel = WATERLEVEL_SWIMMING;
917 			origin1[2] = s->origin[2] + 22;
918 			if (CL_TracePoint(origin1, MOVE_NOMONSTERS, s->self, 0, 0, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
919 				s->waterlevel = WATERLEVEL_SUBMERGED;
920 		}
921 	}
922 
923 	// water jump prediction
924 	if (s->onground || s->velocity[2] <= 0 || s->waterjumptime <= 0)
925 		s->waterjumptime = 0;
926 }
927 
CL_ClientMovement_Move(cl_clientmovement_state_t * s)928 static void CL_ClientMovement_Move(cl_clientmovement_state_t *s)
929 {
930 	int bump;
931 	double t;
932 	vec_t f;
933 	vec3_t neworigin;
934 	vec3_t currentorigin2;
935 	vec3_t neworigin2;
936 	vec3_t primalvelocity;
937 	trace_t trace;
938 	trace_t trace2;
939 	trace_t trace3;
940 	CL_ClientMovement_UpdateStatus(s);
941 	VectorCopy(s->velocity, primalvelocity);
942 	for (bump = 0, t = s->cmd.frametime;bump < 8 && VectorLength2(s->velocity) > 0;bump++)
943 	{
944 		VectorMA(s->origin, t, s->velocity, neworigin);
945 		trace = CL_TraceBox(s->origin, s->mins, s->maxs, neworigin, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true);
946 		if (trace.fraction < 1 && trace.plane.normal[2] == 0)
947 		{
948 			// may be a step or wall, try stepping up
949 			// first move forward at a higher level
950 			VectorSet(currentorigin2, s->origin[0], s->origin[1], s->origin[2] + cl.movevars_stepheight);
951 			VectorSet(neworigin2, neworigin[0], neworigin[1], s->origin[2] + cl.movevars_stepheight);
952 			trace2 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true);
953 			if (!trace2.startsolid)
954 			{
955 				// then move down from there
956 				VectorCopy(trace2.endpos, currentorigin2);
957 				VectorSet(neworigin2, trace2.endpos[0], trace2.endpos[1], s->origin[2]);
958 				trace3 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true);
959 				//Con_Printf("%f %f %f %f : %f %f %f %f : %f %f %f %f\n", trace.fraction, trace.endpos[0], trace.endpos[1], trace.endpos[2], trace2.fraction, trace2.endpos[0], trace2.endpos[1], trace2.endpos[2], trace3.fraction, trace3.endpos[0], trace3.endpos[1], trace3.endpos[2]);
960 				// accept the new trace if it made some progress
961 				if (fabs(trace3.endpos[0] - trace.endpos[0]) >= 0.03125 || fabs(trace3.endpos[1] - trace.endpos[1]) >= 0.03125)
962 				{
963 					trace = trace2;
964 					VectorCopy(trace3.endpos, trace.endpos);
965 				}
966 			}
967 		}
968 
969 		// check if it moved at all
970 		if (trace.fraction >= 0.001)
971 			VectorCopy(trace.endpos, s->origin);
972 
973 		// check if it moved all the way
974 		if (trace.fraction == 1)
975 			break;
976 
977 		// this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
978 		// <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
979 		// this got commented out in a change that supposedly makes the code match QW better
980 		// so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
981 		if (trace.plane.normal[2] > 0.7)
982 			s->onground = true;
983 
984 		t -= t * trace.fraction;
985 
986 		f = DotProduct(s->velocity, trace.plane.normal);
987 		VectorMA(s->velocity, -f, trace.plane.normal, s->velocity);
988 	}
989 	if (s->waterjumptime > 0)
990 		VectorCopy(primalvelocity, s->velocity);
991 }
992 
993 
CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t * s)994 static void CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t *s)
995 {
996 	vec_t wishspeed;
997 	vec_t f;
998 	vec3_t wishvel;
999 	vec3_t wishdir;
1000 
1001 	// water jump only in certain situations
1002 	// this mimics quakeworld code
1003 	if (s->cmd.jump && s->waterlevel == 2 && s->velocity[2] >= -180)
1004 	{
1005 		vec3_t forward;
1006 		vec3_t yawangles;
1007 		vec3_t spot;
1008 		VectorSet(yawangles, 0, s->cmd.viewangles[1], 0);
1009 		AngleVectors(yawangles, forward, NULL, NULL);
1010 		VectorMA(s->origin, 24, forward, spot);
1011 		spot[2] += 8;
1012 		if (CL_TracePoint(spot, MOVE_NOMONSTERS, s->self, 0, 0, 0, true, false, NULL, false).startsolid)
1013 		{
1014 			spot[2] += 24;
1015 			if (!CL_TracePoint(spot, MOVE_NOMONSTERS, s->self, 0, 0, 0, true, false, NULL, false).startsolid)
1016 			{
1017 				VectorScale(forward, 50, s->velocity);
1018 				s->velocity[2] = 310;
1019 				s->waterjumptime = 2;
1020 				s->onground = false;
1021 				s->cmd.canjump = false;
1022 			}
1023 		}
1024 	}
1025 
1026 	if (!(s->cmd.forwardmove*s->cmd.forwardmove + s->cmd.sidemove*s->cmd.sidemove + s->cmd.upmove*s->cmd.upmove))
1027 	{
1028 		// drift towards bottom
1029 		VectorSet(wishvel, 0, 0, -60);
1030 	}
1031 	else
1032 	{
1033 		// swim
1034 		vec3_t forward;
1035 		vec3_t right;
1036 		vec3_t up;
1037 		// calculate movement vector
1038 		AngleVectors(s->cmd.viewangles, forward, right, up);
1039 		VectorSet(up, 0, 0, 1);
1040 		VectorMAMAM(s->cmd.forwardmove, forward, s->cmd.sidemove, right, s->cmd.upmove, up, wishvel);
1041 	}
1042 
1043 	// split wishvel into wishspeed and wishdir
1044 	wishspeed = VectorLength(wishvel);
1045 	if (wishspeed)
1046 		VectorScale(wishvel, 1 / wishspeed, wishdir);
1047 	else
1048 		VectorSet( wishdir, 0.0, 0.0, 0.0 );
1049 	wishspeed = min(wishspeed, cl.movevars_maxspeed) * 0.7;
1050 
1051 	if (s->crouched)
1052 		wishspeed *= 0.5;
1053 
1054 	if (s->waterjumptime <= 0)
1055 	{
1056 		// water friction
1057 		f = 1 - s->cmd.frametime * cl.movevars_waterfriction * (cls.protocol == PROTOCOL_QUAKEWORLD ? s->waterlevel : 1);
1058 		f = bound(0, f, 1);
1059 		VectorScale(s->velocity, f, s->velocity);
1060 
1061 		// water acceleration
1062 		f = wishspeed - DotProduct(s->velocity, wishdir);
1063 		if (f > 0)
1064 		{
1065 			f = min(cl.movevars_wateraccelerate * s->cmd.frametime * wishspeed, f);
1066 			VectorMA(s->velocity, f, wishdir, s->velocity);
1067 		}
1068 
1069 		// holding jump button swims upward slowly
1070 		if (s->cmd.jump)
1071 		{
1072 			if (s->watertype & SUPERCONTENTS_LAVA)
1073 				s->velocity[2] =  50;
1074 			else if (s->watertype & SUPERCONTENTS_SLIME)
1075 				s->velocity[2] =  80;
1076 			else
1077 			{
1078 				if (IS_NEXUIZ_DERIVED(gamemode))
1079 					s->velocity[2] = 200;
1080 				else
1081 					s->velocity[2] = 100;
1082 			}
1083 		}
1084 	}
1085 
1086 	CL_ClientMovement_Move(s);
1087 }
1088 
CL_IsMoveInDirection(vec_t forward,vec_t side,vec_t angle)1089 static vec_t CL_IsMoveInDirection(vec_t forward, vec_t side, vec_t angle)
1090 {
1091 	if(forward == 0 && side == 0)
1092 		return 0; // avoid division by zero
1093 	angle -= RAD2DEG(atan2(side, forward));
1094 	angle = (ANGLEMOD(angle + 180) - 180) / 45;
1095 	if(angle >  1)
1096 		return 0;
1097 	if(angle < -1)
1098 		return 0;
1099 	return 1 - fabs(angle);
1100 }
1101 
CL_GeomLerp(vec_t a,vec_t lerp,vec_t b)1102 static vec_t CL_GeomLerp(vec_t a, vec_t lerp, vec_t b)
1103 {
1104 	if(a == 0)
1105 	{
1106 		if(lerp < 1)
1107 			return 0;
1108 		else
1109 			return b;
1110 	}
1111 	if(b == 0)
1112 	{
1113 		if(lerp > 0)
1114 			return 0;
1115 		else
1116 			return a;
1117 	}
1118 	return a * pow(fabs(b / a), lerp);
1119 }
1120 
CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t * s,vec3_t wishdir,vec_t wishspeed)1121 static void CL_ClientMovement_Physics_CPM_PM_Aircontrol(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed)
1122 {
1123 	vec_t zspeed, speed, dot, k;
1124 
1125 #if 0
1126 	// this doesn't play well with analog input
1127 	if(s->cmd.forwardmove == 0 || s->cmd.sidemove != 0)
1128 		return;
1129 	k = 32;
1130 #else
1131 	k = 32 * (2 * CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, 0) - 1);
1132 	if(k <= 0)
1133 		return;
1134 #endif
1135 
1136 	k *= bound(0, wishspeed / cl.movevars_maxairspeed, 1);
1137 
1138 	zspeed = s->velocity[2];
1139 	s->velocity[2] = 0;
1140 	speed = VectorNormalizeLength(s->velocity);
1141 
1142 	dot = DotProduct(s->velocity, wishdir);
1143 
1144 	if(dot > 0) { // we can't change direction while slowing down
1145 		k *= pow(dot, cl.movevars_aircontrol_power)*s->cmd.frametime;
1146 		speed = max(0, speed - cl.movevars_aircontrol_penalty * sqrt(max(0, 1 - dot*dot)) * k/32);
1147 		k *= cl.movevars_aircontrol;
1148 		VectorMAM(speed, s->velocity, k, wishdir, s->velocity);
1149 		VectorNormalize(s->velocity);
1150 	}
1151 
1152 	VectorScale(s->velocity, speed, s->velocity);
1153 	s->velocity[2] = zspeed;
1154 }
1155 
CL_ClientMovement_Physics_AdjustAirAccelQW(float accelqw,float factor)1156 static float CL_ClientMovement_Physics_AdjustAirAccelQW(float accelqw, float factor)
1157 {
1158 	return
1159 		(accelqw < 0 ? -1 : +1)
1160 		*
1161 		bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1);
1162 }
1163 
CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t * s,vec3_t wishdir,vec_t wishspeed,vec_t wishspeed0,vec_t accel,vec_t accelqw,vec_t stretchfactor,vec_t sidefric,vec_t speedlimit)1164 static void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t wishspeed0, vec_t accel, vec_t accelqw, vec_t stretchfactor, vec_t sidefric, vec_t speedlimit)
1165 {
1166 	vec_t vel_straight;
1167 	vec_t vel_z;
1168 	vec3_t vel_perpend;
1169 	vec_t step;
1170 	vec3_t vel_xy;
1171 	vec_t vel_xy_current;
1172 	vec_t vel_xy_backward, vel_xy_forward;
1173 	vec_t speedclamp;
1174 
1175 	if(stretchfactor > 0)
1176 		speedclamp = stretchfactor;
1177 	else if(accelqw < 0)
1178 		speedclamp = 1;
1179 	else
1180 		speedclamp = -1; // no clamping
1181 
1182 	if(accelqw < 0)
1183 		accelqw = -accelqw;
1184 
1185 	if(cl.moveflags & MOVEFLAG_Q2AIRACCELERATE)
1186 		wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
1187 
1188 	vel_straight = DotProduct(s->velocity, wishdir);
1189 	vel_z = s->velocity[2];
1190 	VectorCopy(s->velocity, vel_xy); vel_xy[2] -= vel_z;
1191 	VectorMA(vel_xy, -vel_straight, wishdir, vel_perpend);
1192 
1193 	step = accel * s->cmd.frametime * wishspeed0;
1194 
1195 	vel_xy_current  = VectorLength(vel_xy);
1196 	if(speedlimit > 0)
1197 		accelqw = CL_ClientMovement_Physics_AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
1198 	vel_xy_forward  = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
1199 	vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
1200 	if(vel_xy_backward < 0)
1201 		vel_xy_backward = 0; // not that it REALLY occurs that this would cause wrong behaviour afterwards
1202 
1203 	vel_straight    = vel_straight   + bound(0, wishspeed - vel_straight,   step) * accelqw + step * (1 - accelqw);
1204 
1205 	if(sidefric < 0 && VectorLength2(vel_perpend))
1206 		// negative: only apply so much sideways friction to stay below the speed you could get by "braking"
1207 	{
1208 		vec_t f, fmin;
1209 		f = max(0, 1 + s->cmd.frametime * wishspeed * sidefric);
1210 		fmin = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / VectorLength2(vel_perpend);
1211 		// assume: fmin > 1
1212 		// vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
1213 		// vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend
1214 		// vel_xy_backward*vel_xy_backward > vel_xy * vel_xy
1215 		// obviously, this cannot be
1216 		if(fmin <= 0)
1217 			VectorScale(vel_perpend, f, vel_perpend);
1218 		else
1219 		{
1220 			fmin = sqrt(fmin);
1221 			VectorScale(vel_perpend, max(fmin, f), vel_perpend);
1222 		}
1223 	}
1224 	else
1225 		VectorScale(vel_perpend, max(0, 1 - s->cmd.frametime * wishspeed * sidefric), vel_perpend);
1226 
1227 	VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
1228 
1229 	if(speedclamp >= 0)
1230 	{
1231 		vec_t vel_xy_preclamp;
1232 		vel_xy_preclamp = VectorLength(s->velocity);
1233 		if(vel_xy_preclamp > 0) // prevent division by zero
1234 		{
1235 			vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
1236 			if(vel_xy_current < vel_xy_preclamp)
1237 				VectorScale(s->velocity, (vel_xy_current / vel_xy_preclamp), s->velocity);
1238 		}
1239 	}
1240 
1241 	s->velocity[2] += vel_z;
1242 }
1243 
CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t * s,vec3_t wishdir,vec_t wishspeed)1244 static void CL_ClientMovement_Physics_PM_AirAccelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed)
1245 {
1246     vec3_t curvel, wishvel, acceldir, curdir;
1247     float addspeed, accelspeed, curspeed;
1248     float dot;
1249 
1250     float airforwardaccel = cl.movevars_warsowbunny_airforwardaccel;
1251     float bunnyaccel = cl.movevars_warsowbunny_accel;
1252     float bunnytopspeed = cl.movevars_warsowbunny_topspeed;
1253     float turnaccel = cl.movevars_warsowbunny_turnaccel;
1254     float backtosideratio = cl.movevars_warsowbunny_backtosideratio;
1255 
1256     if( !wishspeed )
1257         return;
1258 
1259     VectorCopy( s->velocity, curvel );
1260     curvel[2] = 0;
1261     curspeed = VectorLength( curvel );
1262 
1263     if( wishspeed > curspeed * 1.01f )
1264     {
1265         float faccelspeed = curspeed + airforwardaccel * cl.movevars_maxairspeed * s->cmd.frametime;
1266         if( faccelspeed < wishspeed )
1267             wishspeed = faccelspeed;
1268     }
1269     else
1270     {
1271         float f = ( bunnytopspeed - curspeed ) / ( bunnytopspeed - cl.movevars_maxairspeed );
1272         if( f < 0 )
1273             f = 0;
1274         wishspeed = max( curspeed, cl.movevars_maxairspeed ) + bunnyaccel * f * cl.movevars_maxairspeed * s->cmd.frametime;
1275     }
1276     VectorScale( wishdir, wishspeed, wishvel );
1277     VectorSubtract( wishvel, curvel, acceldir );
1278     addspeed = VectorNormalizeLength( acceldir );
1279 
1280     accelspeed = turnaccel * cl.movevars_maxairspeed /* wishspeed */ * s->cmd.frametime;
1281     if( accelspeed > addspeed )
1282         accelspeed = addspeed;
1283 
1284     if( backtosideratio < 1.0f )
1285     {
1286         VectorNormalize2( curvel, curdir );
1287         dot = DotProduct( acceldir, curdir );
1288         if( dot < 0 )
1289             VectorMA( acceldir, -( 1.0f - backtosideratio ) * dot, curdir, acceldir );
1290     }
1291 
1292     VectorMA( s->velocity, accelspeed, acceldir, s->velocity );
1293 }
1294 
CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t * s)1295 static void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s)
1296 {
1297 	vec_t friction;
1298 	vec_t wishspeed;
1299 	vec_t addspeed;
1300 	vec_t accelspeed;
1301 	vec_t f;
1302 	vec_t gravity;
1303 	vec3_t forward;
1304 	vec3_t right;
1305 	vec3_t up;
1306 	vec3_t wishvel;
1307 	vec3_t wishdir;
1308 	vec3_t yawangles;
1309 	trace_t trace;
1310 
1311 	// jump if on ground with jump button pressed but only if it has been
1312 	// released at least once since the last jump
1313 	if (s->cmd.jump)
1314 	{
1315 		if (s->onground && (s->cmd.canjump || !cl_movement_track_canjump.integer))
1316 		{
1317 			s->velocity[2] += cl.movevars_jumpvelocity;
1318 			s->onground = false;
1319 			s->cmd.canjump = false;
1320 		}
1321 	}
1322 	else
1323 		s->cmd.canjump = true;
1324 
1325 	// calculate movement vector
1326 	VectorSet(yawangles, 0, s->cmd.viewangles[1], 0);
1327 	AngleVectors(yawangles, forward, right, up);
1328 	VectorMAM(s->cmd.forwardmove, forward, s->cmd.sidemove, right, wishvel);
1329 
1330 	// split wishvel into wishspeed and wishdir
1331 	wishspeed = VectorLength(wishvel);
1332 	if (wishspeed)
1333 		VectorScale(wishvel, 1 / wishspeed, wishdir);
1334 	else
1335 		VectorSet( wishdir, 0.0, 0.0, 0.0 );
1336 	// check if onground
1337 	if (s->onground)
1338 	{
1339 		wishspeed = min(wishspeed, cl.movevars_maxspeed);
1340 		if (s->crouched)
1341 			wishspeed *= 0.5;
1342 
1343 		// apply edge friction
1344 		f = sqrt(s->velocity[0] * s->velocity[0] + s->velocity[1] * s->velocity[1]);
1345 		if (f > 0)
1346 		{
1347 			friction = cl.movevars_friction;
1348 			if (cl.movevars_edgefriction != 1)
1349 			{
1350 				vec3_t neworigin2;
1351 				vec3_t neworigin3;
1352 				// note: QW uses the full player box for the trace, and yet still
1353 				// uses s->origin[2] + s->mins[2], which is clearly an bug, but
1354 				// this mimics it for compatibility
1355 				VectorSet(neworigin2, s->origin[0] + s->velocity[0]*(16/f), s->origin[1] + s->velocity[1]*(16/f), s->origin[2] + s->mins[2]);
1356 				VectorSet(neworigin3, neworigin2[0], neworigin2[1], neworigin2[2] - 34);
1357 				if (cls.protocol == PROTOCOL_QUAKEWORLD)
1358 					trace = CL_TraceBox(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true);
1359 				else
1360 					trace = CL_TraceLine(neworigin2, neworigin3, MOVE_NORMAL, s->self, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, 0, 0, collision_extendmovelength.value, true, true, NULL, true, false);
1361 				if (trace.fraction == 1 && !trace.startsolid)
1362 					friction *= cl.movevars_edgefriction;
1363 			}
1364 			// apply ground friction
1365 			f = 1 - s->cmd.frametime * friction * ((f < cl.movevars_stopspeed) ? (cl.movevars_stopspeed / f) : 1);
1366 			f = max(f, 0);
1367 			VectorScale(s->velocity, f, s->velocity);
1368 		}
1369 		addspeed = wishspeed - DotProduct(s->velocity, wishdir);
1370 		if (addspeed > 0)
1371 		{
1372 			accelspeed = min(cl.movevars_accelerate * s->cmd.frametime * wishspeed, addspeed);
1373 			VectorMA(s->velocity, accelspeed, wishdir, s->velocity);
1374 		}
1375 		gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime;
1376 		if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND))
1377 		{
1378 			if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
1379 				s->velocity[2] -= gravity * 0.5f;
1380 			else
1381 				s->velocity[2] -= gravity;
1382 		}
1383 		if (cls.protocol == PROTOCOL_QUAKEWORLD)
1384 			s->velocity[2] = 0;
1385 		if (VectorLength2(s->velocity))
1386 			CL_ClientMovement_Move(s);
1387 		if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground)
1388 		{
1389 			if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
1390 				s->velocity[2] -= gravity * 0.5f;
1391 		}
1392 	}
1393 	else
1394 	{
1395 		if (s->waterjumptime <= 0)
1396 		{
1397 			// apply air speed limit
1398 			vec_t accel, wishspeed0, wishspeed2, accelqw, strafity;
1399 			qboolean accelerating;
1400 
1401 			accelqw = cl.movevars_airaccel_qw;
1402 			wishspeed0 = wishspeed;
1403 			wishspeed = min(wishspeed, cl.movevars_maxairspeed);
1404 			if (s->crouched)
1405 				wishspeed *= 0.5;
1406 			accel = cl.movevars_airaccelerate;
1407 
1408 			accelerating = (DotProduct(s->velocity, wishdir) > 0);
1409 			wishspeed2 = wishspeed;
1410 
1411 			// CPM: air control
1412 			if(cl.movevars_airstopaccelerate != 0)
1413 			{
1414 				vec3_t curdir;
1415 				curdir[0] = s->velocity[0];
1416 				curdir[1] = s->velocity[1];
1417 				curdir[2] = 0;
1418 				VectorNormalize(curdir);
1419 				accel = accel + (cl.movevars_airstopaccelerate - accel) * max(0, -DotProduct(curdir, wishdir));
1420 			}
1421 			strafity = CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, -90) + CL_IsMoveInDirection(s->cmd.forwardmove, s->cmd.sidemove, +90); // if one is nonzero, other is always zero
1422 			if(cl.movevars_maxairstrafespeed)
1423 				wishspeed = min(wishspeed, CL_GeomLerp(cl.movevars_maxairspeed, strafity, cl.movevars_maxairstrafespeed));
1424 			if(cl.movevars_airstrafeaccelerate)
1425 				accel = CL_GeomLerp(cl.movevars_airaccelerate, strafity, cl.movevars_airstrafeaccelerate);
1426 			if(cl.movevars_airstrafeaccel_qw)
1427 				accelqw =
1428 					(((strafity > 0.5 ? cl.movevars_airstrafeaccel_qw : cl.movevars_airaccel_qw) >= 0) ? +1 : -1)
1429 					*
1430 					(1 - CL_GeomLerp(1 - fabs(cl.movevars_airaccel_qw), strafity, 1 - fabs(cl.movevars_airstrafeaccel_qw)));
1431 			// !CPM
1432 
1433 			if(cl.movevars_warsowbunny_turnaccel && accelerating && s->cmd.sidemove == 0 && s->cmd.forwardmove != 0)
1434 				CL_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2);
1435 			else
1436 				CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, cl.movevars_airaccel_qw_stretchfactor, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed, cl.movevars_airspeedlimit_nonqw);
1437 
1438 			if(cl.movevars_aircontrol)
1439 				CL_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2);
1440 		}
1441 		gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime;
1442 		if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
1443 			s->velocity[2] -= gravity * 0.5f;
1444 		else
1445 			s->velocity[2] -= gravity;
1446 		CL_ClientMovement_Move(s);
1447 		if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground)
1448 		{
1449 			if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
1450 				s->velocity[2] -= gravity * 0.5f;
1451 		}
1452 	}
1453 }
1454 
CL_ClientMovement_PlayerMove(cl_clientmovement_state_t * s)1455 static void CL_ClientMovement_PlayerMove(cl_clientmovement_state_t *s)
1456 {
1457 	//Con_Printf(" %f", frametime);
1458 	if (!s->cmd.jump)
1459 		s->cmd.canjump = true;
1460 	s->waterjumptime -= s->cmd.frametime;
1461 	CL_ClientMovement_UpdateStatus(s);
1462 	if (s->waterlevel >= WATERLEVEL_SWIMMING)
1463 		CL_ClientMovement_Physics_Swim(s);
1464 	else
1465 		CL_ClientMovement_Physics_Walk(s);
1466 }
1467 
1468 extern cvar_t slowmo;
CL_UpdateMoveVars(void)1469 void CL_UpdateMoveVars(void)
1470 {
1471 	if (cls.protocol == PROTOCOL_QUAKEWORLD)
1472 	{
1473 		cl.moveflags = 0;
1474 	}
1475 	else if (cl.stats[STAT_MOVEVARS_TICRATE])
1476 	{
1477 		cl.moveflags = cl.stats[STAT_MOVEFLAGS];
1478 		cl.movevars_ticrate = cl.statsf[STAT_MOVEVARS_TICRATE];
1479 		cl.movevars_timescale = cl.statsf[STAT_MOVEVARS_TIMESCALE];
1480 		cl.movevars_gravity = cl.statsf[STAT_MOVEVARS_GRAVITY];
1481 		cl.movevars_stopspeed = cl.statsf[STAT_MOVEVARS_STOPSPEED] ;
1482 		cl.movevars_maxspeed = cl.statsf[STAT_MOVEVARS_MAXSPEED];
1483 		cl.movevars_spectatormaxspeed = cl.statsf[STAT_MOVEVARS_SPECTATORMAXSPEED];
1484 		cl.movevars_accelerate = cl.statsf[STAT_MOVEVARS_ACCELERATE];
1485 		cl.movevars_airaccelerate = cl.statsf[STAT_MOVEVARS_AIRACCELERATE];
1486 		cl.movevars_wateraccelerate = cl.statsf[STAT_MOVEVARS_WATERACCELERATE];
1487 		cl.movevars_entgravity = cl.statsf[STAT_MOVEVARS_ENTGRAVITY];
1488 		cl.movevars_jumpvelocity = cl.statsf[STAT_MOVEVARS_JUMPVELOCITY];
1489 		cl.movevars_edgefriction = cl.statsf[STAT_MOVEVARS_EDGEFRICTION];
1490 		cl.movevars_maxairspeed = cl.statsf[STAT_MOVEVARS_MAXAIRSPEED];
1491 		cl.movevars_stepheight = cl.statsf[STAT_MOVEVARS_STEPHEIGHT];
1492 		cl.movevars_airaccel_qw = cl.statsf[STAT_MOVEVARS_AIRACCEL_QW];
1493 		cl.movevars_airaccel_qw_stretchfactor = cl.statsf[STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR];
1494 		cl.movevars_airaccel_sideways_friction = cl.statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION];
1495 		cl.movevars_friction = cl.statsf[STAT_MOVEVARS_FRICTION];
1496 		cl.movevars_wallfriction = cl.statsf[STAT_MOVEVARS_WALLFRICTION];
1497 		cl.movevars_waterfriction = cl.statsf[STAT_MOVEVARS_WATERFRICTION];
1498 		cl.movevars_airstopaccelerate = cl.statsf[STAT_MOVEVARS_AIRSTOPACCELERATE];
1499 		cl.movevars_airstrafeaccelerate = cl.statsf[STAT_MOVEVARS_AIRSTRAFEACCELERATE];
1500 		cl.movevars_maxairstrafespeed = cl.statsf[STAT_MOVEVARS_MAXAIRSTRAFESPEED];
1501 		cl.movevars_airstrafeaccel_qw = cl.statsf[STAT_MOVEVARS_AIRSTRAFEACCEL_QW];
1502 		cl.movevars_aircontrol = cl.statsf[STAT_MOVEVARS_AIRCONTROL];
1503 		cl.movevars_aircontrol_power = cl.statsf[STAT_MOVEVARS_AIRCONTROL_POWER];
1504 		cl.movevars_aircontrol_penalty = cl.statsf[STAT_MOVEVARS_AIRCONTROL_PENALTY];
1505 		cl.movevars_warsowbunny_airforwardaccel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL];
1506 		cl.movevars_warsowbunny_accel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_ACCEL];
1507 		cl.movevars_warsowbunny_topspeed = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED];
1508 		cl.movevars_warsowbunny_turnaccel = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL];
1509 		cl.movevars_warsowbunny_backtosideratio = cl.statsf[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO];
1510 		cl.movevars_airspeedlimit_nonqw = cl.statsf[STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW];
1511 	}
1512 	else
1513 	{
1514 		cl.moveflags = 0;
1515 		cl.movevars_ticrate = (cls.demoplayback ? 1.0f : slowmo.value) / bound(1.0f, cl_netfps.value, 1000.0f);
1516 		cl.movevars_timescale = (cls.demoplayback ? 1.0f : slowmo.value);
1517 		cl.movevars_gravity = sv_gravity.value;
1518 		cl.movevars_stopspeed = cl_movement_stopspeed.value;
1519 		cl.movevars_maxspeed = cl_movement_maxspeed.value;
1520 		cl.movevars_spectatormaxspeed = cl_movement_maxspeed.value;
1521 		cl.movevars_accelerate = cl_movement_accelerate.value;
1522 		cl.movevars_airaccelerate = cl_movement_airaccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_airaccelerate.value;
1523 		cl.movevars_wateraccelerate = cl_movement_wateraccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_wateraccelerate.value;
1524 		cl.movevars_friction = cl_movement_friction.value;
1525 		cl.movevars_wallfriction = cl_movement_wallfriction.value;
1526 		cl.movevars_waterfriction = cl_movement_waterfriction.value < 0 ? cl_movement_friction.value : cl_movement_waterfriction.value;
1527 		cl.movevars_entgravity = 1;
1528 		cl.movevars_jumpvelocity = cl_movement_jumpvelocity.value;
1529 		cl.movevars_edgefriction = cl_movement_edgefriction.value;
1530 		cl.movevars_maxairspeed = cl_movement_maxairspeed.value;
1531 		cl.movevars_stepheight = cl_movement_stepheight.value;
1532 		cl.movevars_airaccel_qw = cl_movement_airaccel_qw.value;
1533 		cl.movevars_airaccel_qw_stretchfactor = 0;
1534 		cl.movevars_airaccel_sideways_friction = cl_movement_airaccel_sideways_friction.value;
1535 		cl.movevars_airstopaccelerate = 0;
1536 		cl.movevars_airstrafeaccelerate = 0;
1537 		cl.movevars_maxairstrafespeed = 0;
1538 		cl.movevars_airstrafeaccel_qw = 0;
1539 		cl.movevars_aircontrol = 0;
1540 		cl.movevars_aircontrol_power = 2;
1541 		cl.movevars_aircontrol_penalty = 0;
1542 		cl.movevars_warsowbunny_airforwardaccel = 0;
1543 		cl.movevars_warsowbunny_accel = 0;
1544 		cl.movevars_warsowbunny_topspeed = 0;
1545 		cl.movevars_warsowbunny_turnaccel = 0;
1546 		cl.movevars_warsowbunny_backtosideratio = 0;
1547 		cl.movevars_airspeedlimit_nonqw = 0;
1548 	}
1549 
1550 	if(!(cl.moveflags & MOVEFLAG_VALID))
1551 	{
1552 		if(gamemode == GAME_NEXUIZ)  // Legacy hack to work with old servers of Nexuiz.
1553 			cl.moveflags = MOVEFLAG_Q2AIRACCELERATE;
1554 	}
1555 
1556 	if(cl.movevars_aircontrol_power <= 0)
1557 		cl.movevars_aircontrol_power = 2; // CPMA default
1558 }
1559 
CL_ClientMovement_PlayerMove_Frame(cl_clientmovement_state_t * s)1560 void CL_ClientMovement_PlayerMove_Frame(cl_clientmovement_state_t *s)
1561 {
1562 	// if a move is more than 50ms, do it as two moves (matching qwsv)
1563 	//Con_Printf("%i ", s.cmd.msec);
1564 	if(s->cmd.frametime > 0.0005)
1565 	{
1566 		if (s->cmd.frametime > 0.05)
1567 		{
1568 			s->cmd.frametime /= 2;
1569 			CL_ClientMovement_PlayerMove(s);
1570 		}
1571 		CL_ClientMovement_PlayerMove(s);
1572 	}
1573 	else
1574 	{
1575 		// we REALLY need this handling to happen, even if the move is not executed
1576 		if (!s->cmd.jump)
1577 			s->cmd.canjump = true;
1578 	}
1579 }
1580 
CL_ClientMovement_Replay(void)1581 void CL_ClientMovement_Replay(void)
1582 {
1583 	int i;
1584 	double totalmovemsec;
1585 	cl_clientmovement_state_t s;
1586 
1587 	VectorCopy(cl.mvelocity[0], cl.movement_velocity);
1588 
1589 	if (cl.movement_predicted && !cl.movement_replay)
1590 		return;
1591 
1592 	if (!cl_movement_replay.integer)
1593 		return;
1594 
1595 	// set up starting state for the series of moves
1596 	memset(&s, 0, sizeof(s));
1597 	VectorCopy(cl.entities[cl.playerentity].state_current.origin, s.origin);
1598 	VectorCopy(cl.mvelocity[0], s.velocity);
1599 	s.crouched = true; // will be updated on first move
1600 	//Con_Printf("movement replay starting org %f %f %f vel %f %f %f\n", s.origin[0], s.origin[1], s.origin[2], s.velocity[0], s.velocity[1], s.velocity[2]);
1601 
1602 	totalmovemsec = 0;
1603 	for (i = 0;i < CL_MAX_USERCMDS;i++)
1604 		if (cl.movecmd[i].sequence > cls.servermovesequence)
1605 			totalmovemsec += cl.movecmd[i].msec;
1606 	cl.movement_predicted = totalmovemsec >= cl_movement_minping.value && cls.servermovesequence && (cl_movement.integer && !cls.demoplayback && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission);
1607 	//Con_Printf("%i = %.0f >= %.0f && %u && (%i && %i && %i == %i && %i > 0 && %i\n", cl.movement_predicted, totalmovemsec, cl_movement_minping.value, cls.servermovesequence, cl_movement.integer, !cls.demoplayback, cls.signon, SIGNONS, cl.stats[STAT_HEALTH], !cl.intermission);
1608 	if (cl.movement_predicted)
1609 	{
1610 		//Con_Printf("%ims\n", cl.movecmd[0].msec);
1611 
1612 		// replay the input queue to predict current location
1613 		// note: this relies on the fact there's always one queue item at the end
1614 
1615 		// find how many are still valid
1616 		for (i = 0;i < CL_MAX_USERCMDS;i++)
1617 			if (cl.movecmd[i].sequence <= cls.servermovesequence)
1618 				break;
1619 		// now walk them in oldest to newest order
1620 		for (i--;i >= 0;i--)
1621 		{
1622 			s.cmd = cl.movecmd[i];
1623 			if (i < CL_MAX_USERCMDS - 1)
1624 				s.cmd.canjump = cl.movecmd[i+1].canjump;
1625 
1626 			CL_ClientMovement_PlayerMove_Frame(&s);
1627 
1628 			cl.movecmd[i].canjump = s.cmd.canjump;
1629 		}
1630 		//Con_Printf("\n");
1631 		CL_ClientMovement_UpdateStatus(&s);
1632 	}
1633 	else
1634 	{
1635 		// get the first movement queue entry to know whether to crouch and such
1636 		s.cmd = cl.movecmd[0];
1637 	}
1638 
1639 	if (!cls.demoplayback) // for bob, speedometer
1640 	{
1641 		cl.movement_replay = false;
1642 		// update the interpolation target position and velocity
1643 		VectorCopy(s.origin, cl.movement_origin);
1644 		VectorCopy(s.velocity, cl.movement_velocity);
1645 	}
1646 
1647 	// update the onground flag if appropriate
1648 	if (cl.movement_predicted)
1649 	{
1650 		// when predicted we simply set the flag according to the UpdateStatus
1651 		cl.onground = s.onground;
1652 	}
1653 	else
1654 	{
1655 		// when not predicted, cl.onground is cleared by cl_parse.c each time
1656 		// an update packet is received, but can be forced on here to hide
1657 		// server inconsistencies in the onground flag
1658 		// (which mostly occur when stepping up stairs at very high framerates
1659 		//  where after the step up the move continues forward and not
1660 		//  downward so the ground is not detected)
1661 		//
1662 		// such onground inconsistencies can cause jittery gun bobbing and
1663 		// stair smoothing, so we set onground if UpdateStatus says so
1664 		if (s.onground)
1665 			cl.onground = true;
1666 	}
1667 }
1668 
QW_MSG_WriteDeltaUsercmd(sizebuf_t * buf,usercmd_t * from,usercmd_t * to)1669 static void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *to)
1670 {
1671 	int bits;
1672 
1673 	bits = 0;
1674 	if (to->viewangles[0] != from->viewangles[0])
1675 		bits |= QW_CM_ANGLE1;
1676 	if (to->viewangles[1] != from->viewangles[1])
1677 		bits |= QW_CM_ANGLE2;
1678 	if (to->viewangles[2] != from->viewangles[2])
1679 		bits |= QW_CM_ANGLE3;
1680 	if (to->forwardmove != from->forwardmove)
1681 		bits |= QW_CM_FORWARD;
1682 	if (to->sidemove != from->sidemove)
1683 		bits |= QW_CM_SIDE;
1684 	if (to->upmove != from->upmove)
1685 		bits |= QW_CM_UP;
1686 	if (to->buttons != from->buttons)
1687 		bits |= QW_CM_BUTTONS;
1688 	if (to->impulse != from->impulse)
1689 		bits |= QW_CM_IMPULSE;
1690 
1691 	MSG_WriteByte(buf, bits);
1692 	if (bits & QW_CM_ANGLE1)
1693 		MSG_WriteAngle16i(buf, to->viewangles[0]);
1694 	if (bits & QW_CM_ANGLE2)
1695 		MSG_WriteAngle16i(buf, to->viewangles[1]);
1696 	if (bits & QW_CM_ANGLE3)
1697 		MSG_WriteAngle16i(buf, to->viewangles[2]);
1698 	if (bits & QW_CM_FORWARD)
1699 		MSG_WriteShort(buf, (short) to->forwardmove);
1700 	if (bits & QW_CM_SIDE)
1701 		MSG_WriteShort(buf, (short) to->sidemove);
1702 	if (bits & QW_CM_UP)
1703 		MSG_WriteShort(buf, (short) to->upmove);
1704 	if (bits & QW_CM_BUTTONS)
1705 		MSG_WriteByte(buf, to->buttons);
1706 	if (bits & QW_CM_IMPULSE)
1707 		MSG_WriteByte(buf, to->impulse);
1708 	MSG_WriteByte(buf, to->msec);
1709 }
1710 
CL_NewFrameReceived(int num)1711 void CL_NewFrameReceived(int num)
1712 {
1713 	if (developer_networkentities.integer >= 10)
1714 		Con_Printf("recv: svc_entities %i\n", num);
1715 	cl.latestframenums[cl.latestframenumsposition] = num;
1716 	cl.latestsendnums[cl.latestframenumsposition] = cl.cmd.sequence;
1717 	cl.latestframenumsposition = (cl.latestframenumsposition + 1) % LATESTFRAMENUMS;
1718 }
1719 
CL_RotateMoves(const matrix4x4_t * m)1720 void CL_RotateMoves(const matrix4x4_t *m)
1721 {
1722 	// rotate viewangles in all previous moves
1723 	vec3_t v;
1724 	vec3_t f, r, u;
1725 	int i;
1726 	for (i = 0;i < CL_MAX_USERCMDS;i++)
1727 	{
1728 		if (cl.movecmd[i].sequence > cls.servermovesequence)
1729 		{
1730 			usercmd_t *c = &cl.movecmd[i];
1731 			AngleVectors(c->viewangles, f, r, u);
1732 			Matrix4x4_Transform(m, f, v); VectorCopy(v, f);
1733 			Matrix4x4_Transform(m, u, v); VectorCopy(v, u);
1734 			AnglesFromVectors(c->viewangles, f, u, false);
1735 		}
1736 	}
1737 }
1738 
1739 /*
1740 ==============
1741 CL_SendMove
1742 ==============
1743 */
1744 usercmd_t nullcmd; // for delta compression of qw moves
CL_SendMove(void)1745 void CL_SendMove(void)
1746 {
1747 	int i, j, packetloss;
1748 	int checksumindex;
1749 	int bits;
1750 	int maxusercmds;
1751 	usercmd_t *cmd;
1752 	sizebuf_t buf;
1753 	unsigned char data[1024];
1754 	double packettime;
1755 	int msecdelta;
1756 	qboolean quemove;
1757 	qboolean important;
1758 
1759 	// if playing a demo, do nothing
1760 	if (!cls.netcon)
1761 		return;
1762 
1763 	// we don't que moves during a lag spike (potential network timeout)
1764 	quemove = realtime - cl.last_received_message < cl_movement_nettimeout.value;
1765 
1766 	// we build up cl.cmd and then decide whether to send or not
1767 	// we store this into cl.movecmd[0] for prediction each frame even if we
1768 	// do not send, to make sure that prediction is instant
1769 	cl.cmd.time = cl.time;
1770 	cl.cmd.sequence = cls.netcon->outgoing_unreliable_sequence;
1771 
1772 	// set button bits
1773 	// LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
1774 	bits = 0;
1775 	if (in_attack.state   & 3) bits |=   1;
1776 	if (in_jump.state     & 3) bits |=   2;
1777 	if (in_button3.state  & 3) bits |=   4;
1778 	if (in_button4.state  & 3) bits |=   8;
1779 	if (in_button5.state  & 3) bits |=  16;
1780 	if (in_button6.state  & 3) bits |=  32;
1781 	if (in_button7.state  & 3) bits |=  64;
1782 	if (in_button8.state  & 3) bits |= 128;
1783 	if (in_use.state      & 3) bits |= 256;
1784 	if (key_dest != key_game || key_consoleactive) bits |= 512;
1785 	if (cl_prydoncursor.integer > 0) bits |= 1024;
1786 	if (in_button9.state  & 3)  bits |=   2048;
1787 	if (in_button10.state  & 3) bits |=   4096;
1788 	if (in_button11.state  & 3) bits |=   8192;
1789 	if (in_button12.state  & 3) bits |=  16384;
1790 	if (in_button13.state  & 3) bits |=  32768;
1791 	if (in_button14.state  & 3) bits |=  65536;
1792 	if (in_button15.state  & 3) bits |= 131072;
1793 	if (in_button16.state  & 3) bits |= 262144;
1794 	// button bits 19-31 unused currently
1795 	// rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
1796 	if(cl_prydoncursor.integer > 0)
1797 	{
1798 		if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
1799 		if (cl.cmd.cursor_screen[0] >=  1) bits |= 16;
1800 		if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
1801 		if (cl.cmd.cursor_screen[1] >=  1) bits |= 64;
1802 	}
1803 
1804 	// set buttons and impulse
1805 	cl.cmd.buttons = bits;
1806 	cl.cmd.impulse = in_impulse;
1807 
1808 	// set viewangles
1809 	VectorCopy(cl.viewangles, cl.cmd.viewangles);
1810 
1811 	msecdelta = (int)(floor(cl.cmd.time * 1000) - floor(cl.movecmd[1].time * 1000));
1812 	cl.cmd.msec = (unsigned char)bound(0, msecdelta, 255);
1813 	// ridiculous value rejection (matches qw)
1814 	if (cl.cmd.msec > 250)
1815 		cl.cmd.msec = 100;
1816 	cl.cmd.frametime = cl.cmd.msec * (1.0 / 1000.0);
1817 
1818 	switch(cls.protocol)
1819 	{
1820 	case PROTOCOL_QUAKEWORLD:
1821 		// quakeworld uses a different cvar with opposite meaning, for compatibility
1822 		cl.cmd.predicted = cl_nopred.integer == 0;
1823 		break;
1824 	case PROTOCOL_DARKPLACES6:
1825 	case PROTOCOL_DARKPLACES7:
1826 		cl.cmd.predicted = cl_movement.integer != 0;
1827 		break;
1828 	default:
1829 		cl.cmd.predicted = false;
1830 		break;
1831 	}
1832 
1833 	// movement is set by input code (forwardmove/sidemove/upmove)
1834 	// always dump the first two moves, because they may contain leftover inputs from the last level
1835 	if (cl.cmd.sequence <= 2)
1836 		cl.cmd.forwardmove = cl.cmd.sidemove = cl.cmd.upmove = cl.cmd.impulse = cl.cmd.buttons = 0;
1837 
1838 	cl.cmd.jump = (cl.cmd.buttons & 2) != 0;
1839 	cl.cmd.crouch = 0;
1840 	switch (cls.protocol)
1841 	{
1842 	case PROTOCOL_QUAKEWORLD:
1843 	case PROTOCOL_QUAKE:
1844 	case PROTOCOL_QUAKEDP:
1845 	case PROTOCOL_NEHAHRAMOVIE:
1846 	case PROTOCOL_NEHAHRABJP:
1847 	case PROTOCOL_NEHAHRABJP2:
1848 	case PROTOCOL_NEHAHRABJP3:
1849 	case PROTOCOL_DARKPLACES1:
1850 	case PROTOCOL_DARKPLACES2:
1851 	case PROTOCOL_DARKPLACES3:
1852 	case PROTOCOL_DARKPLACES4:
1853 	case PROTOCOL_DARKPLACES5:
1854 		break;
1855 	case PROTOCOL_DARKPLACES6:
1856 	case PROTOCOL_DARKPLACES7:
1857 		// FIXME: cl.cmd.buttons & 16 is +button5, Nexuiz/Xonotic specific
1858 		cl.cmd.crouch = (cl.cmd.buttons & 16) != 0;
1859 		break;
1860 	case PROTOCOL_UNKNOWN:
1861 		break;
1862 	}
1863 
1864 	if (quemove)
1865 		cl.movecmd[0] = cl.cmd;
1866 
1867 	// don't predict more than 200fps
1868 	if (realtime >= cl.lastpackettime + 0.005)
1869 		cl.movement_replay = true; // redo the prediction
1870 
1871 	// now decide whether to actually send this move
1872 	// (otherwise it is only for prediction)
1873 
1874 	// don't send too often or else network connections can get clogged by a
1875 	// high renderer framerate
1876 	packettime = 1.0 / bound(1, cl_netfps.value, 1000);
1877 	if (cl.movevars_timescale && cl.movevars_ticrate)
1878 	{
1879 		float maxtic = cl.movevars_ticrate / cl.movevars_timescale;
1880 		packettime = min(packettime, maxtic);
1881 	}
1882 
1883 	// do not send 0ms packets because they mess up physics
1884 	if(cl.cmd.msec == 0 && cl.time > cl.oldtime && (cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS))
1885 		return;
1886 	// always send if buttons changed or an impulse is pending
1887 	// even if it violates the rate limit!
1888 	important = (cl.cmd.impulse || (cl_netimmediatebuttons.integer && cl.cmd.buttons != cl.movecmd[1].buttons));
1889 	// don't send too often (cl_netfps)
1890 	if (!important && realtime < cl.lastpackettime + packettime)
1891 		return;
1892 	// don't choke the connection with packets (obey rate limit)
1893 	// it is important that this check be last, because it adds a new
1894 	// frame to the shownetgraph output and any cancelation after this
1895 	// will produce a nasty spike-like look to the netgraph
1896 	// we also still send if it is important
1897 	if (!NetConn_CanSend(cls.netcon) && !important)
1898 		return;
1899 	// try to round off the lastpackettime to a multiple of the packet interval
1900 	// (this causes it to emit packets at a steady beat)
1901 	if (packettime > 0)
1902 		cl.lastpackettime = floor(realtime / packettime) * packettime;
1903 	else
1904 		cl.lastpackettime = realtime;
1905 
1906 	buf.maxsize = sizeof(data);
1907 	buf.cursize = 0;
1908 	buf.data = data;
1909 
1910 	// send the movement message
1911 	// PROTOCOL_QUAKE        clc_move = 16 bytes total
1912 	// PROTOCOL_QUAKEDP      clc_move = 16 bytes total
1913 	// PROTOCOL_NEHAHRAMOVIE clc_move = 16 bytes total
1914 	// PROTOCOL_DARKPLACES1  clc_move = 19 bytes total
1915 	// PROTOCOL_DARKPLACES2  clc_move = 25 bytes total
1916 	// PROTOCOL_DARKPLACES3  clc_move = 25 bytes total
1917 	// PROTOCOL_DARKPLACES4  clc_move = 19 bytes total
1918 	// PROTOCOL_DARKPLACES5  clc_move = 19 bytes total
1919 	// PROTOCOL_DARKPLACES6  clc_move = 52 bytes total
1920 	// PROTOCOL_DARKPLACES7  clc_move = 56 bytes total per move (can be up to 16 moves)
1921 	// PROTOCOL_QUAKEWORLD   clc_move = 34 bytes total (typically, but can reach 43 bytes, or even 49 bytes with roll)
1922 
1923 	// set prydon cursor info
1924 	CL_UpdatePrydonCursor();
1925 
1926 	if (cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS)
1927 	{
1928 		switch (cls.protocol)
1929 		{
1930 		case PROTOCOL_QUAKEWORLD:
1931 			MSG_WriteByte(&buf, qw_clc_move);
1932 			// save the position for a checksum byte
1933 			checksumindex = buf.cursize;
1934 			MSG_WriteByte(&buf, 0);
1935 			// packet loss percentage
1936 			for (j = 0, packetloss = 0;j < NETGRAPH_PACKETS;j++)
1937 				if (cls.netcon->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
1938 					packetloss++;
1939 			packetloss = packetloss * 100 / NETGRAPH_PACKETS;
1940 			MSG_WriteByte(&buf, packetloss);
1941 			// write most recent 3 moves
1942 			QW_MSG_WriteDeltaUsercmd(&buf, &nullcmd, &cl.movecmd[2]);
1943 			QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[2], &cl.movecmd[1]);
1944 			QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[1], &cl.cmd);
1945 			// calculate the checksum
1946 			buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->outgoing_unreliable_sequence);
1947 			// if delta compression history overflows, request no delta
1948 			if (cls.netcon->outgoing_unreliable_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1)
1949 				cl.qw_validsequence = 0;
1950 			// request delta compression if appropriate
1951 			if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording)
1952 			{
1953 				cl.qw_deltasequence[cls.netcon->outgoing_unreliable_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
1954 				MSG_WriteByte(&buf, qw_clc_delta);
1955 				MSG_WriteByte(&buf, cl.qw_validsequence & 255);
1956 			}
1957 			else
1958 				cl.qw_deltasequence[cls.netcon->outgoing_unreliable_sequence & QW_UPDATE_MASK] = -1;
1959 			break;
1960 		case PROTOCOL_QUAKE:
1961 		case PROTOCOL_QUAKEDP:
1962 		case PROTOCOL_NEHAHRAMOVIE:
1963 		case PROTOCOL_NEHAHRABJP:
1964 		case PROTOCOL_NEHAHRABJP2:
1965 		case PROTOCOL_NEHAHRABJP3:
1966 			// 5 bytes
1967 			MSG_WriteByte (&buf, clc_move);
1968 			MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time
1969 			// 3 bytes (6 bytes in proquake)
1970 			if (cls.proquake_servermod == 1) // MOD_PROQUAKE
1971 			{
1972 				for (i = 0;i < 3;i++)
1973 					MSG_WriteAngle16i (&buf, cl.cmd.viewangles[i]);
1974 			}
1975 			else
1976 			{
1977 				for (i = 0;i < 3;i++)
1978 					MSG_WriteAngle8i (&buf, cl.cmd.viewangles[i]);
1979 			}
1980 			// 6 bytes
1981 			MSG_WriteCoord16i (&buf, cl.cmd.forwardmove);
1982 			MSG_WriteCoord16i (&buf, cl.cmd.sidemove);
1983 			MSG_WriteCoord16i (&buf, cl.cmd.upmove);
1984 			// 2 bytes
1985 			MSG_WriteByte (&buf, cl.cmd.buttons);
1986 			MSG_WriteByte (&buf, cl.cmd.impulse);
1987 			break;
1988 		case PROTOCOL_DARKPLACES2:
1989 		case PROTOCOL_DARKPLACES3:
1990 			// 5 bytes
1991 			MSG_WriteByte (&buf, clc_move);
1992 			MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time
1993 			// 12 bytes
1994 			for (i = 0;i < 3;i++)
1995 				MSG_WriteAngle32f (&buf, cl.cmd.viewangles[i]);
1996 			// 6 bytes
1997 			MSG_WriteCoord16i (&buf, cl.cmd.forwardmove);
1998 			MSG_WriteCoord16i (&buf, cl.cmd.sidemove);
1999 			MSG_WriteCoord16i (&buf, cl.cmd.upmove);
2000 			// 2 bytes
2001 			MSG_WriteByte (&buf, cl.cmd.buttons);
2002 			MSG_WriteByte (&buf, cl.cmd.impulse);
2003 			break;
2004 		case PROTOCOL_DARKPLACES1:
2005 		case PROTOCOL_DARKPLACES4:
2006 		case PROTOCOL_DARKPLACES5:
2007 			// 5 bytes
2008 			MSG_WriteByte (&buf, clc_move);
2009 			MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time
2010 			// 6 bytes
2011 			for (i = 0;i < 3;i++)
2012 				MSG_WriteAngle16i (&buf, cl.cmd.viewangles[i]);
2013 			// 6 bytes
2014 			MSG_WriteCoord16i (&buf, cl.cmd.forwardmove);
2015 			MSG_WriteCoord16i (&buf, cl.cmd.sidemove);
2016 			MSG_WriteCoord16i (&buf, cl.cmd.upmove);
2017 			// 2 bytes
2018 			MSG_WriteByte (&buf, cl.cmd.buttons);
2019 			MSG_WriteByte (&buf, cl.cmd.impulse);
2020 		case PROTOCOL_DARKPLACES6:
2021 		case PROTOCOL_DARKPLACES7:
2022 			// set the maxusercmds variable to limit how many should be sent
2023 			maxusercmds = bound(1, cl_netrepeatinput.integer + 1, min(3, CL_MAX_USERCMDS));
2024 			// when movement prediction is off, there's not much point in repeating old input as it will just be ignored
2025 			if (!cl.cmd.predicted)
2026 				maxusercmds = 1;
2027 
2028 			// send the latest moves in order, the old ones will be
2029 			// ignored by the server harmlessly, however if the previous
2030 			// packets were lost these moves will be used
2031 			//
2032 			// this reduces packet loss impact on gameplay.
2033 			for (j = 0, cmd = &cl.movecmd[maxusercmds-1];j < maxusercmds;j++, cmd--)
2034 			{
2035 				// don't repeat any stale moves
2036 				if (cmd->sequence && cmd->sequence < cls.servermovesequence)
2037 					continue;
2038 				// 5/9 bytes
2039 				MSG_WriteByte (&buf, clc_move);
2040 				if (cls.protocol != PROTOCOL_DARKPLACES6)
2041 					MSG_WriteLong (&buf, cmd->predicted ? cmd->sequence : 0);
2042 				MSG_WriteFloat (&buf, cmd->time); // last server packet time
2043 				// 6 bytes
2044 				for (i = 0;i < 3;i++)
2045 					MSG_WriteAngle16i (&buf, cmd->viewangles[i]);
2046 				// 6 bytes
2047 				MSG_WriteCoord16i (&buf, cmd->forwardmove);
2048 				MSG_WriteCoord16i (&buf, cmd->sidemove);
2049 				MSG_WriteCoord16i (&buf, cmd->upmove);
2050 				// 5 bytes
2051 				MSG_WriteLong (&buf, cmd->buttons);
2052 				MSG_WriteByte (&buf, cmd->impulse);
2053 				// PRYDON_CLIENTCURSOR
2054 				// 30 bytes
2055 				MSG_WriteShort (&buf, (short)(cmd->cursor_screen[0] * 32767.0f));
2056 				MSG_WriteShort (&buf, (short)(cmd->cursor_screen[1] * 32767.0f));
2057 				MSG_WriteFloat (&buf, cmd->cursor_start[0]);
2058 				MSG_WriteFloat (&buf, cmd->cursor_start[1]);
2059 				MSG_WriteFloat (&buf, cmd->cursor_start[2]);
2060 				MSG_WriteFloat (&buf, cmd->cursor_impact[0]);
2061 				MSG_WriteFloat (&buf, cmd->cursor_impact[1]);
2062 				MSG_WriteFloat (&buf, cmd->cursor_impact[2]);
2063 				MSG_WriteShort (&buf, cmd->cursor_entitynumber);
2064 			}
2065 			break;
2066 		case PROTOCOL_UNKNOWN:
2067 			break;
2068 		}
2069 	}
2070 
2071 	if (cls.protocol != PROTOCOL_QUAKEWORLD && buf.cursize)
2072 	{
2073 		// ack entity frame numbers received since the last input was sent
2074 		// (redundent to improve handling of client->server packet loss)
2075 		// if cl_netrepeatinput is 1 and client framerate matches server
2076 		// framerate, this is 10 bytes, if client framerate is lower this
2077 		// will be more...
2078 		unsigned int oldsequence = cl.cmd.sequence;
2079 		unsigned int delta = bound(1, cl_netrepeatinput.integer + 1, 3);
2080 		if (oldsequence > delta)
2081 			oldsequence = oldsequence - delta;
2082 		else
2083 			oldsequence = 1;
2084 		for (i = 0;i < LATESTFRAMENUMS;i++)
2085 		{
2086 			j = (cl.latestframenumsposition + i) % LATESTFRAMENUMS;
2087 			if (cl.latestsendnums[j] >= oldsequence)
2088 			{
2089 				if (developer_networkentities.integer >= 10)
2090 					Con_Printf("send clc_ackframe %i\n", cl.latestframenums[j]);
2091 				MSG_WriteByte(&buf, clc_ackframe);
2092 				MSG_WriteLong(&buf, cl.latestframenums[j]);
2093 			}
2094 		}
2095 	}
2096 
2097 	// PROTOCOL_DARKPLACES6 = 67 bytes per packet
2098 	// PROTOCOL_DARKPLACES7 = 71 bytes per packet
2099 
2100 	// acknowledge any recently received data blocks
2101 	for (i = 0;i < CL_MAX_DOWNLOADACKS && (cls.dp_downloadack[i].start || cls.dp_downloadack[i].size);i++)
2102 	{
2103 		MSG_WriteByte(&buf, clc_ackdownloaddata);
2104 		MSG_WriteLong(&buf, cls.dp_downloadack[i].start);
2105 		MSG_WriteShort(&buf, cls.dp_downloadack[i].size);
2106 		cls.dp_downloadack[i].start = 0;
2107 		cls.dp_downloadack[i].size = 0;
2108 	}
2109 
2110 	// send the reliable message (forwarded commands) if there is one
2111 	if (buf.cursize || cls.netcon->message.cursize)
2112 		NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), cl_rate_burstsize.integer, false);
2113 
2114 	if (quemove)
2115 	{
2116 		// update the cl.movecmd array which holds the most recent moves,
2117 		// because we now need a new slot for the next input
2118 		for (i = CL_MAX_USERCMDS - 1;i >= 1;i--)
2119 			cl.movecmd[i] = cl.movecmd[i-1];
2120 		cl.movecmd[0].msec = 0;
2121 		cl.movecmd[0].frametime = 0;
2122 	}
2123 
2124 	// clear button 'click' states
2125 	in_attack.state  &= ~2;
2126 	in_jump.state    &= ~2;
2127 	in_button3.state &= ~2;
2128 	in_button4.state &= ~2;
2129 	in_button5.state &= ~2;
2130 	in_button6.state &= ~2;
2131 	in_button7.state &= ~2;
2132 	in_button8.state &= ~2;
2133 	in_use.state     &= ~2;
2134 	in_button9.state  &= ~2;
2135 	in_button10.state &= ~2;
2136 	in_button11.state &= ~2;
2137 	in_button12.state &= ~2;
2138 	in_button13.state &= ~2;
2139 	in_button14.state &= ~2;
2140 	in_button15.state &= ~2;
2141 	in_button16.state &= ~2;
2142 	// clear impulse
2143 	in_impulse = 0;
2144 
2145 	if (cls.netcon->message.overflowed)
2146 	{
2147 		Con_Print("CL_SendMove: lost server connection\n");
2148 		CL_Disconnect();
2149 		SV_LockThreadMutex();
2150 		Host_ShutdownServer();
2151 		SV_UnlockThreadMutex();
2152 	}
2153 }
2154 
2155 /*
2156 ============
2157 CL_InitInput
2158 ============
2159 */
CL_InitInput(void)2160 void CL_InitInput (void)
2161 {
2162 	Cmd_AddCommand ("+moveup",IN_UpDown, "swim upward");
2163 	Cmd_AddCommand ("-moveup",IN_UpUp, "stop swimming upward");
2164 	Cmd_AddCommand ("+movedown",IN_DownDown, "swim downward");
2165 	Cmd_AddCommand ("-movedown",IN_DownUp, "stop swimming downward");
2166 	Cmd_AddCommand ("+left",IN_LeftDown, "turn left");
2167 	Cmd_AddCommand ("-left",IN_LeftUp, "stop turning left");
2168 	Cmd_AddCommand ("+right",IN_RightDown, "turn right");
2169 	Cmd_AddCommand ("-right",IN_RightUp, "stop turning right");
2170 	Cmd_AddCommand ("+forward",IN_ForwardDown, "move forward");
2171 	Cmd_AddCommand ("-forward",IN_ForwardUp, "stop moving forward");
2172 	Cmd_AddCommand ("+back",IN_BackDown, "move backward");
2173 	Cmd_AddCommand ("-back",IN_BackUp, "stop moving backward");
2174 	Cmd_AddCommand ("+lookup", IN_LookupDown, "look upward");
2175 	Cmd_AddCommand ("-lookup", IN_LookupUp, "stop looking upward");
2176 	Cmd_AddCommand ("+lookdown", IN_LookdownDown, "look downward");
2177 	Cmd_AddCommand ("-lookdown", IN_LookdownUp, "stop looking downward");
2178 	Cmd_AddCommand ("+strafe", IN_StrafeDown, "activate strafing mode (move instead of turn)");
2179 	Cmd_AddCommand ("-strafe", IN_StrafeUp, "deactivate strafing mode");
2180 	Cmd_AddCommand ("+moveleft", IN_MoveleftDown, "strafe left");
2181 	Cmd_AddCommand ("-moveleft", IN_MoveleftUp, "stop strafing left");
2182 	Cmd_AddCommand ("+moveright", IN_MoverightDown, "strafe right");
2183 	Cmd_AddCommand ("-moveright", IN_MoverightUp, "stop strafing right");
2184 	Cmd_AddCommand ("+speed", IN_SpeedDown, "activate run mode (faster movement and turning)");
2185 	Cmd_AddCommand ("-speed", IN_SpeedUp, "deactivate run mode");
2186 	Cmd_AddCommand ("+attack", IN_AttackDown, "begin firing");
2187 	Cmd_AddCommand ("-attack", IN_AttackUp, "stop firing");
2188 	Cmd_AddCommand ("+jump", IN_JumpDown, "jump");
2189 	Cmd_AddCommand ("-jump", IN_JumpUp, "end jump (so you can jump again)");
2190 	Cmd_AddCommand ("impulse", IN_Impulse, "send an impulse number to server (select weapon, use item, etc)");
2191 	Cmd_AddCommand ("+klook", IN_KLookDown, "activate keyboard looking mode, do not recenter view");
2192 	Cmd_AddCommand ("-klook", IN_KLookUp, "deactivate keyboard looking mode");
2193 	Cmd_AddCommand ("+mlook", IN_MLookDown, "activate mouse looking mode, do not recenter view");
2194 	Cmd_AddCommand ("-mlook", IN_MLookUp, "deactivate mouse looking mode");
2195 
2196 	// LordHavoc: added use button
2197 	Cmd_AddCommand ("+use", IN_UseDown, "use something (may be used by some mods)");
2198 	Cmd_AddCommand ("-use", IN_UseUp, "stop using something");
2199 
2200 	// LordHavoc: added 6 new buttons
2201 	Cmd_AddCommand ("+button3", IN_Button3Down, "activate button3 (behavior depends on mod)");
2202 	Cmd_AddCommand ("-button3", IN_Button3Up, "deactivate button3");
2203 	Cmd_AddCommand ("+button4", IN_Button4Down, "activate button4 (behavior depends on mod)");
2204 	Cmd_AddCommand ("-button4", IN_Button4Up, "deactivate button4");
2205 	Cmd_AddCommand ("+button5", IN_Button5Down, "activate button5 (behavior depends on mod)");
2206 	Cmd_AddCommand ("-button5", IN_Button5Up, "deactivate button5");
2207 	Cmd_AddCommand ("+button6", IN_Button6Down, "activate button6 (behavior depends on mod)");
2208 	Cmd_AddCommand ("-button6", IN_Button6Up, "deactivate button6");
2209 	Cmd_AddCommand ("+button7", IN_Button7Down, "activate button7 (behavior depends on mod)");
2210 	Cmd_AddCommand ("-button7", IN_Button7Up, "deactivate button7");
2211 	Cmd_AddCommand ("+button8", IN_Button8Down, "activate button8 (behavior depends on mod)");
2212 	Cmd_AddCommand ("-button8", IN_Button8Up, "deactivate button8");
2213 	Cmd_AddCommand ("+button9", IN_Button9Down, "activate button9 (behavior depends on mod)");
2214 	Cmd_AddCommand ("-button9", IN_Button9Up, "deactivate button9");
2215 	Cmd_AddCommand ("+button10", IN_Button10Down, "activate button10 (behavior depends on mod)");
2216 	Cmd_AddCommand ("-button10", IN_Button10Up, "deactivate button10");
2217 	Cmd_AddCommand ("+button11", IN_Button11Down, "activate button11 (behavior depends on mod)");
2218 	Cmd_AddCommand ("-button11", IN_Button11Up, "deactivate button11");
2219 	Cmd_AddCommand ("+button12", IN_Button12Down, "activate button12 (behavior depends on mod)");
2220 	Cmd_AddCommand ("-button12", IN_Button12Up, "deactivate button12");
2221 	Cmd_AddCommand ("+button13", IN_Button13Down, "activate button13 (behavior depends on mod)");
2222 	Cmd_AddCommand ("-button13", IN_Button13Up, "deactivate button13");
2223 	Cmd_AddCommand ("+button14", IN_Button14Down, "activate button14 (behavior depends on mod)");
2224 	Cmd_AddCommand ("-button14", IN_Button14Up, "deactivate button14");
2225 	Cmd_AddCommand ("+button15", IN_Button15Down, "activate button15 (behavior depends on mod)");
2226 	Cmd_AddCommand ("-button15", IN_Button15Up, "deactivate button15");
2227 	Cmd_AddCommand ("+button16", IN_Button16Down, "activate button16 (behavior depends on mod)");
2228 	Cmd_AddCommand ("-button16", IN_Button16Up, "deactivate button16");
2229 
2230 	// LordHavoc: added bestweapon command
2231 	Cmd_AddCommand ("bestweapon", IN_BestWeapon, "send an impulse number to server to select the first usable weapon out of several (example: 8 7 6 5 4 3 2 1)");
2232 #if 0
2233 	Cmd_AddCommand ("cycleweapon", IN_CycleWeapon, "send an impulse number to server to select the next usable weapon out of several (example: 9 4 8) if you are holding one of these, and choose the first one if you are holding none of these");
2234 #endif
2235 	Cmd_AddCommand ("register_bestweapon", IN_BestWeapon_Register_f, "(for QC usage only) change weapon parameters to be used by bestweapon; stuffcmd this in ClientConnect");
2236 
2237 	Cvar_RegisterVariable(&cl_movecliptokeyboard);
2238 	Cvar_RegisterVariable(&cl_movement);
2239 	Cvar_RegisterVariable(&cl_movement_replay);
2240 	Cvar_RegisterVariable(&cl_movement_nettimeout);
2241 	Cvar_RegisterVariable(&cl_movement_minping);
2242 	Cvar_RegisterVariable(&cl_movement_track_canjump);
2243 	Cvar_RegisterVariable(&cl_movement_maxspeed);
2244 	Cvar_RegisterVariable(&cl_movement_maxairspeed);
2245 	Cvar_RegisterVariable(&cl_movement_stopspeed);
2246 	Cvar_RegisterVariable(&cl_movement_friction);
2247 	Cvar_RegisterVariable(&cl_movement_wallfriction);
2248 	Cvar_RegisterVariable(&cl_movement_waterfriction);
2249 	Cvar_RegisterVariable(&cl_movement_edgefriction);
2250 	Cvar_RegisterVariable(&cl_movement_stepheight);
2251 	Cvar_RegisterVariable(&cl_movement_accelerate);
2252 	Cvar_RegisterVariable(&cl_movement_airaccelerate);
2253 	Cvar_RegisterVariable(&cl_movement_wateraccelerate);
2254 	Cvar_RegisterVariable(&cl_movement_jumpvelocity);
2255 	Cvar_RegisterVariable(&cl_movement_airaccel_qw);
2256 	Cvar_RegisterVariable(&cl_movement_airaccel_sideways_friction);
2257 	Cvar_RegisterVariable(&cl_nopred);
2258 
2259 	Cvar_RegisterVariable(&in_pitch_min);
2260 	Cvar_RegisterVariable(&in_pitch_max);
2261 	Cvar_RegisterVariable(&m_filter);
2262 	Cvar_RegisterVariable(&m_accelerate);
2263 	Cvar_RegisterVariable(&m_accelerate_minspeed);
2264 	Cvar_RegisterVariable(&m_accelerate_maxspeed);
2265 	Cvar_RegisterVariable(&m_accelerate_filter);
2266 
2267 	Cvar_RegisterVariable(&cl_netfps);
2268 	Cvar_RegisterVariable(&cl_netrepeatinput);
2269 	Cvar_RegisterVariable(&cl_netimmediatebuttons);
2270 
2271 	Cvar_RegisterVariable(&cl_nodelta);
2272 
2273 	Cvar_RegisterVariable(&cl_csqc_generatemousemoveevents);
2274 }
2275 
2276