mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2025-01-09 11:21:34 +00:00
61400d1ae8
The input system backend was once used in the client and the renderers, but for some years now it has been an integral part of the client only. Move it there.
854 lines
15 KiB
C
854 lines
15 KiB
C
/*
|
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* =======================================================================
|
|
*
|
|
* This file implements the input handling like mouse events and
|
|
* keyboard strokes.
|
|
*
|
|
* =======================================================================
|
|
*/
|
|
|
|
#include "header/client.h"
|
|
#include "input/header/input.h"
|
|
|
|
cvar_t *cl_nodelta;
|
|
|
|
unsigned frame_msec;
|
|
unsigned old_sys_frame_time;
|
|
|
|
/*
|
|
* 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
|
|
*
|
|
*
|
|
* Key_Event (int key, qboolean down, unsigned time);
|
|
*
|
|
* +mlook src time
|
|
*/
|
|
|
|
kbutton_t in_klook;
|
|
kbutton_t in_left, in_right, in_forward, in_back;
|
|
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
|
|
kbutton_t in_strafe, in_speed, in_use, in_attack;
|
|
kbutton_t in_up, in_down;
|
|
|
|
int in_impulse;
|
|
|
|
void
|
|
KeyDown(kbutton_t *b)
|
|
{
|
|
int k;
|
|
char *c;
|
|
|
|
c = Cmd_Argv(1);
|
|
|
|
if (c[0])
|
|
{
|
|
k = (int)strtol(c, (char **)NULL, 10);
|
|
}
|
|
|
|
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
|
|
{
|
|
Com_Printf("Three keys down for a button!\n");
|
|
return;
|
|
}
|
|
|
|
if (b->state & 1)
|
|
{
|
|
return; /* still down */
|
|
}
|
|
|
|
/* save timestamp */
|
|
c = Cmd_Argv(2);
|
|
b->downtime = (int)strtol(c, (char **)NULL, 10);
|
|
|
|
if (!b->downtime)
|
|
{
|
|
b->downtime = sys_frame_time - 100;
|
|
}
|
|
|
|
b->state |= 1 + 2; /* down + impulse down */
|
|
}
|
|
|
|
void
|
|
KeyUp(kbutton_t *b)
|
|
{
|
|
int k;
|
|
char *c;
|
|
unsigned uptime;
|
|
|
|
c = Cmd_Argv(1);
|
|
|
|
if (c[0])
|
|
{
|
|
k = (int)strtol(c, (char **)NULL, 10);
|
|
}
|
|
|
|
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))
|
|
{
|
|
return; /* still up (this should not happen) */
|
|
}
|
|
|
|
/* save timestamp */
|
|
c = Cmd_Argv(2);
|
|
uptime = (int)strtol(c, (char **)NULL, 10);
|
|
|
|
if (uptime)
|
|
{
|
|
b->msec += uptime - b->downtime;
|
|
}
|
|
|
|
else
|
|
{
|
|
b->msec += 10;
|
|
}
|
|
|
|
b->state &= ~1; /* now up */
|
|
b->state |= 4; /* impulse up */
|
|
}
|
|
|
|
void
|
|
IN_KLookDown(void)
|
|
{
|
|
KeyDown(&in_klook);
|
|
}
|
|
|
|
void
|
|
IN_KLookUp(void)
|
|
{
|
|
KeyUp(&in_klook);
|
|
}
|
|
|
|
void
|
|
IN_UpDown(void)
|
|
{
|
|
KeyDown(&in_up);
|
|
}
|
|
|
|
void
|
|
IN_UpUp(void)
|
|
{
|
|
KeyUp(&in_up);
|
|
}
|
|
|
|
void
|
|
IN_DownDown(void)
|
|
{
|
|
KeyDown(&in_down);
|
|
}
|
|
|
|
void
|
|
IN_DownUp(void)
|
|
{
|
|
KeyUp(&in_down);
|
|
}
|
|
|
|
void
|
|
IN_LeftDown(void)
|
|
{
|
|
KeyDown(&in_left);
|
|
}
|
|
|
|
void
|
|
IN_LeftUp(void)
|
|
{
|
|
KeyUp(&in_left);
|
|
}
|
|
|
|
void
|
|
IN_RightDown(void)
|
|
{
|
|
KeyDown(&in_right);
|
|
}
|
|
|
|
void
|
|
IN_RightUp(void)
|
|
{
|
|
KeyUp(&in_right);
|
|
}
|
|
|
|
void
|
|
IN_ForwardDown(void)
|
|
{
|
|
KeyDown(&in_forward);
|
|
}
|
|
|
|
void
|
|
IN_ForwardUp(void)
|
|
{
|
|
KeyUp(&in_forward);
|
|
}
|
|
|
|
void
|
|
IN_BackDown(void)
|
|
{
|
|
KeyDown(&in_back);
|
|
}
|
|
|
|
void
|
|
IN_BackUp(void)
|
|
{
|
|
KeyUp(&in_back);
|
|
}
|
|
|
|
void
|
|
IN_LookupDown(void)
|
|
{
|
|
KeyDown(&in_lookup);
|
|
}
|
|
|
|
void
|
|
IN_LookupUp(void)
|
|
{
|
|
KeyUp(&in_lookup);
|
|
}
|
|
|
|
void
|
|
IN_LookdownDown(void)
|
|
{
|
|
KeyDown(&in_lookdown);
|
|
}
|
|
|
|
void
|
|
IN_LookdownUp(void)
|
|
{
|
|
KeyUp(&in_lookdown);
|
|
}
|
|
|
|
void
|
|
IN_MoveleftDown(void)
|
|
{
|
|
KeyDown(&in_moveleft);
|
|
}
|
|
|
|
void
|
|
IN_MoveleftUp(void)
|
|
{
|
|
KeyUp(&in_moveleft);
|
|
}
|
|
|
|
void
|
|
IN_MoverightDown(void)
|
|
{
|
|
KeyDown(&in_moveright);
|
|
}
|
|
|
|
void
|
|
IN_MoverightUp(void)
|
|
{
|
|
KeyUp(&in_moveright);
|
|
}
|
|
|
|
void
|
|
IN_SpeedDown(void)
|
|
{
|
|
KeyDown(&in_speed);
|
|
}
|
|
|
|
void
|
|
IN_SpeedUp(void)
|
|
{
|
|
KeyUp(&in_speed);
|
|
}
|
|
|
|
void
|
|
IN_StrafeDown(void)
|
|
{
|
|
KeyDown(&in_strafe);
|
|
}
|
|
|
|
void
|
|
IN_StrafeUp(void)
|
|
{
|
|
KeyUp(&in_strafe);
|
|
}
|
|
|
|
void
|
|
IN_AttackDown(void)
|
|
{
|
|
KeyDown(&in_attack);
|
|
}
|
|
|
|
void
|
|
IN_AttackUp(void)
|
|
{
|
|
KeyUp(&in_attack);
|
|
}
|
|
|
|
void
|
|
IN_UseDown(void)
|
|
{
|
|
KeyDown(&in_use);
|
|
}
|
|
|
|
void
|
|
IN_UseUp(void)
|
|
{
|
|
KeyUp(&in_use);
|
|
}
|
|
|
|
void
|
|
IN_Impulse(void)
|
|
{
|
|
in_impulse = (int)strtol(Cmd_Argv(1), (char **)NULL, 10);
|
|
}
|
|
|
|
/*
|
|
* Returns the fraction of the
|
|
* frame that the key was down
|
|
*/
|
|
float
|
|
CL_KeyState(kbutton_t *key)
|
|
{
|
|
float val;
|
|
int msec;
|
|
|
|
key->state &= 1; /* clear impulses */
|
|
|
|
msec = key->msec;
|
|
key->msec = 0;
|
|
|
|
if (key->state)
|
|
{
|
|
/* still down */
|
|
msec += sys_frame_time - key->downtime;
|
|
key->downtime = sys_frame_time;
|
|
}
|
|
|
|
val = (float)msec / frame_msec;
|
|
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
|
|
if (val > 1)
|
|
{
|
|
val = 1;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
cvar_t *cl_upspeed;
|
|
cvar_t *cl_forwardspeed;
|
|
cvar_t *cl_sidespeed;
|
|
cvar_t *cl_yawspeed;
|
|
cvar_t *cl_pitchspeed;
|
|
cvar_t *cl_run;
|
|
cvar_t *cl_anglespeedkey;
|
|
|
|
/*
|
|
* Moves the local angle positions
|
|
*/
|
|
void
|
|
CL_AdjustAngles(void)
|
|
{
|
|
float speed;
|
|
float up, down;
|
|
|
|
if (in_speed.state & 1)
|
|
{
|
|
speed = cls.nframetime * cl_anglespeedkey->value;
|
|
}
|
|
|
|
else
|
|
{
|
|
speed = cls.nframetime;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (in_klook.state & 1)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Send the intended movement message to the server
|
|
*/
|
|
void
|
|
CL_BaseMove(usercmd_t *cmd)
|
|
{
|
|
CL_AdjustAngles();
|
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
|
|
VectorCopy(cl.viewangles, cmd->angles);
|
|
|
|
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_up);
|
|
cmd->upmove -= cl_upspeed->value * CL_KeyState(&in_down);
|
|
|
|
if (!(in_klook.state & 1))
|
|
{
|
|
cmd->forwardmove += cl_forwardspeed->value * CL_KeyState(&in_forward);
|
|
cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState(&in_back);
|
|
}
|
|
|
|
/* adjust for speed key / running */
|
|
if ((in_speed.state & 1) ^ (int)(cl_run->value))
|
|
{
|
|
cmd->forwardmove *= 2;
|
|
cmd->sidemove *= 2;
|
|
cmd->upmove *= 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
CL_ClampPitch(void)
|
|
{
|
|
float pitch;
|
|
|
|
pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
|
|
|
|
if (pitch > 180)
|
|
{
|
|
pitch -= 360;
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch < -360)
|
|
{
|
|
cl.viewangles[PITCH] += 360; /* wrapped */
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch > 360)
|
|
{
|
|
cl.viewangles[PITCH] -= 360; /* wrapped */
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch > 89)
|
|
{
|
|
cl.viewangles[PITCH] = 89 - pitch;
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch < -89)
|
|
{
|
|
cl.viewangles[PITCH] = -89 - pitch;
|
|
}
|
|
}
|
|
|
|
void
|
|
CL_FinishMove(usercmd_t *cmd)
|
|
{
|
|
int ms;
|
|
int i;
|
|
|
|
/* figure button bits */
|
|
if (in_attack.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_ATTACK;
|
|
}
|
|
|
|
in_attack.state &= ~2;
|
|
|
|
if (in_use.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_USE;
|
|
}
|
|
|
|
in_use.state &= ~2;
|
|
|
|
if (anykeydown && (cls.key_dest == key_game))
|
|
{
|
|
cmd->buttons |= BUTTON_ANY;
|
|
}
|
|
|
|
/* send milliseconds of time to apply the move */
|
|
ms = cls.nframetime * 1000;
|
|
|
|
if (ms > 250)
|
|
{
|
|
ms = 100; /* time was unreasonable */
|
|
}
|
|
|
|
cmd->msec = ms;
|
|
|
|
CL_ClampPitch();
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
|
|
}
|
|
|
|
cmd->impulse = in_impulse;
|
|
in_impulse = 0;
|
|
|
|
/* send the ambient light level at the player's current position */
|
|
cmd->lightlevel = (byte)cl_lightlevel->value;
|
|
}
|
|
|
|
void
|
|
IN_CenterView(void)
|
|
{
|
|
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
|
|
}
|
|
|
|
/*
|
|
* Centers the view
|
|
*/
|
|
static void
|
|
IN_ForceCenterView(void)
|
|
{
|
|
cl.viewangles[PITCH] = 0;
|
|
}
|
|
|
|
void
|
|
CL_InitInput(void)
|
|
{
|
|
Cmd_AddCommand("centerview", IN_CenterView);
|
|
Cmd_AddCommand("force_centerview", IN_ForceCenterView);
|
|
|
|
Cmd_AddCommand("+moveup", IN_UpDown);
|
|
Cmd_AddCommand("-moveup", IN_UpUp);
|
|
Cmd_AddCommand("+movedown", IN_DownDown);
|
|
Cmd_AddCommand("-movedown", IN_DownUp);
|
|
Cmd_AddCommand("+left", IN_LeftDown);
|
|
Cmd_AddCommand("-left", IN_LeftUp);
|
|
Cmd_AddCommand("+right", IN_RightDown);
|
|
Cmd_AddCommand("-right", IN_RightUp);
|
|
Cmd_AddCommand("+forward", IN_ForwardDown);
|
|
Cmd_AddCommand("-forward", IN_ForwardUp);
|
|
Cmd_AddCommand("+back", IN_BackDown);
|
|
Cmd_AddCommand("-back", IN_BackUp);
|
|
Cmd_AddCommand("+lookup", IN_LookupDown);
|
|
Cmd_AddCommand("-lookup", IN_LookupUp);
|
|
Cmd_AddCommand("+lookdown", IN_LookdownDown);
|
|
Cmd_AddCommand("-lookdown", IN_LookdownUp);
|
|
Cmd_AddCommand("+strafe", IN_StrafeDown);
|
|
Cmd_AddCommand("-strafe", IN_StrafeUp);
|
|
Cmd_AddCommand("+moveleft", IN_MoveleftDown);
|
|
Cmd_AddCommand("-moveleft", IN_MoveleftUp);
|
|
Cmd_AddCommand("+moveright", IN_MoverightDown);
|
|
Cmd_AddCommand("-moveright", IN_MoverightUp);
|
|
Cmd_AddCommand("+speed", IN_SpeedDown);
|
|
Cmd_AddCommand("-speed", IN_SpeedUp);
|
|
Cmd_AddCommand("+attack", IN_AttackDown);
|
|
Cmd_AddCommand("-attack", IN_AttackUp);
|
|
Cmd_AddCommand("+use", IN_UseDown);
|
|
Cmd_AddCommand("-use", IN_UseUp);
|
|
Cmd_AddCommand("impulse", IN_Impulse);
|
|
Cmd_AddCommand("+klook", IN_KLookDown);
|
|
Cmd_AddCommand("-klook", IN_KLookUp);
|
|
|
|
cl_nodelta = Cvar_Get("cl_nodelta", "0", 0);
|
|
}
|
|
|
|
void
|
|
CL_RefreshCmd(void)
|
|
{
|
|
int ms;
|
|
usercmd_t *cmd;
|
|
|
|
// CMD to fill
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
|
|
// Calculate delta
|
|
frame_msec = sys_frame_time - old_sys_frame_time;
|
|
|
|
// Check bounds
|
|
if (frame_msec < 1)
|
|
{
|
|
return;
|
|
}
|
|
else if (frame_msec > 200)
|
|
{
|
|
frame_msec = 200;
|
|
}
|
|
|
|
// Add movement
|
|
CL_BaseMove(cmd);
|
|
IN_Move(cmd);
|
|
|
|
// Clamp angels for prediction
|
|
CL_ClampPitch();
|
|
|
|
cmd->angles[0] = ANGLE2SHORT(cl.viewangles[0]);
|
|
cmd->angles[1] = ANGLE2SHORT(cl.viewangles[1]);
|
|
cmd->angles[2] = ANGLE2SHORT(cl.viewangles[2]);
|
|
|
|
// Update time for prediction
|
|
ms = (int)(cls.nframetime * 1000.0f);
|
|
|
|
if (ms > 250)
|
|
{
|
|
ms = 100;
|
|
}
|
|
|
|
cmd->msec = ms;
|
|
|
|
// Update frame time for the next call
|
|
old_sys_frame_time = sys_frame_time;
|
|
|
|
// Important events are send immediately
|
|
if (((in_attack.state & 2)) || (in_use.state & 2))
|
|
{
|
|
cls.forcePacket = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
CL_RefreshMove(void)
|
|
{
|
|
usercmd_t *cmd;
|
|
|
|
// CMD to fill
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
|
|
// Calculate delta
|
|
frame_msec = sys_frame_time - old_sys_frame_time;
|
|
|
|
// Check bounds
|
|
if (frame_msec < 1)
|
|
{
|
|
return;
|
|
}
|
|
else if (frame_msec > 200)
|
|
{
|
|
frame_msec = 200;
|
|
}
|
|
|
|
// Add movement
|
|
CL_BaseMove(cmd);
|
|
IN_Move(cmd);
|
|
|
|
old_sys_frame_time = sys_frame_time;
|
|
}
|
|
|
|
void
|
|
CL_FinalizeCmd(void)
|
|
{
|
|
usercmd_t *cmd;
|
|
|
|
// CMD to fill
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
|
|
// Mouse button events
|
|
if (in_attack.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_ATTACK;
|
|
}
|
|
|
|
in_attack.state &= ~2;
|
|
|
|
if (in_use.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_USE;
|
|
}
|
|
|
|
in_use.state &= ~2;
|
|
|
|
// Keyboard events
|
|
if (anykeydown && cls.key_dest == key_game)
|
|
{
|
|
cmd->buttons |= BUTTON_ANY;
|
|
}
|
|
|
|
cmd->impulse = in_impulse;
|
|
in_impulse = 0;
|
|
|
|
// Set light level for muzzle flash
|
|
cmd->lightlevel = (byte)cl_lightlevel->value;
|
|
}
|
|
|
|
void
|
|
CL_SendCmd(void)
|
|
{
|
|
sizebuf_t buf;
|
|
byte data[128];
|
|
int i;
|
|
usercmd_t *cmd, *oldcmd;
|
|
usercmd_t nullcmd;
|
|
int checksumIndex;
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
|
|
/* save this command off for prediction */
|
|
i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
cl.cmd_time[i] = cls.realtime; /* for netgraph ping calculation */
|
|
|
|
CL_FinalizeCmd();
|
|
|
|
cl.cmd = *cmd;
|
|
|
|
if ((cls.state == ca_disconnected) || (cls.state == ca_connecting))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (cls.state == ca_connected)
|
|
{
|
|
if (cls.netchan.message.cursize ||
|
|
(curtime - cls.netchan.last_sent > 1000))
|
|
{
|
|
Netchan_Transmit(&cls.netchan, 0, buf.data);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* send a userinfo update if needed */
|
|
if (userinfo_modified)
|
|
{
|
|
CL_FixUpGender();
|
|
userinfo_modified = false;
|
|
MSG_WriteByte(&cls.netchan.message, clc_userinfo);
|
|
MSG_WriteString(&cls.netchan.message, Cvar_Userinfo());
|
|
}
|
|
|
|
SZ_Init(&buf, data, sizeof(data));
|
|
|
|
if (cmd->buttons && (cl.cinematictime > 0) && !cl.attractloop &&
|
|
(cls.realtime - cl.cinematictime > 1000))
|
|
{
|
|
/* skip the rest of the cinematic */
|
|
SCR_FinishCinematic();
|
|
}
|
|
|
|
/* begin a client move command */
|
|
MSG_WriteByte(&buf, clc_move);
|
|
|
|
/* save the position for a checksum byte */
|
|
checksumIndex = buf.cursize;
|
|
MSG_WriteByte(&buf, 0);
|
|
|
|
/* let the server know what the last frame we
|
|
got was, so the next message can be delta
|
|
compressed */
|
|
if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
|
|
{
|
|
MSG_WriteLong(&buf, -1); /* no compression */
|
|
}
|
|
else
|
|
{
|
|
MSG_WriteLong(&buf, cl.frame.serverframe);
|
|
}
|
|
|
|
/* send this and the previous cmds in the message, so
|
|
if the last packet was dropped, it can be recovered */
|
|
i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
memset(&nullcmd, 0, sizeof(nullcmd));
|
|
MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
|
|
oldcmd = cmd;
|
|
|
|
i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
|
|
oldcmd = cmd;
|
|
|
|
i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
|
|
|
|
/* calculate a checksum over the move commands */
|
|
buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
|
|
buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
|
|
cls.netchan.outgoing_sequence);
|
|
|
|
/* deliver the message */
|
|
Netchan_Transmit(&cls.netchan, buf.cursize, buf.data);
|
|
|
|
/* Reinit the current cmd buffer */
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
}
|
|
|