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