]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/movetypes.qc
Network dphitcontentsmask for accurate prediction on clips, almost fix stairs prediction
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / movetypes.qc
index 072f9114cb17c8bacf2a429fe48c1dc329b28755..916e6e0bda6119ce72ed1aabd816c10a10860837 100644 (file)
-const float STAT_MOVEFLAGS = 225;
-const float MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
-#define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
-
-.entity move_groundentity; // FIXME add move_groundnetworkentity?
-.float move_suspendedinair;
-.float move_didgravity;
-
-void _Movetype_CheckVelocity() // SV_CheckVelocity
-{
-}
-
-#if 0
-int Mod_Q1BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents)
-{
-       switch(nativecontents)
-       {
-               case CONTENTS_EMPTY:
-                       return 0;
-               case CONTENTS_SOLID:
-                       return SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
-               case CONTENTS_WATER:
-                       return SUPERCONTENTS_WATER;
-               case CONTENTS_SLIME:
-                       return SUPERCONTENTS_SLIME;
-               case CONTENTS_LAVA:
-                       return SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
-               case CONTENTS_SKY:
-                       return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP | SUPERCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
-       }
-       return 0;
-}
-int Mod_Q1BSP_NativeContentsFromSuperContents(dp_model_t *model, int supercontents)
-{
-       if (supercontents & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY))
-               return CONTENTS_SOLID;
-       if (supercontents & SUPERCONTENTS_SKY)
-               return CONTENTS_SKY;
-       if (supercontents & SUPERCONTENTS_LAVA)
-               return CONTENTS_LAVA;
-       if (supercontents & SUPERCONTENTS_SLIME)
-               return CONTENTS_SLIME;
-       if (supercontents & SUPERCONTENTS_WATER)
-               return CONTENTS_WATER;
-       return CONTENTS_EMPTY;
-}
-static qboolean SV_CheckWater (prvm_edict_t *ent)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int cont;
-       int nNativeContents;
-       vec3_t point;
-
-       point[0] = PRVM_serveredictvector(ent, origin)[0];
-       point[1] = PRVM_serveredictvector(ent, origin)[1];
-       point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
-
-       // DRESK - Support for Entity Contents Transition Event
-       // NOTE: Some logic needed to be slightly re-ordered
-       // to not affect performance and allow for the feature.
-
-       // Acquire Super Contents Prior to Resets
-       cont = SV_PointSuperContents(point);
-       // Acquire Native Contents Here
-       nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
-
-       // DRESK - Support for Entity Contents Transition Event
-       if(PRVM_serveredictfloat(ent, watertype))
-               // Entity did NOT Spawn; Check
-               SV_CheckContentsTransition(ent, nNativeContents);
-
-
-       PRVM_serveredictfloat(ent, waterlevel) = 0;
-       PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
-       cont = SV_PointSuperContents(point);
-       if (cont & (SUPERCONTENTS_LIQUIDSMASK))
-       {
-               PRVM_serveredictfloat(ent, watertype) = nNativeContents;
-               PRVM_serveredictfloat(ent, waterlevel) = 1;
-               point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
-               if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
-               {
-                       PRVM_serveredictfloat(ent, waterlevel) = 2;
-                       point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
-                       if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
-                               PRVM_serveredictfloat(ent, waterlevel) = 3;
-               }
-       }
+#include "../common/physics.qh"
+
+#if defined(CSQC)
+       #include "../dpdefs/csprogsdefs.qh"
+       #include "defs.qh"
+       #include "../common/stats.qh"
+       #include "../common/util.qh"
+       #include "movetypes.qh"
+       #include "../csqcmodellib/common.qh"
+       #include "../server/t_items.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../server/autocvars.qh"
+#endif
 
-       return PRVM_serveredictfloat(ent, waterlevel) > 1;
-}
-static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int bValidFunctionCall;
-
-       // Default Valid Function Call to False
-       bValidFunctionCall = false;
-
-       if(PRVM_serveredictfloat(ent, watertype) != nContents)
-       { // Changed Contents
-               // Acquire Contents Transition Function from QC
-               if(PRVM_serveredictfunction(ent, contentstransition))
-               { // Valid Function; Execute
-                       // Assign Valid Function
-                       bValidFunctionCall = true;
-                       // Prepare Parameters (Original Contents, New Contents)
-                       // Original Contents
-                       PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
-                       // New Contents
-                       PRVM_G_FLOAT(OFS_PARM1) = nContents;
-                       // Assign Self
-                       PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
-                       // Set Time
-                       PRVM_serverglobalfloat(time) = sv.time;
-                       // Execute VM Function
-                       prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
-               }
-       }
 
-       // Return if Function Call was Valid
-       return bValidFunctionCall;
-}
+#ifdef SVQC
+#define GRAVITY_UNAFFECTED_BY_TICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
 
-static void SV_CheckWaterTransition (prvm_edict_t *ent)
-{
-       vec3_t entorigin;
-       prvm_prog_t *prog = SVVM_prog;
-       // LordHavoc: bugfixes in this function are keyed to the sv_gameplayfix_bugfixedcheckwatertransition cvar - if this cvar is 0 then all the original bugs should be reenabled for compatibility
-       int cont;
-       VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
-       cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
-       if (!PRVM_serveredictfloat(ent, watertype))
-       {
-               // just spawned here
-               if (!sv_gameplayfix_fixedcheckwatertransition.integer)
-               {
-                       PRVM_serveredictfloat(ent, watertype) = cont;
-                       PRVM_serveredictfloat(ent, waterlevel) = 1;
-                       return;
-               }
-       }
-       // DRESK - Support for Entity Contents Transition Event
-       // NOTE: Call here BEFORE updating the watertype below,
-       // and suppress watersplash sound if a valid function
-       // call was made to allow for custom "splash" sounds.
-       else if( !SV_CheckContentsTransition(ent, cont) )
-       { // Contents Transition Function Invalid; Potentially Play Water Sound
-               // check if the entity crossed into or out of water
-               if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
-                       SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
-       }
+#define TICRATE sys_frametime
+#elif defined(CSQC)
+#define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
 
-       if (cont <= CONTENTS_WATER)
-       {
-               PRVM_serveredictfloat(ent, watertype) = cont;
-               PRVM_serveredictfloat(ent, waterlevel) = 1;
-       }
-       else
-       {
-               PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
-               PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
-       }
-}
+#define TICRATE ticrate
 #endif
 
-.float watertype;
-.float waterlevel;
-float Mod_Q1BSP_SuperContentsFromNativeContents(float nativecontents)
-{
-       switch(nativecontents)
-       {
-               case CONTENT_EMPTY:
-                       return 0;
-               case CONTENT_SOLID:
-                       return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
-               case CONTENT_WATER:
-                       return DPCONTENTS_WATER;
-               case CONTENT_SLIME:
-                       return DPCONTENTS_SLIME;
-               case CONTENT_LAVA:
-                       return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
-               case CONTENT_SKY:
-                       return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
-       }
-       return 0;
-}
+.entity move_groundentity; // FIXME add move_groundnetworkentity?
+.float move_suspendedinair;
+.float move_didgravity;
 
-float Mod_Q1BSP_NativeContentsFromSuperContents(float supercontents)
+void _Movetype_CheckVelocity() // SV_CheckVelocity
 {
-       if (supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
-               return CONTENT_SOLID;
-       if (supercontents & DPCONTENTS_SKY)
-               return CONTENT_SKY;
-       if (supercontents & DPCONTENTS_LAVA)
-               return CONTENT_LAVA;
-       if (supercontents & DPCONTENTS_SLIME)
-               return CONTENT_SLIME;
-       if (supercontents & DPCONTENTS_WATER)
-               return CONTENT_WATER;
-       return CONTENT_EMPTY;
 }
 
 float _Movetype_CheckWater(entity ent) // SV_CheckWater
 {
-       float contents;
-       float nativecontents;
-       vector point;
-
-       point = ent.move_origin;
+       vector point = ent.move_origin;
        point_z += (ent.mins_z + 1);
 
-       contents = pointcontents(point);
-       nativecontents = Mod_Q1BSP_NativeContentsFromSuperContents(contents);
+       int nativecontents = pointcontents(point);
 
-       if(ent.watertype)
-       if(ent.watertype != nativecontents)
+       if(ent.move_watertype)
+       if(ent.move_watertype != nativecontents)
        {
-               print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.watertype, nativecontents));
-               //ent.contentstransition(ent.watertype, contents);
+               //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
+               if(ent.contentstransition)
+                       ent.contentstransition(ent.move_watertype, nativecontents);
        }
 
-       ent.waterlevel = 0;
-       ent.watertype = CONTENT_EMPTY;
-       
-       contents = pointcontents(point);
-       if(contents & DPCONTENTS_LIQUIDSMASK)
+       ent.move_waterlevel = 0;
+       ent.move_watertype = CONTENT_EMPTY;
+
+       int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
+       if(supercontents & DPCONTENTS_LIQUIDSMASK)
        {
-               ent.watertype = nativecontents;
-               ent.waterlevel = 1;
+               ent.move_watertype = nativecontents;
+               ent.move_waterlevel = 1;
                point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
-               if(pointcontents(point) & DPCONTENTS_LIQUIDSMASK)
+               if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
                {
-                       ent.waterlevel = 2;
+                       ent.move_waterlevel = 2;
                        point_y = ent.origin_y + ent.view_ofs_y;
-                       if(pointcontents(point) & DPCONTENTS_LIQUIDSMASK)
-                               ent.waterlevel = 3;
+                       if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+                               ent.move_waterlevel = 3;
                }
        }
 
-       return (ent.waterlevel > 1);
+       return (ent.move_waterlevel > 1);
 }
 
-#if 0
-void _Movetype_CheckContentsTransition(entity ent, float contents)
-{
-       if(ent.watertype != contents)
-       {
-               print(sprintf("_Movetype_CheckWaterTransition(): Original: '%d', New: '%d'\n", ent.watertype, contents));
-               //ent.contentstransition(ent.watertype, contents);
-       }
-}
-#endif
-
-float autocvar_sv_gameplayfix_fixedcheckwatertransition;
 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
 {
-       float contents = Mod_Q1BSP_NativeContentsFromSuperContents(pointcontents(ent.move_origin));
-       
-       if(!ent.watertype)
+       float contents = pointcontents(ent.move_origin);
+
+       if(!ent.move_watertype)
        {
                // just spawned here
-               if(!autocvar_sv_gameplayfix_fixedcheckwatertransition)
+               if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
                {
-                       ent.watertype = contents;
-                       ent.waterlevel = 1;
+                       ent.move_watertype = contents;
+                       ent.move_waterlevel = 1;
                        return;
                }
        }
-       //else if(!_Movetype_CheckContentsTransition(ent, contents))
-       //{
-               //if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
-               //      SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
-       //}
-       else if(ent.watertype != contents)
+       else if(ent.move_watertype != contents)
        {
-               print(sprintf("_Movetype_CheckWaterTransition(): Original: '%d', New: '%d'\n", ent.watertype, contents));
-               //ent.contentstransition(ent.watertype, contents);
+               //print(sprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents));
+               if(ent.contentstransition)
+                       ent.contentstransition(ent.move_watertype, contents);
        }
 
        if(contents <= CONTENT_WATER)
        {
-               ent.watertype = contents;
-               ent.waterlevel = 1;
+               ent.move_watertype = contents;
+               ent.move_waterlevel = 1;
        }
        else
        {
-               ent.watertype = CONTENT_EMPTY;
-               ent.waterlevel = (autocvar_sv_gameplayfix_fixedcheckwatertransition ? 0 : contents);
+               ent.move_watertype = CONTENT_EMPTY;
+               ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
        }
 }
 
@@ -333,11 +144,11 @@ void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
                        self = e;
                        other = oldself;
 
-                       trace_allsolid = FALSE;
-                       trace_startsolid = FALSE;
+                       trace_allsolid = false;
+                       trace_startsolid = false;
                        trace_fraction = 1;
-                       trace_inwater = FALSE;
-                       trace_inopen = TRUE;
+                       trace_inwater = false;
+                       trace_inopen = true;
                        trace_endpos = e.origin;
                        trace_plane_normal = '0 0 1';
                        trace_plane_dist = 0;
@@ -397,26 +208,25 @@ void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
 {
        vector org;
-       float cont;
        org = self.move_origin + ofs;
 
-       cont = self.dphitcontentsmask;
+       int cont = self.dphitcontentsmask;
        self.dphitcontentsmask = DPCONTENTS_SOLID;
        tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
        self.dphitcontentsmask = cont;
 
        if(trace_startsolid)
-               return TRUE;
+               return true;
 
        if(vlen(trace_endpos - self.move_origin) > 0.0001)
                self.move_origin = trace_endpos;
-       return FALSE;
+       return false;
 }
 
 float _Movetype_UnstickEntity() // SV_UnstickEntity
 {
        if(!_Movetype_TestEntityPosition('0 0 0'))
-               return TRUE;
+               return true;
        if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
        if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
        if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
@@ -431,12 +241,12 @@ float _Movetype_UnstickEntity() // SV_UnstickEntity
                if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
                if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
        }
-       dprint(sprintf(_("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)));
-       return FALSE;
+       dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
+       return false;
 :success
-       dprint(sprintf(_("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)));
-       _Movetype_LinkEdict(TRUE);
-       return TRUE;
+       dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
+       _Movetype_LinkEdict(true);
+       return true;
 }
 
 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
@@ -485,7 +295,239 @@ float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
        return trace_fraction;
 }
 
-#define MAX_CLIP_PLANES 5
+
+.float ltime;
+.void() blocked;
+void _Movetype_AngleVectorsFLU(vector myangles) // AngleVectorsFLU
+{
+       float angle, sr, sp, sy, cr, cp, cy;
+
+       v_forward = v_right = v_up = '0 0 0';
+
+       angle = myangles_y * (M_PI*2 / 360);
+       sy = sin(angle);
+       cy = cos(angle);
+       angle = myangles_x * (M_PI*2 / 360);
+       sp = sin(angle);
+       cp = cos(angle);
+       if(v_forward)
+       {
+               v_forward_x = cp*cy;
+               v_forward_y = cp*sy;
+               v_forward_z = -sp;
+       }
+       if(v_right || v_up)
+       {
+               if(myangles_z)
+               {
+                       angle = myangles_z * (M_PI*2 / 360);
+                       sr = sin(angle);
+                       cr = cos(angle);
+                       if(v_right)
+                       {
+                               v_right_x = sr*sp*cy+cr*-sy;
+                               v_right_y = sr*sp*sy+cr*cy;
+                               v_right_z = sr*cp;
+                       }
+                       if(v_up)
+                       {
+                               v_up_x = cr*sp*cy+-sr*-sy;
+                               v_up_y = cr*sp*sy+-sr*cy;
+                               v_up_z = cr*cp;
+                       }
+               }
+               else
+               {
+                       if(v_right)
+                       {
+                               v_right_x = -sy;
+                               v_right_y = cy;
+                               v_right_z = 0;
+                       }
+                       if(v_up)
+                       {
+                               v_up_x = sp*cy;
+                               v_up_y = sp*sy;
+                               v_up_z = cp;
+                       }
+               }
+       }
+}
+
+void _Movetype_PushMove(float dt) // SV_PushMove
+{
+       bool rotated;
+       int savesolid;
+       float movetime2, pushltime;
+       vector move, move1, moveangle, pushorig, pushang;
+       vector a;
+       vector pivot;
+       entity oldself;
+       entity check;
+
+       if(self.move_velocity == '0 0 0' && self.move_avelocity == '0 0 0')
+       {
+               self.move_ltime += dt;
+               return;
+       }
+
+       switch(self.solid)
+       {
+       // LordHavoc: valid pusher types
+       case SOLID_BSP:
+       case SOLID_BBOX:
+       case SOLID_SLIDEBOX:
+       case SOLID_CORPSE: // LordHavoc: this would be weird...
+               break;
+       // LordHavoc: no collisions
+       case SOLID_NOT:
+       case SOLID_TRIGGER:
+               self.move_origin = self.move_origin + dt * self.move_velocity;
+               self.move_angles = self.move_angles + dt * self.move_avelocity;
+               self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
+               self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
+               self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
+               self.move_ltime += dt;
+               _Movetype_LinkEdict(true);
+               return;
+       default:
+               dprintf("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", self, self.solid);
+               return;
+       }
+
+       rotated = dotproduct(self.move_angles, self.move_angles) + dotproduct(self.move_avelocity, self.move_avelocity) > 0;
+
+       movetime2 = dt;
+
+       move1 = self.move_velocity * movetime2;
+       moveangle = self.move_avelocity * movetime2;
+
+       a = -moveangle;
+       // sets v_forward, v_right and v_up
+       _Movetype_AngleVectorsFLU(a);
+
+       pushorig = self.move_origin;
+       pushang = self.move_angles;
+       pushltime = self.move_ltime;
+
+// move the pusher to its final position
+
+       self.move_origin = self.move_origin + dt * self.move_velocity;
+       self.move_angles = self.move_angles + dt * self.move_avelocity;
+       
+       self.move_ltime += dt;
+       _Movetype_LinkEdict(true);
+
+       savesolid = self.solid;
+
+       if(self.move_movetype != MOVETYPE_FAKEPUSH)
+       for(check = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); check; check = check.chain)
+       {
+               switch(check.move_movetype)
+               {
+               case MOVETYPE_NONE:
+               case MOVETYPE_PUSH:
+               case MOVETYPE_FOLLOW:
+               case MOVETYPE_NOCLIP:
+               case MOVETYPE_FLY_WORLDONLY:
+                       continue;
+               default:
+                       break;
+               }
+
+               if(check.owner == self)
+                       continue;
+
+               if(self.owner == check)
+                       continue;
+
+               pivot = check.mins + 0.5 * (check.maxs - check.mins);
+               //VectorClear(pivot);
+
+               if (rotated)
+               {
+                       vector org2;
+                       vector org = check.move_origin - self.move_origin;
+                       org = org + pivot;
+                       org2_x = dotproduct(org, v_forward);
+                       org2_y = dotproduct(org, v_right);
+                       org2_z = dotproduct(org, v_up);
+                       move = org2 - org;
+                       move = move + move1;
+               }
+               else
+                       move = move1;
+
+               // physics objects need better collisions than this code can do
+               if(check.move_movetype == 32) // MOVETYPE_PHYSICS
+               {
+                       check.move_origin = check.move_origin + move;
+                       oldself = self;
+                       self = check;
+                       _Movetype_LinkEdict(true);
+                       self = oldself;
+                       continue;
+               }
+
+               // try moving the contacted entity
+               self.solid = SOLID_NOT;
+               oldself = self;
+               self = check;
+               if(!_Movetype_PushEntity(move, true))
+               {
+                       self = oldself;
+                       // entity "check" got teleported
+                       check.move_angles_y += trace_fraction * moveangle_y;
+                       self.solid = savesolid;
+                       continue; // pushed enough
+               }
+               self = oldself;
+               // FIXME: turn players specially
+               check.move_angles_y += trace_fraction * moveangle_y;
+               self.solid = savesolid;
+
+               // this trace.fraction < 1 check causes items to fall off of pushers
+               // if they pass under or through a wall
+               // the groundentity check causes items to fall off of ledges
+               if(check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != self))
+                       check.move_flags &= ~FL_ONGROUND;
+       }
+
+       self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
+       self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
+       self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
+}
+
+void _Movetype_Physics_Pusher(float dt) // SV_Physics_Pusher
+{
+       float thinktime, oldltime, movetime;
+
+       oldltime = self.move_ltime;
+
+       thinktime = self.move_nextthink;
+       if(thinktime < self.move_ltime + dt)
+       {
+               movetime = thinktime - self.move_ltime;
+               if(movetime < 0)
+                       movetime = 0;
+       }
+       else
+               movetime = dt;
+
+       if(movetime)
+               // advances self.move_ltime if not blocked
+               _Movetype_PushMove(movetime);
+
+       if(thinktime > oldltime && thinktime <= self.move_ltime)
+       {
+               self.move_nextthink = 0;
+               self.move_time = time;
+               other = world;
+               if(self.move_think)
+                       self.move_think();
+       }
+}
+
 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
 {
        if(self.move_flags & FL_ONGROUND)
@@ -501,7 +543,7 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
                }
        }
 
-       self.move_suspendedinair = FALSE;
+       self.move_suspendedinair = false;
 
        _Movetype_CheckVelocity();
 
@@ -511,16 +553,16 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
                if(GRAVITY_UNAFFECTED_BY_TICRATE)
                {
                        if(self.gravity)
-                               self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                               self.move_velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
                        else
-                               self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
+                               self.move_velocity_z -= 0.5 * dt * PHYS_GRAVITY;
                }
                else
                {
                        if(self.gravity)
-                               self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                               self.move_velocity_z -= dt * self.gravity * PHYS_GRAVITY;
                        else
-                               self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
+                               self.move_velocity_z -= dt * PHYS_GRAVITY;
                }
        }
 
@@ -532,14 +574,14 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
        {
                vector move;
                move = self.move_velocity * movetime;
-               _Movetype_PushEntity(move, TRUE);
+               _Movetype_PushEntity(move, true);
                if(wasfreed(self))
                        return;
 
                if(trace_startsolid)
                {
                        _Movetype_UnstickEntity();
-                       _Movetype_PushEntity(move, FALSE);
+                       _Movetype_PushEntity(move, false);
                        if(wasfreed(self))
                                return;
                }
@@ -561,9 +603,9 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
                        bouncefac = self.move_bounce_factor;     if(!bouncefac)  bouncefac = 0.5;
                        bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
                        if(self.gravity)
-                               bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                               bouncestop *= self.gravity * PHYS_GRAVITY;
                        else
-                               bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
+                               bouncestop *= PHYS_GRAVITY;
 
                        self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
 
@@ -586,7 +628,7 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
                                self.move_flags |= FL_ONGROUND;
                                self.move_groundentity = trace_ent;
                                if(trace_ent.solid == SOLID_BSP)
-                                       self.move_suspendedinair = TRUE;
+                                       self.move_suspendedinair = true;
                                self.move_velocity = '0 0 0';
                                self.move_avelocity = '0 0 0';
                        }
@@ -608,9 +650,9 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
        if(!(self.move_flags & FL_ONGROUND))
        {
                if(self.gravity)
-                       self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                       self.move_velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
                else
-                       self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
+                       self.move_velocity_z -= 0.5 * dt * PHYS_GRAVITY;
        }
 
        _Movetype_CheckWaterTransition(self);
@@ -623,7 +665,7 @@ void _Movetype_Physics_Frame(float movedt)
        {
                case MOVETYPE_PUSH:
                case MOVETYPE_FAKEPUSH:
-                       error("SV_Physics_Pusher not implemented");
+                       _Movetype_Physics_Pusher(movedt);
                        break;
                case MOVETYPE_NONE:
                        break;
@@ -632,9 +674,9 @@ void _Movetype_Physics_Frame(float movedt)
                        break;
                case MOVETYPE_NOCLIP:
                        _Movetype_CheckWater(self);
-                       self.move_origin = self.move_origin + ticrate * self.move_velocity;
-                       self.move_angles = self.move_angles + ticrate * self.move_avelocity;
-                       _Movetype_LinkEdict(FALSE);
+                       self.move_origin = self.move_origin + TICRATE * self.move_velocity;
+                       self.move_angles = self.move_angles + TICRATE * self.move_avelocity;
+                       _Movetype_LinkEdict(false);
                        break;
                case MOVETYPE_STEP:
                        error("SV_Physics_Step not implemented");
@@ -669,12 +711,12 @@ void Movetype_Physics_NoMatchServer() // optimized
        setorigin(self, self.move_origin);
 }
 
-void Movetype_Physics_MatchServer(float sloppy)
+void Movetype_Physics_MatchServer(bool sloppy)
 {
-       Movetype_Physics_MatchTicrate(ticrate, sloppy);
+       Movetype_Physics_MatchTicrate(TICRATE, sloppy);
 }
 
-void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
+void Movetype_Physics_MatchTicrate(float tr, bool sloppy) // SV_Physics_Entity
 {
        float n, i, dt, movedt;
 
@@ -713,16 +755,16 @@ void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
                        if(GRAVITY_UNAFFECTED_BY_TICRATE)
                        {
                                if(self.gravity)
-                                       self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                                       self.velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
                                else
-                                       self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
+                                       self.velocity_z -= 0.5 * dt * PHYS_GRAVITY;
                        }
                        else
                        {
                                if(self.gravity)
-                                       self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                                       self.velocity_z -= dt * self.gravity * PHYS_GRAVITY;
                                else
-                                       self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
+                                       self.velocity_z -= dt * PHYS_GRAVITY;
                        }
                }
 
@@ -744,9 +786,9 @@ void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
                        if(GRAVITY_UNAFFECTED_BY_TICRATE)
                        {
                                if(self.gravity)
-                                       self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
+                                       self.velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
                                else
-                                       self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
+                                       self.velocity_z -= 0.5 * dt * PHYS_GRAVITY;
                        }
                }
        }