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