File: client\cl_input.c

    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 
   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 
  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 
  142 void IN_KLookDown (void) {KeyDown(&in_klook);}
  143 void IN_KLookUp (void) {KeyUp(&in_klook);}
  144 void IN_UpDown(void) {KeyDown(&in_up);}
  145 void IN_UpUp(void) {KeyUp(&in_up);}
  146 void IN_DownDown(void) {KeyDown(&in_down);}
  147 void IN_DownUp(void) {KeyUp(&in_down);}
  148 void IN_LeftDown(void) {KeyDown(&in_left);}
  149 void IN_LeftUp(void) {KeyUp(&in_left);}
  150 void IN_RightDown(void) {KeyDown(&in_right);}
  151 void IN_RightUp(void) {KeyUp(&in_right);}
  152 void IN_ForwardDown(void) {KeyDown(&in_forward);}
  153 void IN_ForwardUp(void) {KeyUp(&in_forward);}
  154 void IN_BackDown(void) {KeyDown(&in_back);}
  155 void IN_BackUp(void) {KeyUp(&in_back);}
  156 void IN_LookupDown(void) {KeyDown(&in_lookup);}
  157 void IN_LookupUp(void) {KeyUp(&in_lookup);}
  158 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
  159 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
  160 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
  161 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
  162 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
  163 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
  164 
  165 void IN_SpeedDown(void) {KeyDown(&in_speed);}
  166 void IN_SpeedUp(void) {KeyUp(&in_speed);}
  167 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
  168 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
  169 
  170 void IN_AttackDown(void) {KeyDown(&in_attack);}
  171 void IN_AttackUp(void) {KeyUp(&in_attack);}
  172 
  173 void IN_UseDown (void) {KeyDown(&in_use);}
  174 void IN_UseUp (void) {KeyUp(&in_use);}
  175 
  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 */
  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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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