1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, 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 24cvar_t cl_nodelta = CVAR2("cl_nodelta","0"); 25 26/* 27=============================================================================== 28 29KEY BUTTONS 30 31Continuous button event tracking is complicated by the fact that two different 32input sources (say, mouse button 1 and the control key) can both press the 33same button, but the button should only be released when both of the 34pressing key have been released. 35 36When a key event issues a button command (+forward, +attack, etc), it appends 37its key number as a parameter to the command so it can be matched up with 38the release. 39 40state bit 0 is the current state of the key 41state bit 1 is edge triggered on the up to down transition 42state bit 2 is edge triggered on the down to up transition 43 44=============================================================================== 45*/ 46 47 48kbutton_t in_mlook, in_klook; 49kbutton_t in_left, in_right, in_forward, in_back; 50kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; 51kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; 52kbutton_t in_up, in_down; 53 54int in_impulse; 55 56 57void 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 86void 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 116void IN_KLookDown (void) {KeyDown(&in_klook);} 117void IN_KLookUp (void) {KeyUp(&in_klook);} 118void IN_MLookDown (void) {KeyDown(&in_mlook);} 119void IN_MLookUp (void) { 120KeyUp(&in_mlook); 121if ( !(in_mlook.state&1) && lookspring.value) 122 V_StartPitchDrift(); 123} 124void IN_UpDown(void) {KeyDown(&in_up);} 125void IN_UpUp(void) {KeyUp(&in_up);} 126void IN_DownDown(void) {KeyDown(&in_down);} 127void IN_DownUp(void) {KeyUp(&in_down);} 128void IN_LeftDown(void) {KeyDown(&in_left);} 129void IN_LeftUp(void) {KeyUp(&in_left);} 130void IN_RightDown(void) {KeyDown(&in_right);} 131void IN_RightUp(void) {KeyUp(&in_right);} 132void IN_ForwardDown(void) {KeyDown(&in_forward);} 133void IN_ForwardUp(void) {KeyUp(&in_forward);} 134void IN_BackDown(void) {KeyDown(&in_back);} 135void IN_BackUp(void) {KeyUp(&in_back);} 136void IN_LookupDown(void) {KeyDown(&in_lookup);} 137void IN_LookupUp(void) {KeyUp(&in_lookup);} 138void IN_LookdownDown(void) {KeyDown(&in_lookdown);} 139void IN_LookdownUp(void) {KeyUp(&in_lookdown);} 140void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} 141void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} 142void IN_MoverightDown(void) {KeyDown(&in_moveright);} 143void IN_MoverightUp(void) {KeyUp(&in_moveright);} 144 145void IN_SpeedDown(void) {KeyDown(&in_speed);} 146void IN_SpeedUp(void) {KeyUp(&in_speed);} 147void IN_StrafeDown(void) {KeyDown(&in_strafe);} 148void IN_StrafeUp(void) {KeyUp(&in_strafe);} 149 150void IN_AttackDown(void) {KeyDown(&in_attack);} 151void IN_AttackUp(void) {KeyUp(&in_attack);} 152 153void IN_UseDown (void) {KeyDown(&in_use);} 154void IN_UseUp (void) {KeyUp(&in_use);} 155void IN_JumpDown (void) {KeyDown(&in_jump);} 156void IN_JumpUp (void) {KeyUp(&in_jump);} 157 158void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));} 159 160/* 161=============== 162CL_KeyState 163 164Returns 0.25 if a key was pressed and released during the frame, 1650.5 if it was pressed and held 1660 if held then released, and 1671.0 if held for the entire time 168=============== 169*/ 170float 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 215cvar_t cl_upspeed = CVAR2("cl_upspeed","200"); 216cvar_t cl_forwardspeed = CVAR3("cl_forwardspeed","200", true); 217cvar_t cl_backspeed = CVAR3("cl_backspeed","200", true); 218cvar_t cl_sidespeed = CVAR2("cl_sidespeed","350"); 219 220cvar_t cl_movespeedkey = CVAR2("cl_movespeedkey","2.0"); 221 222cvar_t cl_yawspeed = CVAR2("cl_yawspeed","140"); 223cvar_t cl_pitchspeed = CVAR2("cl_pitchspeed","150"); 224 225cvar_t cl_anglespeedkey = CVAR2("cl_anglespeedkey","1.5"); 226 227 228/* 229================ 230CL_AdjustAngles 231 232Moves the local angle positions 233================ 234*/ 235void 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================ 281CL_BaseMove 282 283Send the intended movement message to the server 284================ 285*/ 286void 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 322int 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============== 334CL_FinishMove 335============== 336*/ 337void 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================= 384CL_SendCmd 385================= 386*/ 387void 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============ 485CL_InitInput 486============ 487*/ 488void 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============ 531CL_ClearStates 532============ 533*/ 534void CL_ClearStates (void) 535{ 536} 537 538