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