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