1 /*
2 * Copyright (C) 1997-2001 Id Software, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * 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 along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59
17 * 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
24 cvar_t *cl_nodelta;
25
26 extern unsigned sys_frame_time;
27 unsigned frame_msec;
28 unsigned old_sys_frame_time;
29
30 /*
31 *
32 * ============================================================================
33 * ===
34 *
35 * KEY BUTTONS
36 *
37 * Continuous button event tracking is complicated by the fact that two
38 * different input sources (say, mouse button 1 and the control key) can both
39 * press the same button, but the button should only be released when both of
40 * the pressing key have been released.
41 *
42 * When a key event issues a button command (+forward, +attack, etc), it appends
43 * its key number as a parameter to the command so it can be matched up with
44 * the release.
45 *
46 * state bit 0 is the current state of the key state bit 1 is edge triggered on
47 * the up to down transition state bit 2 is edge triggered on the down to up
48 * transition
49 *
50 *
51 * Key_Event (int key, qboolean down, unsigned time);
52 *
53 * +mlook src time
54 *
55 * ===============
56 * ===============================================================
57 */
58
59
60 kbutton_t in_klook;
61 kbutton_t in_left, in_right, in_forward, in_back;
62 kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
63 kbutton_t in_strafe, in_speed, in_use, in_attack;
64 kbutton_t in_up, in_down;
65
66 int in_impulse;
67
68
69 void
KeyDown(kbutton_t * b)70 KeyDown(kbutton_t * b)
71 {
72 int k;
73 char *c;
74
75 c = Cmd_Argv(1);
76 if (c[0])
77 k = atoi(c);
78 else
79 k = -1; /* typed manually at the console for
80 * continuous down */
81
82 if (k == b->down[0] || k == b->down[1])
83 return; /* repeating key */
84
85 if (!b->down[0])
86 b->down[0] = k;
87 else if (!b->down[1])
88 b->down[1] = k;
89 else {
90 Com_Printf("Three keys down for a button!\n");
91 return;
92 }
93
94 if (b->state & 1)
95 return; /* still down */
96
97 /* save timestamp */
98 c = Cmd_Argv(2);
99 b->downtime = atoi(c);
100 if (!b->downtime)
101 b->downtime = sys_frame_time - 100;
102
103 b->state |= 1 + 2; /* down + impulse down */
104 }
105
106 void
KeyUp(kbutton_t * b)107 KeyUp(kbutton_t * b)
108 {
109 int k;
110 char *c;
111 unsigned uptime;
112
113 c = Cmd_Argv(1);
114 if (c[0])
115 k = atoi(c);
116 else { /* typed manually at the console, assume for
117 * unsticking, so clear all */
118 b->down[0] = b->down[1] = 0;
119 b->state = 4; /* impulse up */
120 return;
121 }
122
123 if (b->down[0] == k)
124 b->down[0] = 0;
125 else if (b->down[1] == k)
126 b->down[1] = 0;
127 else
128 return; /* key up without coresponding down (menu
129 * pass through) */
130 if (b->down[0] || b->down[1])
131 return; /* some other key is still holding it down */
132
133 if (!(b->state & 1))
134 return; /* still up (this should not happen) */
135
136 /* save timestamp */
137 c = Cmd_Argv(2);
138 uptime = atoi(c);
139 if (uptime)
140 b->msec += uptime - b->downtime;
141 else
142 b->msec += 10;
143
144 b->state &= ~1; /* now up */
145 b->state |= 4; /* impulse up */
146 }
147
IN_KLookDown(void)148 void IN_KLookDown(void){
149 KeyDown(&in_klook);
150 }
IN_KLookUp(void)151 void IN_KLookUp(void){
152 KeyUp(&in_klook);
153 }
IN_UpDown(void)154 void IN_UpDown (void){
155 KeyDown(&in_up);
156 }
IN_UpUp(void)157 void IN_UpUp (void){
158 KeyUp(&in_up);
159 }
IN_DownDown(void)160 void IN_DownDown(void){
161 KeyDown(&in_down);
162 }
IN_DownUp(void)163 void IN_DownUp (void){
164 KeyUp(&in_down);
165 }
IN_LeftDown(void)166 void IN_LeftDown(void){
167 KeyDown(&in_left);
168 }
IN_LeftUp(void)169 void IN_LeftUp (void){
170 KeyUp(&in_left);
171 }
IN_RightDown(void)172 void IN_RightDown(void){
173 KeyDown(&in_right);
174 }
IN_RightUp(void)175 void IN_RightUp(void){
176 KeyUp(&in_right);
177 }
IN_ForwardDown(void)178 void IN_ForwardDown(void){
179 KeyDown(&in_forward);
180 }
IN_ForwardUp(void)181 void IN_ForwardUp(void){
182 KeyUp(&in_forward);
183 }
IN_BackDown(void)184 void IN_BackDown(void){
185 KeyDown(&in_back);
186 }
IN_BackUp(void)187 void IN_BackUp (void){
188 KeyUp(&in_back);
189 }
IN_LookupDown(void)190 void IN_LookupDown(void){
191 KeyDown(&in_lookup);
192 }
IN_LookupUp(void)193 void IN_LookupUp(void){
194 KeyUp(&in_lookup);
195 }
IN_LookdownDown(void)196 void IN_LookdownDown(void){
197 KeyDown(&in_lookdown);
198 }
IN_LookdownUp(void)199 void IN_LookdownUp(void){
200 KeyUp(&in_lookdown);
201 }
IN_MoveleftDown(void)202 void IN_MoveleftDown(void){
203 KeyDown(&in_moveleft);
204 }
IN_MoveleftUp(void)205 void IN_MoveleftUp(void){
206 KeyUp(&in_moveleft);
207 }
IN_MoverightDown(void)208 void IN_MoverightDown(void){
209 KeyDown(&in_moveright);
210 }
IN_MoverightUp(void)211 void IN_MoverightUp(void){
212 KeyUp(&in_moveright);
213 }
214
IN_SpeedDown(void)215 void IN_SpeedDown(void){
216 KeyDown(&in_speed);
217 }
IN_SpeedUp(void)218 void IN_SpeedUp(void){
219 KeyUp(&in_speed);
220 }
IN_StrafeDown(void)221 void IN_StrafeDown(void){
222 KeyDown(&in_strafe);
223 }
IN_StrafeUp(void)224 void IN_StrafeUp(void){
225 KeyUp(&in_strafe);
226 }
227
IN_AttackDown(void)228 void IN_AttackDown(void){
229 KeyDown(&in_attack);
230 }
IN_AttackUp(void)231 void IN_AttackUp(void){
232 KeyUp(&in_attack);
233 }
234
IN_UseDown(void)235 void IN_UseDown(void){
236 KeyDown(&in_use);
237 }
IN_UseUp(void)238 void IN_UseUp (void){
239 KeyUp(&in_use);
240 }
241
IN_Impulse(void)242 void IN_Impulse(void){
243 in_impulse = atoi(Cmd_Argv(1));
244 }
245
246 /*
247 * =============== CL_KeyState
248 *
249 * Returns the fraction of the frame that the key was down ===============
250 */
251 float
CL_KeyState(kbutton_t * key)252 CL_KeyState(kbutton_t * key)
253 {
254 float val;
255 int msec;
256
257 key->state &= 1; /* clear impulses */
258
259 msec = key->msec;
260 key->msec = 0;
261
262 if (key->state) { /* still down */
263 msec += sys_frame_time - key->downtime;
264 key->downtime = sys_frame_time;
265 }
266 #if 0
267 if (msec) {
268 Com_Printf("%i ", msec);
269 }
270 #endif
271
272 val = (float)msec / frame_msec;
273 if (val < 0)
274 val = 0;
275 if (val > 1)
276 val = 1;
277
278 return val;
279 }
280
281
282
283
284 /* ========================================================================== */
285
286 cvar_t *cl_upspeed;
287 cvar_t *cl_forwardspeed;
288 cvar_t *cl_sidespeed;
289
290 cvar_t *cl_yawspeed;
291 cvar_t *cl_pitchspeed;
292
293 cvar_t *cl_run;
294
295 cvar_t *cl_anglespeedkey;
296
297
298 /*
299 * ================ CL_AdjustAngles
300 *
301 * Moves the local angle positions ================
302 */
303 void
CL_AdjustAngles(void)304 CL_AdjustAngles(void)
305 {
306 float speed;
307 float up , down;
308
309 if (in_speed.state & 1)
310 speed = cls.frametime * cl_anglespeedkey->value;
311 else
312 speed = cls.frametime;
313
314 if (!(in_strafe.state & 1)) {
315 cl.viewangles[YAW] -= speed * cl_yawspeed->value * CL_KeyState(&in_right);
316 cl.viewangles[YAW] += speed * cl_yawspeed->value * CL_KeyState(&in_left);
317 }
318 if (in_klook.state & 1) {
319 cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * CL_KeyState(&in_forward);
320 cl.viewangles[PITCH] += speed * cl_pitchspeed->value * CL_KeyState(&in_back);
321 }
322 up = CL_KeyState(&in_lookup);
323 down = CL_KeyState(&in_lookdown);
324
325 cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * up;
326 cl.viewangles[PITCH] += speed * cl_pitchspeed->value * down;
327 }
328
329 /*
330 * ================ CL_BaseMove
331 *
332 * Send the intended movement message to the server ================
333 */
334 void
CL_BaseMove(usercmd_t * cmd)335 CL_BaseMove(usercmd_t * cmd)
336 {
337 CL_AdjustAngles();
338
339 memset(cmd, 0, sizeof(*cmd));
340
341 VectorCopy(cl.viewangles, cmd->angles);
342 if (in_strafe.state & 1) {
343 cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_right);
344 cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_left);
345 }
346 cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_moveright);
347 cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_moveleft);
348
349 cmd->upmove += cl_upspeed->value * CL_KeyState(&in_up);
350 cmd->upmove -= cl_upspeed->value * CL_KeyState(&in_down);
351
352 if (!(in_klook.state & 1)) {
353 cmd->forwardmove += cl_forwardspeed->value * CL_KeyState(&in_forward);
354 cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState(&in_back);
355 }
356 //
357 /* adjust for speed key / running */
358 //
359 if ((in_speed.state & 1) ^ (int)(cl_run->value)) {
360 cmd->forwardmove *= 2;
361 cmd->sidemove *= 2;
362 cmd->upmove *= 2;
363 }
364 }
365
366 void
CL_ClampPitch(void)367 CL_ClampPitch(void)
368 {
369 float pitch;
370
371 pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
372 if (pitch > 180)
373 pitch -= 360;
374
375 if (cl.viewangles[PITCH] + pitch < -360)
376 cl.viewangles[PITCH] += 360; /* wrapped */
377 if (cl.viewangles[PITCH] + pitch > 360)
378 cl.viewangles[PITCH] -= 360; /* wrapped */
379
380 if (cl.viewangles[PITCH] + pitch > 89)
381 cl.viewangles[PITCH] = 89 - pitch;
382 if (cl.viewangles[PITCH] + pitch < -89)
383 cl.viewangles[PITCH] = -89 - pitch;
384 }
385
386 /*
387 * ============== CL_FinishMove ==============
388 */
389 void
CL_FinishMove(usercmd_t * cmd)390 CL_FinishMove(usercmd_t * cmd)
391 {
392 int ms;
393 int i;
394
395 //
396 /* figure button bits */
397 /* */
398 if (in_attack.state & 3)
399 cmd->buttons |= BUTTON_ATTACK;
400 in_attack.state &= ~2;
401
402 if (in_use.state & 3)
403 cmd->buttons |= BUTTON_USE;
404 in_use.state &= ~2;
405
406 if (anykeydown && cls.key_dest == key_game)
407 cmd->buttons |= BUTTON_ANY;
408
409 /* send milliseconds of time to apply the move */
410 ms = cls.frametime * 1000;
411 if (ms > 250)
412 ms = 100; /* time was unreasonable */
413 cmd->msec = ms;
414
415 CL_ClampPitch();
416 for (i = 0; i < 3; i++)
417 cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
418
419 cmd->impulse = in_impulse;
420 in_impulse = 0;
421
422 /* send the ambient light level at the player's current position */
423 cmd->lightlevel = (byte) cl_lightlevel->value;
424 }
425
426 /*
427 * ================= CL_CreateCmd =================
428 */
429 usercmd_t
CL_CreateCmd(void)430 CL_CreateCmd(void)
431 {
432 usercmd_t cmd;
433
434 frame_msec = sys_frame_time - old_sys_frame_time;
435 if (frame_msec < 1)
436 frame_msec = 1;
437 if (frame_msec > 200)
438 frame_msec = 200;
439
440 /* get basic movement from keyboard */
441 CL_BaseMove(&cmd);
442
443 /* allow mice or other external controllers to add to the move */
444 IN_Move(&cmd);
445
446 CL_FinishMove(&cmd);
447
448 old_sys_frame_time = sys_frame_time;
449
450 /* cmd.impulse = cls.framecount; */
451
452 return cmd;
453 }
454
455
456 void
IN_CenterView(void)457 IN_CenterView(void)
458 {
459 cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
460 }
461
462 /*
463 * ============ CL_InitInput ============
464 */
465 void
CL_InitInput(void)466 CL_InitInput(void)
467 {
468 Cmd_AddCommand("centerview", IN_CenterView);
469
470 Cmd_AddCommand("+moveup", IN_UpDown);
471 Cmd_AddCommand("-moveup", IN_UpUp);
472 Cmd_AddCommand("+movedown", IN_DownDown);
473 Cmd_AddCommand("-movedown", IN_DownUp);
474 Cmd_AddCommand("+left", IN_LeftDown);
475 Cmd_AddCommand("-left", IN_LeftUp);
476 Cmd_AddCommand("+right", IN_RightDown);
477 Cmd_AddCommand("-right", IN_RightUp);
478 Cmd_AddCommand("+forward", IN_ForwardDown);
479 Cmd_AddCommand("-forward", IN_ForwardUp);
480 Cmd_AddCommand("+back", IN_BackDown);
481 Cmd_AddCommand("-back", IN_BackUp);
482 Cmd_AddCommand("+lookup", IN_LookupDown);
483 Cmd_AddCommand("-lookup", IN_LookupUp);
484 Cmd_AddCommand("+lookdown", IN_LookdownDown);
485 Cmd_AddCommand("-lookdown", IN_LookdownUp);
486 Cmd_AddCommand("+strafe", IN_StrafeDown);
487 Cmd_AddCommand("-strafe", IN_StrafeUp);
488 Cmd_AddCommand("+moveleft", IN_MoveleftDown);
489 Cmd_AddCommand("-moveleft", IN_MoveleftUp);
490 Cmd_AddCommand("+moveright", IN_MoverightDown);
491 Cmd_AddCommand("-moveright", IN_MoverightUp);
492 Cmd_AddCommand("+speed", IN_SpeedDown);
493 Cmd_AddCommand("-speed", IN_SpeedUp);
494 Cmd_AddCommand("+attack", IN_AttackDown);
495 Cmd_AddCommand("-attack", IN_AttackUp);
496 Cmd_AddCommand("+use", IN_UseDown);
497 Cmd_AddCommand("-use", IN_UseUp);
498 Cmd_AddCommand("impulse", IN_Impulse);
499 Cmd_AddCommand("+klook", IN_KLookDown);
500 Cmd_AddCommand("-klook", IN_KLookUp);
501
502 cl_nodelta = Cvar_Get("cl_nodelta", "0", 0);
503 }
504
505
506
507 /*
508 * ================= CL_SendCmd =================
509 */
510 void
CL_SendCmd(void)511 CL_SendCmd(void)
512 {
513 sizebuf_t buf;
514 byte data [128];
515 int i;
516 usercmd_t *cmd, *oldcmd;
517 usercmd_t nullcmd;
518 int checksumIndex;
519
520 /* build a command even if not connected */
521
522 /* save this command off for prediction */
523 i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1);
524 cmd = &cl.cmds[i];
525 cl.cmd_time[i] = cls.realtime; /* for netgraph ping calculation */
526
527 *cmd = CL_CreateCmd();
528
529 cl.cmd = *cmd;
530
531 if (cls.state == ca_disconnected || cls.state == ca_connecting)
532 return;
533
534 if (cls.state == ca_connected) {
535 if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1000)
536 Netchan_Transmit(&cls.netchan, 0, buf.data);
537 return;
538 }
539 /* send a userinfo update if needed */
540 if (userinfo_modified) {
541 CL_FixUpGender();
542 userinfo_modified = false;
543 MSG_WriteByte(&cls.netchan.message, clc_userinfo);
544 MSG_WriteString(&cls.netchan.message, Cvar_Userinfo());
545 }
546 SZ_Init(&buf, data, sizeof(data));
547
548 if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop
549 && cls.realtime - cl.cinematictime > 1000) { /* skip the rest of the
550 * cinematic */
551 SCR_FinishCinematic();
552 }
553 /* begin a client move command */
554 MSG_WriteByte(&buf, clc_move);
555
556 /* save the position for a checksum byte */
557 checksumIndex = buf.cursize;
558 MSG_WriteByte(&buf, 0);
559
560 /* let the server know what the last frame we */
561 /* got was, so the next message can be delta compressed */
562 if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
563 MSG_WriteLong(&buf, -1); /* no compression */
564 else
565 MSG_WriteLong(&buf, cl.frame.serverframe);
566
567 /* send this and the previous cmds in the message, so */
568 /* if the last packet was dropped, it can be recovered */
569 i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1);
570 cmd = &cl.cmds[i];
571 memset(&nullcmd, 0, sizeof(nullcmd));
572 MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
573 oldcmd = cmd;
574
575 i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1);
576 cmd = &cl.cmds[i];
577 MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
578 oldcmd = cmd;
579
580 i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1);
581 cmd = &cl.cmds[i];
582 MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
583
584 /* calculate a checksum over the move commands */
585 buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
586 buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
587 cls.netchan.outgoing_sequence);
588
589 //
590 /* deliver the message */
591 //
592 Netchan_Transmit(&cls.netchan, buf.cursize, buf.data);
593 }
594