Git Repos / fte_dogmode / qc / cl_player.qc
Last update to this file was on 2024-03-24 at 02:40.
Show cl_player.qc
//==============================================================================
// CSQC PLAYER MOVEMENT & PREDICTION
//
// This is based on code by Eukara [1] and the CSQCTest code shipped with
// FTEQW [2].
//
// [1]: https://icculus.org/~marco/quakec/csqc/prediction.html
// [2]: https://sourceforge.net/p/fteqw/code/HEAD/tree/trunk/quakec/csqctest/src
//==============================================================================
//======================================================================
// constants
//======================================================================
#ifdef CSQC
const float PRED_ERRORTIME = 20; //
const float PRED_STEPTIME = 8;
#endif
//======================================================================
// forward declarations
//======================================================================
#ifdef CSQC
void(entity ent) PlayerResetPrediction;
void(entity ent, float endframe) PlayerRunMovement;
float() PlayerPreDraw;
static void() PlayerRemove;
void() PlayerNew;
void(float isnew) PlayerUpdate;
#endif
//------------------------------------------------------------------------------
#ifdef CSQC
//----------------------------------------------------------------------
// PlayerResetPrediction
// was Pred_ResetPlayerPrediction in CSQCTest cs/prediction.qc
//----------------------------------------------------------------------
void(entity e) PlayerResetPrediction =
{
e.origin = player_org;
e.velocity = player_vel;
e.flags = player_flags;
e.pmove_flags = player_pmove_flags;
e.doublejump_timer = player_doublejump_timer;
// +1 because the received frame has the move already done (server side)
pmove_frame = player_sequence + 1;
if (pmove_frame < clientcommandframe - 128)
// avoid an infinite loop
pmove_frame = clientcommandframe - 128;
};
//----------------------------------------------------------------------
// PlayerRunMovement
// was Pred_RunMovement in CSQCTest cs/prediction.qc
//----------------------------------------------------------------------
void(entity e, float endframe) PlayerRunMovement =
{
if (servercommandframe >= player_sequence + 63)
{
// we're meant to be updating the player faster than
// this. hopefully its just that we're throttled...
// you can comment out this block and the player will
// continue to be predicted locally. But its best to
// freeze them
// as a note we also hit this block when the console is
// down -- CEV
player_sequence = servercommandframe - 63;
return;
}
PlayerResetPrediction (e);
if (getstatf(STAT_HEALTH) <= 0)
{
// dead, so don't run prediction. :D
pmove_frame = clientcommandframe;
if (!getinputstate(pmove_frame - 1))
{
dprint ("PlayerRunMovement: no-op if statement?\n");
// no-op ? -- CEV
}
return;
}
/*
if (getinputstate(pmove_frame - 1))
// from CSQCTest; what is this doing? -- TODO CEV;
e.pmove_flags |= PMF_JUMP_HELD;
*/
while (pmove_frame <= endframe)
{
if (!getinputstate(pmove_frame))
{
// no input from client
// pmove_frame++;
break;
}
if (world_standardphysics)
// for testing
runstandardplayerphysics (e);
else
// for real
PM_Move (e);
pmove_frame++;
}
// add anything that was applied after (for low packet
// rate protocols)
// TODO CEV
input_angles = view_angles;
};
//----------------------------------------------------------------------
// PlayerPreDraw
// Incorporates Pred_UpdateLocalMovement from CSQCTest.
//----------------------------------------------------------------------
float() PlayerPreDraw =
{
local float viewheight;
if (self.entnum != player_localentnum)
{
self.renderflags = 0;
}
else
{
PlayerRunMovement (self, clientcommandframe);
if (self.origin_z >= player_step_oldz + 6 &&
self.origin_z < player_step_oldz + 24 &&
self.velocity_z == 0)
{
// evaluate out the remaining old step
if (player_steptime - time > 0)
player_step = (player_steptime - time) *
PRED_STEPTIME * player_step;
else
player_step = 0;
// work out the new step
player_step += (player_step_oldz - self.origin_z);
player_steptime = time + 1 / PRED_STEPTIME;
}
player_step_oldz = self.origin_z;
// allow the user to move the viewheight down 6 units so
// it's at +16, where projectiles come from.
viewheight = autocvar (v_viewheight, 0);
if (viewheight < -7)
viewheight = -7;
else if (viewheight > 7)
viewheight = 7;
view_origin = self.origin;
view_origin_z += getstatf (STAT_VIEWHEIGHT) + viewheight;
// correct the view position to compensate for any errors,
// slowly over time, 0.1 seconds.
if (pmove_errortime - time > 0)
{
dprint ("PlayerPreDraw: pmove error\n");
view_origin += (pmove_errortime - time) *
PRED_ERRORTIME * pmove_error;
}
if (player_steptime - time > 0)
view_origin_z += (player_steptime - time) *
PRED_STEPTIME * player_step;
if (self.lerptime)
self.lerpfrac = 1 - (time - self.lerptime) * 10;
self.renderflags |= RF_EXTERNALMODEL;
}
viewentity.angles = view_angles;
viewentity.origin = view_origin;
addentity (self);
return PREDRAW_NEXT;
};
//----------------------------------------------------------------------
// PlayerRemove
//----------------------------------------------------------------------
static void() PlayerRemove =
{
if (player_local == self)
player_local = world;
setmodel (self, "");
self.oldskin = "";
self.predraw = __NULL__;
};
//----------------------------------------------------------------------
// PlayerNew
//----------------------------------------------------------------------
void() PlayerNew =
{
self.classname = "player";
self.customphysics = sub_null;
// MASK_NORMAL ?
self.drawmask = MASK_ENGINE;
self.movetype = MOVETYPE_WALK;
self.predraw = PlayerPreDraw;
self.removefunc = PlayerRemove;
self.solid = SOLID_SLIDEBOX;
setmodel (self, "progs/player.mdl");
};
//----------------------------------------------------------------------
// PlayerUpdate
// called from CSQC_Ent_Update for CT_PLAYER entities
//----------------------------------------------------------------------
void(float isnew) PlayerUpdate =
{
local float f;
f = ReadByte ();
self.origin_x = ReadCoord ();
self.origin_y = ReadCoord ();
self.origin_z = ReadCoord ();
self.angles_x = ReadShort () / (32767 / 360);
self.angles_y = ReadShort () / (32767 / 360);
self.angles_z = ReadShort () / (32767 / 360);
self.velocity_x = ReadShort ();
self.velocity_y = ReadShort ();
self.velocity_z = ReadShort ();
self.effects = ReadFloat ();
self.flags = ReadFloat ();
self.pmove_flags = ReadFloat ();
self.doublejump_timer = ReadFloat ();
if (isnew && !(self.predraw))
PlayerNew ();
if (intermission)
{
// zero out velocity if in intermission -- CEV
world_gravity = 0.0f;
input_movevalues = '0 0 0';
self.velocity = '0 0 0';
viewentity.velocity = '0 0 0';
// viewentity.angles = self.angles;
// viewentity.origin = self.origin;
// setorigin (self, self.origin);
// setorigin (viewentity, viewentity.origin);
return;
}
if (f != self.frame || isnew)
{
self.frame2 = self.frame;
self.lerptime = time;
self.frame = f;
}
if (self.entnum == player_localentnum)
{
local vector o, v;
local float djt, gbt, pmflags, tempflags;
// this is us; save for later
player_local = self;
o = self.origin;
v = self.velocity;
djt = self.doublejump_timer;
tempflags = self.flags;
pmflags = self.pmove_flags;
// PlayerRunMovement calls ResetPrediction -- CEV
// PlayerResetPrediction (self);
PlayerRunMovement (self, servercommandframe + 1);
// TODO CEV ????
player_doublejump_timer = djt;
player_flags = tempflags;
player_pmove_flags = pmflags;
player_org = o;
player_vel = v;
player_sequence = servercommandframe;
if (autocvar(cg_noerror, TRUE))
{
pmove_error = '0 0 0';
pmove_errortime = time;
PlayerResetPrediction (self);
}
else
{
PlayerRunMovement (self, clientcommandframe);
o = self.origin;
// again: PlayerRunMovement calls ResetPrediction
// PlayerResetPrediction (self);
PlayerRunMovement (self, clientcommandframe);
if (vlen(o - self.origin) > 64)
{
// teleport
pmove_error = '0 0 0';
pmove_errortime = time;
}
else
{
pmove_error = (pmove_errortime - time) *
PRED_ERRORTIME * pmove_error +
(o - self.origin);
if (vlen(pmove_error) < 1)
pmove_error = '0 0 0';
pmove_errortime = time + 1 / PRED_ERRORTIME;
}
}
}
setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
self.oldframe = self.frame;
// setorigin (self, self.origin);
};
#endif
Return to the top of this page or return to the overview of this repo.
Log cl_player.qc
Date | Commit Message | Author | + | - |
---|---|---|---|---|
2024-03-24 | 2nd pass refactor, rework QC class structure | cev | +329 |
Return to the top of this page or return to the overview of this repo.