1 /*
2 * cl.input.c -- builds an intended movement command to send to the server
3 * $Id: cl_input.c,v 1.15 2008-03-21 18:00:07 sezero Exp $
4 *
5 * Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc.
6 * All rights reserved.
7 * Copyright (C) 1996-1997 Id Software, Inc.
8 * Copyright (C) 1997-1998 Raven Software Corp.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or (at
13 * your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26
27 #include "quakedef.h"
28
29 static cvar_t cl_nodelta = {"cl_nodelta", "0", CVAR_NONE};
30
31 /*
32 ===============================================================================
33
34 KEY BUTTONS
35
36 Continuous button event tracking is complicated by the fact that two different
37 input sources (say, mouse button 1 and the control key) can both press the
38 same button, but the button should only be released when both of the
39 pressing key have been released.
40
41 When a key event issues a button command (+forward, +attack, etc), it appends
42 its key number as a parameter to the command so it can be matched up with
43 the release.
44
45 state bit 0 is the current state of the key
46 state bit 1 is edge triggered on the up to down transition
47 state bit 2 is edge triggered on the down to up transition
48
49 ===============================================================================
50 */
51
52 kbutton_t in_mlook, in_klook;
53 kbutton_t in_left, in_right, in_forward, in_back;
54 kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
55 kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack;
56 kbutton_t in_up, in_down, in_crouch;
57
58 int in_impulse;
59
KeyDown(kbutton_t * b)60 static void KeyDown (kbutton_t *b)
61 {
62 int k;
63 const char *c;
64
65 c = Cmd_Argv(1);
66 if (c[0])
67 k = atoi(c);
68 else k = -1; // typed manually at the console for continuous down
69
70 if (k == b->down[0] || k == b->down[1])
71 return; // repeating key
72
73 if (!b->down[0])
74 b->down[0] = k;
75 else if (!b->down[1])
76 b->down[1] = k;
77 else
78 {
79 Con_Printf ("Three keys down for a button!\n");
80 return;
81 }
82
83 if (b->state & 1)
84 return; // still down
85 b->state |= 1 + 2; // down + impulse down
86 }
87
KeyUp(kbutton_t * b)88 static void KeyUp (kbutton_t *b)
89 {
90 int k;
91 const char *c;
92
93 c = Cmd_Argv(1);
94 if (c[0])
95 {
96 k = atoi(c);
97 }
98 else
99 { // typed manually at the console, assume for unsticking, so clear all
100 b->down[0] = b->down[1] = 0;
101 b->state = 4; // impulse up
102 return;
103 }
104
105 if (b->down[0] == k)
106 b->down[0] = 0;
107 else if (b->down[1] == k)
108 b->down[1] = 0;
109 else return; // key up without coresponding down (menu pass through)
110
111 if (b->down[0] || b->down[1])
112 return; // some other key is still holding it down
113
114 if (!(b->state & 1))
115 return; // still up (this should not happen)
116
117 b->state &= ~1; // now up
118 b->state |= 4; // impulse up
119 }
120
IN_KLookDown(void)121 static void IN_KLookDown (void)
122 {
123 KeyDown(&in_klook);
124 }
125
IN_KLookUp(void)126 static void IN_KLookUp (void)
127 {
128 KeyUp(&in_klook);
129 }
130
IN_MLookDown(void)131 static void IN_MLookDown (void)
132 {
133 KeyDown(&in_mlook);
134 }
135
IN_MLookUp(void)136 static void IN_MLookUp (void)
137 {
138 KeyUp(&in_mlook);
139 if (!(in_mlook.state & 1) && lookspring.integer)
140 V_StartPitchDrift();
141 }
142
IN_UpDown(void)143 static void IN_UpDown (void)
144 {
145 KeyDown(&in_up);
146 }
147
IN_UpUp(void)148 static void IN_UpUp (void)
149 {
150 KeyUp(&in_up);
151 }
152
IN_DownDown(void)153 static void IN_DownDown (void)
154 {
155 KeyDown(&in_down);
156 }
157
IN_DownUp(void)158 static void IN_DownUp (void)
159 {
160 KeyUp(&in_down);
161 }
162
IN_LeftDown(void)163 static void IN_LeftDown (void)
164 {
165 KeyDown(&in_left);
166 }
167
IN_LeftUp(void)168 static void IN_LeftUp (void)
169 {
170 KeyUp(&in_left);
171 }
172
IN_RightDown(void)173 static void IN_RightDown (void)
174 {
175 KeyDown(&in_right);
176 }
177
IN_RightUp(void)178 static void IN_RightUp (void)
179 {
180 KeyUp(&in_right);
181 }
182
IN_ForwardDown(void)183 static void IN_ForwardDown (void)
184 {
185 KeyDown(&in_forward);
186 }
187
IN_ForwardUp(void)188 static void IN_ForwardUp (void)
189 {
190 KeyUp(&in_forward);
191 }
192
IN_BackDown(void)193 static void IN_BackDown (void)
194 {
195 KeyDown(&in_back);
196 }
197
IN_BackUp(void)198 static void IN_BackUp (void)
199 {
200 KeyUp(&in_back);
201 }
202
IN_LookupDown(void)203 static void IN_LookupDown (void)
204 {
205 KeyDown(&in_lookup);
206 }
207
IN_LookupUp(void)208 static void IN_LookupUp (void)
209 {
210 KeyUp(&in_lookup);
211 }
212
IN_LookdownDown(void)213 static void IN_LookdownDown (void)
214 {
215 KeyDown(&in_lookdown);
216 }
217
IN_LookdownUp(void)218 static void IN_LookdownUp (void)
219 {
220 KeyUp(&in_lookdown);
221 }
222
IN_MoveleftDown(void)223 static void IN_MoveleftDown (void)
224 {
225 KeyDown(&in_moveleft);
226 }
227
IN_MoveleftUp(void)228 static void IN_MoveleftUp (void)
229 {
230 KeyUp(&in_moveleft);
231 }
232
IN_MoverightDown(void)233 static void IN_MoverightDown (void)
234 {
235 KeyDown(&in_moveright);
236 }
237
IN_MoverightUp(void)238 static void IN_MoverightUp (void)
239 {
240 KeyUp(&in_moveright);
241 }
242
IN_SpeedDown(void)243 static void IN_SpeedDown (void)
244 {
245 KeyDown(&in_speed);
246 }
247
IN_SpeedUp(void)248 static void IN_SpeedUp (void)
249 {
250 KeyUp(&in_speed);
251 }
252
IN_StrafeDown(void)253 static void IN_StrafeDown (void)
254 {
255 KeyDown(&in_strafe);
256 }
257
IN_StrafeUp(void)258 static void IN_StrafeUp (void)
259 {
260 KeyUp(&in_strafe);
261 }
262
IN_AttackDown(void)263 static void IN_AttackDown (void)
264 {
265 KeyDown(&in_attack);
266 }
267
IN_AttackUp(void)268 static void IN_AttackUp (void)
269 {
270 KeyUp(&in_attack);
271 }
272
IN_UseDown(void)273 static void IN_UseDown (void)
274 {
275 KeyDown(&in_use);
276 }
277
IN_UseUp(void)278 static void IN_UseUp (void)
279 {
280 KeyUp(&in_use);
281 }
282
IN_JumpDown(void)283 static void IN_JumpDown (void)
284 {
285 KeyDown(&in_jump);
286 }
287
IN_JumpUp(void)288 static void IN_JumpUp (void)
289 {
290 KeyUp(&in_jump);
291 }
292
IN_Impulse(void)293 static void IN_Impulse (void)
294 {
295 in_impulse = atoi(Cmd_Argv(1));
296 }
297
IN_CrouchDown(void)298 static void IN_CrouchDown (void)
299 {
300 if (Key_GetDest() == key_game)
301 {
302 // int state = in_crouch.state;
303 KeyDown(&in_crouch);
304 // if (!(state & 1) && (in_crouch.state & 1))
305 // in_impulse = 22;
306 }
307 }
308
IN_CrouchUp(void)309 static void IN_CrouchUp (void)
310 {
311 // if (Key_GetDest() == key_game)
312 // {
313 // int state = in_crouch.state;
314 KeyUp(&in_crouch);
315 // if ((state & 1) && !(in_crouch.state & 1))
316 // in_impulse = 22;
317 // }
318 }
319
320 /*
321 ===============
322 CL_KeyState
323
324 Returns 0.25 if a key was pressed and released during the frame,
325 0.5 if it was pressed and held
326 0 if held then released, and
327 1.0 if held for the entire time
328 ===============
329 */
CL_KeyState(kbutton_t * key)330 static float CL_KeyState (kbutton_t *key)
331 {
332 float val;
333 qboolean impulsedown, impulseup, down;
334
335 impulsedown = key->state & 2;
336 impulseup = key->state & 4;
337 down = key->state & 1;
338 val = 0;
339
340 if (impulsedown && !impulseup)
341 {
342 if (down)
343 val = 0.5; // pressed and held this frame
344 else val = 0;
345 }
346 if (impulseup && !impulsedown)
347 {
348 if (down)
349 val = 0;
350 else val = 0; // released this frame
351 }
352 if (!impulsedown && !impulseup)
353 {
354 if (down)
355 val = 1.0; // held the entire frame
356 else val = 0; // up the entire frame
357 }
358 if (impulsedown && impulseup)
359 {
360 if (down)
361 val = 0.75; // released and re-pressed this frame
362 else val = 0.25; // pressed and released this frame
363 }
364
365 key->state &= 1; // clear impulses
366
367 return val;
368 }
369
370
371 //==========================================================================
372
373 cvar_t cl_upspeed = {"cl_upspeed", "200", CVAR_NONE};
374 cvar_t cl_forwardspeed = {"cl_forwardspeed", "200", CVAR_ARCHIVE};
375 cvar_t cl_backspeed = {"cl_backspeed", "200", CVAR_ARCHIVE};
376 cvar_t cl_sidespeed = {"cl_sidespeed", "225", CVAR_NONE};
377 cvar_t cl_movespeedkey = {"cl_movespeedkey", "2.0", CVAR_NONE};
378 cvar_t cl_yawspeed = {"cl_yawspeed", "140", CVAR_NONE};
379 cvar_t cl_pitchspeed = {"cl_pitchspeed", "150", CVAR_NONE};
380 cvar_t cl_anglespeedkey = {"cl_anglespeedkey", "1.5", CVAR_NONE};
381
382
383 /*
384 ================
385 CL_AdjustAngles
386
387 Moves the local angle positions
388 ================
389 */
CL_AdjustAngles(void)390 static void CL_AdjustAngles (void)
391 {
392 float speed;
393 float up, down;
394
395 if ((in_speed.state & 1) || cl.spectator)
396 speed = host_frametime * cl_anglespeedkey.value;
397 else speed = host_frametime;
398
399 if (!(in_strafe.state & 1))
400 {
401 cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
402 cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
403 cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
404 }
405 if (in_klook.state & 1)
406 {
407 V_StopPitchDrift ();
408 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
409 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
410 }
411
412 // FIXME: This is a cheap way of doing this, it belongs in V_CalcViewRoll
413 // but I don't see where I can get the yaw velocity.
414 cl.idealroll = 0;
415 if (cl.v.movetype == MOVETYPE_FLY)
416 {
417 if (CL_KeyState (&in_left) != 0)
418 cl.idealroll = -10;
419 else if (CL_KeyState (&in_right) != 0)
420 cl.idealroll = 10;
421 }
422
423 up = CL_KeyState (&in_lookup);
424 down = CL_KeyState(&in_lookdown);
425
426 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
427 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
428
429 if (up || down)
430 V_StopPitchDrift ();
431
432 if (cl.viewangles[PITCH] > 80)
433 cl.viewangles[PITCH] = 80;
434 if (cl.viewangles[PITCH] < -70)
435 cl.viewangles[PITCH] = -70;
436
437 if (cl.viewangles[ROLL] > 50)
438 cl.viewangles[ROLL] = 50;
439 if (cl.viewangles[ROLL] < -50)
440 cl.viewangles[ROLL] = -50;
441 }
442
443 /*
444 ================
445 CL_BaseMove
446
447 Send the intended movement message to the server
448 ================
449 */
CL_BaseMove(usercmd_t * cmd)450 void CL_BaseMove (usercmd_t *cmd)
451 {
452 if (cl.v.cameramode) // stuck in a different camera so don't move
453 {
454 memset (cmd, 0, sizeof(*cmd));
455 return;
456 }
457
458 CL_AdjustAngles ();
459
460 memset (cmd, 0, sizeof(*cmd));
461 VectorCopy (cl.viewangles, cmd->angles);
462
463 if (in_strafe.state & 1)
464 {
465 // cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
466 // cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
467 cmd->sidemove += 225 * CL_KeyState (&in_right);
468 cmd->sidemove -= 225 * CL_KeyState (&in_left);
469 }
470
471 // cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
472 // cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
473 cmd->sidemove += 225 * CL_KeyState (&in_moveright);
474 cmd->sidemove -= 225 * CL_KeyState (&in_moveleft);
475
476 cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
477 cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
478
479 if (! (in_klook.state & 1))
480 {
481 // cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
482 cmd->forwardmove += 200 * CL_KeyState (&in_forward);
483 // cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
484 cmd->forwardmove -= 200 * CL_KeyState (&in_back);
485 }
486
487 // adjust for speed key, but not if "always run" has been chosen
488 // speed key now acts as slow key when always run is chosen - OS
489 // if ( ( (cl_forwardspeed.value > 200) ||(in_speed.state & 1) || cl.spectator)
490 if ( (((cl_forwardspeed.value > 200) ^ (in_speed.state & 1)) || cl.spectator)
491 && (cl.v.hasted <= 1) )
492 {
493 cmd->forwardmove *= cl_movespeedkey.value;
494 cmd->sidemove *= cl_movespeedkey.value;
495 cmd->upmove *= cl_movespeedkey.value;
496 }
497
498 // Hasted player?
499 if (cl.v.hasted)
500 {
501 cmd->forwardmove = cmd->forwardmove * cl.v.hasted;
502 cmd->sidemove = cmd->sidemove * cl.v.hasted;
503 cmd->upmove = cmd->upmove * cl.v.hasted;
504 }
505
506 cmd->light_level = cl.light_level;
507 }
508
MakeChar(int i)509 static int MakeChar (int i)
510 {
511 i &= ~3;
512 if (i < -127*4)
513 i = -127*4;
514 if (i > 127*4)
515 i = 127*4;
516 return i;
517 }
518
519 /*
520 ==============
521 CL_FinishMove
522 ==============
523 */
CL_FinishMove(usercmd_t * cmd)524 static void CL_FinishMove (usercmd_t *cmd)
525 {
526 int i, ms;
527
528 // always dump the first two message, because it may
529 // contain leftover inputs from the last level
530 if (++cl.movemessages <= 2)
531 return;
532
533 // figure button bits
534 if (in_attack.state & 3)
535 cmd->buttons |= 1;
536 in_attack.state &= ~2;
537
538 if (in_jump.state & 3)
539 cmd->buttons |= 2;
540 in_jump.state &= ~2;
541
542 if (in_crouch.state & 1)
543 cmd->buttons |= 4;
544
545 // send milliseconds of time to apply the move
546 ms = host_frametime * 1000;
547 if (ms > 250)
548 ms = 100; // time was unreasonable
549 cmd->msec = ms;
550
551 VectorCopy (cl.viewangles, cmd->angles);
552
553 cmd->impulse = in_impulse;
554 in_impulse = 0;
555
556 // chop down so no extra bits are kept that the server wouldn't get
557 if ((int)cl.v.artifact_active & ARTFLAG_FROZEN)
558 {
559 cmd->forwardmove = 0;
560 cmd->sidemove = 0;
561 cmd->upmove = 0;
562 }
563 else
564 {
565 cmd->forwardmove = MakeChar (cmd->forwardmove);
566 cmd->sidemove = MakeChar (cmd->sidemove);
567 cmd->upmove = MakeChar (cmd->upmove);
568 }
569
570 for (i = 0; i < 3; i++)
571 cmd->angles[i] = ((int)(cmd->angles[i]*65536.0/360)&65535) * (360.0/65536.0);
572 }
573
574 /*
575 =================
576 CL_SendCmd
577 =================
578 */
CL_SendCmd(void)579 void CL_SendCmd (void)
580 {
581 sizebuf_t buf;
582 byte data[128];
583 int i;
584 usercmd_t *cmd;
585
586 if (cls.demoplayback)
587 return;
588
589 // save this command off for prediction
590 i = cls.netchan.outgoing_sequence & UPDATE_MASK;
591 cmd = &cl.frames[i].cmd;
592 cl.frames[i].senttime = realtime;
593 cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet
594
595 // get basic movement from keyboard
596 CL_BaseMove (cmd);
597
598 // allow mice or other external controllers to add to the move
599 IN_Move (cmd);
600
601 // if we are spectator, try autocam
602 if (cl.spectator)
603 Cam_Track(cmd);
604
605 CL_FinishMove(cmd);
606
607 Cam_FinishMove(cmd);
608
609 // send this and the previous cmds in the message, so
610 // if the last packet was dropped, it can be recovered
611 SZ_Init (&buf, data, sizeof(data));
612
613 MSG_WriteByte (&buf, clc_move);
614 i = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK;
615 MSG_WriteUsercmd (&buf, &cl.frames[i].cmd, false);
616 i = (cls.netchan.outgoing_sequence-1) & UPDATE_MASK;
617 MSG_WriteUsercmd (&buf, &cl.frames[i].cmd, false);
618 i = (cls.netchan.outgoing_sequence) & UPDATE_MASK;
619 MSG_WriteUsercmd (&buf, &cl.frames[i].cmd, true);
620
621 // Con_Printf("I %hd %hd %hd\n",cmd->forwardmove, cmd->sidemove, cmd->upmove);
622
623 // request delta compression of entities
624 if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1)
625 cl.validsequence = 0;
626 if (cl.validsequence && !cl_nodelta.integer && cls.state == ca_active &&
627 !cls.demorecording)
628 {
629 cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = cl.validsequence;
630 MSG_WriteByte (&buf, clc_delta);
631 MSG_WriteByte (&buf, cl.validsequence&255);
632 }
633 else cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = -1;
634
635 if (cls.demorecording)
636 CL_WriteDemoCmd(cmd);
637
638 // deliver the message
639 Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);
640 }
641
642
643 /*
644 ============
645 CL_InitInput
646 ============
647 */
CL_InitInput(void)648 void CL_InitInput (void)
649 {
650 Cmd_AddCommand ("+moveup",IN_UpDown);
651 Cmd_AddCommand ("-moveup",IN_UpUp);
652 Cmd_AddCommand ("+movedown",IN_DownDown);
653 Cmd_AddCommand ("-movedown",IN_DownUp);
654 Cmd_AddCommand ("+left",IN_LeftDown);
655 Cmd_AddCommand ("-left",IN_LeftUp);
656 Cmd_AddCommand ("+right",IN_RightDown);
657 Cmd_AddCommand ("-right",IN_RightUp);
658 Cmd_AddCommand ("+forward",IN_ForwardDown);
659 Cmd_AddCommand ("-forward",IN_ForwardUp);
660 Cmd_AddCommand ("+back",IN_BackDown);
661 Cmd_AddCommand ("-back",IN_BackUp);
662 Cmd_AddCommand ("+lookup", IN_LookupDown);
663 Cmd_AddCommand ("-lookup", IN_LookupUp);
664 Cmd_AddCommand ("+lookdown", IN_LookdownDown);
665 Cmd_AddCommand ("-lookdown", IN_LookdownUp);
666 Cmd_AddCommand ("+strafe", IN_StrafeDown);
667 Cmd_AddCommand ("-strafe", IN_StrafeUp);
668 Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
669 Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
670 Cmd_AddCommand ("+moveright", IN_MoverightDown);
671 Cmd_AddCommand ("-moveright", IN_MoverightUp);
672 Cmd_AddCommand ("+speed", IN_SpeedDown);
673 Cmd_AddCommand ("-speed", IN_SpeedUp);
674 Cmd_AddCommand ("+attack", IN_AttackDown);
675 Cmd_AddCommand ("-attack", IN_AttackUp);
676 Cmd_AddCommand ("+use", IN_UseDown);
677 Cmd_AddCommand ("-use", IN_UseUp);
678 Cmd_AddCommand ("+jump", IN_JumpDown);
679 Cmd_AddCommand ("-jump", IN_JumpUp);
680 Cmd_AddCommand ("impulse", IN_Impulse);
681 Cmd_AddCommand ("+klook", IN_KLookDown);
682 Cmd_AddCommand ("-klook", IN_KLookUp);
683 Cmd_AddCommand ("+mlook", IN_MLookDown);
684 Cmd_AddCommand ("-mlook", IN_MLookUp);
685 Cmd_AddCommand ("+crouch", IN_CrouchDown);
686 Cmd_AddCommand ("-crouch", IN_CrouchUp);
687
688 Cvar_RegisterVariable (&cl_nodelta);
689 }
690
691 /*
692 ============
693 CL_ClearStates
694 ============
695 */
696 #if 0
697 void CL_ClearStates (void)
698 {
699 in_mlook.state = 0;
700 in_klook.state = 0;
701 in_left.state = 0;
702 in_right.state = 0;
703 in_forward.state = 0;
704 in_back.state = 0;
705 in_lookup.state = 0;
706 in_lookdown.state = 0;
707 in_moveleft.state = 0;
708 in_moveright.state = 0;
709 in_strafe.state = 0;
710 in_speed.state = 0;
711 in_use.state = 0;
712 in_jump.state = 0;
713 in_attack.state = 0;
714 in_up.state = 0;
715 in_down.state = 0;
716 in_crouch.state = 0;
717 }
718 #endif
719
720