fixed another C99 lazy variable declaration bug in div0's physics patch
[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 /*
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 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, s->movevars_maxairspeed);
1003
1004                         /*
1005                         addspeed = wishspeed - DotProduct(s->velocity, wishdir);
1006                         if (addspeed > 0)
1007                         {
1008                                 accelspeed = min(s->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, s->movevars_accelerate * s->q.frametime * wishspeed) * s->movevars_airaccel_qw;
1021                         if(wishspeed > 0)
1022                                 vel_straight += min(wishspeed, s->movevars_accelerate * s->q.frametime * wishspeed) * (1 - s->movevars_airaccel_qw);
1023
1024                         VectorM(1 - (s->q.frametime * (wishspeed / s->movevars_maxairspeed) * s->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_gravity.value * 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->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 void CL_ClientMovement_Replay(void)
1048 {
1049         int i;
1050         cl_clientmovement_state_t s;
1051
1052         if (!cl.movement_replay)
1053                 return;
1054         cl.movement_replay = false;
1055
1056         // set up starting state for the series of moves
1057         memset(&s, 0, sizeof(s));
1058         VectorCopy(cl.entities[cl.playerentity].state_current.origin, s.origin);
1059         VectorCopy(cl.mvelocity[0], s.velocity);
1060         s.crouched = true; // will be updated on first move
1061         s.canjump = cl.movement_replay_canjump;
1062
1063         // set up movement variables
1064         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1065         {
1066                 s.movevars_gravity = cl.qw_movevars_gravity;
1067                 s.movevars_stopspeed = cl.qw_movevars_stopspeed;
1068                 s.movevars_maxspeed = cl.qw_movevars_maxspeed;
1069                 s.movevars_spectatormaxspeed = cl.qw_movevars_spectatormaxspeed;
1070                 s.movevars_accelerate = cl.qw_movevars_accelerate;
1071                 s.movevars_airaccelerate = cl.qw_movevars_airaccelerate;
1072                 s.movevars_wateraccelerate = cl.qw_movevars_wateraccelerate;
1073                 s.movevars_friction = cl.qw_movevars_friction;
1074                 s.movevars_waterfriction = cl.qw_movevars_waterfriction;
1075                 s.movevars_entgravity = cl.qw_movevars_entgravity;
1076                 s.movevars_jumpvelocity = cl_movement_jumpvelocity.value;
1077                 s.movevars_edgefriction = cl_movement_edgefriction.value;
1078                 s.movevars_maxairspeed = cl_movement_maxairspeed.value;
1079                 s.movevars_stepheight = cl_movement_stepheight.value;
1080                 s.movevars_airaccel_qw = 1.0;
1081                 s.movevars_airaccel_sideways_friction = 0.0;
1082         }
1083         else
1084         {
1085                 s.movevars_gravity = sv_gravity.value;
1086                 s.movevars_stopspeed = cl_movement_stopspeed.value;
1087                 s.movevars_maxspeed = cl_movement_maxspeed.value;
1088                 s.movevars_spectatormaxspeed = cl_movement_maxspeed.value;
1089                 s.movevars_accelerate = cl_movement_accelerate.value;
1090                 s.movevars_airaccelerate = cl_movement_airaccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_airaccelerate.value;
1091                 s.movevars_wateraccelerate = cl_movement_wateraccelerate.value < 0 ? cl_movement_accelerate.value : cl_movement_wateraccelerate.value;
1092                 s.movevars_friction = cl_movement_friction.value;
1093                 s.movevars_waterfriction = cl_movement_waterfriction.value < 0 ? cl_movement_friction.value : cl_movement_waterfriction.value;
1094                 s.movevars_entgravity = 1;
1095                 s.movevars_jumpvelocity = cl_movement_jumpvelocity.value;
1096                 s.movevars_edgefriction = cl_movement_edgefriction.value;
1097                 s.movevars_maxairspeed = cl_movement_maxairspeed.value;
1098                 s.movevars_stepheight = cl_movement_stepheight.value;
1099                 s.movevars_airaccel_qw = cl_movement_airaccel_qw.value;
1100                 s.movevars_airaccel_sideways_friction = cl_movement_airaccel_sideways_friction.value;
1101         }
1102
1103         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);
1104         if (cl.movement_predicted)
1105         {
1106                 //Con_Printf("%f: ", cl.mtime[0]);
1107
1108                 // replay the input queue to predict current location
1109                 // note: this relies on the fact there's always one queue item at the end
1110
1111                 for (i = 0;i < cl.movement_numqueue;i++)
1112                 {
1113                         s.q = cl.movement_queue[i];
1114                         // if a move is more than 50ms, do it as two moves (matching qwsv)
1115                         if (s.q.frametime > 0.05)
1116                         {
1117                                 s.q.frametime *= 0.5;
1118                                 CL_ClientMovement_PlayerMove(&s);
1119                         }
1120                         CL_ClientMovement_PlayerMove(&s);
1121                 }
1122         }
1123         else
1124         {
1125                 // get the first movement queue entry to know whether to crouch and such
1126                 s.q = cl.movement_queue[0];
1127         }
1128         // store replay location
1129         CL_ClientMovement_UpdateStatus(&s);
1130         cl.onground = s.onground;
1131         cl.movement_time[1] = cl.movement_time[0];
1132         cl.movement_time[0] = cl.movement_queue[cl.movement_numqueue-1].time;
1133         VectorCopy(cl.movement_origin, cl.movement_oldorigin);
1134         VectorCopy(s.origin, cl.movement_origin);
1135         VectorCopy(s.velocity, cl.movement_velocity);
1136         //VectorCopy(s.origin, cl.entities[cl.playerentity].state_current.origin);
1137         //VectorSet(cl.entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0);
1138 }
1139
1140 void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, qw_usercmd_t *from, qw_usercmd_t *to)
1141 {
1142         int bits;
1143
1144         bits = 0;
1145         if (to->angles[0] != from->angles[0])
1146                 bits |= QW_CM_ANGLE1;
1147         if (to->angles[1] != from->angles[1])
1148                 bits |= QW_CM_ANGLE2;
1149         if (to->angles[2] != from->angles[2])
1150                 bits |= QW_CM_ANGLE3;
1151         if (to->forwardmove != from->forwardmove)
1152                 bits |= QW_CM_FORWARD;
1153         if (to->sidemove != from->sidemove)
1154                 bits |= QW_CM_SIDE;
1155         if (to->upmove != from->upmove)
1156                 bits |= QW_CM_UP;
1157         if (to->buttons != from->buttons)
1158                 bits |= QW_CM_BUTTONS;
1159         if (to->impulse != from->impulse)
1160                 bits |= QW_CM_IMPULSE;
1161
1162         MSG_WriteByte(buf, bits);
1163         if (bits & QW_CM_ANGLE1)
1164                 MSG_WriteAngle16i(buf, to->angles[0]);
1165         if (bits & QW_CM_ANGLE2)
1166                 MSG_WriteAngle16i(buf, to->angles[1]);
1167         if (bits & QW_CM_ANGLE3)
1168                 MSG_WriteAngle16i(buf, to->angles[2]);
1169         if (bits & QW_CM_FORWARD)
1170                 MSG_WriteShort(buf, to->forwardmove);
1171         if (bits & QW_CM_SIDE)
1172                 MSG_WriteShort(buf, to->sidemove);
1173         if (bits & QW_CM_UP)
1174                 MSG_WriteShort(buf, to->upmove);
1175         if (bits & QW_CM_BUTTONS)
1176                 MSG_WriteByte(buf, to->buttons);
1177         if (bits & QW_CM_IMPULSE)
1178                 MSG_WriteByte(buf, to->impulse);
1179         MSG_WriteByte(buf, to->msec);
1180 }
1181
1182 /*
1183 ==============
1184 CL_SendMove
1185 ==============
1186 */
1187 extern cvar_t cl_netinputpacketspersecond;
1188 void CL_SendMove(void)
1189 {
1190         int i;
1191         int bits;
1192         int impulse;
1193         sizebuf_t buf;
1194         unsigned char data[128];
1195         static double lastsendtime = 0;
1196 #define MOVEAVERAGING 0
1197 #if MOVEAVERAGING
1198         static float accumforwardmove = 0, accumsidemove = 0, accumupmove = 0, accumtotal = 0; // accumulation
1199 #endif
1200         float forwardmove, sidemove, upmove;
1201
1202         // if playing a demo, do nothing
1203         if (!cls.netcon)
1204                 return;
1205
1206 #if MOVEAVERAGING
1207         // accumulate changes between messages
1208         accumforwardmove += cl.cmd.forwardmove;
1209         accumsidemove += cl.cmd.sidemove;
1210         accumupmove += cl.cmd.upmove;
1211         accumtotal++;
1212 #endif
1213
1214         if (cl_movement.integer && cls.signon == SIGNONS && cls.protocol != PROTOCOL_QUAKEWORLD)
1215         {
1216                 if (!cl.movement_needupdate)
1217                         return;
1218                 cl.movement_needupdate = false;
1219         }
1220         else
1221         {
1222                 if (realtime < lastsendtime + 1.0 / bound(10, cl_netinputpacketspersecond.value, 100))
1223                         return;
1224                 // don't let it fall behind if CL_SendMove hasn't been called recently
1225                 // (such is the case when framerate is too low for instance)
1226                 lastsendtime = max(lastsendtime + 1.0 / bound(10, cl_netinputpacketspersecond.value, 100), realtime);
1227         }
1228 #if MOVEAVERAGING
1229         // average the accumulated changes
1230         accumtotal = 1.0f / accumtotal;
1231         forwardmove = accumforwardmove * accumtotal;
1232         sidemove = accumsidemove * accumtotal;
1233         upmove = accumupmove * accumtotal;
1234         accumforwardmove = 0;
1235         accumsidemove = 0;
1236         accumupmove = 0;
1237         accumtotal = 0;
1238 #else
1239         // use the latest values
1240         forwardmove = cl.cmd.forwardmove;
1241         sidemove = cl.cmd.sidemove;
1242         upmove = cl.cmd.upmove;
1243 #endif
1244
1245         if (cls.signon == SIGNONS)
1246                 CL_UpdatePrydonCursor();
1247
1248         buf.maxsize = 128;
1249         buf.cursize = 0;
1250         buf.data = data;
1251
1252         // set button bits
1253         // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
1254         bits = 0;
1255         if (in_attack.state   & 3) bits |=   1;in_attack.state  &= ~2;
1256         if (in_jump.state     & 3) bits |=   2;in_jump.state    &= ~2;
1257         if (in_button3.state  & 3) bits |=   4;in_button3.state &= ~2;
1258         if (in_button4.state  & 3) bits |=   8;in_button4.state &= ~2;
1259         if (in_button5.state  & 3) bits |=  16;in_button5.state &= ~2;
1260         if (in_button6.state  & 3) bits |=  32;in_button6.state &= ~2;
1261         if (in_button7.state  & 3) bits |=  64;in_button7.state &= ~2;
1262         if (in_button8.state  & 3) bits |= 128;in_button8.state &= ~2;
1263         if (in_use.state      & 3) bits |= 256;in_use.state     &= ~2;
1264         if (key_dest != key_game || key_consoleactive) bits |= 512;
1265         if (cl_prydoncursor.integer) bits |= 1024;
1266         if (in_button9.state  & 3)  bits |=   2048;in_button9.state  &= ~2;
1267         if (in_button10.state  & 3) bits |=   4096;in_button10.state &= ~2;
1268         if (in_button11.state  & 3) bits |=   8192;in_button11.state &= ~2;
1269         if (in_button12.state  & 3) bits |=  16384;in_button12.state &= ~2;
1270         if (in_button13.state  & 3) bits |=  32768;in_button13.state &= ~2;
1271         if (in_button14.state  & 3) bits |=  65536;in_button14.state &= ~2;
1272         if (in_button15.state  & 3) bits |= 131072;in_button15.state &= ~2;
1273         if (in_button16.state  & 3) bits |= 262144;in_button16.state &= ~2;
1274         // button bits 19-31 unused currently
1275         // rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
1276         if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
1277         if (cl.cmd.cursor_screen[0] >=  1) bits |= 16;
1278         if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
1279         if (cl.cmd.cursor_screen[1] >=  1) bits |= 64;
1280
1281         impulse = in_impulse;
1282         in_impulse = 0;
1283
1284         csqc_buttons = bits;
1285
1286         if (cls.signon == SIGNONS)
1287         {
1288                 // always dump the first two messages, because they may contain leftover inputs from the last level
1289                 if (++cl.movemessages >= 2)
1290                 {
1291                         // send the movement message
1292                         // PROTOCOL_QUAKE        clc_move = 16 bytes total
1293                         // PROTOCOL_QUAKEDP      clc_move = 16 bytes total
1294                         // PROTOCOL_NEHAHRAMOVIE clc_move = 16 bytes total
1295                         // PROTOCOL_DARKPLACES1  clc_move = 19 bytes total
1296                         // PROTOCOL_DARKPLACES2  clc_move = 25 bytes total
1297                         // PROTOCOL_DARKPLACES3  clc_move = 25 bytes total
1298                         // PROTOCOL_DARKPLACES4  clc_move = 19 bytes total
1299                         // PROTOCOL_DARKPLACES5  clc_move = 19 bytes total
1300                         // PROTOCOL_DARKPLACES6  clc_move = 52 bytes total
1301                         // PROTOCOL_DARKPLACES7  clc_move = 56 bytes total
1302                         // PROTOCOL_QUAKEWORLD   clc_move = 34 bytes total (typically, but can reach 43 bytes, or even 49 bytes with roll)
1303                         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1304                         {
1305                                 int checksumindex;
1306                                 double msectime;
1307                                 static double oldmsectime;
1308                                 qw_usercmd_t *cmd, *oldcmd;
1309                                 qw_usercmd_t nullcmd;
1310
1311                                 //Con_Printf("code qw_clc_move\n");
1312
1313                                 i = cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK;
1314                                 cmd = &cl.qw_moves[i];
1315                                 memset(&nullcmd, 0, sizeof(nullcmd));
1316                                 memset(cmd, 0, sizeof(*cmd));
1317                                 cmd->buttons = bits;
1318                                 cmd->impulse = impulse;
1319                                 cmd->forwardmove = (short)bound(-32768, forwardmove, 32767);
1320                                 cmd->sidemove = (short)bound(-32768, sidemove, 32767);
1321                                 cmd->upmove = (short)bound(-32768, upmove, 32767);
1322                                 VectorCopy(cl.viewangles, cmd->angles);
1323                                 msectime = realtime * 1000;
1324                                 cmd->msec = (unsigned char)bound(0, msectime - oldmsectime, 255);
1325                                 // ridiculous value rejection (matches qw)
1326                                 if (cmd->msec > 250)
1327                                         cmd->msec = 100;
1328                                 oldmsectime = msectime;
1329
1330                                 CL_ClientMovement_InputQW(cmd);
1331
1332                                 MSG_WriteByte(&buf, qw_clc_move);
1333                                 // save the position for a checksum byte
1334                                 checksumindex = buf.cursize;
1335                                 MSG_WriteByte(&buf, 0);
1336                                 // packet loss percentage
1337                                 // FIXME: netgraph stuff
1338                                 MSG_WriteByte(&buf, 0);
1339                                 // write most recent 3 moves
1340                                 i = (cls.netcon->qw.outgoing_sequence-2) & QW_UPDATE_MASK;
1341                                 cmd = &cl.qw_moves[i];
1342                                 QW_MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
1343                                 oldcmd = cmd;
1344                                 i = (cls.netcon->qw.outgoing_sequence-1) & QW_UPDATE_MASK;
1345                                 cmd = &cl.qw_moves[i];
1346                                 QW_MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
1347                                 oldcmd = cmd;
1348                                 i = cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK;
1349                                 cmd = &cl.qw_moves[i];
1350                                 QW_MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
1351                                 // calculate the checksum
1352                                 buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->qw.outgoing_sequence);
1353                                 // if delta compression history overflows, request no delta
1354                                 if (cls.netcon->qw.outgoing_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1)
1355                                         cl.qw_validsequence = 0;
1356                                 // request delta compression if appropriate
1357                                 if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording)
1358                                 {
1359                                         cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
1360                                         MSG_WriteByte(&buf, qw_clc_delta);
1361                                         MSG_WriteByte(&buf, cl.qw_validsequence & 255);
1362                                 }
1363                                 else
1364                                         cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1;
1365                         }
1366                         else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE)
1367                         {
1368                                 // 5 bytes
1369                                 MSG_WriteByte (&buf, clc_move);
1370                                 MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
1371                                 // 3 bytes
1372                                 for (i = 0;i < 3;i++)
1373                                         MSG_WriteAngle8i (&buf, cl.viewangles[i]);
1374                                 // 6 bytes
1375                                 MSG_WriteCoord16i (&buf, forwardmove);
1376                                 MSG_WriteCoord16i (&buf, sidemove);
1377                                 MSG_WriteCoord16i (&buf, upmove);
1378                                 // 2 bytes
1379                                 MSG_WriteByte (&buf, bits);
1380                                 MSG_WriteByte (&buf, impulse);
1381
1382                                 CL_ClientMovement_Input((bits & 2) != 0, false);
1383                         }
1384                         else if (cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
1385                         {
1386                                 // 5 bytes
1387                                 MSG_WriteByte (&buf, clc_move);
1388                                 MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
1389                                 // 12 bytes
1390                                 for (i = 0;i < 3;i++)
1391                                         MSG_WriteAngle32f (&buf, cl.viewangles[i]);
1392                                 // 6 bytes
1393                                 MSG_WriteCoord16i (&buf, forwardmove);
1394                                 MSG_WriteCoord16i (&buf, sidemove);
1395                                 MSG_WriteCoord16i (&buf, upmove);
1396                                 // 2 bytes
1397                                 MSG_WriteByte (&buf, bits);
1398                                 MSG_WriteByte (&buf, impulse);
1399
1400                                 CL_ClientMovement_Input((bits & 2) != 0, false);
1401                         }
1402                         else if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5)
1403                         {
1404                                 // 5 bytes
1405                                 MSG_WriteByte (&buf, clc_move);
1406                                 MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
1407                                 // 6 bytes
1408                                 for (i = 0;i < 3;i++)
1409                                         MSG_WriteAngle16i (&buf, cl.viewangles[i]);
1410                                 // 6 bytes
1411                                 MSG_WriteCoord16i (&buf, forwardmove);
1412                                 MSG_WriteCoord16i (&buf, sidemove);
1413                                 MSG_WriteCoord16i (&buf, upmove);
1414                                 // 2 bytes
1415                                 MSG_WriteByte (&buf, bits);
1416                                 MSG_WriteByte (&buf, impulse);
1417
1418                                 CL_ClientMovement_Input((bits & 2) != 0, false);
1419                         }
1420                         else
1421                         {
1422                                 // 5 bytes
1423                                 MSG_WriteByte (&buf, clc_move);
1424                                 if (cls.protocol != PROTOCOL_DARKPLACES6)
1425                                 {
1426                                         if (cl_movement.integer)
1427                                         {
1428                                                 cl.movesequence++;
1429                                                 MSG_WriteLong (&buf, cl.movesequence);
1430                                         }
1431                                         else
1432                                                 MSG_WriteLong (&buf, 0);
1433                                 }
1434                                 MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
1435                                 // 6 bytes
1436                                 for (i = 0;i < 3;i++)
1437                                         MSG_WriteAngle16i (&buf, cl.viewangles[i]);
1438                                 // 6 bytes
1439                                 MSG_WriteCoord16i (&buf, forwardmove);
1440                                 MSG_WriteCoord16i (&buf, sidemove);
1441                                 MSG_WriteCoord16i (&buf, upmove);
1442                                 // 5 bytes
1443                                 MSG_WriteLong (&buf, bits);
1444                                 MSG_WriteByte (&buf, impulse);
1445                                 // PRYDON_CLIENTCURSOR
1446                                 // 30 bytes
1447                                 MSG_WriteShort (&buf, (short)(cl.cmd.cursor_screen[0] * 32767.0f));
1448                                 MSG_WriteShort (&buf, (short)(cl.cmd.cursor_screen[1] * 32767.0f));
1449                                 MSG_WriteFloat (&buf, cl.cmd.cursor_start[0]);
1450                                 MSG_WriteFloat (&buf, cl.cmd.cursor_start[1]);
1451                                 MSG_WriteFloat (&buf, cl.cmd.cursor_start[2]);
1452                                 MSG_WriteFloat (&buf, cl.cmd.cursor_impact[0]);
1453                                 MSG_WriteFloat (&buf, cl.cmd.cursor_impact[1]);
1454                                 MSG_WriteFloat (&buf, cl.cmd.cursor_impact[2]);
1455                                 MSG_WriteShort (&buf, cl.cmd.cursor_entitynumber);
1456
1457                                 // FIXME: bits & 16 is +button5, Nexuiz specific
1458                                 CL_ClientMovement_Input((bits & 2) != 0, (bits & 16) != 0);
1459                         }
1460                 }
1461
1462                 if (cls.protocol != PROTOCOL_QUAKEWORLD)
1463                 {
1464                         // ack the last few frame numbers
1465                         // (redundent to improve handling of client->server packet loss)
1466                         // for LATESTFRAMENUMS == 3 case this is 15 bytes
1467                         for (i = 0;i < LATESTFRAMENUMS;i++)
1468                         {
1469                                 if (cl.latestframenums[i] > 0)
1470                                 {
1471                                         if (developer_networkentities.integer >= 1)
1472                                                 Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
1473                                         MSG_WriteByte(&buf, clc_ackframe);
1474                                         MSG_WriteLong(&buf, cl.latestframenums[i]);
1475                                 }
1476                         }
1477                 }
1478
1479                 // PROTOCOL_DARKPLACES6 = 67 bytes per packet
1480                 // PROTOCOL_DARKPLACES7 = 71 bytes per packet
1481         }
1482
1483         // send the reliable message (forwarded commands) if there is one
1484         NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
1485
1486         if (cls.netcon->message.overflowed)
1487         {
1488                 Con_Print("CL_SendMove: lost server connection\n");
1489                 CL_Disconnect();
1490                 Host_ShutdownServer();
1491         }
1492 }
1493
1494 /*
1495 ============
1496 CL_InitInput
1497 ============
1498 */
1499 void CL_InitInput (void)
1500 {
1501         Cmd_AddCommand ("+moveup",IN_UpDown, "swim upward");
1502         Cmd_AddCommand ("-moveup",IN_UpUp, "stop swimming upward");
1503         Cmd_AddCommand ("+movedown",IN_DownDown, "swim downward");
1504         Cmd_AddCommand ("-movedown",IN_DownUp, "stop swimming downward");
1505         Cmd_AddCommand ("+left",IN_LeftDown, "turn left");
1506         Cmd_AddCommand ("-left",IN_LeftUp, "stop turning left");
1507         Cmd_AddCommand ("+right",IN_RightDown, "turn right");
1508         Cmd_AddCommand ("-right",IN_RightUp, "stop turning right");
1509         Cmd_AddCommand ("+forward",IN_ForwardDown, "move forward");
1510         Cmd_AddCommand ("-forward",IN_ForwardUp, "stop moving forward");
1511         Cmd_AddCommand ("+back",IN_BackDown, "move backward");
1512         Cmd_AddCommand ("-back",IN_BackUp, "stop moving backward");
1513         Cmd_AddCommand ("+lookup", IN_LookupDown, "look upward");
1514         Cmd_AddCommand ("-lookup", IN_LookupUp, "stop looking upward");
1515         Cmd_AddCommand ("+lookdown", IN_LookdownDown, "look downward");
1516         Cmd_AddCommand ("-lookdown", IN_LookdownUp, "stop looking downward");
1517         Cmd_AddCommand ("+strafe", IN_StrafeDown, "activate strafing mode (move instead of turn)\n");
1518         Cmd_AddCommand ("-strafe", IN_StrafeUp, "deactivate strafing mode");
1519         Cmd_AddCommand ("+moveleft", IN_MoveleftDown, "strafe left");
1520         Cmd_AddCommand ("-moveleft", IN_MoveleftUp, "stop strafing left");
1521         Cmd_AddCommand ("+moveright", IN_MoverightDown, "strafe right");
1522         Cmd_AddCommand ("-moveright", IN_MoverightUp, "stop strafing right");
1523         Cmd_AddCommand ("+speed", IN_SpeedDown, "activate run mode (faster movement and turning)");
1524         Cmd_AddCommand ("-speed", IN_SpeedUp, "deactivate run mode");
1525         Cmd_AddCommand ("+attack", IN_AttackDown, "begin firing");
1526         Cmd_AddCommand ("-attack", IN_AttackUp, "stop firing");
1527         Cmd_AddCommand ("+jump", IN_JumpDown, "jump");
1528         Cmd_AddCommand ("-jump", IN_JumpUp, "end jump (so you can jump again)");
1529         Cmd_AddCommand ("impulse", IN_Impulse, "send an impulse number to server (select weapon, use item, etc)");
1530         Cmd_AddCommand ("+klook", IN_KLookDown, "activate keyboard looking mode, do not recenter view");
1531         Cmd_AddCommand ("-klook", IN_KLookUp, "deactivate keyboard looking mode");
1532         Cmd_AddCommand ("+mlook", IN_MLookDown, "activate mouse looking mode, do not recenter view");
1533         Cmd_AddCommand ("-mlook", IN_MLookUp, "deactivate mouse looking mode");
1534
1535         // LordHavoc: added use button
1536         Cmd_AddCommand ("+use", IN_UseDown, "use something (may be used by some mods)");
1537         Cmd_AddCommand ("-use", IN_UseUp, "stop using something");
1538
1539         // LordHavoc: added 6 new buttons
1540         Cmd_AddCommand ("+button3", IN_Button3Down, "activate button3 (behavior depends on mod)");
1541         Cmd_AddCommand ("-button3", IN_Button3Up, "deactivate button3");
1542         Cmd_AddCommand ("+button4", IN_Button4Down, "activate button4 (behavior depends on mod)");
1543         Cmd_AddCommand ("-button4", IN_Button4Up, "deactivate button4");
1544         Cmd_AddCommand ("+button5", IN_Button5Down, "activate button5 (behavior depends on mod)");
1545         Cmd_AddCommand ("-button5", IN_Button5Up, "deactivate button5");
1546         Cmd_AddCommand ("+button6", IN_Button6Down, "activate button6 (behavior depends on mod)");
1547         Cmd_AddCommand ("-button6", IN_Button6Up, "deactivate button6");
1548         Cmd_AddCommand ("+button7", IN_Button7Down, "activate button7 (behavior depends on mod)");
1549         Cmd_AddCommand ("-button7", IN_Button7Up, "deactivate button7");
1550         Cmd_AddCommand ("+button8", IN_Button8Down, "activate button8 (behavior depends on mod)");
1551         Cmd_AddCommand ("-button8", IN_Button8Up, "deactivate button8");
1552         Cmd_AddCommand ("+button9", IN_Button9Down, "activate button9 (behavior depends on mod)");
1553         Cmd_AddCommand ("-button9", IN_Button9Up, "deactivate button9");
1554         Cmd_AddCommand ("+button10", IN_Button10Down, "activate button10 (behavior depends on mod)");
1555         Cmd_AddCommand ("-button10", IN_Button10Up, "deactivate button10");
1556         Cmd_AddCommand ("+button11", IN_Button11Down, "activate button11 (behavior depends on mod)");
1557         Cmd_AddCommand ("-button11", IN_Button11Up, "deactivate button11");
1558         Cmd_AddCommand ("+button12", IN_Button12Down, "activate button12 (behavior depends on mod)");
1559         Cmd_AddCommand ("-button12", IN_Button12Up, "deactivate button12");
1560         Cmd_AddCommand ("+button13", IN_Button13Down, "activate button13 (behavior depends on mod)");
1561         Cmd_AddCommand ("-button13", IN_Button13Up, "deactivate button13");
1562         Cmd_AddCommand ("+button14", IN_Button14Down, "activate button14 (behavior depends on mod)");
1563         Cmd_AddCommand ("-button14", IN_Button14Up, "deactivate button14");
1564         Cmd_AddCommand ("+button15", IN_Button15Down, "activate button15 (behavior depends on mod)");
1565         Cmd_AddCommand ("-button15", IN_Button15Up, "deactivate button15");
1566         Cmd_AddCommand ("+button16", IN_Button16Down, "activate button16 (behavior depends on mod)");
1567         Cmd_AddCommand ("-button16", IN_Button16Up, "deactivate button16");
1568
1569         Cvar_RegisterVariable(&cl_movement);
1570         Cvar_RegisterVariable(&cl_movement_latency);
1571         Cvar_RegisterVariable(&cl_movement_maxspeed);
1572         Cvar_RegisterVariable(&cl_movement_maxairspeed);
1573         Cvar_RegisterVariable(&cl_movement_stopspeed);
1574         Cvar_RegisterVariable(&cl_movement_friction);
1575         Cvar_RegisterVariable(&cl_movement_edgefriction);
1576         Cvar_RegisterVariable(&cl_movement_stepheight);
1577         Cvar_RegisterVariable(&cl_movement_airaccelerate);
1578         Cvar_RegisterVariable(&cl_movement_accelerate);
1579         Cvar_RegisterVariable(&cl_movement_jumpvelocity);
1580         Cvar_RegisterVariable(&cl_movement_airaccel_qw);
1581         Cvar_RegisterVariable(&cl_movement_airaccel_sideways_friction);
1582         Cvar_RegisterVariable(&cl_gravity);
1583         Cvar_RegisterVariable(&cl_slowmo);
1584
1585         Cvar_RegisterVariable(&in_pitch_min);
1586         Cvar_RegisterVariable(&in_pitch_max);
1587         Cvar_RegisterVariable(&m_filter);
1588
1589         Cvar_RegisterVariable(&cl_netinputpacketspersecond);
1590
1591         Cvar_RegisterVariable(&cl_nodelta);
1592 }
1593