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