yquake2remaster/src/client/cl_prediction.c
Daniel Gibson 690e46512e Restore stair step smoothing code for step sizes != 16
commit 93e9633382 "Port step smoothing code from r1q2." ported
r1q2 stair step smoothing for ranges that are equivalent to 8, 12 and 16
units/step.
Later in commit 7f996cc "Don't smooth half height steps" we removed
step sizes 8 and 12, because it appeared to make an elevator in hangar2
(over the water, after jump'n'run sequency) stutter.
However, in reality what made them stutter was the broken comparison
for velocity with an illegal cast, that we later fixed in
e30f82494 "Don't cast the array itself but it's content."

So now restore the smoothing for step sizes 8 and 12, which hopefully
fixes bug #923, and definitely does *not* break the elevater in hangar2.
2022-10-22 17:09:49 +02:00

315 lines
6.5 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 interpolation between two frames. This is used
* to smooth down network play
*
* =======================================================================
*/
#include "header/client.h"
void
CL_CheckPredictionError(void)
{
int frame;
int delta[3];
int i;
int len;
if (!cl_predict->value ||
(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
{
return;
}
/* calculate the last usercmd_t we sent that the server has processed */
frame = cls.netchan.incoming_acknowledged;
frame &= (CMD_BACKUP - 1);
/* compare what the server returned with what we had predicted it to be */
VectorSubtract(cl.frame.playerstate.pmove.origin,
cl.predicted_origins[frame], delta);
/* save the prediction error for interpolation */
len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
/* 80 world units */
if (len > 640)
{
/* a teleport or something */
VectorClear(cl.prediction_error);
}
else
{
if (cl_showmiss->value && (delta[0] || delta[1] || delta[2]))
{
Com_Printf("prediction miss on %i: %i\n", cl.frame.serverframe,
delta[0] + delta[1] + delta[2]);
}
VectorCopy(cl.frame.playerstate.pmove.origin,
cl.predicted_origins[frame]);
/* save for error itnerpolation */
for (i = 0; i < 3; i++)
{
cl.prediction_error[i] = delta[i] * 0.125f;
}
}
}
void
CL_ClipMoveToEntities(vec3_t start, vec3_t mins, vec3_t maxs,
vec3_t end, trace_t *tr)
{
int i, x, zd, zu;
trace_t trace;
int headnode;
float *angles;
entity_state_t *ent;
int num;
cmodel_t *cmodel;
vec3_t bmins, bmaxs;
for (i = 0; i < cl.frame.num_entities; i++)
{
num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1);
ent = &cl_parse_entities[num];
if (!ent->solid)
{
continue;
}
if (ent->number == cl.playernum + 1)
{
continue;
}
if (ent->solid == 31)
{
/* special value for bmodel */
cmodel = cl.model_clip[ent->modelindex];
if (!cmodel)
{
continue;
}
headnode = cmodel->headnode;
angles = ent->angles;
}
else
{
/* encoded bbox */
x = 8 * (ent->solid & 31);
zd = 8 * ((ent->solid >> 5) & 31);
zu = 8 * ((ent->solid >> 10) & 63) - 32;
bmins[0] = bmins[1] = -(float)x;
bmaxs[0] = bmaxs[1] = (float)x;
bmins[2] = -(float)zd;
bmaxs[2] = (float)zu;
headnode = CM_HeadnodeForBox(bmins, bmaxs);
angles = vec3_origin; /* boxes don't rotate */
}
if (tr->allsolid)
{
return;
}
trace = CM_TransformedBoxTrace(start, end,
mins, maxs, headnode, MASK_PLAYERSOLID,
ent->origin, angles);
if (trace.allsolid || trace.startsolid ||
(trace.fraction < tr->fraction))
{
trace.ent = (struct edict_s *)ent;
if (tr->startsolid)
{
*tr = trace;
tr->startsolid = true;
}
else
{
*tr = trace;
}
}
}
}
trace_t
CL_PMTrace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
{
trace_t t;
/* check against world */
t = CM_BoxTrace(start, end, mins, maxs, 0, MASK_PLAYERSOLID);
if (t.fraction < 1.0)
{
t.ent = (struct edict_s *)1;
}
/* check all other solid models */
CL_ClipMoveToEntities(start, mins, maxs, end, &t);
return t;
}
int
CL_PMpointcontents(vec3_t point)
{
int i;
entity_state_t *ent;
int num;
cmodel_t *cmodel;
int contents;
contents = CM_PointContents(point, 0);
for (i = 0; i < cl.frame.num_entities; i++)
{
num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1);
ent = &cl_parse_entities[num];
if (ent->solid != 31) /* special value for bmodel */
{
continue;
}
cmodel = cl.model_clip[ent->modelindex];
if (!cmodel)
{
continue;
}
contents |= CM_TransformedPointContents(point, cmodel->headnode,
ent->origin, ent->angles);
}
return contents;
}
/*
* Sets cl.predicted_origin and cl.predicted_angles
*/
void
CL_PredictMovement(void)
{
int ack, current;
int frame;
usercmd_t *cmd;
pmove_t pm;
int i;
int step;
vec3_t tmp;
if (cls.state != ca_active)
{
return;
}
if (cl_paused->value)
{
return;
}
if (!cl_predict->value ||
(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
{
/* just set angles */
for (i = 0; i < 3; i++)
{
cl.predicted_angles[i] = cl.viewangles[i] + SHORT2ANGLE(
cl.frame.playerstate.pmove.delta_angles[i]);
}
return;
}
ack = cls.netchan.incoming_acknowledged;
current = cls.netchan.outgoing_sequence;
/* if we are too far out of date, just freeze */
if (current - ack >= CMD_BACKUP)
{
if (cl_showmiss->value)
{
Com_Printf("exceeded CMD_BACKUP\n");
}
return;
}
/* copy current state to pmove */
memset (&pm, 0, sizeof(pm));
pm.trace = CL_PMTrace;
pm.pointcontents = CL_PMpointcontents;
pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]);
pm.s = cl.frame.playerstate.pmove;
/* run frames */
while (++ack <= current)
{
frame = ack & (CMD_BACKUP - 1);
cmd = &cl.cmds[frame];
// Ignore null entries
if (!cmd->msec)
{
continue;
}
pm.cmd = *cmd;
Pmove(&pm);
/* save for debug checking */
VectorCopy(pm.s.origin, cl.predicted_origins[frame]);
}
// step is used for movement prediction on stairs
// (so moving up/down stairs is smooth)
step = pm.s.origin[2] - (int)(cl.predicted_origin[2] * 8);
VectorCopy(pm.s.velocity, tmp);
if (((step > 62 && step < 66) || (step > 94 && step < 98) || (step > 126 && step < 130))
&& !VectorCompare(tmp, vec3_origin)
&& (pm.s.pm_flags & PMF_ON_GROUND))
{
cl.predicted_step = step * 0.125f;
cl.predicted_step_time = cls.realtime - (int)(cls.nframetime * 500);
}
/* copy results out for rendering */
cl.predicted_origin[0] = pm.s.origin[0] * 0.125f;
cl.predicted_origin[1] = pm.s.origin[1] * 0.125f;
cl.predicted_origin[2] = pm.s.origin[2] * 0.125f;
VectorCopy(pm.viewangles, cl.predicted_angles);
}