#include "quakedef.h" /* * KEY BUTTONS * * Continuous button event tracking is complicated by the fact that two different * input sources (say, mouse button 1 and the control key) can both press the * same button, but the button should only be released when both of the * pressing key have been released. * * When a key event issues a button command (+forward, +attack, etc), it appends * its key number as a parameter to the command so it can be matched up with * the release. * * state bit 0 is the current state of the key * state bit 1 is edge triggered on the up to down transition * state bit 2 is edge triggered on the down to up transition */ cvar_t cl_upspeed = {"cl_upspeed", "200"}; cvar_t cl_forwardspeed = {"cl_forwardspeed", "400", true}; cvar_t cl_backspeed = {"cl_backspeed", "400", true}; cvar_t cl_sidespeed = {"cl_sidespeed", "350"}; cvar_t cl_movespeedkey = {"cl_movespeedkey", "2.0"}; cvar_t cl_yawspeed = {"cl_yawspeed", "140"}; cvar_t cl_pitchspeed = {"cl_pitchspeed", "150"}; cvar_t cl_anglespeedkey = {"cl_anglespeedkey", "1.5"}; kbutton_t in_strafe, in_mlook; static kbutton_t in_left, in_right, in_forward, in_back; static kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; static kbutton_t in_klook, in_speed; static kbutton_t in_moveup, in_movedown; static kbutton_t in_attack, in_jump, in_button3, in_button4, in_use; static kbutton_t in_button5, in_button6, in_button7, in_button8; static int in_impulse; static void KeyDown(kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if(c[0]) k = atoi(c); else k = -1; // typed manually at the console for continuous down if(k == b->down[0] || k == b->down[1]) return; // repeating key if(!b->down[0]) b->down[0] = k; else if(!b->down[1]) b->down[1] = k; else { Con_Printf("Three keys down for a button!\n"); return; } if(b->state & 1) return; // still down b->state |= 1 + 2; // down + impulse down } static void KeyUp(kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if(c[0]) k = atoi(c); else { // typed manually at the console, assume for unsticking, so clear all b->down[0] = b->down[1] = 0; b->state = 4; // impulse up return; } if(b->down[0] == k) b->down[0] = 0; else if(b->down[1] == k) b->down[1] = 0; else return; // key up without coresponding down (menu pass through) if(b->down[0] || b->down[1]) return; // some other key is still holding it down if((b->state & 1) == 0) return; // still up (this should not happen) b->state &= ~1; // now up b->state |= 4; // impulse up } #define IN_KEY_DOWN(key) \ static void IN_##key##Down(cmd_t *c) \ { \ USED(c); \ KeyDown(&in_##key); \ } #define IN_KEY_UP(key) \ static void IN_##key##Up(cmd_t *c) \ { \ USED(c); \ KeyUp(&in_##key); \ } #define IN_KEY(key) \ IN_KEY_DOWN(key); \ IN_KEY_UP(key); IN_KEY(klook); IN_KEY_DOWN(mlook); static void IN_mlookUp(cmd_t *c) { KeyUp(&in_mlook); if((in_mlook.state & 1) == 0 && lookspring.value) V_StartPitchDrift(c); } IN_KEY(left); IN_KEY(right); IN_KEY(forward); IN_KEY(back); IN_KEY(lookup); IN_KEY(lookdown); IN_KEY(moveleft); IN_KEY(moveright); IN_KEY(moveup); IN_KEY(movedown); IN_KEY(speed); IN_KEY(strafe); IN_KEY(attack); IN_KEY(jump); IN_KEY(button3); IN_KEY(button4); IN_KEY(button5); IN_KEY(button6); IN_KEY(button7); IN_KEY(button8); IN_KEY(use); static void IN_Impulse(cmd_t *c) { USED(c); in_impulse = atoi(Cmd_Argv(1)); } /* Returns 0.25 if a key was pressed and released during the frame, 0.5 if it was pressed and held 0 if held then released, and 1.0 if held for the entire time */ static float CL_KeyState(kbutton_t *key) { float val; bool impulsedown, impulseup, down; impulsedown = key->state & 2; impulseup = key->state & 4; down = key->state & 1; val = 0; if(impulsedown && !impulseup) val = down ? 0.5 : 0; else if(impulseup && !impulsedown) val = 0; else if(!impulsedown && !impulseup) val = down ? 1.0 : 0; else if(impulsedown && impulseup) val = down ? 0.75 : 0.25; key->state &= 1; // clear impulses return val; } /* Moves the local angle positions */ static void CL_AdjustAngles(void) { float speed, up, down; if(in_speed.state & 1) speed = scr_frametime * cl_anglespeedkey.value; else speed = scr_frametime; if(!(in_strafe.state & 1)) { cl.viewangles[YAW] -= speed * cl_yawspeed.value * CL_KeyState(&in_right); cl.viewangles[YAW] += speed * cl_yawspeed.value * CL_KeyState(&in_left); cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); } if(in_klook.state & 1) { V_StopPitchDrift(); cl.viewangles[PITCH] -= speed * cl_pitchspeed.value * CL_KeyState(&in_forward); cl.viewangles[PITCH] += speed * cl_pitchspeed.value * CL_KeyState(&in_back); } up = CL_KeyState(&in_lookup); down = CL_KeyState(&in_lookdown); cl.viewangles[PITCH] -= speed * cl_pitchspeed.value * up; cl.viewangles[PITCH] += speed * cl_pitchspeed.value * down; if(up || down) V_StopPitchDrift(); cl.viewangles[PITCH] = clamp(cl.viewangles[PITCH], -MAX_PITCH, MAX_PITCH); cl.viewangles[ROLL] = clamp(cl.viewangles[ROLL], -MAX_ROLL, MAX_ROLL); } /* Send the intended movement message to the server */ void CL_BaseMove(usercmd_t *cmd) { if(cls.signon != SIGNONS) return; CL_AdjustAngles(); memset(cmd, 0, sizeof(*cmd)); if(in_strafe.state & 1) { cmd->sidemove += cl_sidespeed.value * CL_KeyState(&in_right); cmd->sidemove -= cl_sidespeed.value * CL_KeyState(&in_left); } cmd->sidemove += cl_sidespeed.value * CL_KeyState(&in_moveright); cmd->sidemove -= cl_sidespeed.value * CL_KeyState(&in_moveleft); cmd->upmove += cl_upspeed.value * CL_KeyState(&in_moveup); cmd->upmove -= cl_upspeed.value * CL_KeyState(&in_movedown); if((in_klook.state & 1) == 0) { cmd->forwardmove += cl_forwardspeed.value * CL_KeyState(&in_forward); cmd->forwardmove -= cl_backspeed.value * CL_KeyState(&in_back); } // adjust for speed key if(in_speed.state & 1) { if((cmd->forwardmove > 0 && cl_forwardspeed.value > 200) || (cmd->forwardmove < 0 && cl_backspeed.value > 200)) cmd->forwardmove /= cl_movespeedkey.value; else cmd->forwardmove *= cl_movespeedkey.value; cmd->sidemove *= cl_movespeedkey.value; cmd->upmove *= cl_movespeedkey.value; } } void CL_SendMove(usercmd_t *cmd) { byte data[128]; sizebuf_t buf; int i, bits; buf.maxsize = 128; buf.cursize = 0; buf.data = data; cl.cmd = *cmd; // send the movement message MSG_WriteByte(&buf, clc_move); MSG_WriteFloat(&buf, cl.mtime[0]); // so server can get ping times for(i = 0; i < 3; i++) cl.protocol.MSG_WriteAngle(&buf, cl.viewangles[i]); MSG_WriteShort(&buf, cmd->forwardmove); MSG_WriteShort(&buf, cmd->sidemove); MSG_WriteShort(&buf, cmd->upmove); // send button bits bits = 0; if(in_attack.state & 3) bits |= 1 << 0; in_attack.state &= ~2; if(in_jump.state & 3) bits |= 1 << 1; in_jump.state &= ~2; if(in_button3.state & 3) bits |= 1 << 2; in_button3.state &= ~2; if(in_button4.state & 3) bits |= 1 << 3; in_button4.state &= ~2; if(in_button5.state & 3) bits |= 1 << 4; in_button5.state &= ~2; if(in_button6.state & 3) bits |= 1 << 5; in_button6.state &= ~2; if(in_button7.state & 3) bits |= 1 << 6; in_button7.state &= ~2; if(in_button8.state & 3) bits |= 1 << 7; in_button8.state &= ~2; /* FIXME if(in_use.state & 3) bits |= 4; in_use.state &= ~2; */ MSG_WriteByte(&buf, bits); MSG_WriteByte(&buf, in_impulse); in_impulse = 0; // deliver the message if(cls.demoplayback) return; // always dump the first two message, because it may contain leftover inputs // from the last level if(++cl.movemessages <= 2) return; if(NET_SendUnreliableMessage(cls.netcon, &buf) == -1) { Con_Printf("CL_SendMove: lost server connection\n"); CL_Disconnect(); } } void CL_NewMap(void) { memset(&in_strafe, 0, sizeof(kbutton_t)); memset(&in_left, 0, sizeof(kbutton_t)); memset(&in_right, 0, sizeof(kbutton_t)); memset(&in_forward, 0, sizeof(kbutton_t)); memset(&in_back, 0, sizeof(kbutton_t)); memset(&in_lookup, 0, sizeof(kbutton_t)); memset(&in_lookdown, 0, sizeof(kbutton_t)); memset(&in_moveleft, 0, sizeof(kbutton_t)); memset(&in_moveright, 0, sizeof(kbutton_t)); memset(&in_moveup, 0, sizeof(kbutton_t)); memset(&in_movedown, 0, sizeof(kbutton_t)); memset(&in_attack, 0, sizeof(kbutton_t)); memset(&in_jump, 0, sizeof(kbutton_t)); memset(&in_button3, 0, sizeof(kbutton_t)); memset(&in_button4, 0, sizeof(kbutton_t)); memset(&in_button5, 0, sizeof(kbutton_t)); memset(&in_button6, 0, sizeof(kbutton_t)); memset(&in_button7, 0, sizeof(kbutton_t)); memset(&in_button8, 0, sizeof(kbutton_t)); memset(&in_use, 0, sizeof(kbutton_t)); in_impulse = 0; } void CL_InitInput(void) { #define KEY_CMD(key) \ Cmd_AddCommand("+" #key, IN_##key##Down); \ Cmd_AddCommand("-" #key, IN_##key##Up); KEY_CMD(strafe); KEY_CMD(left); KEY_CMD(right); KEY_CMD(forward); KEY_CMD(back); KEY_CMD(lookup); KEY_CMD(lookdown); KEY_CMD(moveleft); KEY_CMD(moveright); KEY_CMD(moveup); KEY_CMD(movedown); KEY_CMD(attack); KEY_CMD(jump); KEY_CMD(button3); KEY_CMD(button4); KEY_CMD(button5); KEY_CMD(button6); KEY_CMD(button7); KEY_CMD(button8); KEY_CMD(use); KEY_CMD(klook); KEY_CMD(mlook); KEY_CMD(speed); Cmd_AddCommand("impulse", IN_Impulse); }