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