moved almost all server cvars to sv_main.c and added corresponding
[divverent/darkplaces.git] / cl_input.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // cl.input.c  -- builds an intended movement command to send to the server
21
22 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
23 // rights reserved.
24
25 #include "quakedef.h"
26 #include "csprogs.h"
27
28 /*
29 ===============================================================================
30
31 KEY BUTTONS
32
33 Continuous button event tracking is complicated by the fact that two different
34 input sources (say, mouse button 1 and the control key) can both press the
35 same button, but the button should only be released when both of the
36 pressing key have been released.
37
38 When a key event issues a button command (+forward, +attack, etc), it appends
39 its key number as a parameter to the command so it can be matched up with
40 the release.
41
42 state bit 0 is the current state of the key
43 state bit 1 is edge triggered on the up to down transition
44 state bit 2 is edge triggered on the down to up transition
45
46 ===============================================================================
47 */
48
49
50 kbutton_t       in_mlook, in_klook;
51 kbutton_t       in_left, in_right, in_forward, in_back;
52 kbutton_t       in_lookup, in_lookdown, in_moveleft, in_moveright;
53 kbutton_t       in_strafe, in_speed, in_jump, in_attack, in_use;
54 kbutton_t       in_up, in_down;
55 // LordHavoc: added 6 new buttons
56 kbutton_t       in_button3, in_button4, in_button5, in_button6, in_button7, in_button8;
57 //even more
58 kbutton_t       in_button9, in_button10, in_button11, in_button12, in_button13, in_button14, in_button15, in_button16;
59
60 int                     in_impulse;
61
62
63
64 void KeyDown (kbutton_t *b)
65 {
66         int k;
67         const char *c;
68
69         c = Cmd_Argv(1);
70         if (c[0])
71                 k = atoi(c);
72         else
73                 k = -1;         // typed manually at the console for continuous down
74
75         if (k == b->down[0] || k == b->down[1])
76                 return;         // repeating key
77
78         if (!b->down[0])
79                 b->down[0] = k;
80         else if (!b->down[1])
81                 b->down[1] = k;
82         else
83         {
84                 Con_Print("Three keys down for a button!\n");
85                 return;
86         }
87
88         if (b->state & 1)
89                 return;         // still down
90         b->state |= 1 + 2;      // down + impulse down
91 }
92
93 void KeyUp (kbutton_t *b)
94 {
95         int k;
96         const char *c;
97
98         c = Cmd_Argv(1);
99         if (c[0])
100                 k = atoi(c);
101         else
102         { // typed manually at the console, assume for unsticking, so clear all
103                 b->down[0] = b->down[1] = 0;
104                 b->state = 4;   // impulse up
105                 return;
106         }
107
108         if (b->down[0] == k)
109                 b->down[0] = 0;
110         else if (b->down[1] == k)
111                 b->down[1] = 0;
112         else
113                 return;         // key up without coresponding down (menu pass through)
114         if (b->down[0] || b->down[1])
115                 return;         // some other key is still holding it down
116
117         if (!(b->state & 1))
118                 return;         // still up (this should not happen)
119         b->state &= ~1;         // now up
120         b->state |= 4;          // impulse up
121 }
122
123 void IN_KLookDown (void) {KeyDown(&in_klook);}
124 void IN_KLookUp (void) {KeyUp(&in_klook);}
125 void IN_MLookDown (void) {KeyDown(&in_mlook);}
126 void IN_MLookUp (void)
127 {
128         KeyUp(&in_mlook);
129         if ( !(in_mlook.state&1) && lookspring.value)
130                 V_StartPitchDrift();
131 }
132 void IN_UpDown(void) {KeyDown(&in_up);}
133 void IN_UpUp(void) {KeyUp(&in_up);}
134 void IN_DownDown(void) {KeyDown(&in_down);}
135 void IN_DownUp(void) {KeyUp(&in_down);}
136 void IN_LeftDown(void) {KeyDown(&in_left);}
137 void IN_LeftUp(void) {KeyUp(&in_left);}
138 void IN_RightDown(void) {KeyDown(&in_right);}
139 void IN_RightUp(void) {KeyUp(&in_right);}
140 void IN_ForwardDown(void) {KeyDown(&in_forward);}
141 void IN_ForwardUp(void) {KeyUp(&in_forward);}
142 void IN_BackDown(void) {KeyDown(&in_back);}
143 void IN_BackUp(void) {KeyUp(&in_back);}
144 void IN_LookupDown(void) {KeyDown(&in_lookup);}
145 void IN_LookupUp(void) {KeyUp(&in_lookup);}
146 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
147 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
148 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
149 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
150 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
151 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
152
153 void IN_SpeedDown(void) {KeyDown(&in_speed);}
154 void IN_SpeedUp(void) {KeyUp(&in_speed);}
155 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
156 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
157
158 void IN_AttackDown(void) {KeyDown(&in_attack);}
159 void IN_AttackUp(void) {KeyUp(&in_attack);}
160
161 void IN_UseDown(void) {KeyDown(&in_use);}
162 void IN_UseUp(void) {KeyUp(&in_use);}
163
164 // LordHavoc: added 6 new buttons
165 void IN_Button3Down(void) {KeyDown(&in_button3);}
166 void IN_Button3Up(void) {KeyUp(&in_button3);}
167 void IN_Button4Down(void) {KeyDown(&in_button4);}
168 void IN_Button4Up(void) {KeyUp(&in_button4);}
169 void IN_Button5Down(void) {KeyDown(&in_button5);}
170 void IN_Button5Up(void) {KeyUp(&in_button5);}
171 void IN_Button6Down(void) {KeyDown(&in_button6);}
172 void IN_Button6Up(void) {KeyUp(&in_button6);}
173 void IN_Button7Down(void) {KeyDown(&in_button7);}
174 void IN_Button7Up(void) {KeyUp(&in_button7);}
175 void IN_Button8Down(void) {KeyDown(&in_button8);}
176 void IN_Button8Up(void) {KeyUp(&in_button8);}
177
178 void IN_Button9Down(void) {KeyDown(&in_button9);}
179 void IN_Button9Up(void) {KeyUp(&in_button9);}
180 void IN_Button10Down(void) {KeyDown(&in_button10);}
181 void IN_Button10Up(void) {KeyUp(&in_button10);}
182 void IN_Button11Down(void) {KeyDown(&in_button11);}
183 void IN_Button11Up(void) {KeyUp(&in_button11);}
184 void IN_Button12Down(void) {KeyDown(&in_button12);}
185 void IN_Button12Up(void) {KeyUp(&in_button12);}
186 void IN_Button13Down(void) {KeyDown(&in_button13);}
187 void IN_Button13Up(void) {KeyUp(&in_button13);}
188 void IN_Button14Down(void) {KeyDown(&in_button14);}
189 void IN_Button14Up(void) {KeyUp(&in_button14);}
190 void IN_Button15Down(void) {KeyDown(&in_button15);}
191 void IN_Button15Up(void) {KeyUp(&in_button15);}
192 void IN_Button16Down(void) {KeyDown(&in_button16);}
193 void IN_Button16Up(void) {KeyUp(&in_button16);}
194
195 void IN_JumpDown (void) {KeyDown(&in_jump);}
196 void IN_JumpUp (void) {KeyUp(&in_jump);}
197
198 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
199
200 struct
201 {
202         const char *name;
203         int impulse;
204         int weaponbit;
205         int ammostat;
206         int ammomin;
207 }
208 in_bestweapon_info[] =
209 {
210         {"1", 1, IT_AXE, STAT_SHELLS, 0},
211         {"2", 2, IT_SHOTGUN, STAT_SHELLS, 1},
212         {"3", 3, IT_SUPER_SHOTGUN, STAT_SHELLS, 1},
213         {"4", 4, IT_NAILGUN, STAT_NAILS, 1},
214         {"5", 5, IT_SUPER_NAILGUN, STAT_NAILS, 1},
215         {"6", 6, IT_GRENADE_LAUNCHER, STAT_ROCKETS, 1},
216         {"7", 7, IT_ROCKET_LAUNCHER, STAT_ROCKETS, 1},
217         {"8", 8, IT_LIGHTNING, STAT_CELLS, 1},
218         {"9", 9, 128, STAT_CELLS, 1}, // generic energy weapon for mods
219         {"p", 209, 128, STAT_CELLS, 1}, // dpmod plasma gun
220         {"w", 210, 8388608, STAT_CELLS, 1}, // dpmod plasma wave cannon
221         {"l", 225, HIT_LASER_CANNON, STAT_CELLS, 1}, // hipnotic laser cannon
222         {"h", 226, HIT_MJOLNIR, STAT_CELLS, 0}, // hipnotic mjolnir hammer
223         {NULL, 0, 0, 0, 0}
224 };
225 void IN_BestWeapon (void)
226 {
227         int i, n;
228         const char *t;
229         if (Cmd_Argc() < 2)
230         {
231                 Con_Printf("bestweapon requires 1 or more parameters\n");
232                 return;
233         }
234         for (i = 1;i < Cmd_Argc();i++)
235         {
236                 t = Cmd_Argv(i);
237                 // figure out which weapon this character refers to
238                 for (n = 0;in_bestweapon_info[n].name;n++)
239                 {
240                         if (!strcmp(in_bestweapon_info[n].name, t))
241                         {
242                                 // we found out what weapon this character refers to
243                                 // check if the inventory contains the weapon and enough ammo
244                                 if ((cl.stats[STAT_ITEMS] & in_bestweapon_info[n].weaponbit) && (cl.stats[in_bestweapon_info[n].ammostat] >= in_bestweapon_info[n].ammomin))
245                                 {
246                                         // we found one of the weapons the player wanted
247                                         // send an impulse to switch to it
248                                         in_impulse = in_bestweapon_info[n].impulse;
249                                         return;
250                                 }
251                                 break;
252                         }
253                 }
254                 // if we couldn't identify the weapon we just ignore it and continue checking for other weapons
255         }
256         // if we couldn't find any of the weapons, there's nothing more we can do...
257 }
258
259 /*
260 ===============
261 CL_KeyState
262
263 Returns 0.25 if a key was pressed and released during the frame,
264 0.5 if it was pressed and held
265 0 if held then released, and
266 1.0 if held for the entire time
267 ===============
268 */
269 float CL_KeyState (kbutton_t *key)
270 {
271         float           val;
272         qboolean        impulsedown, impulseup, down;
273
274         impulsedown = key->state & 2;
275         impulseup = key->state & 4;
276         down = key->state & 1;
277         val = 0;
278
279         if (impulsedown && !impulseup)
280         {
281                 if (down)
282                         val = 0.5;      // pressed and held this frame
283                 else
284                         val = 0;        //      I_Error ();
285         }
286         if (impulseup && !impulsedown)
287         {
288                 if (down)
289                         val = 0;        //      I_Error ();
290                 else
291                         val = 0;        // released this frame
292         }
293         if (!impulsedown && !impulseup)
294         {
295                 if (down)
296                         val = 1.0;      // held the entire frame
297                 else
298                         val = 0;        // up the entire frame
299         }
300         if (impulsedown && impulseup)
301         {
302                 if (down)
303                         val = 0.75;     // released and re-pressed this frame
304                 else
305                         val = 0.25;     // pressed and released this frame
306         }
307
308         key->state &= 1;                // clear impulses
309
310         return val;
311 }
312
313
314
315
316 //==========================================================================
317
318 cvar_t cl_upspeed = {CVAR_SAVE, "cl_upspeed","400","vertical movement speed (while swimming or flying)"};
319 cvar_t cl_forwardspeed = {CVAR_SAVE, "cl_forwardspeed","400","forward movement speed"};
320 cvar_t cl_backspeed = {CVAR_SAVE, "cl_backspeed","400","backward movement speed"};
321 cvar_t cl_sidespeed = {CVAR_SAVE, "cl_sidespeed","350","strafe movement speed"};
322
323 cvar_t cl_movespeedkey = {CVAR_SAVE, "cl_movespeedkey","2.0","how much +speed multiplies keyboard movement speed"};
324
325 cvar_t cl_yawspeed = {CVAR_SAVE, "cl_yawspeed","140","keyboard yaw turning speed"};
326 cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150","keyboard pitch turning speed"};
327
328 cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5","how much +speed multiplies keyboard turning speed"};
329
330 cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0", "enables clientside prediction of your player movement"};
331 cvar_t cl_movement_minping = {CVAR_SAVE, "cl_movement_minping", "0", "whether to use prediction when ping is lower than this value in milliseconds"};
332 cvar_t cl_movement_maxspeed = {0, "cl_movement_maxspeed", "320", "how fast you can move (should match sv_maxspeed)"};
333 cvar_t cl_movement_maxairspeed = {0, "cl_movement_maxairspeed", "30", "how fast you can move while in the air (should match sv_maxairspeed)"};
334 cvar_t cl_movement_stopspeed = {0, "cl_movement_stopspeed", "100", "speed below which you will be slowed rapidly to a stop rather than sliding endlessly (should match sv_stopspeed)"};
335 cvar_t cl_movement_friction = {0, "cl_movement_friction", "4", "how fast you slow down (should match sv_friction)"};
336 cvar_t cl_movement_wallfriction = {0, "cl_movement_wallfriction", "1", "how fast you slow down while sliding along a wall (should match sv_wallfriction)"};
337 cvar_t cl_movement_waterfriction = {0, "cl_movement_waterfriction", "-1", "how fast you slow down (should match sv_waterfriction), if less than 0 the cl_movement_friction variable is used instead"};
338 cvar_t cl_movement_edgefriction = {0, "cl_movement_edgefriction", "2", "how much to slow down when you may be about to fall off a ledge (should match edgefriction)"};
339 cvar_t cl_movement_stepheight = {0, "cl_movement_stepheight", "18", "how tall a step you can step in one instant (should match sv_stepheight)"};
340 cvar_t cl_movement_accelerate = {0, "cl_movement_accelerate", "10", "how fast you accelerate (should match sv_accelerate)"};
341 cvar_t cl_movement_airaccelerate = {0, "cl_movement_airaccelerate", "-1", "how fast you accelerate while in the air (should match sv_airaccelerate), if less than 0 the cl_movement_accelerate variable is used instead"};
342 cvar_t cl_movement_wateraccelerate = {0, "cl_movement_wateraccelerate", "-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"};
343 cvar_t cl_movement_jumpvelocity = {0, "cl_movement_jumpvelocity", "270", "how fast you move upward when you begin a jump (should match the quakec code)"};
344 cvar_t cl_movement_airaccel_qw = {0, "cl_movement_airaccel_qw", "1", "ratio of QW-style air control as opposed to simple acceleration (should match sv_airaccel_qw)"};
345 cvar_t cl_movement_airaccel_sideways_friction = {0, "cl_movement_airaccel_sideways_friction", "0", "anti-sideways movement stabilization (should match sv_airaccel_sideways_friction)"};
346
347 cvar_t in_pitch_min = {0, "in_pitch_min", "-90", "how far downward you can aim (quake used -70"};
348 cvar_t in_pitch_max = {0, "in_pitch_max", "90", "how far upward you can aim (quake used 80"};
349
350 cvar_t m_filter = {CVAR_SAVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"};
351
352 cvar_t cl_netinputpacketspersecond = {CVAR_SAVE, "cl_netinputpacketspersecond","50", "how many input packets to send to server each second"};
353 cvar_t cl_netinputpacketlosstolerance = {CVAR_SAVE, "cl_netinputpacketlosstolerance", "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)"};
354
355 cvar_t cl_nodelta = {0, "cl_nodelta", "0", "disables delta compression of non-player entities in QW network protocol"};
356
357
358 /*
359 ================
360 CL_AdjustAngles
361
362 Moves the local angle positions
363 ================
364 */
365 void CL_AdjustAngles (void)
366 {
367         float   speed;
368         float   up, down;
369
370         if (in_speed.state & 1)
371                 speed = cl.realframetime * cl_anglespeedkey.value;
372         else
373                 speed = cl.realframetime;
374
375         if (!(in_strafe.state & 1))
376         {
377                 cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
378                 cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
379         }
380         if (in_klook.state & 1)
381         {
382                 V_StopPitchDrift ();
383                 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
384                 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
385         }
386
387         up = CL_KeyState (&in_lookup);
388         down = CL_KeyState(&in_lookdown);
389
390         cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
391         cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
392
393         if (up || down)
394                 V_StopPitchDrift ();
395
396         cl.viewangles[YAW] = ANGLEMOD(cl.viewangles[YAW]);
397         cl.viewangles[PITCH] = ANGLEMOD(cl.viewangles[PITCH]);
398         cl.viewangles[ROLL] = ANGLEMOD(cl.viewangles[ROLL]);
399         if (cl.viewangles[YAW] >= 180)
400                 cl.viewangles[YAW] -= 360;
401         if (cl.viewangles[PITCH] >= 180)
402                 cl.viewangles[PITCH] -= 360;
403         if (cl.viewangles[ROLL] >= 180)
404                 cl.viewangles[ROLL] -= 360;
405
406         cl.viewangles[PITCH] = bound (in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
407         cl.viewangles[ROLL] = bound(-50, cl.viewangles[ROLL], 50);
408 }
409
410 qboolean cl_ignoremousemove = false;
411
412 /*
413 ================
414 CL_Input
415
416 Send the intended movement message to the server
417 ================
418 */
419 void CL_Input (void)
420 {
421         float mx, my;
422         static float old_mouse_x = 0, old_mouse_y = 0;
423
424         // clamp before the move to prevent starting with bad angles
425         CL_AdjustAngles ();
426
427         // reset some of the command fields
428         cl.cmd.forwardmove = 0;
429         cl.cmd.sidemove = 0;
430         cl.cmd.upmove = 0;
431
432         // get basic movement from keyboard
433         if (in_strafe.state & 1)
434         {
435                 cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
436                 cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
437         }
438
439         cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
440         cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
441
442         cl.cmd.upmove += cl_upspeed.value * CL_KeyState (&in_up);
443         cl.cmd.upmove -= cl_upspeed.value * CL_KeyState (&in_down);
444
445         if (! (in_klook.state & 1) )
446         {
447                 cl.cmd.forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
448                 cl.cmd.forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
449         }
450
451         // adjust for speed key
452         if (in_speed.state & 1)
453         {
454                 cl.cmd.forwardmove *= cl_movespeedkey.value;
455                 cl.cmd.sidemove *= cl_movespeedkey.value;
456                 cl.cmd.upmove *= cl_movespeedkey.value;
457         }
458
459         in_mouse_x = 0;
460         in_mouse_y = 0;
461
462         // allow mice or other external controllers to add to the move
463         IN_Move ();
464
465         // ignore a mouse move if mouse was activated/deactivated this frame
466         if (cl_ignoremousemove)
467         {
468                 cl_ignoremousemove = false;
469                 in_mouse_x = 0;
470                 in_mouse_y = 0;
471         }
472
473         // apply m_filter if it is on
474         mx = in_mouse_x;
475         my = in_mouse_y;
476         if (m_filter.integer)
477         {
478                 in_mouse_x = (mx + old_mouse_x) * 0.5;
479                 in_mouse_y = (my + old_mouse_y) * 0.5;
480         }
481         old_mouse_x = mx;
482         old_mouse_y = my;
483
484         // if not in menu, apply mouse move to viewangles/movement
485         if (!cl.csqc_wantsmousemove && in_client_mouse)
486         {
487                 if (cl_prydoncursor.integer)
488                 {
489                         // mouse interacting with the scene, mostly stationary view
490                         V_StopPitchDrift();
491                         cl.cmd.cursor_screen[0] += in_mouse_x * sensitivity.value / vid.width;
492                         cl.cmd.cursor_screen[1] += in_mouse_y * sensitivity.value / vid.height;
493                 }
494                 else if (in_strafe.state & 1)
495                 {
496                         // strafing mode, all looking is movement
497                         V_StopPitchDrift();
498                         cl.cmd.sidemove += m_side.value * in_mouse_x * sensitivity.value;
499                         if (noclip_anglehack)
500                                 cl.cmd.upmove -= m_forward.value * in_mouse_y * sensitivity.value;
501                         else
502                                 cl.cmd.forwardmove -= m_forward.value * in_mouse_y * sensitivity.value;
503                 }
504                 else if ((in_mlook.state & 1) || freelook.integer)
505                 {
506                         // mouselook, lookstrafe causes turning to become strafing
507                         V_StopPitchDrift();
508                         if (lookstrafe.integer)
509                                 cl.cmd.sidemove += m_side.value * in_mouse_x * sensitivity.value;
510                         else
511                                 cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * sensitivity.value * cl.viewzoom;
512                         cl.viewangles[PITCH] += m_pitch.value * in_mouse_y * sensitivity.value * cl.viewzoom;
513                 }
514                 else
515                 {
516                         // non-mouselook, yaw turning and forward/back movement
517                         cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * sensitivity.value * cl.viewzoom;
518                         cl.cmd.forwardmove -= m_forward.value * in_mouse_y * sensitivity.value;
519                 }
520         }
521
522         // clamp after the move to prevent rendering with bad angles
523         CL_AdjustAngles ();
524 }
525
526 #include "cl_collision.h"
527
528 void CL_UpdatePrydonCursor(void)
529 {
530         vec3_t temp;
531
532         if (!cl_prydoncursor.integer)
533                 VectorClear(cl.cmd.cursor_screen);
534
535         /*
536         if (cl.cmd.cursor_screen[0] < -1)
537         {
538                 cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - -1) * vid.width * sensitivity.value * cl.viewzoom;
539                 cl.cmd.cursor_screen[0] = -1;
540         }
541         if (cl.cmd.cursor_screen[0] > 1)
542         {
543                 cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - 1) * vid.width * sensitivity.value * cl.viewzoom;
544                 cl.cmd.cursor_screen[0] = 1;
545         }
546         if (cl.cmd.cursor_screen[1] < -1)
547         {
548                 cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - -1) * vid.height * sensitivity.value * cl.viewzoom;
549                 cl.cmd.cursor_screen[1] = -1;
550         }
551         if (cl.cmd.cursor_screen[1] > 1)
552         {
553                 cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - 1) * vid.height * sensitivity.value * cl.viewzoom;
554                 cl.cmd.cursor_screen[1] = 1;
555         }
556         */
557         cl.cmd.cursor_screen[0] = bound(-1, cl.cmd.cursor_screen[0], 1);
558         cl.cmd.cursor_screen[1] = bound(-1, cl.cmd.cursor_screen[1], 1);
559         cl.cmd.cursor_screen[2] = 1;
560
561         // calculate current view matrix
562         Matrix4x4_OriginFromMatrix(&r_view.matrix, cl.cmd.cursor_start);
563         // calculate direction vector of cursor in viewspace by using frustum slopes
564         VectorSet(temp, cl.cmd.cursor_screen[2] * 1000000, cl.cmd.cursor_screen[0] * -r_view.frustum_x * 1000000, cl.cmd.cursor_screen[1] * -r_view.frustum_y * 1000000);
565         Matrix4x4_Transform(&r_view.matrix, temp, cl.cmd.cursor_end);
566         // trace from view origin to the cursor
567         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);
568 }
569
570 void CL_ClientMovement_ExpireOldMoves(void)
571 {
572         int i;
573         int n;
574         // remove stale queue items
575         n = cl.movement_numqueue;
576         cl.movement_numqueue = 0;
577         if (cls.servermovesequence)
578         {
579                 for (i = 0;i < n;i++)
580                 {
581                         if (cl.movement_queue[i].sequence > cls.servermovesequence)
582                                 cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i];
583                         else
584                                 cl.movement_replay_canjump = cl.movement_queue[i].canjump;
585                 }
586         }
587 }
588
589 void CL_ClientMovement_Input(qboolean buttonjump, qboolean buttoncrouch)
590 {
591         // if time has not advanced, do nothing
592         if (cl.movecmd[0].msec <= 0)
593                 return;
594         // add to input queue if there is room
595         if (cl.movement_numqueue < (int)(sizeof(cl.movement_queue)/sizeof(cl.movement_queue[0])))
596         {
597                 // add to input queue
598                 cl.movement_queue[cl.movement_numqueue].sequence = cl.movecmd[0].sequence;
599                 cl.movement_queue[cl.movement_numqueue].time = cl.movecmd[0].time;
600                 cl.movement_queue[cl.movement_numqueue].frametime = cl.movecmd[0].msec * 0.001;
601                 VectorCopy(cl.movecmd[0].viewangles, cl.movement_queue[cl.movement_numqueue].viewangles);
602                 cl.movement_queue[cl.movement_numqueue].move[0] = cl.movecmd[0].forwardmove;
603                 cl.movement_queue[cl.movement_numqueue].move[1] = cl.movecmd[0].sidemove;
604                 cl.movement_queue[cl.movement_numqueue].move[2] = cl.movecmd[0].upmove;
605                 cl.movement_queue[cl.movement_numqueue].jump = buttonjump;
606                 cl.movement_queue[cl.movement_numqueue].crouch = buttoncrouch;
607                 cl.movement_numqueue++;
608         }
609
610         cl.movement_replay = true;
611 }
612
613 typedef enum waterlevel_e
614 {
615         WATERLEVEL_NONE,
616         WATERLEVEL_WETFEET,
617         WATERLEVEL_SWIMMING,
618         WATERLEVEL_SUBMERGED
619 }
620 waterlevel_t;
621
622 typedef struct cl_clientmovement_state_s
623 {
624         // position
625         vec3_t origin;
626         vec3_t velocity;
627         // current bounding box (different if crouched vs standing)
628         vec3_t mins;
629         vec3_t maxs;
630         // currently on the ground
631         qboolean onground;
632         // currently crouching
633         qboolean crouched;
634         // what kind of water (SUPERCONTENTS_LAVA for instance)
635         int watertype;
636         // how deep
637         waterlevel_t waterlevel;
638         // weird hacks when jumping out of water
639         // (this is in seconds and counts down to 0)
640         float waterjumptime;
641
642         // user command
643         client_movementqueue_t q;
644 }
645 cl_clientmovement_state_t;
646
647 #define NUMOFFSETS 27
648 static vec3_t offsets[NUMOFFSETS] =
649 {
650 // 1 no nudge (just return the original if this test passes)
651         { 0.000,  0.000,  0.000},
652 // 6 simple nudges
653         { 0.000,  0.000,  0.125}, { 0.000,  0.000, -0.125},
654         {-0.125,  0.000,  0.000}, { 0.125,  0.000,  0.000},
655         { 0.000, -0.125,  0.000}, { 0.000,  0.125,  0.000},
656 // 4 diagonal flat nudges
657         {-0.125, -0.125,  0.000}, { 0.125, -0.125,  0.000},
658         {-0.125,  0.125,  0.000}, { 0.125,  0.125,  0.000},
659 // 8 diagonal upward nudges
660         {-0.125,  0.000,  0.125}, { 0.125,  0.000,  0.125},
661         { 0.000, -0.125,  0.125}, { 0.000,  0.125,  0.125},
662         {-0.125, -0.125,  0.125}, { 0.125, -0.125,  0.125},
663         {-0.125,  0.125,  0.125}, { 0.125,  0.125,  0.125},
664 // 8 diagonal downward nudges
665         {-0.125,  0.000, -0.125}, { 0.125,  0.000, -0.125},
666         { 0.000, -0.125, -0.125}, { 0.000,  0.125, -0.125},
667         {-0.125, -0.125, -0.125}, { 0.125, -0.125, -0.125},
668         {-0.125,  0.125, -0.125}, { 0.125,  0.125, -0.125},
669 };
670
671 qboolean CL_ClientMovement_Unstick(cl_clientmovement_state_t *s)
672 {
673         int i;
674         vec3_t neworigin;
675         for (i = 0;i < NUMOFFSETS;i++)
676         {
677                 VectorAdd(offsets[i], s->origin, neworigin);
678                 if (!CL_Move(neworigin, cl.playercrouchmins, cl.playercrouchmaxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false).startsolid)
679                 {
680                         VectorCopy(neworigin, s->origin);
681                         return true;
682                 }
683         }
684         // if all offsets failed, give up
685         return false;
686 }
687
688 void CL_ClientMovement_UpdateStatus(cl_clientmovement_state_t *s)
689 {
690         vec3_t origin1, origin2;
691         trace_t trace;
692
693         // make sure player is not stuck
694         CL_ClientMovement_Unstick(s);
695
696         // set crouched
697         if (s->q.crouch)
698         {
699                 // wants to crouch, this always works..
700                 if (!s->crouched)
701                         s->crouched = true;
702         }
703         else
704         {
705                 // wants to stand, if currently crouching we need to check for a
706                 // low ceiling first
707                 if (s->crouched)
708                 {
709                         trace = CL_Move(s->origin, cl.playerstandmins, cl.playerstandmaxs, s->origin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
710                         if (!trace.startsolid)
711                                 s->crouched = false;
712                 }
713         }
714         if (s->crouched)
715         {
716                 VectorCopy(cl.playercrouchmins, s->mins);
717                 VectorCopy(cl.playercrouchmaxs, s->maxs);
718         }
719         else
720         {
721                 VectorCopy(cl.playerstandmins, s->mins);
722                 VectorCopy(cl.playerstandmaxs, s->maxs);
723         }
724
725         // set onground
726         VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + 1);
727         VectorSet(origin2, s->origin[0], s->origin[1], s->origin[2] - 2);
728         trace = CL_Move(origin1, s->mins, s->maxs, origin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
729         s->onground = trace.fraction < 1 && trace.plane.normal[2] > 0.7;
730
731         // set watertype/waterlevel
732         VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + s->mins[2] + 1);
733         s->waterlevel = WATERLEVEL_NONE;
734         s->watertype = CL_Move(origin1, vec3_origin, vec3_origin, origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
735         if (s->watertype)
736         {
737                 s->waterlevel = WATERLEVEL_WETFEET;
738                 origin1[2] = s->origin[2] + (s->mins[2] + s->maxs[2]) * 0.5f;
739                 if (CL_Move(origin1, vec3_origin, vec3_origin, origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
740                 {
741                         s->waterlevel = WATERLEVEL_SWIMMING;
742                         origin1[2] = s->origin[2] + 22;
743                         if (CL_Move(origin1, vec3_origin, vec3_origin, origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
744                                 s->waterlevel = WATERLEVEL_SUBMERGED;
745                 }
746         }
747
748         // water jump prediction
749         if (s->onground || s->velocity[2] <= 0 || s->waterjumptime <= 0)
750                 s->waterjumptime = 0;
751 }
752
753 void CL_ClientMovement_Move(cl_clientmovement_state_t *s)
754 {
755         int bump;
756         double t;
757         vec_t f;
758         vec3_t neworigin;
759         vec3_t currentorigin2;
760         vec3_t neworigin2;
761         vec3_t primalvelocity;
762         trace_t trace;
763         trace_t trace2;
764         trace_t trace3;
765         CL_ClientMovement_UpdateStatus(s);
766         VectorCopy(s->velocity, primalvelocity);
767         for (bump = 0, t = s->q.frametime;bump < 8 && VectorLength2(s->velocity) > 0;bump++)
768         {
769                 VectorMA(s->origin, t, s->velocity, neworigin);
770                 trace = CL_Move(s->origin, s->mins, s->maxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
771                 if (trace.fraction < 1 && trace.plane.normal[2] == 0)
772                 {
773                         // may be a step or wall, try stepping up
774                         // first move forward at a higher level
775                         VectorSet(currentorigin2, s->origin[0], s->origin[1], s->origin[2] + cl.movevars_stepheight);
776                         VectorSet(neworigin2, neworigin[0], neworigin[1], s->origin[2] + cl.movevars_stepheight);
777                         trace2 = CL_Move(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
778                         if (!trace2.startsolid)
779                         {
780                                 // then move down from there
781                                 VectorCopy(trace2.endpos, currentorigin2);
782                                 VectorSet(neworigin2, trace2.endpos[0], trace2.endpos[1], s->origin[2]);
783                                 trace3 = CL_Move(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
784                                 //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]);
785                                 // accept the new trace if it made some progress
786                                 if (fabs(trace3.endpos[0] - trace.endpos[0]) >= 0.03125 || fabs(trace3.endpos[1] - trace.endpos[1]) >= 0.03125)
787                                 {
788                                         trace = trace2;
789                                         VectorCopy(trace3.endpos, trace.endpos);
790                                 }
791                         }
792                 }
793
794                 // check if it moved at all
795                 if (trace.fraction >= 0.001)
796                         VectorCopy(trace.endpos, s->origin);
797
798                 // check if it moved all the way
799                 if (trace.fraction == 1)
800                         break;
801
802                 //if (trace.plane.normal[2] > 0.7)
803                 //      s->onground = true;
804
805                 t -= t * trace.fraction;
806
807                 f = DotProduct(s->velocity, trace.plane.normal);
808                 VectorMA(s->velocity, -f, trace.plane.normal, s->velocity);
809         }
810         if (s->waterjumptime > 0)
811                 VectorCopy(primalvelocity, s->velocity);
812 }
813
814
815 void CL_ClientMovement_Physics_Swim(cl_clientmovement_state_t *s)
816 {
817         vec_t wishspeed;
818         vec_t f;
819         vec3_t wishvel;
820         vec3_t wishdir;
821
822         // water jump only in certain situations
823         // this mimics quakeworld code
824         if (s->q.jump && s->waterlevel == 2 && s->velocity[2] >= -180)
825         {
826                 vec3_t forward;
827                 vec3_t yawangles;
828                 vec3_t spot;
829                 VectorSet(yawangles, 0, s->q.viewangles[1], 0);
830                 AngleVectors(yawangles, forward, NULL, NULL);
831                 VectorMA(s->origin, 24, forward, spot);
832                 spot[2] += 8;
833                 if (CL_Move(spot, vec3_origin, vec3_origin, spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid)
834                 {
835                         spot[2] += 24;
836                         if (!CL_Move(spot, vec3_origin, vec3_origin, spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid)
837                         {
838                                 VectorScale(forward, 50, s->velocity);
839                                 s->velocity[2] = 310;
840                                 s->waterjumptime = 2;
841                                 s->onground = false;
842                                 s->q.canjump = false;
843                         }
844                 }
845         }
846
847         if (!VectorLength2(s->q.move))
848         {
849                 // drift towards bottom
850                 VectorSet(wishvel, 0, 0, -60);
851         }
852         else
853         {
854                 // swim
855                 vec3_t forward;
856                 vec3_t right;
857                 vec3_t up;
858                 // calculate movement vector
859                 AngleVectors(s->q.viewangles, forward, right, up);
860                 VectorSet(up, 0, 0, 1);
861                 VectorMAMAM(s->q.move[0], forward, s->q.move[1], right, s->q.move[2], up, wishvel);
862         }
863
864         // split wishvel into wishspeed and wishdir
865         wishspeed = VectorLength(wishvel);
866         if (wishspeed)
867                 VectorScale(wishvel, 1 / wishspeed, wishdir);
868         else
869                 VectorSet( wishdir, 0.0, 0.0, 0.0 );
870         wishspeed = min(wishspeed, cl.movevars_maxspeed) * 0.7;
871
872         if (s->crouched)
873                 wishspeed *= 0.5;
874
875         if (s->waterjumptime <= 0)
876         {
877                 // water friction
878                 f = 1 - s->q.frametime * cl.movevars_waterfriction * s->waterlevel;
879                 f = bound(0, f, 1);
880                 VectorScale(s->velocity, f, s->velocity);
881
882                 // water acceleration
883                 f = wishspeed - DotProduct(s->velocity, wishdir);
884                 if (f > 0)
885                 {
886                         f = min(cl.movevars_wateraccelerate * s->q.frametime * wishspeed, f);
887                         VectorMA(s->velocity, f, wishdir, s->velocity);
888                 }
889
890                 // holding jump button swims upward slowly
891                 if (s->q.jump)
892                 {
893                         if (s->watertype & SUPERCONTENTS_LAVA)
894                                 s->velocity[2] =  50;
895                         else if (s->watertype & SUPERCONTENTS_SLIME)
896                                 s->velocity[2] =  80;
897                         else
898                         {
899                                 if (gamemode == GAME_NEXUIZ)
900                                         s->velocity[2] = 200;
901                                 else
902                                         s->velocity[2] = 100;
903                         }
904                 }
905         }
906
907         CL_ClientMovement_Move(s);
908 }
909
910 void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s)
911 {
912         vec_t friction;
913         vec_t wishspeed;
914         vec_t addspeed;
915         vec_t accelspeed;
916         vec_t f;
917         vec3_t forward;
918         vec3_t right;
919         vec3_t up;
920         vec3_t wishvel;
921         vec3_t wishdir;
922         vec3_t yawangles;
923         trace_t trace;
924
925         // jump if on ground with jump button pressed but only if it has been
926         // released at least once since the last jump
927         if (s->q.jump)
928         {
929                 if (s->onground && s->q.canjump)
930                 {
931                         s->velocity[2] += cl.movevars_jumpvelocity;
932                         s->onground = false;
933                         s->q.canjump = false;
934                 }
935         }
936         else
937                 s->q.canjump = true;
938
939         // calculate movement vector
940         VectorSet(yawangles, 0, s->q.viewangles[1], 0);
941         AngleVectors(yawangles, forward, right, up);
942         VectorMAM(s->q.move[0], forward, s->q.move[1], right, wishvel);
943
944         // split wishvel into wishspeed and wishdir
945         wishspeed = VectorLength(wishvel);
946         if (wishspeed)
947                 VectorScale(wishvel, 1 / wishspeed, wishdir);
948         else
949                 VectorSet( wishdir, 0.0, 0.0, 0.0 );
950         wishspeed = min(wishspeed, cl.movevars_maxspeed);
951         if (s->crouched)
952                 wishspeed *= 0.5;
953
954         // check if onground
955         if (s->onground)
956         {
957                 // apply edge friction
958                 f = sqrt(s->velocity[0] * s->velocity[0] + s->velocity[1] * s->velocity[1]);
959                 friction = cl.movevars_friction;
960                 if (f > 0 && cl.movevars_edgefriction != 1)
961                 {
962                         vec3_t neworigin2;
963                         vec3_t neworigin3;
964                         // note: QW uses the full player box for the trace, and yet still
965                         // uses s->origin[2] + s->mins[2], which is clearly an bug, but
966                         // this mimics it for compatibility
967                         VectorSet(neworigin2, s->origin[0] + s->velocity[0]*(16/f), s->origin[1] + s->velocity[1]*(16/f), s->origin[2] + s->mins[2]);
968                         VectorSet(neworigin3, neworigin2[0], neworigin2[1], neworigin2[2] - 34);
969                         if (cls.protocol == PROTOCOL_QUAKEWORLD)
970                                 trace = CL_Move(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
971                         else
972                                 trace = CL_Move(neworigin2, vec3_origin, vec3_origin, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
973                         if (trace.fraction == 1 && !trace.startsolid)
974                                 friction *= cl.movevars_edgefriction;
975                 }
976                 // apply ground friction
977                 f = 1 - s->q.frametime * friction * ((f < cl.movevars_stopspeed) ? (cl.movevars_stopspeed / f) : 1);
978                 f = max(f, 0);
979                 VectorScale(s->velocity, f, s->velocity);
980                 addspeed = wishspeed - DotProduct(s->velocity, wishdir);
981                 if (addspeed > 0)
982                 {
983                         accelspeed = min(cl.movevars_accelerate * s->q.frametime * wishspeed, addspeed);
984                         VectorMA(s->velocity, accelspeed, wishdir, s->velocity);
985                 }
986                 s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->q.frametime;
987                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
988                         s->velocity[2] = 0;
989                 if (VectorLength2(s->velocity))
990                         CL_ClientMovement_Move(s);
991         }
992         else
993         {
994                 if (s->waterjumptime <= 0)
995                 {
996                         vec_t f;
997                         vec_t vel_straight;
998                         vec_t vel_z;
999                         vec3_t vel_perpend;
1000
1001                         // apply air speed limit
1002                         wishspeed = min(wishspeed, cl.movevars_maxairspeed);
1003
1004                         /*
1005                         addspeed = wishspeed - DotProduct(s->velocity, wishdir);
1006                         if (addspeed > 0)
1007                         {
1008                                 accelspeed = min(cl.movevars_accelerate * s->q.frametime * wishspeed, addspeed);
1009                                 VectorMA(s->velocity, accelspeed, wishdir, s->velocity);
1010                         }
1011                         */
1012
1013                         vel_straight = DotProduct(s->velocity, wishdir);
1014                         vel_z = s->velocity[2];
1015                         VectorMA(s->velocity, -vel_straight, wishdir, vel_perpend);
1016                         vel_perpend[2] -= vel_z;
1017
1018                         f = wishspeed - vel_straight;
1019                         if(f > 0)
1020                                 vel_straight += min(f, cl.movevars_accelerate * s->q.frametime * wishspeed) * cl.movevars_airaccel_qw;
1021                         if(wishspeed > 0)
1022                                 vel_straight += min(wishspeed, cl.movevars_accelerate * s->q.frametime * wishspeed) * (1 - cl.movevars_airaccel_qw);
1023
1024                         VectorM(1 - (s->q.frametime * (wishspeed / cl.movevars_maxairspeed) * cl.movevars_airaccel_sideways_friction), vel_perpend, vel_perpend);
1025
1026                         VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
1027                         s->velocity[2] += vel_z;
1028                 }
1029                 s->velocity[2] -= cl.movevars_gravity * cl.movevars_entgravity * s->q.frametime;
1030                 CL_ClientMovement_Move(s);
1031         }
1032 }
1033
1034 void CL_ClientMovement_PlayerMove(cl_clientmovement_state_t *s)
1035 {
1036         //Con_Printf(" %f", frametime);
1037         if (!s->q.jump)
1038                 s->q.canjump = true;
1039         s->waterjumptime -= s->q.frametime;
1040         CL_ClientMovement_UpdateStatus(s);
1041         if (s->waterlevel >= WATERLEVEL_SWIMMING)
1042                 CL_ClientMovement_Physics_Swim(s);
1043         else
1044                 CL_ClientMovement_Physics_Walk(s);
1045 }
1046
1047 extern cvar_t slowmo;
1048 void CL_UpdateMoveVars(void)
1049 {
1050         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1051         {
1052                 cl.movevars_timescale = 1;
1053                 cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond.value, 100);
1054                 // scale playback speed of demos by slowmo cvar
1055                 if (cls.demoplayback)
1056                         cl.movevars_timescale *= slowmo.value;
1057         }
1058         else if (cl.stats[STAT_MOVEVARS_TICRATE])
1059         {
1060                 cl.movevars_ticrate = cl.statsf[STAT_MOVEVARS_TICRATE];
1061                 cl.movevars_timescale = cl.statsf[STAT_MOVEVARS_TIMESCALE];
1062                 cl.movevars_gravity = cl.statsf[STAT_MOVEVARS_GRAVITY];
1063                 cl.movevars_stopspeed = cl.statsf[STAT_MOVEVARS_STOPSPEED] ;
1064                 cl.movevars_maxspeed = cl.statsf[STAT_MOVEVARS_MAXSPEED];
1065                 cl.movevars_spectatormaxspeed = cl.statsf[STAT_MOVEVARS_SPECTATORMAXSPEED];
1066                 cl.movevars_accelerate = cl.statsf[STAT_MOVEVARS_ACCELERATE];
1067                 cl.movevars_airaccelerate = cl.statsf[STAT_MOVEVARS_AIRACCELERATE];
1068                 cl.movevars_wateraccelerate = cl.statsf[STAT_MOVEVARS_WATERACCELERATE];
1069                 cl.movevars_entgravity = cl.statsf[STAT_MOVEVARS_ENTGRAVITY];
1070                 cl.movevars_jumpvelocity = cl.statsf[STAT_MOVEVARS_JUMPVELOCITY];
1071                 cl.movevars_edgefriction = cl.statsf[STAT_MOVEVARS_EDGEFRICTION];
1072                 cl.movevars_maxairspeed = cl.statsf[STAT_MOVEVARS_MAXAIRSPEED];
1073                 cl.movevars_stepheight = cl.statsf[STAT_MOVEVARS_STEPHEIGHT];
1074                 cl.movevars_airaccel_qw = cl.statsf[STAT_MOVEVARS_AIRACCEL_QW];
1075                 cl.movevars_airaccel_sideways_friction = cl.statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION];
1076                 cl.movevars_friction = cl.statsf[STAT_MOVEVARS_FRICTION];
1077                 cl.movevars_wallfriction = cl.statsf[STAT_MOVEVARS_WALLFRICTION];
1078                 cl.movevars_waterfriction = cl.statsf[STAT_MOVEVARS_WATERFRICTION];
1079                 // scale playback speed of demos by slowmo cvar
1080                 if (cls.demoplayback)
1081                         cl.movevars_timescale *= slowmo.value;
1082         }
1083         else
1084         {
1085                 cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond.value, 100);
1086                 cl.movevars_timescale = slowmo.value;
1087                 cl.movevars_gravity = sv_gravity.value;
1088                 cl.movevars_stopspeed = cl_movement_stopspeed.value;
1089                 cl.movevars_maxspeed = cl_movement_maxspeed.value;
1090                 cl.movevars_spectatormaxspeed = cl_movement_maxspeed.value;
1091                 cl.movevars_accelerate = cl_movement_accelerate.value;
1092                 cl.movevars_airaccelerate = cl_movement_airaccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_airaccelerate.value;
1093                 cl.movevars_wateraccelerate = cl_movement_wateraccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_wateraccelerate.value;
1094                 cl.movevars_friction = cl_movement_friction.value;
1095                 cl.movevars_wallfriction = cl_movement_wallfriction.value;
1096                 cl.movevars_waterfriction = cl_movement_waterfriction.value < 0 ? cl_movement_friction.value : cl_movement_waterfriction.value;
1097                 cl.movevars_entgravity = 1;
1098                 cl.movevars_jumpvelocity = cl_movement_jumpvelocity.value;
1099                 cl.movevars_edgefriction = cl_movement_edgefriction.value;
1100                 cl.movevars_maxairspeed = cl_movement_maxairspeed.value;
1101                 cl.movevars_stepheight = cl_movement_stepheight.value;
1102                 cl.movevars_airaccel_qw = cl_movement_airaccel_qw.value;
1103                 cl.movevars_airaccel_sideways_friction = cl_movement_airaccel_sideways_friction.value;
1104         }
1105 }
1106
1107 void CL_ClientMovement_Replay(void)
1108 {
1109         int i;
1110         qboolean canjump;
1111         double totalmovetime;
1112         cl_clientmovement_state_t s;
1113
1114         CL_ClientMovement_ExpireOldMoves();
1115
1116         // set up starting state for the series of moves
1117         memset(&s, 0, sizeof(s));
1118         VectorCopy(cl.entities[cl.playerentity].state_current.origin, s.origin);
1119         VectorCopy(cl.mvelocity[0], s.velocity);
1120         s.crouched = true; // will be updated on first move
1121         //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]);
1122
1123         totalmovetime = 0;
1124         for (i = 0;i < cl.movement_numqueue - 1;i++)
1125                 totalmovetime += cl.movement_queue[i].frametime;
1126         cl.movement_predicted = totalmovetime * 1000.0 >= cl_movement_minping.value && cls.servermovesequence && (cl_movement.integer && !cls.demoplayback && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission);
1127         //Con_Printf("%i = %.0f >= %.0f && %i && (%i && %i && %i == %i && %i > 0 && %i\n", cl.movement_predicted, totalmovetime * 1000.0, cl_movement_minping.value, cls.servermovesequence, cl_movement.integer, !cls.demoplayback, cls.signon, SIGNONS, cl.stats[STAT_HEALTH], !cl.intermission);
1128         if (cl.movement_predicted)
1129         {
1130                 //Con_Printf("%ims\n", cl.movecmd[0].msec);
1131
1132                 // replay the input queue to predict current location
1133                 // note: this relies on the fact there's always one queue item at the end
1134
1135                 canjump = cl.movement_replay_canjump;
1136                 for (i = 0;i < cl.movement_numqueue;i++)
1137                 {
1138                         s.q = cl.movement_queue[i];
1139                         s.q.canjump = canjump;
1140                         // if a move is more than 50ms, do it as two moves (matching qwsv)
1141                         if (s.q.frametime > 0.05)
1142                         {
1143                                 s.q.frametime *= 0.5;
1144                                 CL_ClientMovement_PlayerMove(&s);
1145                         }
1146                         CL_ClientMovement_PlayerMove(&s);
1147                         canjump = cl.movement_queue[i].canjump = s.q.canjump;
1148                 }
1149         }
1150         else
1151         {
1152                 // get the first movement queue entry to know whether to crouch and such
1153                 s.q = cl.movement_queue[0];
1154         }
1155         CL_ClientMovement_UpdateStatus(&s);
1156
1157         if (cl.movement_replay)
1158         {
1159                 cl.movement_replay = false;
1160                 // update interpolation timestamps if time has passed
1161                 if (cl.movement_time[0] != cl.movecmd[0].time)
1162                 {
1163                         cl.movement_time[1] = cl.movement_time[0];
1164                         cl.movement_time[0] = cl.movecmd[0].time;
1165                         cl.movement_time[2] = cl.time;
1166                         VectorCopy(cl.movement_origin, cl.movement_oldorigin);
1167                         //VectorCopy(s.origin, cl.entities[cl.playerentity].state_current.origin);
1168                         //VectorSet(cl.entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0);
1169                 }
1170
1171                 // update the interpolation target position and velocity
1172                 VectorCopy(s.origin, cl.movement_origin);
1173                 VectorCopy(s.velocity, cl.movement_velocity);
1174         }
1175
1176         // update the onground flag if appropriate
1177         if (cl.movement_predicted)
1178         {
1179                 // when predicted we simply set the flag according to the UpdateStatus
1180                 cl.onground = s.onground;
1181         }
1182         else
1183         {
1184                 // when not predicted, cl.onground is cleared by cl_parse.c each time
1185                 // an update packet is received, but can be forced on here to hide
1186                 // server inconsistencies in the onground flag
1187                 // (which mostly occur when stepping up stairs at very high framerates
1188                 //  where after the step up the move continues forward and not
1189                 //  downward so the ground is not detected)
1190                 //
1191                 // such onground inconsistencies can cause jittery gun bobbing and
1192                 // stair smoothing, so we set onground if UpdateStatus says so
1193                 if (s.onground)
1194                         cl.onground = true;
1195         }
1196
1197         // react to onground state changes (for gun bob)
1198         if (cl.onground)
1199         {
1200                 if (!cl.oldonground)
1201                         cl.hitgroundtime = cl.movecmd[0].time;
1202                 cl.lastongroundtime = cl.movecmd[0].time;
1203         }
1204         cl.oldonground = cl.onground;
1205 }
1206
1207 void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *to)
1208 {
1209         int bits;
1210
1211         bits = 0;
1212         if (to->viewangles[0] != from->viewangles[0])
1213                 bits |= QW_CM_ANGLE1;
1214         if (to->viewangles[1] != from->viewangles[1])
1215                 bits |= QW_CM_ANGLE2;
1216         if (to->viewangles[2] != from->viewangles[2])
1217                 bits |= QW_CM_ANGLE3;
1218         if (to->forwardmove != from->forwardmove)
1219                 bits |= QW_CM_FORWARD;
1220         if (to->sidemove != from->sidemove)
1221                 bits |= QW_CM_SIDE;
1222         if (to->upmove != from->upmove)
1223                 bits |= QW_CM_UP;
1224         if (to->buttons != from->buttons)
1225                 bits |= QW_CM_BUTTONS;
1226         if (to->impulse != from->impulse)
1227                 bits |= QW_CM_IMPULSE;
1228
1229         MSG_WriteByte(buf, bits);
1230         if (bits & QW_CM_ANGLE1)
1231                 MSG_WriteAngle16i(buf, to->viewangles[0]);
1232         if (bits & QW_CM_ANGLE2)
1233                 MSG_WriteAngle16i(buf, to->viewangles[1]);
1234         if (bits & QW_CM_ANGLE3)
1235                 MSG_WriteAngle16i(buf, to->viewangles[2]);
1236         if (bits & QW_CM_FORWARD)
1237                 MSG_WriteShort(buf, to->forwardmove);
1238         if (bits & QW_CM_SIDE)
1239                 MSG_WriteShort(buf, to->sidemove);
1240         if (bits & QW_CM_UP)
1241                 MSG_WriteShort(buf, to->upmove);
1242         if (bits & QW_CM_BUTTONS)
1243                 MSG_WriteByte(buf, to->buttons);
1244         if (bits & QW_CM_IMPULSE)
1245                 MSG_WriteByte(buf, to->impulse);
1246         MSG_WriteByte(buf, to->msec);
1247 }
1248
1249 /*
1250 ==============
1251 CL_SendMove
1252 ==============
1253 */
1254 usercmd_t nullcmd; // for delta compression of qw moves
1255 void CL_SendMove(void)
1256 {
1257         int i, j, packetloss;
1258         int bits;
1259         sizebuf_t buf;
1260         unsigned char data[1024];
1261         static double lastsendtime = 0;
1262         double packettime;
1263         int msecdelta;
1264
1265         CL_ClientMovement_ExpireOldMoves();
1266
1267         // if playing a demo, do nothing
1268         if (!cls.netcon)
1269                 return;
1270
1271         // don't send too often or else network connections can get clogged by a high renderer framerate
1272         packettime = cl.movevars_ticrate;
1273         // quakeworld servers take only frametimes
1274         // predicted dp7 servers take current interpolation time
1275         // unpredicted servers take an echo of the latest server timestamp
1276         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1277         {
1278                 if (realtime < lastsendtime + packettime)
1279                         return;
1280                 cl.cmd.time = realtime;
1281                 cl.cmd.sequence = cls.netcon->qw.outgoing_sequence;
1282         }
1283         else if (cl.movement_predicted)
1284         {
1285                 if (realtime < lastsendtime + packettime)
1286                         return;
1287                 cl.cmd.time = cl.time;
1288                 cl.cmd.sequence = cls.movesequence;
1289         }
1290         else
1291         {
1292                 // unpredicted movement should be sent immediately whenever a server
1293                 // packet is received, to minimize ping times
1294                 if (!cl.movement_needupdate && realtime < lastsendtime + packettime)
1295                         return;
1296                 if (cl.mtime[0] == cl.movecmd[0].time && cl.mtime[1] != cl.mtime[0])
1297                         return;
1298                 cl.cmd.time = cl.mtime[0];
1299                 cl.cmd.sequence = cls.movesequence;
1300         }
1301
1302         // don't let it fall behind if CL_SendMove hasn't been called recently
1303         // (such is the case when framerate is too low for instance)
1304         lastsendtime = bound(realtime, lastsendtime + packettime, realtime + packettime);
1305         // set the flag indicating that we sent a packet recently
1306         cl.movement_needupdate = false;
1307
1308         buf.maxsize = sizeof(data);
1309         buf.cursize = 0;
1310         buf.data = data;
1311
1312         // conditions for sending a move:
1313         // if the move advances time or if the game is paused (in which case time
1314         // is not advancing)
1315         // don't send a new input packet if the connection is still saturated from
1316         // the last one (or chat messages, etc)
1317         // note: this behavior comes from QW
1318         if ((cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS) && !NetConn_CanSend(cls.netcon))
1319                 return;
1320
1321         // increase the move counter since we intend to send a move
1322         cls.movesequence++;
1323
1324         // send the movement message
1325         // PROTOCOL_QUAKE        clc_move = 16 bytes total
1326         // PROTOCOL_QUAKEDP      clc_move = 16 bytes total
1327         // PROTOCOL_NEHAHRAMOVIE clc_move = 16 bytes total
1328         // PROTOCOL_DARKPLACES1  clc_move = 19 bytes total
1329         // PROTOCOL_DARKPLACES2  clc_move = 25 bytes total
1330         // PROTOCOL_DARKPLACES3  clc_move = 25 bytes total
1331         // PROTOCOL_DARKPLACES4  clc_move = 19 bytes total
1332         // PROTOCOL_DARKPLACES5  clc_move = 19 bytes total
1333         // PROTOCOL_DARKPLACES6  clc_move = 52 bytes total
1334         // PROTOCOL_DARKPLACES7  clc_move = 56 bytes total per move (can be up to 16 moves)
1335         // PROTOCOL_QUAKEWORLD   clc_move = 34 bytes total (typically, but can reach 43 bytes, or even 49 bytes with roll)
1336
1337         // set prydon cursor info
1338         CL_UpdatePrydonCursor();
1339
1340         // set button bits
1341         // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
1342         bits = 0;
1343         if (in_attack.state   & 3) bits |=   1;in_attack.state  &= ~2;
1344         if (in_jump.state     & 3) bits |=   2;in_jump.state    &= ~2;
1345         if (in_button3.state  & 3) bits |=   4;in_button3.state &= ~2;
1346         if (in_button4.state  & 3) bits |=   8;in_button4.state &= ~2;
1347         if (in_button5.state  & 3) bits |=  16;in_button5.state &= ~2;
1348         if (in_button6.state  & 3) bits |=  32;in_button6.state &= ~2;
1349         if (in_button7.state  & 3) bits |=  64;in_button7.state &= ~2;
1350         if (in_button8.state  & 3) bits |= 128;in_button8.state &= ~2;
1351         if (in_use.state      & 3) bits |= 256;in_use.state     &= ~2;
1352         if (key_dest != key_game || key_consoleactive) bits |= 512;
1353         if (cl_prydoncursor.integer) bits |= 1024;
1354         if (in_button9.state  & 3)  bits |=   2048;in_button9.state  &= ~2;
1355         if (in_button10.state  & 3) bits |=   4096;in_button10.state &= ~2;
1356         if (in_button11.state  & 3) bits |=   8192;in_button11.state &= ~2;
1357         if (in_button12.state  & 3) bits |=  16384;in_button12.state &= ~2;
1358         if (in_button13.state  & 3) bits |=  32768;in_button13.state &= ~2;
1359         if (in_button14.state  & 3) bits |=  65536;in_button14.state &= ~2;
1360         if (in_button15.state  & 3) bits |= 131072;in_button15.state &= ~2;
1361         if (in_button16.state  & 3) bits |= 262144;in_button16.state &= ~2;
1362         // button bits 19-31 unused currently
1363         // rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
1364         if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
1365         if (cl.cmd.cursor_screen[0] >=  1) bits |= 16;
1366         if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
1367         if (cl.cmd.cursor_screen[1] >=  1) bits |= 64;
1368         cl.cmd.buttons = bits;
1369
1370         // set impulse
1371         cl.cmd.impulse = in_impulse;
1372         in_impulse = 0;
1373
1374         // movement is set by input code (forwardmove/sidemove/upmove)
1375
1376         // set viewangles
1377         VectorCopy(cl.viewangles, cl.cmd.viewangles);
1378
1379         msecdelta = (int)(floor(cl.cmd.time * 1000) - floor(cl.movecmd[0].time * 1000));
1380         cl.cmd.msec = (unsigned char)bound(0, msecdelta, 255);
1381         // ridiculous value rejection (matches qw)
1382         if (cl.cmd.msec > 250)
1383                 cl.cmd.msec = 100;
1384
1385         cl.cmd.predicted = cl_movement.integer;
1386
1387         // always dump the first two messages, because they may contain leftover inputs from the last level
1388         if (cl.cmd.sequence <= 2)
1389                 cl.cmd.forwardmove = cl.cmd.sidemove = cl.cmd.upmove = cl.cmd.impulse = cl.cmd.buttons = 0;
1390
1391         // update the cl.movecmd array which holds the most recent moves
1392         for (i = CL_MAX_USERCMDS - 1;i >= 1;i--)
1393                 cl.movecmd[i] = cl.movecmd[i-1];
1394         cl.movecmd[0] = cl.cmd;
1395
1396         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1397         {
1398                 int checksumindex;
1399
1400                 CL_ClientMovement_Input((cl.movecmd[0].buttons & 2) != 0, false);
1401
1402                 MSG_WriteByte(&buf, qw_clc_move);
1403                 // save the position for a checksum byte
1404                 checksumindex = buf.cursize;
1405                 MSG_WriteByte(&buf, 0);
1406                 // packet loss percentage
1407                 for (j = 0, packetloss = 0;j < NETGRAPH_PACKETS;j++)
1408                         if (cls.netcon->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
1409                                 packetloss++;
1410                 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
1411                 MSG_WriteByte(&buf, packetloss);
1412                 // write most recent 3 moves
1413                 QW_MSG_WriteDeltaUsercmd(&buf, &nullcmd, &cl.movecmd[2]);
1414                 QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[2], &cl.movecmd[1]);
1415                 QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[1], &cl.movecmd[0]);
1416                 // calculate the checksum
1417                 buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->qw.outgoing_sequence);
1418                 // if delta compression history overflows, request no delta
1419                 if (cls.netcon->qw.outgoing_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1)
1420                         cl.qw_validsequence = 0;
1421                 // request delta compression if appropriate
1422                 if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording)
1423                 {
1424                         cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
1425                         MSG_WriteByte(&buf, qw_clc_delta);
1426                         MSG_WriteByte(&buf, cl.qw_validsequence & 255);
1427                 }
1428                 else
1429                         cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1;
1430         }
1431         else if (cls.signon == SIGNONS)
1432         {
1433                 if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE)
1434                 {
1435                         // 5 bytes
1436                         MSG_WriteByte (&buf, clc_move);
1437                         MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
1438                         // 3 bytes
1439                         for (i = 0;i < 3;i++)
1440                                 MSG_WriteAngle8i (&buf, cl.movecmd[0].viewangles[i]);
1441                         // 6 bytes
1442                         MSG_WriteCoord16i (&buf, cl.movecmd[0].forwardmove);
1443                         MSG_WriteCoord16i (&buf, cl.movecmd[0].sidemove);
1444                         MSG_WriteCoord16i (&buf, cl.movecmd[0].upmove);
1445                         // 2 bytes
1446                         MSG_WriteByte (&buf, cl.movecmd[0].buttons);
1447                         MSG_WriteByte (&buf, cl.movecmd[0].impulse);
1448                 }
1449                 else if (cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
1450                 {
1451                         // 5 bytes
1452                         MSG_WriteByte (&buf, clc_move);
1453                         MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
1454                         // 12 bytes
1455                         for (i = 0;i < 3;i++)
1456                                 MSG_WriteAngle32f (&buf, cl.movecmd[0].viewangles[i]);
1457                         // 6 bytes
1458                         MSG_WriteCoord16i (&buf, cl.movecmd[0].forwardmove);
1459                         MSG_WriteCoord16i (&buf, cl.movecmd[0].sidemove);
1460                         MSG_WriteCoord16i (&buf, cl.movecmd[0].upmove);
1461                         // 2 bytes
1462                         MSG_WriteByte (&buf, cl.movecmd[0].buttons);
1463                         MSG_WriteByte (&buf, cl.movecmd[0].impulse);
1464                 }
1465                 else if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5)
1466                 {
1467                         // 5 bytes
1468                         MSG_WriteByte (&buf, clc_move);
1469                         MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
1470                         // 6 bytes
1471                         for (i = 0;i < 3;i++)
1472                                 MSG_WriteAngle16i (&buf, cl.movecmd[0].viewangles[i]);
1473                         // 6 bytes
1474                         MSG_WriteCoord16i (&buf, cl.movecmd[0].forwardmove);
1475                         MSG_WriteCoord16i (&buf, cl.movecmd[0].sidemove);
1476                         MSG_WriteCoord16i (&buf, cl.movecmd[0].upmove);
1477                         // 2 bytes
1478                         MSG_WriteByte (&buf, cl.movecmd[0].buttons);
1479                         MSG_WriteByte (&buf, cl.movecmd[0].impulse);
1480                 }
1481                 else if (cls.signon == SIGNONS)
1482                 {
1483                         int maxusercmds;
1484                         usercmd_t *cmd;
1485
1486                         // FIXME: cl.movecmd[0].buttons & 16 is +button5, Nexuiz specific
1487                         CL_ClientMovement_Input((cl.movecmd[0].buttons & 2) != 0, (cl.movecmd[0].buttons & 16) != 0);
1488
1489                         // set the maxusercmds variable to limit how many should be sent
1490                         maxusercmds = bound(1, cl_netinputpacketlosstolerance.integer + 1, CL_MAX_USERCMDS);
1491                         // when movement prediction is off, there's not much point in repeating old input as it will just be ignored
1492                         if (!cl.cmd.predicted)
1493                                 maxusercmds = 1;
1494
1495                         // send the latest moves in order, the old ones will be
1496                         // ignored by the server harmlessly, however if the previous
1497                         // packets were lost these moves will be used
1498                         //
1499                         // this reduces packet loss impact on gameplay.
1500                         for (j = 0, cmd = &cl.movecmd[maxusercmds-1];j < maxusercmds;j++, cmd--)
1501                         {
1502                                 // don't repeat any stale moves
1503                                 if (cmd->sequence && cmd->sequence < cls.servermovesequence)
1504                                         continue;
1505                                 // 5/9 bytes
1506                                 MSG_WriteByte (&buf, clc_move);
1507                                 if (cls.protocol != PROTOCOL_DARKPLACES6)
1508                                         MSG_WriteLong (&buf, cmd->predicted ? cmd->sequence : 0);
1509                                 MSG_WriteFloat (&buf, cmd->time); // last server packet time
1510                                 // 6 bytes
1511                                 for (i = 0;i < 3;i++)
1512                                         MSG_WriteAngle16i (&buf, cmd->viewangles[i]);
1513                                 // 6 bytes
1514                                 MSG_WriteCoord16i (&buf, cmd->forwardmove);
1515                                 MSG_WriteCoord16i (&buf, cmd->sidemove);
1516                                 MSG_WriteCoord16i (&buf, cmd->upmove);
1517                                 // 5 bytes
1518                                 MSG_WriteLong (&buf, cmd->buttons);
1519                                 MSG_WriteByte (&buf, cmd->impulse);
1520                                 // PRYDON_CLIENTCURSOR
1521                                 // 30 bytes
1522                                 MSG_WriteShort (&buf, (short)(cmd->cursor_screen[0] * 32767.0f));
1523                                 MSG_WriteShort (&buf, (short)(cmd->cursor_screen[1] * 32767.0f));
1524                                 MSG_WriteFloat (&buf, cmd->cursor_start[0]);
1525                                 MSG_WriteFloat (&buf, cmd->cursor_start[1]);
1526                                 MSG_WriteFloat (&buf, cmd->cursor_start[2]);
1527                                 MSG_WriteFloat (&buf, cmd->cursor_impact[0]);
1528                                 MSG_WriteFloat (&buf, cmd->cursor_impact[1]);
1529                                 MSG_WriteFloat (&buf, cmd->cursor_impact[2]);
1530                                 MSG_WriteShort (&buf, cmd->cursor_entitynumber);
1531                         }
1532                 }
1533         }
1534
1535         if (cls.protocol != PROTOCOL_QUAKEWORLD && buf.cursize)
1536         {
1537                 // ack the last few frame numbers
1538                 // (redundent to improve handling of client->server packet loss)
1539                 // for LATESTFRAMENUMS == 3 case this is 15 bytes
1540                 for (i = 0;i < LATESTFRAMENUMS;i++)
1541                 {
1542                         if (cl.latestframenums[i] > 0)
1543                         {
1544                                 if (developer_networkentities.integer >= 10)
1545                                         Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
1546                                 MSG_WriteByte(&buf, clc_ackframe);
1547                                 MSG_WriteLong(&buf, cl.latestframenums[i]);
1548                         }
1549                 }
1550         }
1551
1552         // PROTOCOL_DARKPLACES6 = 67 bytes per packet
1553         // PROTOCOL_DARKPLACES7 = 71 bytes per packet
1554
1555         // acknowledge any recently received data blocks
1556         for (i = 0;i < CL_MAX_DOWNLOADACKS && (cls.dp_downloadack[i].start || cls.dp_downloadack[i].size);i++)
1557         {
1558                 MSG_WriteByte(&buf, clc_ackdownloaddata);
1559                 MSG_WriteLong(&buf, cls.dp_downloadack[i].start);
1560                 MSG_WriteShort(&buf, cls.dp_downloadack[i].size);
1561                 cls.dp_downloadack[i].start = 0;
1562                 cls.dp_downloadack[i].size = 0;
1563         }
1564
1565         // send the reliable message (forwarded commands) if there is one
1566         if (buf.cursize || cls.netcon->message.cursize)
1567                 NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer));
1568
1569         if (cls.netcon->message.overflowed)
1570         {
1571                 Con_Print("CL_SendMove: lost server connection\n");
1572                 CL_Disconnect();
1573                 Host_ShutdownServer();
1574         }
1575 }
1576
1577 /*
1578 ============
1579 CL_InitInput
1580 ============
1581 */
1582 void CL_InitInput (void)
1583 {
1584         Cmd_AddCommand ("+moveup",IN_UpDown, "swim upward");
1585         Cmd_AddCommand ("-moveup",IN_UpUp, "stop swimming upward");
1586         Cmd_AddCommand ("+movedown",IN_DownDown, "swim downward");
1587         Cmd_AddCommand ("-movedown",IN_DownUp, "stop swimming downward");
1588         Cmd_AddCommand ("+left",IN_LeftDown, "turn left");
1589         Cmd_AddCommand ("-left",IN_LeftUp, "stop turning left");
1590         Cmd_AddCommand ("+right",IN_RightDown, "turn right");
1591         Cmd_AddCommand ("-right",IN_RightUp, "stop turning right");
1592         Cmd_AddCommand ("+forward",IN_ForwardDown, "move forward");
1593         Cmd_AddCommand ("-forward",IN_ForwardUp, "stop moving forward");
1594         Cmd_AddCommand ("+back",IN_BackDown, "move backward");
1595         Cmd_AddCommand ("-back",IN_BackUp, "stop moving backward");
1596         Cmd_AddCommand ("+lookup", IN_LookupDown, "look upward");
1597         Cmd_AddCommand ("-lookup", IN_LookupUp, "stop looking upward");
1598         Cmd_AddCommand ("+lookdown", IN_LookdownDown, "look downward");
1599         Cmd_AddCommand ("-lookdown", IN_LookdownUp, "stop looking downward");
1600         Cmd_AddCommand ("+strafe", IN_StrafeDown, "activate strafing mode (move instead of turn)");
1601         Cmd_AddCommand ("-strafe", IN_StrafeUp, "deactivate strafing mode");
1602         Cmd_AddCommand ("+moveleft", IN_MoveleftDown, "strafe left");
1603         Cmd_AddCommand ("-moveleft", IN_MoveleftUp, "stop strafing left");
1604         Cmd_AddCommand ("+moveright", IN_MoverightDown, "strafe right");
1605         Cmd_AddCommand ("-moveright", IN_MoverightUp, "stop strafing right");
1606         Cmd_AddCommand ("+speed", IN_SpeedDown, "activate run mode (faster movement and turning)");
1607         Cmd_AddCommand ("-speed", IN_SpeedUp, "deactivate run mode");
1608         Cmd_AddCommand ("+attack", IN_AttackDown, "begin firing");
1609         Cmd_AddCommand ("-attack", IN_AttackUp, "stop firing");
1610         Cmd_AddCommand ("+jump", IN_JumpDown, "jump");
1611         Cmd_AddCommand ("-jump", IN_JumpUp, "end jump (so you can jump again)");
1612         Cmd_AddCommand ("impulse", IN_Impulse, "send an impulse number to server (select weapon, use item, etc)");
1613         Cmd_AddCommand ("+klook", IN_KLookDown, "activate keyboard looking mode, do not recenter view");
1614         Cmd_AddCommand ("-klook", IN_KLookUp, "deactivate keyboard looking mode");
1615         Cmd_AddCommand ("+mlook", IN_MLookDown, "activate mouse looking mode, do not recenter view");
1616         Cmd_AddCommand ("-mlook", IN_MLookUp, "deactivate mouse looking mode");
1617
1618         // LordHavoc: added use button
1619         Cmd_AddCommand ("+use", IN_UseDown, "use something (may be used by some mods)");
1620         Cmd_AddCommand ("-use", IN_UseUp, "stop using something");
1621
1622         // LordHavoc: added 6 new buttons
1623         Cmd_AddCommand ("+button3", IN_Button3Down, "activate button3 (behavior depends on mod)");
1624         Cmd_AddCommand ("-button3", IN_Button3Up, "deactivate button3");
1625         Cmd_AddCommand ("+button4", IN_Button4Down, "activate button4 (behavior depends on mod)");
1626         Cmd_AddCommand ("-button4", IN_Button4Up, "deactivate button4");
1627         Cmd_AddCommand ("+button5", IN_Button5Down, "activate button5 (behavior depends on mod)");
1628         Cmd_AddCommand ("-button5", IN_Button5Up, "deactivate button5");
1629         Cmd_AddCommand ("+button6", IN_Button6Down, "activate button6 (behavior depends on mod)");
1630         Cmd_AddCommand ("-button6", IN_Button6Up, "deactivate button6");
1631         Cmd_AddCommand ("+button7", IN_Button7Down, "activate button7 (behavior depends on mod)");
1632         Cmd_AddCommand ("-button7", IN_Button7Up, "deactivate button7");
1633         Cmd_AddCommand ("+button8", IN_Button8Down, "activate button8 (behavior depends on mod)");
1634         Cmd_AddCommand ("-button8", IN_Button8Up, "deactivate button8");
1635         Cmd_AddCommand ("+button9", IN_Button9Down, "activate button9 (behavior depends on mod)");
1636         Cmd_AddCommand ("-button9", IN_Button9Up, "deactivate button9");
1637         Cmd_AddCommand ("+button10", IN_Button10Down, "activate button10 (behavior depends on mod)");
1638         Cmd_AddCommand ("-button10", IN_Button10Up, "deactivate button10");
1639         Cmd_AddCommand ("+button11", IN_Button11Down, "activate button11 (behavior depends on mod)");
1640         Cmd_AddCommand ("-button11", IN_Button11Up, "deactivate button11");
1641         Cmd_AddCommand ("+button12", IN_Button12Down, "activate button12 (behavior depends on mod)");
1642         Cmd_AddCommand ("-button12", IN_Button12Up, "deactivate button12");
1643         Cmd_AddCommand ("+button13", IN_Button13Down, "activate button13 (behavior depends on mod)");
1644         Cmd_AddCommand ("-button13", IN_Button13Up, "deactivate button13");
1645         Cmd_AddCommand ("+button14", IN_Button14Down, "activate button14 (behavior depends on mod)");
1646         Cmd_AddCommand ("-button14", IN_Button14Up, "deactivate button14");
1647         Cmd_AddCommand ("+button15", IN_Button15Down, "activate button15 (behavior depends on mod)");
1648         Cmd_AddCommand ("-button15", IN_Button15Up, "deactivate button15");
1649         Cmd_AddCommand ("+button16", IN_Button16Down, "activate button16 (behavior depends on mod)");
1650         Cmd_AddCommand ("-button16", IN_Button16Up, "deactivate button16");
1651
1652         // LordHavoc: added bestweapon command
1653         Cmd_AddCommand ("bestweapon", IN_BestWeapon, "send an impulse number to server to select the first usable weapon out of several (example: 8 7 6 5 4 3 2 1)");
1654
1655         Cvar_RegisterVariable(&cl_movement);
1656         Cvar_RegisterVariable(&cl_movement_minping);
1657         Cvar_RegisterVariable(&cl_movement_maxspeed);
1658         Cvar_RegisterVariable(&cl_movement_maxairspeed);
1659         Cvar_RegisterVariable(&cl_movement_stopspeed);
1660         Cvar_RegisterVariable(&cl_movement_friction);
1661         Cvar_RegisterVariable(&cl_movement_wallfriction);
1662         Cvar_RegisterVariable(&cl_movement_waterfriction);
1663         Cvar_RegisterVariable(&cl_movement_edgefriction);
1664         Cvar_RegisterVariable(&cl_movement_stepheight);
1665         Cvar_RegisterVariable(&cl_movement_accelerate);
1666         Cvar_RegisterVariable(&cl_movement_airaccelerate);
1667         Cvar_RegisterVariable(&cl_movement_wateraccelerate);
1668         Cvar_RegisterVariable(&cl_movement_jumpvelocity);
1669         Cvar_RegisterVariable(&cl_movement_airaccel_qw);
1670         Cvar_RegisterVariable(&cl_movement_airaccel_sideways_friction);
1671
1672         Cvar_RegisterVariable(&in_pitch_min);
1673         Cvar_RegisterVariable(&in_pitch_max);
1674         Cvar_RegisterVariable(&m_filter);
1675
1676         Cvar_RegisterVariable(&cl_netinputpacketspersecond);
1677         Cvar_RegisterVariable(&cl_netinputpacketlosstolerance);
1678
1679         Cvar_RegisterVariable(&cl_nodelta);
1680 }
1681