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