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