]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_input.c
added onground tracking to cl_movement code, instead of checking constantly (which...
[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
27 /*
28 ===============================================================================
29
30 KEY BUTTONS
31
32 Continuous button event tracking is complicated by the fact that two different
33 input sources (say, mouse button 1 and the control key) can both press the
34 same button, but the button should only be released when both of the
35 pressing key have been released.
36
37 When a key event issues a button command (+forward, +attack, etc), it appends
38 its key number as a parameter to the command so it can be matched up with
39 the release.
40
41 state bit 0 is the current state of the key
42 state bit 1 is edge triggered on the up to down transition
43 state bit 2 is edge triggered on the down to up transition
44
45 ===============================================================================
46 */
47
48
49 kbutton_t       in_mlook, in_klook;
50 kbutton_t       in_left, in_right, in_forward, in_back;
51 kbutton_t       in_lookup, in_lookdown, in_moveleft, in_moveright;
52 kbutton_t       in_strafe, in_speed, in_jump, in_attack, in_use;
53 kbutton_t       in_up, in_down;
54 // LordHavoc: added 6 new buttons
55 kbutton_t       in_button3, in_button4, in_button5, in_button6, in_button7, in_button8;
56
57 int                     in_impulse;
58
59 extern cvar_t sys_ticrate;
60
61
62 void KeyDown (kbutton_t *b)
63 {
64         int k;
65         const char *c;
66
67         c = Cmd_Argv(1);
68         if (c[0])
69                 k = atoi(c);
70         else
71                 k = -1;         // typed manually at the console for continuous down
72
73         if (k == b->down[0] || k == b->down[1])
74                 return;         // repeating key
75
76         if (!b->down[0])
77                 b->down[0] = k;
78         else if (!b->down[1])
79                 b->down[1] = k;
80         else
81         {
82                 Con_Print("Three keys down for a button!\n");
83                 return;
84         }
85
86         if (b->state & 1)
87                 return;         // still down
88         b->state |= 1 + 2;      // down + impulse down
89 }
90
91 void KeyUp (kbutton_t *b)
92 {
93         int k;
94         const char *c;
95
96         c = Cmd_Argv(1);
97         if (c[0])
98                 k = atoi(c);
99         else
100         { // typed manually at the console, assume for unsticking, so clear all
101                 b->down[0] = b->down[1] = 0;
102                 b->state = 4;   // impulse up
103                 return;
104         }
105
106         if (b->down[0] == k)
107                 b->down[0] = 0;
108         else if (b->down[1] == k)
109                 b->down[1] = 0;
110         else
111                 return;         // key up without coresponding down (menu pass through)
112         if (b->down[0] || b->down[1])
113                 return;         // some other key is still holding it down
114
115         if (!(b->state & 1))
116                 return;         // still up (this should not happen)
117         b->state &= ~1;         // now up
118         b->state |= 4;          // impulse up
119 }
120
121 void IN_KLookDown (void) {KeyDown(&in_klook);}
122 void IN_KLookUp (void) {KeyUp(&in_klook);}
123 void IN_MLookDown (void) {KeyDown(&in_mlook);}
124 void IN_MLookUp (void)
125 {
126         KeyUp(&in_mlook);
127         if ( !(in_mlook.state&1) && lookspring.value)
128                 V_StartPitchDrift();
129 }
130 void IN_UpDown(void) {KeyDown(&in_up);}
131 void IN_UpUp(void) {KeyUp(&in_up);}
132 void IN_DownDown(void) {KeyDown(&in_down);}
133 void IN_DownUp(void) {KeyUp(&in_down);}
134 void IN_LeftDown(void) {KeyDown(&in_left);}
135 void IN_LeftUp(void) {KeyUp(&in_left);}
136 void IN_RightDown(void) {KeyDown(&in_right);}
137 void IN_RightUp(void) {KeyUp(&in_right);}
138 void IN_ForwardDown(void) {KeyDown(&in_forward);}
139 void IN_ForwardUp(void) {KeyUp(&in_forward);}
140 void IN_BackDown(void) {KeyDown(&in_back);}
141 void IN_BackUp(void) {KeyUp(&in_back);}
142 void IN_LookupDown(void) {KeyDown(&in_lookup);}
143 void IN_LookupUp(void) {KeyUp(&in_lookup);}
144 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
145 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
146 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
147 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
148 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
149 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
150
151 void IN_SpeedDown(void) {KeyDown(&in_speed);}
152 void IN_SpeedUp(void) {KeyUp(&in_speed);}
153 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
154 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
155
156 void IN_AttackDown(void) {KeyDown(&in_attack);}
157 void IN_AttackUp(void) {KeyUp(&in_attack);}
158
159 void IN_UseDown(void) {KeyDown(&in_use);}
160 void IN_UseUp(void) {KeyUp(&in_use);}
161
162 // LordHavoc: added 6 new buttons
163 void IN_Button3Down(void) {KeyDown(&in_button3);}
164 void IN_Button3Up(void) {KeyUp(&in_button3);}
165 void IN_Button4Down(void) {KeyDown(&in_button4);}
166 void IN_Button4Up(void) {KeyUp(&in_button4);}
167 void IN_Button5Down(void) {KeyDown(&in_button5);}
168 void IN_Button5Up(void) {KeyUp(&in_button5);}
169 void IN_Button6Down(void) {KeyDown(&in_button6);}
170 void IN_Button6Up(void) {KeyUp(&in_button6);}
171 void IN_Button7Down(void) {KeyDown(&in_button7);}
172 void IN_Button7Up(void) {KeyUp(&in_button7);}
173 void IN_Button8Down(void) {KeyDown(&in_button8);}
174 void IN_Button8Up(void) {KeyUp(&in_button8);}
175
176 void IN_JumpDown (void) {KeyDown(&in_jump);}
177 void IN_JumpUp (void) {KeyUp(&in_jump);}
178
179 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
180
181 /*
182 ===============
183 CL_KeyState
184
185 Returns 0.25 if a key was pressed and released during the frame,
186 0.5 if it was pressed and held
187 0 if held then released, and
188 1.0 if held for the entire time
189 ===============
190 */
191 float CL_KeyState (kbutton_t *key)
192 {
193         float           val;
194         qboolean        impulsedown, impulseup, down;
195
196         impulsedown = key->state & 2;
197         impulseup = key->state & 4;
198         down = key->state & 1;
199         val = 0;
200
201         if (impulsedown && !impulseup)
202         {
203                 if (down)
204                         val = 0.5;      // pressed and held this frame
205                 else
206                         val = 0;        //      I_Error ();
207         }
208         if (impulseup && !impulsedown)
209         {
210                 if (down)
211                         val = 0;        //      I_Error ();
212                 else
213                         val = 0;        // released this frame
214         }
215         if (!impulsedown && !impulseup)
216         {
217                 if (down)
218                         val = 1.0;      // held the entire frame
219                 else
220                         val = 0;        // up the entire frame
221         }
222         if (impulsedown && impulseup)
223         {
224                 if (down)
225                         val = 0.75;     // released and re-pressed this frame
226                 else
227                         val = 0.25;     // pressed and released this frame
228         }
229
230         key->state &= 1;                // clear impulses
231
232         return val;
233 }
234
235
236
237
238 //==========================================================================
239
240 cvar_t cl_upspeed = {CVAR_SAVE, "cl_upspeed","400"};
241 cvar_t cl_forwardspeed = {CVAR_SAVE, "cl_forwardspeed","400"};
242 cvar_t cl_backspeed = {CVAR_SAVE, "cl_backspeed","400"};
243 cvar_t cl_sidespeed = {CVAR_SAVE, "cl_sidespeed","350"};
244
245 cvar_t cl_movespeedkey = {CVAR_SAVE, "cl_movespeedkey","2.0"};
246
247 cvar_t cl_yawspeed = {CVAR_SAVE, "cl_yawspeed","140"};
248 cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150"};
249
250 cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5"};
251
252 cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0"};
253 cvar_t cl_movement_latency = {0, "cl_movement_latency", "0"};
254 cvar_t cl_movement_maxspeed = {0, "cl_movement_maxspeed", "320"};
255 cvar_t cl_movement_maxairspeed = {0, "cl_movement_maxairspeed", "30"};
256 cvar_t cl_movement_stopspeed = {0, "cl_movement_stopspeed", "100"};
257 cvar_t cl_movement_friction = {0, "cl_movement_friction", "4"};
258 cvar_t cl_movement_edgefriction = {0, "cl_movement_edgefriction", "2"};
259 cvar_t cl_movement_stepheight = {0, "cl_movement_stepheight", "18"};
260 cvar_t cl_movement_accelerate = {0, "cl_movement_accelerate", "10"};
261 cvar_t cl_movement_jumpvelocity = {0, "cl_movement_jumpvelocity", "270"};
262 cvar_t cl_gravity = {0, "cl_gravity", "800"};
263 cvar_t cl_slowmo = {0, "cl_slowmo", "1"};
264
265 cvar_t in_pitch_min = {0, "in_pitch_min", "-90"}; // quake used -70
266 cvar_t in_pitch_max = {0, "in_pitch_max", "90"}; // quake used 80
267
268 cvar_t m_filter = {CVAR_SAVE, "m_filter","0"};
269
270
271 /*
272 ================
273 CL_AdjustAngles
274
275 Moves the local angle positions
276 ================
277 */
278 void CL_AdjustAngles (void)
279 {
280         float   speed;
281         float   up, down;
282
283         if (in_speed.state & 1)
284                 speed = host_realframetime * cl_anglespeedkey.value;
285         else
286                 speed = host_realframetime;
287
288         if (!(in_strafe.state & 1))
289         {
290                 cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
291                 cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
292         }
293         if (in_klook.state & 1)
294         {
295                 V_StopPitchDrift ();
296                 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
297                 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
298         }
299
300         up = CL_KeyState (&in_lookup);
301         down = CL_KeyState(&in_lookdown);
302
303         cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
304         cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
305
306         if (up || down)
307                 V_StopPitchDrift ();
308
309         cl.viewangles[YAW] = ANGLEMOD(cl.viewangles[YAW]);
310         cl.viewangles[PITCH] = ANGLEMOD(cl.viewangles[PITCH]);
311         cl.viewangles[ROLL] = ANGLEMOD(cl.viewangles[ROLL]);
312         if (cl.viewangles[YAW] >= 180)
313                 cl.viewangles[YAW] -= 360;
314         if (cl.viewangles[PITCH] >= 180)
315                 cl.viewangles[PITCH] -= 360;
316         if (cl.viewangles[ROLL] >= 180)
317                 cl.viewangles[ROLL] -= 360;
318
319         cl.viewangles[PITCH] = bound (in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
320         cl.viewangles[ROLL] = bound(-50, cl.viewangles[ROLL], 50);
321 }
322
323 qboolean cl_ignoremousemove = false;
324
325 /*
326 ================
327 CL_Move
328
329 Send the intended movement message to the server
330 ================
331 */
332 void CL_Move (void)
333 {
334         vec3_t temp;
335         float mx, my;
336         static float old_mouse_x = 0, old_mouse_y = 0;
337
338         // clamp before the move to prevent starting with bad angles
339         CL_AdjustAngles ();
340
341         // get basic movement from keyboard
342         // PRYDON_CLIENTCURSOR needs to survive basemove resets
343         VectorCopy (cl.cmd.cursor_screen, temp);
344         memset (&cl.cmd, 0, sizeof(cl.cmd));
345         VectorCopy (temp, cl.cmd.cursor_screen);
346
347         if (in_strafe.state & 1)
348         {
349                 cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
350                 cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
351         }
352
353         cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
354         cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
355
356         cl.cmd.upmove += cl_upspeed.value * CL_KeyState (&in_up);
357         cl.cmd.upmove -= cl_upspeed.value * CL_KeyState (&in_down);
358
359         if (! (in_klook.state & 1) )
360         {
361                 cl.cmd.forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
362                 cl.cmd.forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
363         }
364
365         // adjust for speed key
366         if (in_speed.state & 1)
367         {
368                 cl.cmd.forwardmove *= cl_movespeedkey.value;
369                 cl.cmd.sidemove *= cl_movespeedkey.value;
370                 cl.cmd.upmove *= cl_movespeedkey.value;
371         }
372
373         in_mouse_x = 0;
374         in_mouse_y = 0;
375
376         // allow mice or other external controllers to add to the move
377         IN_Move ();
378
379         // ignore a mouse move if mouse was activated/deactivated this frame
380         if (cl_ignoremousemove)
381         {
382                 cl_ignoremousemove = false;
383                 in_mouse_x = 0;
384                 in_mouse_y = 0;
385         }
386
387         // apply m_filter if it is on
388         mx = in_mouse_x;
389         my = in_mouse_y;
390         if (m_filter.integer)
391         {
392                 in_mouse_x = (mx + old_mouse_x) * 0.5;
393                 in_mouse_y = (my + old_mouse_y) * 0.5;
394         }
395         old_mouse_x = mx;
396         old_mouse_y = my;
397
398         // if not in menu, apply mouse move to viewangles/movement
399         if (in_client_mouse)
400         {
401                 if (cl_prydoncursor.integer)
402                 {
403                         // mouse interacting with the scene, mostly stationary view
404                         V_StopPitchDrift();
405                         cl.cmd.cursor_screen[0] += in_mouse_x * sensitivity.value / vid.width;
406                         cl.cmd.cursor_screen[1] += in_mouse_y * sensitivity.value / vid.height;
407                 }
408                 else if (in_strafe.state & 1)
409                 {
410                         // strafing mode, all looking is movement
411                         V_StopPitchDrift();
412                         cl.cmd.sidemove += m_side.value * in_mouse_x * sensitivity.value * cl.viewzoom;
413                         if (noclip_anglehack)
414                                 cl.cmd.upmove -= m_forward.value * in_mouse_y * sensitivity.value * cl.viewzoom;
415                         else
416                                 cl.cmd.forwardmove -= m_forward.value * in_mouse_y * sensitivity.value * cl.viewzoom;
417                 }
418                 else if ((in_mlook.state & 1) || freelook.integer)
419                 {
420                         // mouselook, lookstrafe causes turning to become strafing
421                         V_StopPitchDrift();
422                         if (lookstrafe.integer)
423                                 cl.cmd.sidemove += m_side.value * in_mouse_x * sensitivity.value * cl.viewzoom;
424                         else
425                                 cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * sensitivity.value * cl.viewzoom;
426                         cl.viewangles[PITCH] += m_pitch.value * in_mouse_y * sensitivity.value * cl.viewzoom;
427                 }
428                 else
429                 {
430                         // non-mouselook, yaw turning and forward/back movement
431                         cl.viewangles[YAW] -= m_yaw.value * in_mouse_x * sensitivity.value * cl.viewzoom;
432                         cl.cmd.forwardmove -= m_forward.value * in_mouse_y * sensitivity.value * cl.viewzoom;
433                 }
434         }
435
436         // clamp after the move to prevent rendering with bad angles
437         CL_AdjustAngles ();
438 }
439
440 #include "cl_collision.h"
441
442 void CL_UpdatePrydonCursor(void)
443 {
444         vec3_t temp, scale;
445
446         if (!cl_prydoncursor.integer)
447                 VectorClear(cl.cmd.cursor_screen);
448
449         /*
450         if (cl.cmd.cursor_screen[0] < -1)
451         {
452                 cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - -1) * vid.width * sensitivity.value * cl.viewzoom;
453                 cl.cmd.cursor_screen[0] = -1;
454         }
455         if (cl.cmd.cursor_screen[0] > 1)
456         {
457                 cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - 1) * vid.width * sensitivity.value * cl.viewzoom;
458                 cl.cmd.cursor_screen[0] = 1;
459         }
460         if (cl.cmd.cursor_screen[1] < -1)
461         {
462                 cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - -1) * vid.height * sensitivity.value * cl.viewzoom;
463                 cl.cmd.cursor_screen[1] = -1;
464         }
465         if (cl.cmd.cursor_screen[1] > 1)
466         {
467                 cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - 1) * vid.height * sensitivity.value * cl.viewzoom;
468                 cl.cmd.cursor_screen[1] = 1;
469         }
470         */
471         cl.cmd.cursor_screen[0] = bound(-1, cl.cmd.cursor_screen[0], 1);
472         cl.cmd.cursor_screen[1] = bound(-1, cl.cmd.cursor_screen[1], 1);
473         cl.cmd.cursor_screen[2] = 1;
474
475         scale[0] = -tan(r_refdef.fov_x * M_PI / 360.0);
476         scale[1] = -tan(r_refdef.fov_y * M_PI / 360.0);
477         scale[2] = 1;
478
479         // trace distance
480         VectorScale(scale, 1000000, scale);
481
482         // FIXME: use something other than renderer variables here
483         // (but they need to match)
484         VectorCopy(r_vieworigin, cl.cmd.cursor_start);
485         VectorSet(temp, cl.cmd.cursor_screen[2] * scale[2], cl.cmd.cursor_screen[0] * scale[0], cl.cmd.cursor_screen[1] * scale[1]);
486         Matrix4x4_Transform(&r_view_matrix, temp, cl.cmd.cursor_end);
487         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);
488         // makes sparks where cursor is
489         //CL_SparkShower(cl.cmd.cursor_impact, cl.cmd.cursor_normal, 5, 0);
490 }
491
492 void CL_ClientMovement(qboolean buttonjump, qboolean buttoncrouch)
493 {
494         int i;
495         int n;
496         int bump;
497         int contents;
498         int crouch;
499         int onground;
500         double edgefriction;
501         double simulatedtime;
502         double currenttime;
503         double newtime;
504         double frametime;
505         double t;
506         vec_t wishspeed;
507         vec_t addspeed;
508         vec_t accelspeed;
509         vec_t f;
510         vec_t *playermins;
511         vec_t *playermaxs;
512         vec3_t currentorigin;
513         vec3_t currentvelocity;
514         vec3_t forward;
515         vec3_t right;
516         vec3_t up;
517         vec3_t wishvel;
518         vec3_t wishdir;
519         vec3_t neworigin;
520         vec3_t currentorigin2;
521         vec3_t neworigin2;
522         vec3_t yawangles;
523         trace_t trace;
524         trace_t trace2;
525         trace_t trace3;
526         // remove stale queue items
527         n = cl.movement_numqueue;
528         cl.movement_numqueue = 0;
529         // calculate time to execute for
530         currenttime = cl.mtime[0];
531         simulatedtime = currenttime + cl_movement_latency.value / 1000.0;
532         for (i = 0;i < n;i++)
533                 if (cl.movement_queue[i].time >= cl.mtime[0] && cl.movement_queue[i].time <= simulatedtime)
534                         cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i];
535         // add to input queue if there is room
536         if (cl.movement_numqueue < sizeof(cl.movement_queue)/sizeof(cl.movement_queue[0]))
537         {
538                 // add to input queue
539                 cl.movement_queue[cl.movement_numqueue].time = simulatedtime;
540                 VectorCopy(cl.viewangles, cl.movement_queue[cl.movement_numqueue].viewangles);
541                 cl.movement_queue[cl.movement_numqueue].move[0] = cl.cmd.forwardmove;
542                 cl.movement_queue[cl.movement_numqueue].move[1] = cl.cmd.sidemove;
543                 cl.movement_queue[cl.movement_numqueue].move[2] = cl.cmd.upmove;
544                 cl.movement_queue[cl.movement_numqueue].jump = buttonjump;
545                 cl.movement_queue[cl.movement_numqueue].crouch = buttoncrouch;
546                 cl.movement_numqueue++;
547         }
548         // fetch current starting values
549         VectorCopy(cl_entities[cl.playerentity].state_current.origin, currentorigin);
550         VectorCopy(cl.mvelocity[0], currentvelocity);
551         // check if onground
552         VectorSet(currentorigin2, currentorigin[0], currentorigin[1], currentorigin[2] + 1);
553         VectorSet(neworigin2, currentorigin[0], currentorigin[1], currentorigin[2] - 1);
554         trace = CL_TraceBox(currentorigin2, cl_playercrouchmins, cl_playercrouchmaxs, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
555         onground = trace.fraction < 1 && trace.plane.normal[2] > 0.7;
556         // FIXME: try minor nudges in various directions if startsolid to find a
557         // safe place to start the walk (due to network compression in some
558         // protocols this starts in solid)
559         //currentorigin[2] += (1.0 / 32.0); // slight nudge to get out of the floor
560         crouch = false; // this will be updated on first move
561         //Con_Printf("%f: ", currenttime);
562         // replay input queue, and remove any stale queue items
563         // note: this relies on the fact there's always one queue item at the end
564         // abort if client movement is disabled
565         cl.movement = cl_movement.integer && cl.stats[STAT_HEALTH] > 0 && !cls.demoplayback;
566         if (!cl.movement)
567                 cl.movement_numqueue = 0;
568         for (i = 0;i <= cl.movement_numqueue;i++)
569         {
570                 newtime = (i >= cl.movement_numqueue) ? simulatedtime : cl.movement_queue[i].time;
571                 frametime = newtime - currenttime;
572                 if (frametime <= 0)
573                         continue;
574                 //Con_Printf(" %f", frametime);
575                 currenttime = newtime;
576                 if (i >= 1 && i <= cl.movement_numqueue)
577                 if (i > 0 || (cl_movement.integer & 8))
578                 if (i < cl.movement_numqueue - 1 || (cl_movement.integer & 16))
579                 {
580                         client_movementqueue_t *q = cl.movement_queue + i - 1;
581                         if (q->crouch)
582                         {
583                                 // wants to crouch, this always works...
584                                 if (!crouch)
585                                         crouch = true;
586                         }
587                         else
588                         {
589                                 // wants to stand, if currently crouching we need to check for a
590                                 // low ceiling first
591                                 if (crouch)
592                                 {
593                                         trace = CL_TraceBox(currentorigin, cl_playerstandmins, cl_playerstandmaxs, currentorigin, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
594                                         if (!trace.startsolid)
595                                                 crouch = false;
596                                 }
597                         }
598                         if (crouch)
599                         {
600                                 playermins = cl_playercrouchmins;
601                                 playermaxs = cl_playercrouchmaxs;
602                         }
603                         else
604                         {
605                                 playermins = cl_playerstandmins;
606                                 playermaxs = cl_playerstandmaxs;
607                         }
608                         // change velocity according to q->viewangles and q->move
609                         contents = CL_PointSuperContents(currentorigin);
610                         if (contents & SUPERCONTENTS_LIQUIDSMASK)
611                         {
612                                 // swim
613                                 AngleVectors(q->viewangles, forward, right, up);
614                                 VectorSet(up, 0, 0, 1);
615                                 VectorMAMAM(q->move[0], forward, q->move[1], right, q->move[2], up, wishvel);
616                                 wishspeed = VectorLength(wishvel);
617                                 if (wishspeed)
618                                         VectorScale(wishvel, 1 / wishspeed, wishdir);
619                                 wishspeed = min(wishspeed, cl_movement_maxspeed.value);
620                                 if (crouch)
621                                         wishspeed *= 0.5;
622                                 wishspeed *= 0.6;
623                                 VectorScale(currentvelocity, (1 - frametime * cl_movement_friction.value), currentvelocity);
624                                 f = wishspeed - DotProduct(currentvelocity, wishdir);
625                                 if (f > 0)
626                                 {
627                                         f = min(f, cl_movement_accelerate.value * frametime * wishspeed);
628                                         VectorMA(currentvelocity, f, wishdir, currentvelocity);
629                                 }
630                                 if (q->jump)
631                                 {
632                                         if (contents & SUPERCONTENTS_LAVA)
633                                                 currentvelocity[2] =  50;
634                                         else if (contents & SUPERCONTENTS_SLIME)
635                                                 currentvelocity[2] =  80;
636                                         else
637                                         {
638                                                 if (gamemode == GAME_NEXUIZ)
639                                                         currentvelocity[2] = 200;
640                                                 else
641                                                         currentvelocity[2] = 100;
642                                         }
643                                 }
644                         }
645                         else
646                         {
647                                 // walk
648                                 if (onground && q->jump)
649                                 {
650                                         currentvelocity[2] += cl_movement_jumpvelocity.value;
651                                         onground = false;
652                                 }
653                                 VectorSet(yawangles, 0, q->viewangles[1], 0);
654                                 AngleVectors(yawangles, forward, right, up);
655                                 VectorMAM(q->move[0], forward, q->move[1], right, wishvel);
656                                 wishspeed = VectorLength(wishvel);
657                                 if (wishspeed)
658                                         VectorScale(wishvel, 1 / wishspeed, wishdir);
659                                 wishspeed = min(wishspeed, cl_movement_maxspeed.value);
660                                 if (crouch)
661                                         wishspeed *= 0.5;
662                                 // check if onground
663                                 if (onground)
664                                 {
665                                         // apply ground friction
666                                         f = sqrt(currentvelocity[0] * currentvelocity[0] + currentvelocity[1] * currentvelocity[1]);
667                                         edgefriction = 1;
668                                         if (f > 0)
669                                         {
670                                                 VectorSet(currentorigin2, currentorigin[0] + currentvelocity[0]*(16/f), currentorigin[1] + currentvelocity[1]*(16/f), currentorigin[2] + playermins[2]);
671                                                 VectorSet(neworigin2, currentorigin2[0], currentorigin2[1], currentorigin2[2] - 34);
672                                                 trace = CL_TraceBox(currentorigin2, vec3_origin, vec3_origin, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
673                                                 if (trace.fraction == 1)
674                                                         edgefriction = cl_movement_edgefriction.value;
675                                         }
676                                         // apply friction
677                                         f = 1 - frametime * edgefriction * ((f < cl_movement_stopspeed.value) ? (cl_movement_stopspeed.value / f) : 1) * cl_movement_friction.value;
678                                         f = max(f, 0);
679                                         VectorScale(currentvelocity, f, currentvelocity);
680                                 }
681                                 else
682                                 {
683                                         // apply air speed limit
684                                         wishspeed = min(wishspeed, cl_movement_maxairspeed.value);
685                                 }
686                                 if (gamemode == GAME_NEXUIZ)
687                                         addspeed = wishspeed;
688                                 else
689                                         addspeed = wishspeed - DotProduct(currentvelocity, wishdir);
690                                 if (addspeed > 0)
691                                 {
692                                         accelspeed = min(cl_movement_accelerate.value * frametime * wishspeed, addspeed);
693                                         VectorMA(currentvelocity, accelspeed, wishdir, currentvelocity);
694                                 }
695                                 currentvelocity[2] -= cl_gravity.value * frametime;
696                         }
697                 }
698                 if (i > 0 || (cl_movement.integer & 2))
699                 if (i < cl.movement_numqueue - 1 || (cl_movement.integer & 4))
700                 {
701                         if (crouch)
702                         {
703                                 playermins = cl_playercrouchmins;
704                                 playermaxs = cl_playercrouchmaxs;
705                         }
706                         else
707                         {
708                                 playermins = cl_playerstandmins;
709                                 playermaxs = cl_playerstandmaxs;
710                         }
711                         for (bump = 0, t = frametime;bump < 8 && VectorLength2(currentvelocity) > 0;bump++)
712                         {
713                                 VectorMA(currentorigin, t, currentvelocity, neworigin);
714                                 trace = CL_TraceBox(currentorigin, playermins, playermaxs, neworigin, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
715                                 if (trace.fraction < 1 && trace.plane.normal[2] == 0)
716                                 {
717                                         // may be a step or wall, try stepping up
718                                         // first move forward at a higher level
719                                         VectorSet(currentorigin2, currentorigin[0], currentorigin[1], currentorigin[2] + cl_movement_stepheight.value);
720                                         VectorSet(neworigin2, neworigin[0], neworigin[1], currentorigin[2] + cl_movement_stepheight.value);
721                                         trace2 = CL_TraceBox(currentorigin2, playermins, playermaxs, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
722                                         // then move down from there
723                                         VectorCopy(trace2.endpos, currentorigin2);
724                                         VectorSet(neworigin2, trace2.endpos[0], trace2.endpos[1], currentorigin[2]);
725                                         trace3 = CL_TraceBox(currentorigin2, playermins, playermaxs, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
726                                         //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]);
727                                         // accept the new trace if it made some progress
728                                         if (fabs(trace3.endpos[0] - trace.endpos[0]) >= 0.03125 || fabs(trace3.endpos[1] - trace.endpos[1]) >= 0.03125)
729                                         {
730                                                 trace = trace2;
731                                                 VectorCopy(trace3.endpos, trace.endpos);
732                                         }
733                                 }
734                                 if (trace.fraction == 1)
735                                 {
736                                         VectorCopy(trace.endpos, currentorigin);
737                                         break;
738                                 }
739                                 if (trace.plane.normal[2] > 0.7)
740                                         onground = true;
741                                 t *= 1 - trace.fraction;
742                                 if (trace.fraction >= 0.001)
743                                         VectorCopy(trace.endpos, currentorigin);
744                                 f = DotProduct(currentvelocity, trace.plane.normal);
745                                 VectorMA(currentvelocity, -f, trace.plane.normal, currentvelocity);
746                         }
747                 }
748         }
749         //Con_Printf(" :%f\n", currenttime);
750         // store replay location
751         VectorCopy(cl.movement_origin, cl.movement_oldorigin);
752         VectorCopy(currentorigin, cl.movement_origin);
753         VectorCopy(currentvelocity, cl.movement_velocity);
754         //VectorCopy(currentorigin, cl_entities[cl.playerentity].state_current.origin);
755         //VectorSet(cl_entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0);
756 }
757
758 /*
759 ==============
760 CL_SendMove
761 ==============
762 */
763 void CL_SendMove(void)
764 {
765         int i;
766         int bits;
767         sizebuf_t buf;
768         qbyte data[128];
769 #define MOVEAVERAGING 0
770 #if MOVEAVERAGING
771         static float forwardmove, sidemove, upmove, total; // accumulation
772 #else
773         float forwardmove, sidemove, upmove;
774 #endif
775
776 #if MOVEAVERAGING
777         // accumulate changes between messages
778         forwardmove += cl.cmd.forwardmove;
779         sidemove += cl.cmd.sidemove;
780         upmove += cl.cmd.upmove;
781         total++;
782 #endif
783         if (cls.signon != SIGNONS)
784                 return;
785 #if MOVEAVERAGING
786         // average the accumulated changes
787         total = 1.0f / total;
788         forwardmove *= total;
789         sidemove *= total;
790         upmove *= total;
791         total = 0;
792 #else
793         // use the latest values
794         forwardmove = cl.cmd.forwardmove;
795         sidemove = cl.cmd.sidemove;
796         upmove = cl.cmd.upmove;
797 #endif
798
799         buf.maxsize = 128;
800         buf.cursize = 0;
801         buf.data = data;
802
803         // set button bits
804         // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
805         bits = 0;
806         if (in_attack.state   & 3) bits |=   1;in_attack.state  &= ~2;
807         if (in_jump.state     & 3) bits |=   2;in_jump.state    &= ~2;
808         if (in_button3.state  & 3) bits |=   4;in_button3.state &= ~2;
809         if (in_button4.state  & 3) bits |=   8;in_button4.state &= ~2;
810         if (in_button5.state  & 3) bits |=  16;in_button5.state &= ~2;
811         if (in_button6.state  & 3) bits |=  32;in_button6.state &= ~2;
812         if (in_button7.state  & 3) bits |=  64;in_button7.state &= ~2;
813         if (in_button8.state  & 3) bits |= 128;in_button8.state &= ~2;
814         if (in_use.state      & 3) bits |= 256;in_use.state     &= ~2;
815         if (key_dest != key_game || key_consoleactive) bits |= 512;
816         if (cl_prydoncursor.integer) bits |= 1024;
817         // button bits 11-31 unused currently
818         // rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
819         if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
820         if (cl.cmd.cursor_screen[0] >=  1) bits |= 16;
821         if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
822         if (cl.cmd.cursor_screen[1] >=  1) bits |= 64;
823
824         // always dump the first two messages, because they may contain leftover inputs from the last level
825         if (++cl.movemessages >= 2)
826         {
827                 // send the movement message
828                 // PROTOCOL_QUAKE       clc_move = 16 bytes total
829                 // PROTOCOL_DARKPLACES1 clc_move = 19 bytes total
830                 // PROTOCOL_DARKPLACES2 clc_move = 25 bytes total
831                 // PROTOCOL_DARKPLACES3 clc_move = 25 bytes total
832                 // PROTOCOL_DARKPLACES4 clc_move = 19 bytes total
833                 // PROTOCOL_DARKPLACES5 clc_move = 19 bytes total
834                 // PROTOCOL_DARKPLACES6 clc_move = 52 bytes total
835                 // 5 bytes
836                 MSG_WriteByte (&buf, clc_move);
837                 MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
838                 if (cl.protocol == PROTOCOL_DARKPLACES6)
839                 {
840                         // 6 bytes
841                         for (i = 0;i < 3;i++)
842                                 MSG_WriteAngle16i (&buf, cl.viewangles[i]);
843                         // 6 bytes
844                         MSG_WriteCoord16i (&buf, forwardmove);
845                         MSG_WriteCoord16i (&buf, sidemove);
846                         MSG_WriteCoord16i (&buf, upmove);
847                         // 5 bytes
848                         MSG_WriteLong (&buf, bits);
849                         MSG_WriteByte (&buf, in_impulse);
850                         // PRYDON_CLIENTCURSOR
851                         // 30 bytes
852                         MSG_WriteShort (&buf, cl.cmd.cursor_screen[0] * 32767.0f);
853                         MSG_WriteShort (&buf, cl.cmd.cursor_screen[1] * 32767.0f);
854                         MSG_WriteFloat (&buf, cl.cmd.cursor_start[0]);
855                         MSG_WriteFloat (&buf, cl.cmd.cursor_start[1]);
856                         MSG_WriteFloat (&buf, cl.cmd.cursor_start[2]);
857                         MSG_WriteFloat (&buf, cl.cmd.cursor_impact[0]);
858                         MSG_WriteFloat (&buf, cl.cmd.cursor_impact[1]);
859                         MSG_WriteFloat (&buf, cl.cmd.cursor_impact[2]);
860                         MSG_WriteShort (&buf, cl.cmd.cursor_entitynumber);
861                 }
862                 else
863                 {
864                         if (cl.protocol == PROTOCOL_QUAKE || cl.protocol == PROTOCOL_NEHAHRAMOVIE)
865                         {
866                                 // 3 bytes
867                                 for (i = 0;i < 3;i++)
868                                         MSG_WriteAngle8i (&buf, cl.viewangles[i]);
869                         }
870                         else if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3)
871                         {
872                                 // 12 bytes
873                                 for (i = 0;i < 3;i++)
874                                         MSG_WriteAngle32f (&buf, cl.viewangles[i]);
875                         }
876                         else if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5)
877                         {
878                                 // 6 bytes
879                                 for (i = 0;i < 3;i++)
880                                         MSG_WriteAngle16i (&buf, cl.viewangles[i]);
881                         }
882                         else
883                                 Host_Error("CL_SendMove: unknown cl.protocol %i\n", cl.protocol);
884                         // 6 bytes
885                         MSG_WriteCoord16i (&buf, forwardmove);
886                         MSG_WriteCoord16i (&buf, sidemove);
887                         MSG_WriteCoord16i (&buf, upmove);
888                         // 2 bytes
889                         MSG_WriteByte (&buf, bits);
890                         MSG_WriteByte (&buf, in_impulse);
891                 }
892         }
893
894 #if MOVEAVERAGING
895         forwardmove = sidemove = upmove = 0;
896 #endif
897         in_impulse = 0;
898
899         // ack the last few frame numbers
900         // (redundent to improve handling of client->server packet loss)
901         // for LATESTFRAMENUMS == 3 case this is 15 bytes
902         for (i = 0;i < LATESTFRAMENUMS;i++)
903         {
904                 if (cl.latestframenums[i] > 0)
905                 {
906                         if (developer_networkentities.integer >= 1)
907                                 Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
908                         MSG_WriteByte(&buf, clc_ackframe);
909                         MSG_WriteLong(&buf, cl.latestframenums[i]);
910                 }
911         }
912
913         // PROTOCOL_DARKPLACES6 = 67 bytes per packet
914
915         // deliver the message
916         if (cls.demoplayback)
917                 return;
918         // nothing to send
919         if (!buf.cursize)
920                 return;
921
922         // FIXME: bits & 64 is +button5, Nexuiz specific
923         CL_ClientMovement((bits & 2) != 0, (bits & 64) != 0);
924
925         if (NetConn_SendUnreliableMessage(cls.netcon, &buf) == -1)
926         {
927                 Con_Print("CL_SendMove: lost server connection\n");
928                 CL_Disconnect();
929                 Host_ShutdownServer(false);
930         }
931 }
932
933 /*
934 ============
935 CL_InitInput
936 ============
937 */
938 void CL_InitInput (void)
939 {
940         Cmd_AddCommand ("+moveup",IN_UpDown);
941         Cmd_AddCommand ("-moveup",IN_UpUp);
942         Cmd_AddCommand ("+movedown",IN_DownDown);
943         Cmd_AddCommand ("-movedown",IN_DownUp);
944         Cmd_AddCommand ("+left",IN_LeftDown);
945         Cmd_AddCommand ("-left",IN_LeftUp);
946         Cmd_AddCommand ("+right",IN_RightDown);
947         Cmd_AddCommand ("-right",IN_RightUp);
948         Cmd_AddCommand ("+forward",IN_ForwardDown);
949         Cmd_AddCommand ("-forward",IN_ForwardUp);
950         Cmd_AddCommand ("+back",IN_BackDown);
951         Cmd_AddCommand ("-back",IN_BackUp);
952         Cmd_AddCommand ("+lookup", IN_LookupDown);
953         Cmd_AddCommand ("-lookup", IN_LookupUp);
954         Cmd_AddCommand ("+lookdown", IN_LookdownDown);
955         Cmd_AddCommand ("-lookdown", IN_LookdownUp);
956         Cmd_AddCommand ("+strafe", IN_StrafeDown);
957         Cmd_AddCommand ("-strafe", IN_StrafeUp);
958         Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
959         Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
960         Cmd_AddCommand ("+moveright", IN_MoverightDown);
961         Cmd_AddCommand ("-moveright", IN_MoverightUp);
962         Cmd_AddCommand ("+speed", IN_SpeedDown);
963         Cmd_AddCommand ("-speed", IN_SpeedUp);
964         Cmd_AddCommand ("+attack", IN_AttackDown);
965         Cmd_AddCommand ("-attack", IN_AttackUp);
966         Cmd_AddCommand ("+jump", IN_JumpDown);
967         Cmd_AddCommand ("-jump", IN_JumpUp);
968         Cmd_AddCommand ("impulse", IN_Impulse);
969         Cmd_AddCommand ("+klook", IN_KLookDown);
970         Cmd_AddCommand ("-klook", IN_KLookUp);
971         Cmd_AddCommand ("+mlook", IN_MLookDown);
972         Cmd_AddCommand ("-mlook", IN_MLookUp);
973
974         // LordHavoc: added use button
975         Cmd_AddCommand ("+use", IN_UseDown);
976         Cmd_AddCommand ("-use", IN_UseUp);
977
978         // LordHavoc: added 6 new buttons
979         Cmd_AddCommand ("+button3", IN_Button3Down);
980         Cmd_AddCommand ("-button3", IN_Button3Up);
981         Cmd_AddCommand ("+button4", IN_Button4Down);
982         Cmd_AddCommand ("-button4", IN_Button4Up);
983         Cmd_AddCommand ("+button5", IN_Button5Down);
984         Cmd_AddCommand ("-button5", IN_Button5Up);
985         Cmd_AddCommand ("+button6", IN_Button6Down);
986         Cmd_AddCommand ("-button6", IN_Button6Up);
987         Cmd_AddCommand ("+button7", IN_Button7Down);
988         Cmd_AddCommand ("-button7", IN_Button7Up);
989         Cmd_AddCommand ("+button8", IN_Button8Down);
990         Cmd_AddCommand ("-button8", IN_Button8Up);
991
992         Cvar_RegisterVariable(&cl_movement);
993         Cvar_RegisterVariable(&cl_movement_latency);
994         Cvar_RegisterVariable(&cl_movement_maxspeed);
995         Cvar_RegisterVariable(&cl_movement_maxairspeed);
996         Cvar_RegisterVariable(&cl_movement_stopspeed);
997         Cvar_RegisterVariable(&cl_movement_friction);
998         Cvar_RegisterVariable(&cl_movement_edgefriction);
999         Cvar_RegisterVariable(&cl_movement_stepheight);
1000         Cvar_RegisterVariable(&cl_movement_accelerate);
1001         Cvar_RegisterVariable(&cl_movement_jumpvelocity);
1002         Cvar_RegisterVariable(&cl_gravity);
1003         Cvar_RegisterVariable(&cl_slowmo);
1004
1005         Cvar_RegisterVariable(&in_pitch_min);
1006         Cvar_RegisterVariable(&in_pitch_max);
1007         Cvar_RegisterVariable(&m_filter);
1008 }
1009