]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_input.c
-Changed the SDL window icon back to DP's icon.
[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_gravity = {0, "cl_gravity", "800"};
262 cvar_t cl_slowmo = {0, "cl_slowmo", "1"};
263
264
265 /*
266 ================
267 CL_AdjustAngles
268
269 Moves the local angle positions
270 ================
271 */
272 void CL_AdjustAngles (void)
273 {
274         float   speed;
275         float   up, down;
276
277         if (in_speed.state & 1)
278                 speed = host_realframetime * cl_anglespeedkey.value;
279         else
280                 speed = host_realframetime;
281
282         if (!(in_strafe.state & 1))
283         {
284                 cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
285                 cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
286         }
287         if (in_klook.state & 1)
288         {
289                 V_StopPitchDrift ();
290                 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
291                 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
292         }
293
294         up = CL_KeyState (&in_lookup);
295         down = CL_KeyState(&in_lookdown);
296
297         cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
298         cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
299
300         if (up || down)
301                 V_StopPitchDrift ();
302
303         cl.viewangles[YAW] = ANGLEMOD(cl.viewangles[YAW]);
304         cl.viewangles[PITCH] = ANGLEMOD(cl.viewangles[PITCH]);
305         cl.viewangles[ROLL] = ANGLEMOD(cl.viewangles[ROLL]);
306         if (cl.viewangles[YAW] >= 180)
307                 cl.viewangles[YAW] -= 360;
308         if (cl.viewangles[PITCH] >= 180)
309                 cl.viewangles[PITCH] -= 360;
310         if (cl.viewangles[ROLL] >= 180)
311                 cl.viewangles[ROLL] -= 360;
312
313         cl.viewangles[PITCH] = bound (in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
314         cl.viewangles[ROLL] = bound(-50, cl.viewangles[ROLL], 50);
315 }
316
317 /*
318 ================
319 CL_BaseMove
320
321 Send the intended movement message to the server
322 ================
323 */
324 void CL_BaseMove (void)
325 {
326         vec3_t temp;
327         if (cls.signon != SIGNONS)
328                 return;
329
330         CL_AdjustAngles ();
331
332         // PRYDON_CLIENTCURSOR needs to survive basemove resets
333         VectorCopy (cl.cmd.cursor_screen, temp);
334         memset (&cl.cmd, 0, sizeof(cl.cmd));
335         VectorCopy (temp, cl.cmd.cursor_screen);
336
337         if (in_strafe.state & 1)
338         {
339                 cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
340                 cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
341         }
342
343         cl.cmd.sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
344         cl.cmd.sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
345
346         cl.cmd.upmove += cl_upspeed.value * CL_KeyState (&in_up);
347         cl.cmd.upmove -= cl_upspeed.value * CL_KeyState (&in_down);
348
349         if (! (in_klook.state & 1) )
350         {
351                 cl.cmd.forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
352                 cl.cmd.forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
353         }
354
355 //
356 // adjust for speed key
357 //
358         if (in_speed.state & 1)
359         {
360                 cl.cmd.forwardmove *= cl_movespeedkey.value;
361                 cl.cmd.sidemove *= cl_movespeedkey.value;
362                 cl.cmd.upmove *= cl_movespeedkey.value;
363         }
364 }
365
366
367 #include "cl_collision.h"
368
369 void CL_UpdatePrydonCursor(void)
370 {
371         vec3_t temp, scale;
372
373         if (!cl_prydoncursor.integer)
374                 VectorClear(cl.cmd.cursor_screen);
375
376         /*
377         if (cl.cmd.cursor_screen[0] < -1)
378         {
379                 cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - -1) * vid.realwidth * sensitivity.value * cl.viewzoom;
380                 cl.cmd.cursor_screen[0] = -1;
381         }
382         if (cl.cmd.cursor_screen[0] > 1)
383         {
384                 cl.viewangles[YAW] -= m_yaw.value * (cl.cmd.cursor_screen[0] - 1) * vid.realwidth * sensitivity.value * cl.viewzoom;
385                 cl.cmd.cursor_screen[0] = 1;
386         }
387         if (cl.cmd.cursor_screen[1] < -1)
388         {
389                 cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - -1) * vid.realheight * sensitivity.value * cl.viewzoom;
390                 cl.cmd.cursor_screen[1] = -1;
391         }
392         if (cl.cmd.cursor_screen[1] > 1)
393         {
394                 cl.viewangles[PITCH] += m_pitch.value * (cl.cmd.cursor_screen[1] - 1) * vid.realheight * sensitivity.value * cl.viewzoom;
395                 cl.cmd.cursor_screen[1] = 1;
396         }
397         */
398         cl.cmd.cursor_screen[0] = bound(-1, cl.cmd.cursor_screen[0], 1);
399         cl.cmd.cursor_screen[1] = bound(-1, cl.cmd.cursor_screen[1], 1);
400         cl.cmd.cursor_screen[2] = 1;
401
402         scale[0] = -tan(r_refdef.fov_x * M_PI / 360.0);
403         scale[1] = -tan(r_refdef.fov_y * M_PI / 360.0);
404         scale[2] = 1;
405
406         // trace distance
407         VectorScale(scale, 1000000, scale);
408
409         // FIXME: use something other than renderer variables here
410         // (but they need to match)
411         VectorCopy(r_vieworigin, cl.cmd.cursor_start);
412         VectorSet(temp, cl.cmd.cursor_screen[2] * scale[2], cl.cmd.cursor_screen[0] * scale[0], cl.cmd.cursor_screen[1] * scale[1]);
413         Matrix4x4_Transform(&r_view_matrix, temp, cl.cmd.cursor_end);
414         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);
415         // makes sparks where cursor is
416         //CL_SparkShower(cl.cmd.cursor_impact, cl.cmd.cursor_normal, 5, 0);
417 }
418
419 void CL_ClientMovement(qboolean buttonjump, qboolean buttoncrouch)
420 {
421         int i, n, bump, contents, crouch;
422         double edgefriction;
423         double simulatedtime;
424         double currenttime;
425         double newtime;
426         double frametime;
427         double t;
428         vec_t wishspeed, addspeed, accelspeed, f, *playermins, *playermaxs;
429         client_movementqueue_t *q;
430         vec3_t currentorigin, currentvelocity, forward, right, up, wishvel, wishdir, neworigin, currentorigin2, neworigin2, yawangles;
431         trace_t trace, trace2, trace3;
432         // remove stale queue items
433         n = cl.movement_numqueue;
434         cl.movement_numqueue = 0;
435         // calculate time to execute for
436         simulatedtime = cl.mtime[0] + cl_movement_latency.value / 1000.0;
437         for (i = 0;i < n;i++)
438                 if (cl.movement_queue[i].time >= cl.mtime[0] && cl.movement_queue[i].time <= simulatedtime)
439                         cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i];
440         // add to input queue if there is room
441         if (cl.movement_numqueue < sizeof(cl.movement_queue)/sizeof(cl.movement_queue[0]))
442         {
443                 // add to input queue
444                 cl.movement_queue[cl.movement_numqueue].time = simulatedtime;
445                 VectorCopy(cl.viewangles, cl.movement_queue[cl.movement_numqueue].viewangles);
446                 cl.movement_queue[cl.movement_numqueue].move[0] = cl.cmd.forwardmove;
447                 cl.movement_queue[cl.movement_numqueue].move[1] = cl.cmd.sidemove;
448                 cl.movement_queue[cl.movement_numqueue].move[2] = cl.cmd.upmove;
449                 cl.movement_queue[cl.movement_numqueue].jump = buttonjump;
450                 cl.movement_queue[cl.movement_numqueue].crouch = buttoncrouch;
451                 cl.movement_numqueue++;
452         }
453         cl.movement = false;
454         // abort if client movement is disabled
455         if (!cl_movement.integer || cl.stats[STAT_HEALTH] <= 0)
456                 return;
457         // fetch current starting values
458         currenttime = cl.mtime[0];
459         VectorCopy(cl_entities[cl.playerentity].state_current.origin, currentorigin);
460         VectorCopy(cl.mvelocity[0], currentvelocity);
461         // FIXME: try minor nudges in various directions if startsolid to find a
462         // safe place to start the walk (due to network compression in some
463         // protocols this starts in solid)
464         //currentorigin[2] += (1.0 / 32.0); // slight nudge to get out of the floor
465         crouch = false; // this will be updated on first move
466         //Con_Printf("%f: ", currenttime);
467         // replay input queue, and remove any stale queue items
468         // note: this relies on the fact there's always one queue item at the end
469         for (i = 0, q = cl.movement_queue;i < cl.movement_numqueue;i++, q++)
470         {
471                 //newtime = (i == cl.movement_numqueue) ? simulatedtime : q->time;
472                 newtime = q->time;
473                 frametime = newtime - currenttime;
474                 if (frametime < 0)
475                         continue;
476                 //Con_Printf(" %f", frametime);
477                 currenttime = newtime;
478                 if (crouch)
479                 {
480                         playermins = cl_playercrouchmins;
481                         playermaxs = cl_playercrouchmaxs;
482                 }
483                 else
484                 {
485                         playermins = cl_playerstandmins;
486                         playermaxs = cl_playerstandmaxs;
487                 }
488                 for (bump = 0, t = frametime;bump < 8 && VectorLength2(currentvelocity) > 0;bump++)
489                 {
490                         VectorMA(currentorigin, t, currentvelocity, neworigin);
491                         trace = CL_TraceBox(currentorigin, playermins, playermaxs, neworigin, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
492                         if (trace.fraction < 1 && trace.plane.normal[2] == 0)
493                         {
494                                 // may be a step or wall, try stepping up
495                                 // first move forward at a higher level
496                                 VectorSet(currentorigin2, currentorigin[0], currentorigin[1], currentorigin[2] + cl_movement_stepheight.value);
497                                 VectorSet(neworigin2, neworigin[0], neworigin[1], currentorigin[2] + cl_movement_stepheight.value);
498                                 trace2 = CL_TraceBox(currentorigin2, playermins, playermaxs, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
499                                 // then move down from there
500                                 VectorCopy(trace2.endpos, currentorigin2);
501                                 VectorSet(neworigin2, trace2.endpos[0], trace2.endpos[1], currentorigin[2]);
502                                 trace3 = CL_TraceBox(currentorigin2, playermins, playermaxs, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
503                                 //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]);
504                                 // accept the new trace if it made some progress
505                                 if (fabs(trace3.endpos[0] - trace.endpos[0]) >= 0.03125 || fabs(trace3.endpos[1] - trace.endpos[1]) >= 0.03125)
506                                 {
507                                         trace = trace2;
508                                         VectorCopy(trace3.endpos, trace.endpos);
509                                 }
510                         }
511                         if (trace.fraction == 1)
512                         {
513                                 VectorCopy(trace.endpos, currentorigin);
514                                 break;
515                         }
516                         t *= 1 - trace.fraction;
517                         if (trace.fraction >= 0.001)
518                                 VectorCopy(trace.endpos, currentorigin);
519                         f = DotProduct(currentvelocity, trace.plane.normal);
520                         VectorMA(currentvelocity, -f, trace.plane.normal, currentvelocity);
521                 }
522                 if (i < cl.movement_numqueue)
523                 {
524                         if (q->crouch)
525                         {
526                                 // wants to crouch, this always works...
527                                 if (!crouch)
528                                         crouch = true;
529                         }
530                         else
531                         {
532                                 // wants to stand, if currently crouching we need to check for a
533                                 // low ceiling first
534                                 if (crouch)
535                                 {
536                                         trace = CL_TraceBox(currentorigin, cl_playerstandmins, cl_playerstandmaxs, currentorigin, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
537                                         if (!trace.startsolid)
538                                                 crouch = false;
539                                 }
540                         }
541                         // change velocity according to q->viewangles and q->move
542                         contents = CL_PointSuperContents(currentorigin);
543                         if (contents & SUPERCONTENTS_LIQUIDSMASK)
544                         {
545                                 // swim
546                                 AngleVectors(q->viewangles, forward, right, up);
547                                 VectorSet(up, 0, 0, 1);
548                                 VectorMAMAM(q->move[0], forward, q->move[1], right, q->move[2], up, wishvel);
549                                 wishspeed = VectorLength(wishvel);
550                                 if (wishspeed)
551                                         VectorScale(wishvel, 1 / wishspeed, wishdir);
552                                 wishspeed = min(wishspeed, cl_movement_maxspeed.value);
553                                 if (crouch)
554                                         wishspeed *= 0.5;
555                                 wishspeed *= 0.6;
556                                 VectorScale(currentvelocity, (1 - frametime * cl_movement_friction.value), currentvelocity);
557                                 f = wishspeed - DotProduct(currentvelocity, wishdir);
558                                 if (f > 0)
559                                 {
560                                         f = min(f, cl_movement_accelerate.value * frametime * wishspeed);
561                                         VectorMA(currentvelocity, f, wishdir, currentvelocity);
562                                 }
563                                 if (q->jump)
564                                 {
565                                         if (contents & SUPERCONTENTS_LAVA)
566                                                 currentvelocity[2] =  50;
567                                         else if (contents & SUPERCONTENTS_SLIME)
568                                                 currentvelocity[2] =  80;
569                                         else
570                                         {
571                                                 if (gamemode == GAME_NEXUIZ)
572                                                         currentvelocity[2] = 200;
573                                                 else
574                                                         currentvelocity[2] = 100;
575                                         }
576                                 }
577                         }
578                         else
579                         {
580                                 // walk
581                                 VectorSet(yawangles, 0, cl.viewangles[1], 0);
582                                 AngleVectors(yawangles, forward, right, up);
583                                 VectorMAM(q->move[0], forward, q->move[1], right, wishvel);
584                                 wishspeed = VectorLength(wishvel);
585                                 if (wishspeed)
586                                         VectorScale(wishvel, 1 / wishspeed, wishdir);
587                                 wishspeed = min(wishspeed, cl_movement_maxspeed.value);
588                                 if (crouch)
589                                         wishspeed *= 0.5;
590                                 // check if onground
591                                 VectorSet(currentorigin2, currentorigin[0], currentorigin[1], currentorigin[2] + 1);
592                                 VectorSet(neworigin2, currentorigin[0], currentorigin[1], currentorigin[2] - 1);
593                                 trace = CL_TraceBox(currentorigin2, playermins, playermaxs, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
594                                 if (trace.fraction < 1 && trace.plane.normal[2] > 0.7)
595                                 {
596                                         // apply ground friction
597                                         f = sqrt(currentvelocity[0] * currentvelocity[0] + currentvelocity[1] * currentvelocity[1]);
598                                         edgefriction = 1;
599                                         if (f > 0)
600                                         {
601                                                 VectorSet(currentorigin2, currentorigin[0] + currentvelocity[0]*(16/f), currentorigin[1] + currentvelocity[1]*(16/f), currentorigin[2] + playermins[2]);
602                                                 VectorSet(neworigin2, currentorigin2[0], currentorigin2[1], currentorigin2[2] - 34);
603                                                 trace = CL_TraceBox(currentorigin2, vec3_origin, vec3_origin, neworigin2, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true);
604                                                 if (trace.fraction < 1)
605                                                         edgefriction = cl_movement_edgefriction.value;
606                                         }
607                                         // apply friction
608                                         f = 1 - frametime * edgefriction * ((f < cl_movement_stopspeed.value) ? (cl_movement_stopspeed.value / f) : 1) * cl_movement_friction.value;
609                                         f = max(f, 0);
610                                         VectorScale(currentvelocity, f, currentvelocity);
611                                 }
612                                 else
613                                 {
614                                         // apply air speed limit
615                                         wishspeed = min(wishspeed, cl_movement_maxairspeed.value);
616                                 }
617                                 if (gamemode == GAME_NEXUIZ)
618                                         addspeed = wishspeed;
619                                 else
620                                         addspeed = wishspeed - DotProduct(currentvelocity, wishdir);
621                                 if (addspeed > 0)
622                                 {
623                                         accelspeed = min(cl_movement_accelerate.value * frametime * wishspeed, addspeed);
624                                         VectorMA(currentvelocity, accelspeed, wishdir, currentvelocity);
625                                 }
626                                 currentvelocity[2] -= cl_gravity.value * frametime;
627                         }
628                 }
629         }
630         //Con_Printf(" :%f\n", currenttime);
631         // store replay location
632         cl.movement = true;
633         VectorCopy(cl.movement_origin, cl.movement_oldorigin);
634         VectorCopy(currentorigin, cl.movement_origin);
635         //VectorCopy(currentorigin, cl_entities[cl.playerentity].state_current.origin);
636         //VectorSet(cl_entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0);
637 }
638
639 /*
640 ==============
641 CL_SendMove
642 ==============
643 */
644 void CL_SendMove(void)
645 {
646         int i;
647         int bits;
648         sizebuf_t buf;
649         qbyte data[128];
650 #define MOVEAVERAGING 0
651 #if MOVEAVERAGING
652         static float forwardmove, sidemove, upmove, total; // accumulation
653 #else
654         float forwardmove, sidemove, upmove;
655 #endif
656
657 #if MOVEAVERAGING
658         // accumulate changes between messages
659         forwardmove += cl.cmd.forwardmove;
660         sidemove += cl.cmd.sidemove;
661         upmove += cl.cmd.upmove;
662         total++;
663 #endif
664         if (cls.signon != SIGNONS)
665                 return;
666 #if MOVEAVERAGING
667         // average the accumulated changes
668         total = 1.0f / total;
669         forwardmove *= total;
670         sidemove *= total;
671         upmove *= total;
672         total = 0;
673 #else
674         // use the latest values
675         forwardmove = cl.cmd.forwardmove;
676         sidemove = cl.cmd.sidemove;
677         upmove = cl.cmd.upmove;
678 #endif
679
680         buf.maxsize = 128;
681         buf.cursize = 0;
682         buf.data = data;
683
684         // set button bits
685         // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
686         bits = 0;
687         if (in_attack.state   & 3) bits |=   1;in_attack.state  &= ~2;
688         if (in_jump.state     & 3) bits |=   2;in_jump.state    &= ~2;
689         if (in_button3.state  & 3) bits |=   4;in_button3.state &= ~2;
690         if (in_button4.state  & 3) bits |=   8;in_button4.state &= ~2;
691         if (in_button5.state  & 3) bits |=  16;in_button5.state &= ~2;
692         if (in_button6.state  & 3) bits |=  32;in_button6.state &= ~2;
693         if (in_button7.state  & 3) bits |=  64;in_button7.state &= ~2;
694         if (in_button8.state  & 3) bits |= 128;in_button8.state &= ~2;
695         if (in_use.state      & 3) bits |= 256;in_use.state     &= ~2;
696         if (key_dest != key_game || key_consoleactive) bits |= 512;
697         if (cl_prydoncursor.integer) bits |= 1024;
698         // button bits 11-31 unused currently
699         // rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
700         if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
701         if (cl.cmd.cursor_screen[0] >=  1) bits |= 16;
702         if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
703         if (cl.cmd.cursor_screen[1] >=  1) bits |= 64;
704
705         // always dump the first two messages, because they may contain leftover inputs from the last level
706         if (++cl.movemessages >= 2)
707         {
708                 // send the movement message
709                 // PROTOCOL_QUAKE       clc_move = 16 bytes total
710                 // PROTOCOL_DARKPLACES1 clc_move = 19 bytes total
711                 // PROTOCOL_DARKPLACES2 clc_move = 25 bytes total
712                 // PROTOCOL_DARKPLACES3 clc_move = 25 bytes total
713                 // PROTOCOL_DARKPLACES4 clc_move = 19 bytes total
714                 // PROTOCOL_DARKPLACES5 clc_move = 19 bytes total
715                 // PROTOCOL_DARKPLACES6 clc_move = 52 bytes total
716                 // 5 bytes
717                 MSG_WriteByte (&buf, clc_move);
718                 MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
719                 if (cl.protocol == PROTOCOL_DARKPLACES6)
720                 {
721                         // 6 bytes
722                         for (i = 0;i < 3;i++)
723                                 MSG_WriteAngle16i (&buf, cl.viewangles[i]);
724                         // 6 bytes
725                         MSG_WriteCoord16i (&buf, forwardmove);
726                         MSG_WriteCoord16i (&buf, sidemove);
727                         MSG_WriteCoord16i (&buf, upmove);
728                         // 5 bytes
729                         MSG_WriteLong (&buf, bits);
730                         MSG_WriteByte (&buf, in_impulse);
731                         // PRYDON_CLIENTCURSOR
732                         // 30 bytes
733                         MSG_WriteShort (&buf, cl.cmd.cursor_screen[0] * 32767.0f);
734                         MSG_WriteShort (&buf, cl.cmd.cursor_screen[1] * 32767.0f);
735                         MSG_WriteFloat (&buf, cl.cmd.cursor_start[0]);
736                         MSG_WriteFloat (&buf, cl.cmd.cursor_start[1]);
737                         MSG_WriteFloat (&buf, cl.cmd.cursor_start[2]);
738                         MSG_WriteFloat (&buf, cl.cmd.cursor_impact[0]);
739                         MSG_WriteFloat (&buf, cl.cmd.cursor_impact[1]);
740                         MSG_WriteFloat (&buf, cl.cmd.cursor_impact[2]);
741                         MSG_WriteShort (&buf, cl.cmd.cursor_entitynumber);
742                 }
743                 else
744                 {
745                         if (cl.protocol == PROTOCOL_QUAKE || cl.protocol == PROTOCOL_NEHAHRAMOVIE)
746                         {
747                                 // 3 bytes
748                                 for (i = 0;i < 3;i++)
749                                         MSG_WriteAngle8i (&buf, cl.viewangles[i]);
750                         }
751                         else if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3)
752                         {
753                                 // 12 bytes
754                                 for (i = 0;i < 3;i++)
755                                         MSG_WriteAngle32f (&buf, cl.viewangles[i]);
756                         }
757                         else if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5)
758                         {
759                                 // 6 bytes
760                                 for (i = 0;i < 3;i++)
761                                         MSG_WriteAngle16i (&buf, cl.viewangles[i]);
762                         }
763                         else
764                                 Host_Error("CL_SendMove: unknown cl.protocol %i\n", cl.protocol);
765                         // 6 bytes
766                         MSG_WriteCoord16i (&buf, forwardmove);
767                         MSG_WriteCoord16i (&buf, sidemove);
768                         MSG_WriteCoord16i (&buf, upmove);
769                         // 2 bytes
770                         MSG_WriteByte (&buf, bits);
771                         MSG_WriteByte (&buf, in_impulse);
772                 }
773         }
774
775 #if MOVEAVERAGING
776         forwardmove = sidemove = upmove = 0;
777 #endif
778         in_impulse = 0;
779
780         // ack the last few frame numbers
781         // (redundent to improve handling of client->server packet loss)
782         // for LATESTFRAMENUMS == 3 case this is 15 bytes
783         for (i = 0;i < LATESTFRAMENUMS;i++)
784         {
785                 if (cl.latestframenums[i] > 0)
786                 {
787                         if (developer_networkentities.integer >= 1)
788                                 Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
789                         MSG_WriteByte(&buf, clc_ackframe);
790                         MSG_WriteLong(&buf, cl.latestframenums[i]);
791                 }
792         }
793
794         // PROTOCOL_DARKPLACES6 = 67 bytes per packet
795
796         // deliver the message
797         if (cls.demoplayback)
798                 return;
799         // nothing to send
800         if (!buf.cursize)
801                 return;
802
803         // FIXME: bits & 64 is +button5, Nexuiz specific
804         CL_ClientMovement((bits & 2) != 0, (bits & 64) != 0);
805
806         if (NetConn_SendUnreliableMessage(cls.netcon, &buf) == -1)
807         {
808                 Con_Print("CL_SendMove: lost server connection\n");
809                 CL_Disconnect();
810                 Host_ShutdownServer(false);
811         }
812 }
813
814 /*
815 ============
816 CL_InitInput
817 ============
818 */
819 void CL_InitInput (void)
820 {
821         Cmd_AddCommand ("+moveup",IN_UpDown);
822         Cmd_AddCommand ("-moveup",IN_UpUp);
823         Cmd_AddCommand ("+movedown",IN_DownDown);
824         Cmd_AddCommand ("-movedown",IN_DownUp);
825         Cmd_AddCommand ("+left",IN_LeftDown);
826         Cmd_AddCommand ("-left",IN_LeftUp);
827         Cmd_AddCommand ("+right",IN_RightDown);
828         Cmd_AddCommand ("-right",IN_RightUp);
829         Cmd_AddCommand ("+forward",IN_ForwardDown);
830         Cmd_AddCommand ("-forward",IN_ForwardUp);
831         Cmd_AddCommand ("+back",IN_BackDown);
832         Cmd_AddCommand ("-back",IN_BackUp);
833         Cmd_AddCommand ("+lookup", IN_LookupDown);
834         Cmd_AddCommand ("-lookup", IN_LookupUp);
835         Cmd_AddCommand ("+lookdown", IN_LookdownDown);
836         Cmd_AddCommand ("-lookdown", IN_LookdownUp);
837         Cmd_AddCommand ("+strafe", IN_StrafeDown);
838         Cmd_AddCommand ("-strafe", IN_StrafeUp);
839         Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
840         Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
841         Cmd_AddCommand ("+moveright", IN_MoverightDown);
842         Cmd_AddCommand ("-moveright", IN_MoverightUp);
843         Cmd_AddCommand ("+speed", IN_SpeedDown);
844         Cmd_AddCommand ("-speed", IN_SpeedUp);
845         Cmd_AddCommand ("+attack", IN_AttackDown);
846         Cmd_AddCommand ("-attack", IN_AttackUp);
847         Cmd_AddCommand ("+jump", IN_JumpDown);
848         Cmd_AddCommand ("-jump", IN_JumpUp);
849         Cmd_AddCommand ("impulse", IN_Impulse);
850         Cmd_AddCommand ("+klook", IN_KLookDown);
851         Cmd_AddCommand ("-klook", IN_KLookUp);
852         Cmd_AddCommand ("+mlook", IN_MLookDown);
853         Cmd_AddCommand ("-mlook", IN_MLookUp);
854
855         // LordHavoc: added use button
856         Cmd_AddCommand ("+use", IN_UseDown);
857         Cmd_AddCommand ("-use", IN_UseUp);
858
859         // LordHavoc: added 6 new buttons
860         Cmd_AddCommand ("+button3", IN_Button3Down);
861         Cmd_AddCommand ("-button3", IN_Button3Up);
862         Cmd_AddCommand ("+button4", IN_Button4Down);
863         Cmd_AddCommand ("-button4", IN_Button4Up);
864         Cmd_AddCommand ("+button5", IN_Button5Down);
865         Cmd_AddCommand ("-button5", IN_Button5Up);
866         Cmd_AddCommand ("+button6", IN_Button6Down);
867         Cmd_AddCommand ("-button6", IN_Button6Up);
868         Cmd_AddCommand ("+button7", IN_Button7Down);
869         Cmd_AddCommand ("-button7", IN_Button7Up);
870         Cmd_AddCommand ("+button8", IN_Button8Down);
871         Cmd_AddCommand ("-button8", IN_Button8Up);
872
873         Cvar_RegisterVariable(&cl_movement);
874         Cvar_RegisterVariable(&cl_movement_latency);
875         Cvar_RegisterVariable(&cl_movement_maxspeed);
876         Cvar_RegisterVariable(&cl_movement_maxairspeed);
877         Cvar_RegisterVariable(&cl_movement_stopspeed);
878         Cvar_RegisterVariable(&cl_movement_friction);
879         Cvar_RegisterVariable(&cl_movement_edgefriction);
880         Cvar_RegisterVariable(&cl_movement_stepheight);
881         Cvar_RegisterVariable(&cl_movement_accelerate);
882         Cvar_RegisterVariable(&cl_gravity);
883         Cvar_RegisterVariable(&cl_slowmo);
884 }
885