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