]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/lib/csqcmodel/cl_player.qc
Update the view smoothing cvars to Xonotic values in code
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / csqcmodel / cl_player.qc
index 155ad7f9229103526c52881c4a42106aa8827d2a..2af309c02e2a2c364973127c210c70c5f5f4dbb4 100644 (file)
 #include "cl_model.qh"
 #include "common.qh"
 #include "interpolate.qh"
-#include "../../client/defs.qh"
-#include "../../client/main.qh"
-#include "../../common/constants.qh"
-#include "../../common/physics.qh"
-#include "../../common/stats.qh"
-#include "../../common/triggers/trigger/viewloc.qh"
-#include "../../common/util.qh"
-#include "../../common/viewloc.qh"
 
 float autocvar_cl_movement_errorcompensation = 0;
-int autocvar_cl_movement = 1;
+bool autocvar_cl_movement_intermissionrunning = false;
 
 // engine stuff
 float pmove_onground; // weird engine flag we shouldn't really use but have to for now
@@ -51,15 +43,13 @@ float csqcplayer_predictionerrorfactor;
 
 vector CSQCPlayer_GetPredictionErrorO()
 {
-       if(time >= csqcplayer_predictionerrortime)
-               return '0 0 0';
+       if (time >= csqcplayer_predictionerrortime) return '0 0 0';
        return csqcplayer_predictionerroro * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
 }
 
 vector CSQCPlayer_GetPredictionErrorV()
 {
-       if(time >= csqcplayer_predictionerrortime)
-               return '0 0 0';
+       if (time >= csqcplayer_predictionerrortime) return '0 0 0';
        return csqcplayer_predictionerrorv * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
 }
 
@@ -78,7 +68,7 @@ void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff)
                return;
        }
        */
-       if(vlen(o) > 32 || vlen(v) > 192)
+       if(vdist(o, >, 32) || vdist(v, >, 192))
        {
                //printf("TOO BIG: x=%v v=%v\n", o, v);
                return;
@@ -96,90 +86,94 @@ void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff)
        csqcplayer_predictionerrortime = time + 1.0 / csqcplayer_predictionerrorfactor;
 }
 
-void CSQCPlayer_Unpredict()
-{SELFPARAM();
-       if(csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED)
-               return;
-       if(csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED)
-               error("Cannot unpredict in current status");
-       self.origin = csqcplayer_origin;
-       self.velocity = csqcplayer_velocity;
-       csqcplayer_moveframe = csqcplayer_sequence+1; //+1 because the recieved frame has the move already done (server side)
-       self.flags = player_pmflags;
+void CSQCPlayer_Unpredict(entity this)
+{
+       if (csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED) return;
+       if (csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED) LOG_FATALF("Cannot unpredict in current status (%d)", csqcplayer_status);
+       this.origin = csqcplayer_origin;
+       this.velocity = csqcplayer_velocity;
+       csqcplayer_moveframe = csqcplayer_sequence + 1; // + 1 because the recieved frame has the move already done (server side)
+       this.flags = player_pmflags;
 }
 
-void CSQCPlayer_SetMinsMaxs()
-{SELFPARAM();
-       if(self.flags & FL_DUCKED)
+void CSQCPlayer_SetMinsMaxs(entity this)
+{
+       if (IS_DUCKED(this) || !(this.isplayermodel & ISPLAYER_PLAYER))
        {
-               self.mins = PL_CROUCH_MIN;
-               self.maxs = PL_CROUCH_MAX;
-               self.view_ofs = PL_CROUCH_VIEW_OFS;
+               this.mins = PHYS_PL_CROUCH_MIN(this);
+               this.maxs = PHYS_PL_CROUCH_MAX(this);
+               this.view_ofs = PHYS_PL_CROUCH_VIEWOFS(this);
        }
        else
        {
-               self.mins = PL_MIN;
-               self.maxs = PL_MAX;
-               self.view_ofs = PL_VIEW_OFS;
+               this.mins = PHYS_PL_MIN(this);
+               this.maxs = PHYS_PL_MAX(this);
+               this.view_ofs = PHYS_PL_VIEWOFS(this);
        }
 }
 
-void CSQCPlayer_SavePrediction()
-{SELFPARAM();
-       player_pmflags = self.flags;
-       csqcplayer_origin = self.origin;
-       csqcplayer_velocity = self.velocity;
+void CSQCPlayer_SavePrediction(entity this)
+{
+       player_pmflags = this.flags;
+       csqcplayer_origin = this.origin;
+       csqcplayer_velocity = this.velocity;
        csqcplayer_sequence = servercommandframe;
        csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
 }
 
-void CSQC_ClientMovement_PlayerMove_Frame();
+void CSQC_ClientMovement_PlayerMove_Frame(entity this);
 
-void PM_Movement_Move()
-{SELFPARAM();
-       runstandardplayerphysics(self);
-#ifdef CSQC
-       self.flags =
-                       ((self.pmove_flags & PMF_DUCKED) ? FL_DUCKED : 0) |
-                       (!(self.pmove_flags & PMF_JUMP_HELD) ? FL_JUMPRELEASED : 0) |
-                       ((self.pmove_flags & PMF_ONGROUND) ? FL_ONGROUND : 0);
-#endif
-}
-
-void CSQCPlayer_Physics()
+void CSQCPlayer_Physics(entity this)
 {
-       switch(autocvar_cl_movement)
-       {
-               case 1: CSQC_ClientMovement_PlayerMove_Frame(); break;
-               case 2: PM_Movement_Move(); break;
-       }
+       if(!autocvar_cl_movement) { return; }
+
+       _Movetype_CheckWater(this); // we apparently need to check water *before* physics so it can use this for water jump
+
+       vector oldv_angle = this.v_angle;
+       vector oldangles = this.angles; // we need to save these, as they're abused by other code
+       this.v_angle = PHYS_INPUT_ANGLES(this);
+       this.angles = PHYS_WORLD_ANGLES(this);
+
+       CSQC_ClientMovement_PlayerMove_Frame(this);
+
+       Movetype_Physics_NoMatchTicrate(this, PHYS_INPUT_TIMELENGTH, true);
+
+       view_angles = this.v_angle;
+       input_angles = this.angles;
+       this.v_angle = oldv_angle;
+       this.angles = oldangles;
+
+       this.pmove_flags =
+                       ((IS_DUCKED(this)) ? PMF_DUCKED : 0) |
+                       ((IS_JUMP_HELD(this)) ? PMF_JUMP_HELD : 0) |
+                       ((IS_ONGROUND(this)) ? PMF_ONGROUND : 0);
 }
 
-void CSQCPlayer_PredictTo(float endframe, float apply_error)
-{SELFPARAM();
-       CSQCPlayer_Unpredict();
-       if(apply_error)
+void CSQCPlayer_PredictTo(entity this, float endframe, bool apply_error)
+{
+       CSQCPlayer_Unpredict(this);
+       if (apply_error)
        {
-               self.origin += CSQCPlayer_GetPredictionErrorO();
-               self.velocity += CSQCPlayer_GetPredictionErrorV();
+               this.origin += CSQCPlayer_GetPredictionErrorO();
+               this.velocity += CSQCPlayer_GetPredictionErrorV();
        }
-       CSQCPlayer_SetMinsMaxs();
+       CSQCPlayer_SetMinsMaxs(this);
 
        csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
 
 #if 0
        // we don't need this
        // darkplaces makes servercommandframe == 0 in these cases anyway
-       if (getstatf(STAT_HEALTH) <= 0)
+       if (STAT(HEALTH) <= 0)
        {
                csqcplayer_moveframe = clientcommandframe;
                getinputstate(csqcplayer_moveframe-1);
-               LOG_INFO("the Weird code path got hit\n");
+               LOG_INFO("the Weird code path got hit");
                return;
        }
 #endif
 
-       if(csqcplayer_moveframe >= endframe)
+       if (csqcplayer_moveframe >= endframe)
        {
                getinputstate(csqcplayer_moveframe - 1);
        }
@@ -187,178 +181,454 @@ void CSQCPlayer_PredictTo(float endframe, float apply_error)
        {
                do
                {
-                       if (!getinputstate(csqcplayer_moveframe))
-                               break;
-                       CSQCPlayer_Physics();
-                       CSQCPlayer_SetMinsMaxs();
-                       csqcplayer_moveframe++;
+                       if (!getinputstate(csqcplayer_moveframe)) break;
+                       /*if (input_timelength > 0.0005)
+                       {
+                               if (input_timelength > 0.05)
+                               {
+                                       input_timelength /= 2;
+                                       CSQCPlayer_Physics(this);
+                               }
+                               CSQCPlayer_Physics(this);
+                       }*/
+                       CSQCPlayer_Physics(this);
+                       CSQCPlayer_SetMinsMaxs(this);
+                       ++csqcplayer_moveframe;
                }
-               while(csqcplayer_moveframe < endframe);
+               while (csqcplayer_moveframe < endframe);
        }
 
-       //add in anything that was applied after (for low packet rate protocols)
+       // add in anything that was applied after (for low packet rate protocols)
        input_angles = view_angles;
 }
 
-bool CSQCPlayer_IsLocalPlayer()
-{SELFPARAM();
-       return (self == csqcplayer);
+bool CSQCPlayer_IsLocalPlayer(entity this)
+{
+       return (this == csqcplayer);
 }
 
-void CSQCPlayer_SetViewLocation()
+float stairsmoothz;
+float autocvar_cl_stairsmoothspeed = 200;
+float autocvar_cl_smoothviewheight = 0.05;
+float smooth_prevtime;
+float viewheightavg;
+vector CSQCPlayer_ApplySmoothing(entity this, vector v)
 {
-       viewloc_SetViewLocation();
+       float smoothtime = bound(0, time - smooth_prevtime, 0.1);
+       smooth_prevtime = max(smooth_prevtime, drawtime); // drawtime is the previous frame's time at this point
+
+       if(this.csqcmodel_teleported || !(this.pmove_flags & PMF_ONGROUND) || autocvar_cl_stairsmoothspeed <= 0)
+               stairsmoothz = v.z;
+       else
+       {
+               if(stairsmoothz < v.z)
+                       v.z = stairsmoothz = bound(v.z - PHYS_STEPHEIGHT(this), stairsmoothz + smoothtime * autocvar_cl_stairsmoothspeed, v.z);
+               else if(stairsmoothz > v.z)
+                       v.z = stairsmoothz = bound(v.z, stairsmoothz - smoothtime * autocvar_cl_stairsmoothspeed, v.z + PHYS_STEPHEIGHT(this));
+       }
+
+       float viewheight = bound(0, (time - smooth_prevtime) / max(0.0001, autocvar_cl_smoothviewheight), 1);
+       viewheightavg = viewheightavg * (1 - viewheight) + this.view_ofs.z * viewheight;
+       v.z += viewheightavg;
+
+       smooth_prevtime = time;
+
+       return v;
 }
 
-void CSQCPlayer_SetCamera()
-{SELFPARAM();
-       vector v0;
-       v0 = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
+bool autocvar_v_deathtilt;
+float autocvar_v_deathtiltangle;
+void CSQCPlayer_ApplyDeathTilt(entity this)
+{
+       if(!this.csqcmodel_isdead || !autocvar_v_deathtilt)
+               return;
+       view_angles.z = autocvar_v_deathtiltangle;
+}
+
+float autocvar_v_idlescale;
+float autocvar_v_ipitch_cycle;
+float autocvar_v_iyaw_cycle;
+float autocvar_v_iroll_cycle;
+float autocvar_v_ipitch_level;
+float autocvar_v_iyaw_level;
+float autocvar_v_iroll_level;
+void CSQCPlayer_ApplyIdleScaling(entity this)
+{
+       if(!autocvar_v_idlescale)
+               return;
+       view_angles.x += autocvar_v_idlescale * sin(time * autocvar_v_ipitch_cycle) * autocvar_v_ipitch_level;
+       view_angles.y += autocvar_v_idlescale * sin(time * autocvar_v_iyaw_cycle) * autocvar_v_iyaw_level;
+       view_angles.z += autocvar_v_idlescale * sin(time * autocvar_v_iroll_cycle) * autocvar_v_iroll_level;
+       //setproperty(VF_CL_VIEWANGLES, view_angles); // update view angles as well so we can aim
+}
+
+float autocvar_cl_bob = 0;
+float autocvar_cl_bobcycle = 0.5;
+float autocvar_cl_bob_limit = 7;
+float autocvar_cl_bob_limit_heightcheck = 0;
+float autocvar_cl_bob_velocity_limit = 400;
+float autocvar_cl_bobup = 0.5;
+float autocvar_cl_bobfall = 0.05;
+float autocvar_cl_bobfallcycle = 3;
+float autocvar_cl_bobfallminspeed = 200;
+float autocvar_cl_bob2 = 0;
+float autocvar_cl_bob2cycle = 1;
+float autocvar_cl_bob2smooth = 0.05;
+float bobfall_swing;
+float bobfall_speed;
+float bob2_smooth;
+vector CSQCPlayer_ApplyBobbing(entity this, vector v)
+{
+       if(this.csqcmodel_isdead)
+               return v;
 
-       if(csqcplayer)
+       // bounded XY speed, used by several effects below
+       float bob, cycle;
+
+       // vertical view bobbing code
+       if(autocvar_cl_bob && autocvar_cl_bobcycle)
        {
-               setself(csqcplayer);
+               float bob_limit = autocvar_cl_bob_limit;
 
-               if(servercommandframe == 0 || clientcommandframe == 0)
+               if(autocvar_cl_bob_limit_heightcheck)
                {
-                       InterpolateOrigin_Do();
-                       self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+                       // use traces to determine what range the view can bob in, and scale down the bob as needed
+                       vector bob_height_check_dest = v;
+                       bob_height_check_dest.z += autocvar_cl_bob_limit * 1.1;
+                       traceline(v, bob_height_check_dest, MOVE_NOMONSTERS, NULL);
+                       float trace1fraction = trace_fraction;
+
+                       bob_height_check_dest = v;
+                       bob_height_check_dest.z += autocvar_cl_bob_limit * -0.5;
+                       traceline(v, bob_height_check_dest, MOVE_NOMONSTERS, NULL);
+                       float trace2fraction = trace_fraction;
+
+                       bob_limit *= min(trace1fraction, trace2fraction);
+               }
+
+               // LordHavoc: figured out bobup: the time at which the sin is at 180
+               // degrees (which allows lengthening or squishing the peak or valley)
+               cycle = time / autocvar_cl_bobcycle;
+               cycle -= rint(cycle);
+               if(cycle < autocvar_cl_bobup)
+                       cycle = sin(M_PI * cycle / autocvar_cl_bobup);
+               else
+                       cycle = sin(M_PI + M_PI * (cycle - autocvar_cl_bobup) / (1.0 - autocvar_cl_bobup));
+               // bob is proportional to velocity in the xy plane
+               // (don't count Z, or jumping messes it up)
+               float xyspeed = bound(0, sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y), autocvar_cl_bob_velocity_limit);
+               bob = xyspeed * autocvar_cl_bob;
+               bob = bound(0, bob, bob_limit);
+               bob = bob * 0.3 + bob * 0.7 * cycle;
+               v.z += bob;
+       }
+
+       // horizontal view bobbing code
+       if(autocvar_cl_bob2 && autocvar_cl_bob2cycle)
+       {
+               cycle = time / autocvar_cl_bob2cycle;
+               cycle -= rint(cycle);
+               if(cycle < 0.5)
+                       cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin
+               else
+                       cycle = cos(M_PI + M_PI * (cycle - 0.5) / 0.5);
+               bob = autocvar_cl_bob2 * cycle;
+
+               // this value slowly decreases from 1 to 0 when we stop touching the ground.
+               // The cycle is later multiplied with it so the view smooths back to normal
+               if(IS_ONGROUND(this) && !(input_buttons & BIT(1))) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping
+                       bob2_smooth = 1;
+               else
+               {
+                       if(bob2_smooth > 0)
+                               bob2_smooth -= bound(0, autocvar_cl_bob2smooth, 1);
+                       else
+                               bob2_smooth = 0;
+               }
+
+               // calculate the front and side of the player between the X and Y axes
+               makevectors(view_angles);
+               // now get the speed based on those angles. The bounds should match the same value as xyspeed's
+               float side = bound(-autocvar_cl_bob_velocity_limit, (this.velocity * v_right) * bob2_smooth, autocvar_cl_bob_velocity_limit);
+               float front = bound(-autocvar_cl_bob_velocity_limit, (this.velocity * v_forward) * bob2_smooth, autocvar_cl_bob_velocity_limit);
+               v_forward = v_forward * bob;
+               v_right = v_right * bob;
+               // we use side with forward and front with right, so the bobbing goes
+               // to the side when we walk forward and to the front when we strafe
+               vector bob2vel;
+               bob2vel.x = side * v_forward.x + front * v_right.x + 0 * v_up.x;
+               bob2vel.y = side * v_forward.y + front * v_right.y + 0 * v_up.y;
+               bob2vel.z = side * v_forward.z + front * v_right.z + 0 * v_up.z;
+               v.x += bob2vel.x;
+               v.y += bob2vel.y;
+       }
+
+       // fall bobbing code
+       // causes the view to swing down and back up when touching the ground
+       if(autocvar_cl_bobfall && autocvar_cl_bobfallcycle)
+       {
+               if(!IS_ONGROUND(this))
+               {
+                       bobfall_speed = bound(-400, this.velocity.z, 0) * bound(0, autocvar_cl_bobfall, 0.1);
+                       if(this.velocity.z < -autocvar_cl_bobfallminspeed)
+                               bobfall_swing = 1;
+                       else
+                               bobfall_swing = 0; // really?
+               }
+               else
+               {
+                       bobfall_swing = max(0, bobfall_swing - autocvar_cl_bobfallcycle * frametime);
+                       float bobfall = sin(M_PI * bobfall_swing) * bobfall_speed;
+                       v.z += bobfall;
+               }
+       }
+
+       return v;
+}
+
+float autocvar_cl_rollangle;
+float autocvar_cl_rollspeed;
+float CSQCPlayer_CalcRoll(entity this)
+{
+       makevectors(view_angles);
+       float side = (this.velocity * v_right);
+       float sign = (side < 0) ? -1 : 1;
+       side = fabs(side);
+
+       if(side < autocvar_cl_rollspeed)
+               side = side * autocvar_cl_rollangle / autocvar_cl_rollspeed;
+       else
+               side = autocvar_cl_rollangle;
+
+       return side * sign;
+}
+
+float autocvar_chase_back;
+float autocvar_chase_up;
+bool autocvar_chase_overhead;
+float autocvar_chase_pitchangle;
+vector CSQCPlayer_ApplyChase(entity this, vector v)
+{
+       vector forward;
+       vector chase_dest;
+
+       if(autocvar_chase_overhead)
+       {
+               view_angles.x = 0;
+               makevectors(view_angles);
+               forward = v_forward;
+               vector up = v_up;
+               // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
+               chase_dest.x = v.x - forward.x * autocvar_chase_back + up.x * autocvar_chase_up;
+               chase_dest.y = v.y - forward.y * autocvar_chase_back + up.y * autocvar_chase_up;
+               chase_dest.z = v.z - forward.z * autocvar_chase_back + up.z * autocvar_chase_up;
+
+               // trace from first person view location to our chosen third person view location
+               traceline(v, chase_dest, MOVE_NOMONSTERS, NULL);
+
+               vector bestvieworg = trace_endpos;
+               vector offset = '0 0 0';
+               for(offset.x = -16; offset.x <= 16; offset.x += 8)
+               {
+                       for(offset.y = -16; offset.y <= 16; offset.y += 8)
+                       {
+                               makevectors(view_angles);
+                               up = v_up;
+                               chase_dest.x = v.x - forward.x * autocvar_chase_back + up.x * autocvar_chase_up + offset.x;
+                               chase_dest.y = v.y - forward.y * autocvar_chase_back + up.y * autocvar_chase_up + offset.y;
+                               chase_dest.z = v.z - forward.z * autocvar_chase_back + up.z * autocvar_chase_up + offset.z;
+                               traceline(v, chase_dest, MOVE_NOMONSTERS, NULL);
+                               if(bestvieworg.z > trace_endpos.z)
+                                       bestvieworg.z = trace_endpos.z;
+                       }
+               }
+               bestvieworg.z -= 8;
+               v = bestvieworg;
+
+               view_angles.x = autocvar_chase_pitchangle;
+               //setproperty(VF_CL_VIEWANGLES, view_angles); // update view angles as well so we can aim
+       }
+       else
+       {
+               makevectors(view_angles);
+               forward = v_forward;
+               // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
+               float cdist = -autocvar_chase_back - 8;
+               chase_dest.x = v.x + forward.x * cdist;
+               chase_dest.y = v.y + forward.y * cdist;
+               chase_dest.z = v.z + forward.z * cdist + autocvar_chase_up;
+               traceline(v, chase_dest, MOVE_NOMONSTERS, NULL);
+               v.x = 1 * trace_endpos.x + 8 * forward.x + 4 * trace_plane_normal.x;
+               v.y = 1 * trace_endpos.y + 8 * forward.y + 4 * trace_plane_normal.y;
+               v.z = 1 * trace_endpos.z + 8 * forward.z + 4 * trace_plane_normal.z;
+       }
+
+#if 0
+       tracebox(v, '-4 -4 -4', '4 4 4', v - v_forward * autocvar_chase_back, MOVE_NORMAL, this);
+       v = trace_endpos;
+       tracebox(v, '-4 -4 -4', '4 4 4', v + v_up * autocvar_chase_up, MOVE_NORMAL, this);
+       v = trace_endpos;
+#endif
+       return v;
+}
+
+void CSQCPlayer_CalcRefdef(entity this)
+{
+       vector vieworg = this.origin;
+       if(intermission)
+       {
+               // just update view offset, don't need to do anything else
+               vieworg.z += this.view_ofs.z;
+       }
+       else
+       {
+               vieworg = CSQCPlayer_ApplySmoothing(this, vieworg);
+               if(autocvar_chase_active)
+                       vieworg = CSQCPlayer_ApplyChase(this, vieworg);
+               else
+               {
+                       // angles
+                       CSQCPlayer_ApplyDeathTilt(this);
+                       view_angles = view_angles + view_punchangle;
+                       view_angles.z += CSQCPlayer_CalcRoll(this);
+                       // TODO? we don't have damage time accessible here
+                       // origin
+                       vieworg = vieworg + view_punchvector;
+                       vieworg = CSQCPlayer_ApplyBobbing(this, vieworg);
+               }
+               CSQCPlayer_ApplyIdleScaling(this);
+       }
+       setproperty(VF_ORIGIN, vieworg);
+       setproperty(VF_ANGLES, view_angles);
+}
+
+bool autocvar_cl_useenginerefdef = true;
+
+/** Called once per CSQC_UpdateView() */
+void CSQCPlayer_SetCamera()
+{
+       vector v0 = ((intermission && !autocvar_cl_movement_intermissionrunning) ? '0 0 0' : pmove_vel); // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
+       float vh = PHYS_VIEWHEIGHT(NULL);
+       vector pl_viewofs = PHYS_PL_VIEWOFS(NULL);
+       vector pl_viewofs_crouch = PHYS_PL_CROUCH_VIEWOFS(NULL);
+       entity e = csqcplayer;
+       if (e)
+       {
+               if (servercommandframe == 0 || clientcommandframe == 0)
+               {
+                       InterpolateOrigin_Do(e);
+                       e.view_ofs = '0 0 1' * vh;
 
                        // get crouch state from the server
-                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
-                               self.flags &= ~FL_DUCKED;
-                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
-                               self.flags |= FL_DUCKED;
+                       if (vh == pl_viewofs.z) e.flags &= ~FL_DUCKED;
+                       else if (vh == pl_viewofs_crouch.z) e.flags |= FL_DUCKED;
 
                        // get onground state from the server
-                       if(pmove_onground)
-                               self.flags |= FL_ONGROUND;
-                       else
-                               self.flags &= ~FL_ONGROUND;
+                       e.flags = BITSET(e.flags, FL_ONGROUND, pmove_onground);
 
-                       CSQCPlayer_SetMinsMaxs();
+                       CSQCPlayer_SetMinsMaxs(e);
 
                        // override it back just in case
-                       self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+                       e.view_ofs = '0 0 1' * vh;
 
                        // set velocity
-                       self.velocity = v0;
+                       e.velocity = v0;
                }
                else
                {
-                       float flg = self.iflags;
-                       self.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES);
-                       InterpolateOrigin_Do();
-                       self.iflags = flg;
+                       int flg = e.iflags; e.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES);
+                       InterpolateOrigin_Do(e);
+                       e.iflags = flg;
 
-                       if(csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
+                       if (csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
                        {
-                               vector o, v;
-                               o = self.origin;
-                               v = v0;
+                               vector o = e.origin;
                                csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
-                               CSQCPlayer_PredictTo(servercommandframe + 1, false);
-                               CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v, pmove_onground - !!(self.flags & FL_ONGROUND));
-                               self.origin = o;
-                               self.velocity = v;
+                               CSQCPlayer_PredictTo(e, servercommandframe + 1, false);
+                               CSQCPlayer_SetPredictionError(e.origin - o, e.velocity - v0, pmove_onground - IS_ONGROUND(e));
+                               e.origin = o;
+                               e.velocity = v0;
 
                                // get crouch state from the server
-                               if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
-                                       self.flags &= ~FL_DUCKED;
-                               else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
-                                       self.flags |= FL_DUCKED;
+                               if (vh == pl_viewofs.z) e.flags &= ~FL_DUCKED;
+                               else if(vh == pl_viewofs_crouch.z) e.flags |= FL_DUCKED;
 
                                // get onground state from the server
-                               if(pmove_onground)
-                                       self.flags |= FL_ONGROUND;
-                               else
-                                       self.flags &= ~FL_ONGROUND;
+                               e.flags = BITSET(e.flags, FL_ONGROUND, pmove_onground);
 
-                               CSQCPlayer_SavePrediction();
+                               CSQCPlayer_SavePrediction(e);
                        }
-                       CSQCPlayer_PredictTo(clientcommandframe + 1, true);
+                       CSQCPlayer_PredictTo(e, clientcommandframe + 1, true);
 
 #ifdef CSQCMODEL_SERVERSIDE_CROUCH
                        // get crouch state from the server (LAG)
-                       if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
-                               self.flags &= ~FL_DUCKED;
-                       else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
-                               self.flags |= FL_DUCKED;
+                       if (vh == pl_viewofs.z) e.flags &= ~FL_DUCKED;
+                       else if (vh == pl_viewofs_crouch.z) e.flags |= FL_DUCKED;
 #endif
+                       CSQCPlayer_SetMinsMaxs(e);
 
-                       CSQCPlayer_SetMinsMaxs();
-
-                       self.angles_y = input_angles.y;
+                       e.angles_y = input_angles.y;
                }
 
                // relink
-               setorigin(self, self.origin);
-
-               setself(this);
-       }
-
-       entity view = CSQCModel_server2csqc(player_localentnum);
-
-       if(view && view != csqcplayer)
-       {
-               WITH(entity, self, view, InterpolateOrigin_Do());
-               view.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+               setorigin(e, e.origin);
        }
 
-       if(view)
+       const entity view = CSQCModel_server2csqc(player_localentnum - 1);
+       if (view)
        {
-               int refdefflags = 0;
-
-               if(view.csqcmodel_teleported)
-                       refdefflags |= REFDEFFLAG_TELEPORTED;
-
-               if(input_buttons & 4)
-                       refdefflags |= REFDEFFLAG_JUMPING;
-
-               // note: these two only work in WIP2, but are harmless in WIP1
-               if(getstati(STAT_HEALTH) <= 0)
-                       refdefflags |= REFDEFFLAG_DEAD;
-
-               if(intermission)
-                       refdefflags |= REFDEFFLAG_INTERMISSION;
-
-               V_CalcRefdef(view, refdefflags);
+               if (view != csqcplayer)
+               {
+                       InterpolateOrigin_Do(view);
+                       view.view_ofs = '0 0 1' * vh;
+               }
+               if(autocvar_cl_useenginerefdef)
+               {
+                       int refdefflags = 0;
+                       if (view.csqcmodel_teleported) refdefflags |= REFDEFFLAG_TELEPORTED;
+                       if (input_buttons & BIT(1)) refdefflags |= REFDEFFLAG_JUMPING;
+                       // note: these two only work in WIP2, but are harmless in WIP1
+                       if (PHYS_HEALTH(NULL) <= 0 && PHYS_HEALTH(NULL) != -666 && PHYS_HEALTH(NULL) != -2342) refdefflags |= REFDEFFLAG_DEAD;
+                       if (intermission) refdefflags |= REFDEFFLAG_INTERMISSION;
+                       V_CalcRefdef(view, refdefflags); // TODO? uses .health stat in the engine when this isn't called here, may be broken!
+               }
+               else
+               {
+                       CSQCPlayer_CalcRefdef(view);
+               }
+                       
        }
        else
        {
                // FIXME by CSQC spec we have to do this:
                // but it breaks chase cam
                /*
-               setproperty(VF_ORIGIN, pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT));
+               setproperty(VF_ORIGIN, pmove_org + '0 0 1' * vh);
                setproperty(VF_ANGLES, view_angles);
                */
        }
-
-       { CSQCPLAYER_HOOK_POSTCAMERASETUP }
+       CSQCPLAYER_HOOK_POSTCAMERASETUP();
 }
 
-void CSQCPlayer_Remove()
+void CSQCPlayer_Remove(entity this)
 {
-       csqcplayer = world;
+       csqcplayer = NULL;
        cvar_settemp("cl_movement_replay", "1");
 }
 
-float CSQCPlayer_PreUpdate()
-{SELFPARAM();
-       if(self != csqcplayer)
-               return 0;
-       if(csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER)
-               CSQCPlayer_Unpredict();
-       return 1;
+bool CSQCPlayer_PreUpdate(entity this)
+{
+       if (this != csqcplayer) return false;
+       if (csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER) CSQCPlayer_Unpredict(this);
+       return true;
 }
 
-float CSQCPlayer_PostUpdate()
-{SELFPARAM();
-       if(self.entnum != player_localnum + 1)
-               return 0;
-       csqcplayer = self;
+bool CSQCPlayer_PostUpdate(entity this)
+{
+       if (this.entnum != player_localnum + 1) return false;
+       csqcplayer = this;
        csqcplayer_status = CSQCPLAYERSTATUS_FROMSERVER;
        cvar_settemp("cl_movement_replay", "0");
-       self.entremove = CSQCPlayer_Remove;
-       return 1;
+       this.entremove = CSQCPlayer_Remove;
+       return true;
 }