Map triggers are more sane.

Really buggy example custom prediction code in this commit.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3180 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2009-04-19 01:15:18 +00:00
parent a96b5ee4e9
commit b3b0f3da9f
10 changed files with 350 additions and 177 deletions

View File

@ -1,48 +1,294 @@
/*
WARNING: This entire file is pretty much GPLed.
If you want to release your csqc mod free from the GPL, do not define OWNPLAYERPHYSICS, and remove this file from your progs.src
*/
#ifdef OWNPLAYERPHYSICS
void PMove_Move(vector dest) //move forwards (preferably on the level) (does step ups)
/*
be very careful about the fields/globals that are read/written in this code.
Using any that are changed elsewhere can and will result in prediction errors.
Any fields that are expected to persist need to be added to csqc code to revert them.
Any fields that are read need to be the same between csqc and ssqc code somehow. Changing such fields will result in brief errors.
*/
#define movevars_stepheight 22
#define movevars_friction 4
#define movevars_gravity 800
#define movevars_accelerate 10
#define movevars_stopspeed 100
#define movevars_maxspeed 320
#define movevars_jumpheight 270
.float pmove_flags;
#ifdef HAVE_DOTGRAVITY
.float gravity;
#endif
enumflags
{
vector src;
float stepped;
tracebox(pmove_org, pmove_mins, pmove_maxs, dest, false, self); //try going straight there
if (trace_fraction < 1)
{ //step up
src = trace_endpos;
trace_endpos_z += movevars_stepheight;
tracebox(src, pmove_mins, pmove_maxs, dest, false, self);
stepped = trace_endpos_z - src_z;
dest_z += stepped;
//move forwards
tracebox(trace_endpos, pmove_mins, pmove_maxs, dest, false, self);
//move down
dest_z -= stepped;
tracebox(trace_endpos, pmove_mins, pmove_maxs, dest, false, self);
}
pmove_org = trace_endpos;
PMF_JUMP_HELD,
PMF_RESERVED,
PMF_ONGROUND
};
void PMove(void)
static void(entity tother) dotouch =
{
entity oself;
//if (tother.touch == __NULL__)
return;
oself = self;
other = self;
self = tother;
self.touch();
self = oself;
};
//this function 'bounces' off any surfaces that were hit
void(vector surfnorm) PMove_Rebound =
{
float v;
v = self.velocity*surfnorm;
self.velocity = self.velocity - surfnorm*(v);
};
void(void) PMove_Move = //move forwards (preferably on the level) (does step ups)
{
vector dest;
vector saved_plane_normal;
float stepped;
float movetime;
float attempts;
//we need to bounce off surfaces (in order to slide along them), so we need at 2 attempts
for (attempts = 3, movetime = input_timelength; movetime>0 && attempts; attempts--)
{
dest = self.origin + self.velocity*movetime;
tracebox(self.origin, self.mins, self.maxs, dest, false, self); //try going straight there
self.origin = trace_endpos;
if (trace_fraction < 1)
{
saved_plane_normal = trace_plane_normal;
movetime -= movetime * trace_fraction;
if (movetime)
{
//step up if we can
trace_endpos = self.origin;
trace_endpos_z += movevars_stepheight;
tracebox(self.origin, self.mins, self.maxs, trace_endpos, false, self);
stepped = trace_endpos_z - self.origin_z;
dest = trace_endpos + self.velocity*movetime;
dest_z = trace_endpos_z;
//move forwards
tracebox(trace_endpos, self.mins, self.maxs, dest, false, self);
//if we got anywhere, make this raised-step move count
if (trace_fraction != 0)
{
if (trace_fraction < 1)
PMove_Rebound(trace_plane_normal);
//move down
dest = trace_endpos;
dest_z -= stepped+1;
tracebox(trace_endpos, self.mins, self.maxs, dest, false, self);
if (trace_fraction < 1)
PMove_Rebound(trace_plane_normal);
self.origin = trace_endpos;
movetime -= movetime * input_timelength;
continue;
}
}
//stepping failed, just bounce off
PMove_Rebound(saved_plane_normal);
dotouch(trace_ent);
}
else
break;
}
};
/*
void(vector dest) PMove_StepMove =
{
//we hit something...
//step up
src = trace_endpos;
trace_endpos_z += movevars_stepheight;
tracebox(src, self.mins, self.maxs, dest, false, self);
stepped = trace_endpos_z - src_z;
dest_z += stepped;
//move forwards
tracebox(trace_endpos, self.mins, self.maxs, dest, false, self);
//move down
dest_z -= stepped;
tracebox(trace_endpos, self.mins, self.maxs, dest, false, self);
}
*/
void() PMove_ApplyFriction =
{
float newspeed, oldspeed;
oldspeed = vlen(self.velocity);
if (oldspeed < 1)
{
self.velocity = '0 0 0';
return;
}
//calculate what their new speed should be
newspeed = oldspeed - oldspeed*movevars_friction*input_timelength;
//and slow them
if (newspeed < 0)
newspeed = 0;
self.velocity = self.velocity * (newspeed/oldspeed);
};
void(vector wishdir, float wishspeed, float accel) PMove_Accelerate =
{
float addspeed, accelspeed;
float d;
d = self.velocity*wishdir;
addspeed = wishspeed - (d);
if (addspeed <= 0)
return;
accelspeed = accel*input_timelength*wishspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
self.velocity = self.velocity + accelspeed*wishdir;
};
void() PMove_InAirAccelerate =
{
vector hforward;
vector hright;
vector desireddir;
float desiredspeed;
hforward = v_forward;
hforward_z = 0;
hforward = normalize(hforward);
hright = v_right;
hright_z = 0;
hright = normalize(hright);
desireddir = hforward*input_movevalues_x + hright*input_movevalues_y;
desiredspeed = vlen(desireddir);
desireddir = normalize(desireddir);
if (desiredspeed > movevars_maxspeed)
desiredspeed = movevars_maxspeed;
if (self.pmove_flags & PMF_ONGROUND)
{
if (input_buttons & 2)
{
if (!(self.pmove_flags & PMF_JUMP_HELD))
{
self.velocity_z += movevars_jumpheight;
self.pmove_flags (+) PMF_ONGROUND;
}
}
}
if (self.pmove_flags & PMF_ONGROUND)
{
PMove_ApplyFriction();
PMove_Accelerate(desireddir, desiredspeed, movevars_accelerate);
}
else
{
//there's no friction in air...
if (desiredspeed > 30)
desiredspeed = 30;
PMove_Accelerate(desireddir, desiredspeed, movevars_accelerate);
#ifdef HAVE_DOTGRAVITY
if (self.gravity)
self.velocity_z -= self.gravity * movevars_gravity * input_timelength;
else
#endif
self.velocity_z -= movevars_gravity * input_timelength;
}
};
void() PMove_NoclipAccelerate =
{
vector desireddir;
float desiredspeed;
desireddir = v_forward*input_movevalues_x + v_right*input_movevalues_y+v_up*input_movevalues_z;
desiredspeed = vlen(desireddir);
desireddir = normalize(desireddir);
PMove_ApplyFriction();
PMove_Accelerate(desireddir, desiredspeed, movevars_accelerate);
};
void() PMove_Categorise =
{
//if we're moving up, we're not on the ground
if (self.velocity_z > 0)
self.pmove_flags (-) PMF_ONGROUND;
else
{
//don't know, maybe we are, maybe we're not
tracebox(self.origin, self.mins, self.maxs, self.origin-'0 0 1', false, self);
if (trace_fraction == 1 || trace_plane_normal_z < 0.7)
{
self.pmove_flags (-) PMF_ONGROUND;
// self.groundentity = trace_ent;
}
else
self.pmove_flags (+) PMF_ONGROUND;
}
};
void(entity ent) PMove =
{
self = ent;
makevectors(input_angles);
if (!(input_buttons & PMF_JUMP_HELD))
self.pmove_flags (-) PMF_JUMP_HELD;
pmove_vel *= movevars_friction*input_timelength;
pmove_vel += input_timelength*(
v_forward * input_movevalues_x +
v_right * input_movevalues_y +
v_up * input_movevalues_z);
PMove_Categorise();
switch(pmove_type)
self.movetype = MOVETYPE_WALK;
switch(self.movetype)
{
case MOVETYPE_WALK:
pmove_vel_z += movevars_gravity*input_timelength;
PMove_Move(pmove_org + pmove_vel*input_timelength);
PMove_InAirAccelerate();
PMove_Move();
break;
case MOVETYPE_FLY:
PMove_NoclipAccelerate();
PMove_Move();
break;
case MOVETYPE_NOCLIP:
pmove_org += pmove_vel*input_timelength;
PMove_NoclipAccelerate();
self.origin += self.velocity*input_timelength;
break;
case MOVETYPE_NONE:
break;

View File

@ -9,15 +9,3 @@ noref float input_impulse;
//float input_weapon;
//float input_servertime;
float pmove_type;
vector pmove_org;
vector pmove_vel;
vector pmove_mins;
vector pmove_maxs;
float pmove_jump_held;
float pmove_waterjumptime;
float movevars_friction;
float movevars_gravity;
const float movevars_stepheight = 22;

View File

@ -62,7 +62,7 @@ string(float keynum) getkeybind = #342; // (EXT_CSQC)
float(float framenum) getinputstate = #345; // (EXT_CSQC)
void(float sens) setsensitivityscaler = #346; // (EXT_CSQC)
void() runstandardplayerphysics = #347; // (EXT_CSQC)
void(entity ent) runstandardplayerphysics = #347; // (EXT_CSQC_1)
string(float playernum, string keyname) getplayerkeyvalue = #348; // (EXT_CSQC)

View File

@ -57,8 +57,8 @@ float(string skinname) Anim_GetHeadSkinNumber;
vector(string skinname) Anim_GetHeadOffset;
//prediction
void() Pred_PlayerUpdated;
void() Pred_UpdateLocalMovement;
void(entity ent) Pred_PlayerUpdated;
void(entity ent) Pred_UpdateLocalMovement;
vector vieworg;
//menu

View File

@ -245,7 +245,6 @@ void() CSQC_Init =
drawloadingscreen ();
Pred_ResetPlayerPrediction();
regcommands();
#ifndef FTEDEPENDANT

View File

@ -5,62 +5,24 @@
string() getentitytoken = #355;
//fixme: I want an engine solution
entity triggerchain;
.entity nexttrigger;
void() pmovetouchtriggers =
void() cs_teleport_touch =
{
vector amin = pmove_org+pmove_mins;
vector amax = pmove_org+pmove_maxs;
vector emin;
vector emax;
entity t;
entity oself = self;
for (self = triggerchain; self; self = self.nexttrigger)
local entity t;
t = find(world, targetname, self.target);
if (t)
{
emin = self.origin+self.mins;
if (emin_x > amax_x)
continue;
if (emin_y > amax_y)
continue;
if (emin_z > amax_z)
continue;
emax = self.origin+self.maxs;
if (emax_x < amin_x)
continue;
if (emax_y < amin_y)
continue;
if (emax_z < amin_z)
continue;
if (self.classname == "trigger_teleport")
{
t = find(world, targetname, self.target);
if (t)
{
makevectors(t.angles);
Pred_Predict_Teleport(t.origin + '0 0 27', v_forward*300, t.angles);
}
//maybe they use a mod that uses some other choice pattern
}
if (self.classname == "trigger_push")
{
//fixme: add support for trigger_push
}
makevectors(t.angles);
Pred_Predict_Teleport(t.origin + '0 0 27', v_forward*300, t.angles);
}
self = oself;
}
};
void() spawn_trigger_teleport =
{
self.solid = SOLID_NOT;//SOLID_TRIGGER;
self.solid = SOLID_TRIGGER;
setmodel(self, self.model);
self.model = "";
self.nexttrigger = triggerchain;
triggerchain = self;
self.touch = cs_teleport_touch;
};
void() spawn_info_teleport_destination =
@ -69,18 +31,16 @@ void() spawn_info_teleport_destination =
void() spawn_trigger_push =
{
self.solid = SOLID_NOT;//SOLID_TRIGGER;
self.solid = SOLID_TRIGGER;
setmodel(self, self.model);
self.model = "";
self.nexttrigger = triggerchain;
triggerchain = self;
// self.touch = cs_teleport_touch;
};
void() spawn_worldspawn =
{
levelname = self.message;
mapname = "that was the mapname, dumbass";
remove(self);
};

View File

@ -30,10 +30,7 @@ static void() Player_Interpolate =
{ //do some frame interpolation.
if (self.entnum == player_localentnum)
{
Pred_UpdateLocalMovement();
self.origin = pmove_org;
self.angles = input_angles;
self.angles_x = self.angles_x * -0.3333;
Pred_UpdateLocalMovement(self);
}
switch(self.modelstyle)
@ -99,6 +96,10 @@ static void(float g) Player_SetLocalInfoGender =
{
if (player_local == self)
{
//if it was forced, don't lie to everyone else.
if (cvar_string("cg_forceskin") != "")
return;
switch(g)
{
case GENDER_FEMALE:
@ -299,10 +300,12 @@ void(float isnew) RefreshPlayer =
JustRessed();
self.haddied = false;
}
self.solid = SOLID_BBOX;
}
else
{
self.haddied = true;
self.solid = SOLID_NOT;
}
self.lastorg = self.origin;
@ -372,7 +375,7 @@ void(float isnew) RefreshPlayer =
return;
}
Pred_PlayerUpdated();
Pred_PlayerUpdated(self);
};
//this is sent after the server has run our movement command.

View File

@ -1,7 +1,7 @@
/*
UpdateLocalMovement: runs the local prediction (called from player.qc - local players are drawn for mirrors etc)
out: vieworg (view origin, with chasecam/viewheight added)
out: pmove_org (raw player ent origin)
out: ` (raw player ent origin)
PlayerUpdated: updates internal state, called from player.qc any time we got an update from the server.
in: self (the player ent origin/velocity/mins/maxs)
@ -14,8 +14,9 @@ ResetPlayerPrediction: call if you broke the special pmove globals
vector player_org;
vector player_vel;
float player_jump_held;
float player_pmflags;
float player_sequence;
float player_steptime;
float player_step;
@ -26,16 +27,16 @@ float pmove_step;
float pmove_steptime;
float pmove_step_oldz;
void() pmovetouchtriggers;
.float pmove_flags;
float pmoveframe;
nonstatic void() Pred_ResetPlayerPrediction =
nonstatic void(entity ent) Pred_ResetPlayerPrediction =
{
//reset the pmove to lerp from the new position
pmove_org = player_org;
pmove_vel = player_vel;
ent.origin = player_org;
ent.velocity = player_vel;
pmoveframe = player_sequence+1; //+1 because the recieved frame has the move already done (server side)
pmove_jump_held = player_jump_held;
ent.pmove_flags = player_pmflags;
if (pmoveframe < clientcommandframe-128)
pmoveframe = clientcommandframe-128; //avoid an infinate loop
@ -54,32 +55,37 @@ void(vector newteleorg, vector newtelevel, vector newteleang) Pred_Predict_Telep
setviewprop(33, newteleang);
view_angles = newteleang;
}
pmove_org = newteleorg;
pmove_vel = newtelevel;
other.origin = newteleorg;
other.velocity = newtelevel;
input_angles = newteleang;
};
void(float endframe) Pred_RunMovement;
void(entity ent, float endframe) Pred_RunMovement;
nonstatic void() Pred_PlayerUpdated =
nonstatic void(entity ent) Pred_PlayerUpdated =
{
local float noerror;
local vector o;
local vector v;
local float pmf;
o = ent.origin;
v = ent.velocity;
pmf = ent.pmove_flags;
noerror = cvar("cg_noerror");
//reset the prediction to last-known-good state
Pred_ResetPlayerPrediction();
Pred_RunMovement(servercommandframe+1);
player_jump_held = pmove_jump_held;
Pred_ResetPlayerPrediction(ent);
Pred_RunMovement(ent, servercommandframe+1);
player_pmflags = ent.pmove_flags;
player_step = pmove_step;
player_steptime = pmove_steptime;
//pull out the new values
player_org = self.origin;
player_vel = self.velocity;
pmove_mins = self.mins;
pmove_maxs = self.maxs;
player_org = o;
player_vel = v;
player_sequence = servercommandframe;
if (noerror)
@ -87,25 +93,25 @@ nonstatic void() Pred_PlayerUpdated =
pmove_error = '0 0 0';
pmove_errortime = time;
Pred_ResetPlayerPrediction();
Pred_ResetPlayerPrediction(ent);
}
else
{
Pred_RunMovement(clientcommandframe); //make sure we're up to date
o = pmove_org; //save off the old for the teleport check below.
Pred_RunMovement(ent, clientcommandframe); //make sure we're up to date
o = ent.origin; //save off the old for the teleport check below.
//reset it, then update to now to guage how much our previous prediction was incorrect by
Pred_ResetPlayerPrediction();
Pred_RunMovement(clientcommandframe);
Pred_ResetPlayerPrediction(ent);
Pred_RunMovement(ent, clientcommandframe);
if (vlen(o - pmove_org) > 64)
if (vlen(o - ent.origin) > 64)
{//teleport
pmove_error = '0 0 0';
pmove_errortime = time;
}
else
{ //figure out the error ammount, and lerp back to it, without forgetting about any current inaccuracies.
pmove_error = (pmove_errortime - time)*ERRORTIME * pmove_error + (o - pmove_org);
pmove_error = (pmove_errortime - time)*ERRORTIME * pmove_error + (o - ent.origin);
if (vlen(pmove_error) < 1)
pmove_error = '0 0 0';
pmove_errortime = time + 1/ERRORTIME;
@ -113,7 +119,7 @@ nonstatic void() Pred_PlayerUpdated =
}
};
void(float endframe) Pred_RunMovement =
void(entity ent, float endframe) Pred_RunMovement =
{
if (servercommandframe >= player_sequence+63)
{
@ -160,9 +166,7 @@ void(float endframe) Pred_RunMovement =
{
break;
}
runstandardplayerphysics();
pmovetouchtriggers();
runstandardplayerphysics(ent);
pmoveframe++;
}
@ -171,13 +175,13 @@ void(float endframe) Pred_RunMovement =
input_angles = view_angles;
};
nonstatic void() Pred_UpdateLocalMovement =
nonstatic void(entity ent) Pred_UpdateLocalMovement =
{
local float viewheight;
Pred_RunMovement(clientcommandframe);
Pred_RunMovement(ent, clientcommandframe);
if (pmove_org_z > pmove_step_oldz+8 && pmove_org_z < pmove_step_oldz+24 && pmove_vel_z == 0)
if (ent.origin_z > pmove_step_oldz+8 && ent.origin_z < pmove_step_oldz+24 && pmove_vel_z == 0)
{
//evaluate out the remaining old step
if (pmove_steptime - time > 0)
@ -186,10 +190,10 @@ nonstatic void() Pred_UpdateLocalMovement =
pmove_step = 0;
//work out the new step
pmove_step += (pmove_step_oldz-pmove_org_z);
pmove_step += (pmove_step_oldz-self.origin_z);
pmove_steptime = time + 1/STEPTIME;
}
pmove_step_oldz = pmove_org_z;
pmove_step_oldz = ent.origin_z;
//allow the user to move the viewheight down 6 units so it's at +16, where projectiles come from.
viewheight = cvar("v_viewheight");
@ -198,7 +202,7 @@ nonstatic void() Pred_UpdateLocalMovement =
else if (viewheight > 7)
viewheight = 7;
vieworg = pmove_org; //the default view height
vieworg = ent.origin; //the default view height
vieworg_z += getstati(STAT_VIEWHEIGHT) + viewheight;
//correct the view position to compensate for any errors, slowly over time, 0.1 seconds.
@ -211,9 +215,10 @@ nonstatic void() Pred_UpdateLocalMovement =
if (chasecam)
{
view_angles_y += 180;
view_angles_y += cvar("cg_thirdPersonAngle");
makevectors(view_angles);
traceline(pmove_org, vieworg - v_forward * 72+v_up*32, TRUE, self);
traceline(self.origin, vieworg - v_forward * cvar("cg_thirdPersonRange")+v_up*cvar("cg_thirdPersonHeight"), TRUE, self);
vieworg = trace_endpos + v_forward*8;
}
};

View File

@ -17,12 +17,12 @@ common/econstants.qc
cs/constants.qc
common/pmovedefs.qc
common/pmove.qc
cs/keys.qc
common/makeallstatic.qc
common/pmove.qc
cs/prediction.qc
cs/q3playerm.qc
cs/hlpm.qc

View File

@ -781,12 +781,15 @@ void() PlayerJump =
if (self.waterlevel >= 2)
{
//pmove code predicts this
/*
if (self.watertype == CONTENT_WATER)
self.velocity_z = 100;
else if (self.watertype == CONTENT_SLIME)
self.velocity_z = 80;
else
self.velocity_z = 50;
*/
// play swiming sound
if (self.swim_flag < time)
@ -814,7 +817,9 @@ void() PlayerJump =
self.button2 = 0;
// player jumping sound
sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
self.velocity_z = self.velocity_z + 270;
//pmove code predicts this
// self.velocity_z = self.velocity_z + 270;
};
@ -1547,44 +1552,11 @@ void(entity targ, entity attacker) ClientObituary =
void() DefaultPlayerPhysics = #347;
void() SV_RunClientCommand =
{
pmove_org = self.origin;
pmove_vel = self.velocity;
pmove_mins = self.mins;
pmove_maxs = self.maxs;
// pmove_jump_held = self.jump_held;
pmove_waterjumptime = self.teleport_time;
//should match the one used by csqc.
#ifdef OWNPLAYERPHYSICS
PMove();
PMove(self);
#else
DefaultPlayerPhysics();
DefaultPlayerPhysics(self);
#endif
self.origin = pmove_org;
self.velocity = pmove_vel;
// self.jump_held = pmove_jump_held;
self.teleport_time = pmove_waterjumptime;
self.waterlevel = 0;//FIXME
self.watertype = 0;//FIXME
self.button0 = (input_buttons & 1);
self.button2 = !!(input_buttons & 2);
/*
self.button3 = !!(input_buttons & 4);
self.button4 = !!(input_buttons & 8);
self.button5 = !!(input_buttons & 16);
self.button6 = !!(input_buttons & 32);
self.button7 = !!(input_buttons & 64);
self.button8 = !!(input_buttons & 128);
*/
self.v_angle = input_angles;
self.angles = input_angles;
self.angles_x *= -1/3;
self.impulse = input_impulse;
//we don't need this DP extension
// self.movement = input_movevalues;
};