]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_phys.c
added prvm_offsets.h which centralizes field/global/function lookups for
[xonotic/darkplaces.git] / sv_phys.c
index 507168c571fc8bbea6e958756e437949b3d0a065..343b1dc9c124b6a25280c5a443e46ab8b451f842 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -43,6 +43,24 @@ solid_edge items only clip against bsp models.
 
 void SV_Physics_Toss (prvm_edict_t *ent);
 
+int SV_GetPitchSign(prvm_edict_t *ent)
+{
+       dp_model_t *model;
+       if (
+                       (model = SV_GetModelFromEdict(ent))
+                       ?
+                       model->type == mod_alias
+                       :
+                       (
+                        (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
+                        ||
+                        ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
+                       )
+          )
+               return -1;
+       return 1;
+}
+
 /*
 ===============================================================================
 
@@ -53,12 +71,11 @@ LINE TESTING IN HULLS
 
 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
 {
-       prvm_eval_t *val;
        if (passedict)
        {
-               val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
-               if (val && val->_float)
-                       return (int)val->_float;
+               int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
+               if (dphitcontentsmask)
+                       return dphitcontentsmask;
                else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
                {
                        if ((int)passedict->fields.server->flags & FL_MONSTER)
@@ -77,6 +94,317 @@ int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
                return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
 }
 
+/*
+==================
+SV_TracePoint
+==================
+*/
+trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+{
+       int i, bodysupercontents;
+       int passedictprog;
+       float pitchsign = 1;
+       prvm_edict_t *traceowner, *touch;
+       trace_t trace;
+       // bounding box of entire move area
+       vec3_t clipboxmins, clipboxmaxs;
+       // size when clipping against monsters
+       vec3_t clipmins2, clipmaxs2;
+       // start and end origin of move
+       vec3_t clipstart;
+       // trace results
+       trace_t cliptrace;
+       // matrices to transform into/out of other entity's space
+       matrix4x4_t matrix, imatrix;
+       // model of other entity
+       dp_model_t *model;
+       // list of entities to test for collisions
+       int numtouchedicts;
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
+
+       //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
+
+       VectorCopy(start, clipstart);
+       VectorClear(clipmins2);
+       VectorClear(clipmaxs2);
+#if COLLISIONPARANOID >= 3
+       Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
+#endif
+
+       // clip to world
+       Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
+       cliptrace.bmodelstartsolid = cliptrace.startsolid;
+       if (cliptrace.startsolid || cliptrace.fraction < 1)
+               cliptrace.ent = prog->edicts;
+       if (type == MOVE_WORLDONLY)
+               goto finished;
+
+       if (type == MOVE_MISSILE)
+       {
+               // LordHavoc: modified this, was = -15, now -= 15
+               for (i = 0;i < 3;i++)
+               {
+                       clipmins2[i] -= 15;
+                       clipmaxs2[i] += 15;
+               }
+       }
+
+       // create the bounding box of the entire move
+       for (i = 0;i < 3;i++)
+       {
+               clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
+               clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
+       }
+
+       // debug override to test against everything
+       if (sv_debugmove.integer)
+       {
+               clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
+               clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
+       }
+
+       // if the passedict is world, make it NULL (to avoid two checks each time)
+       if (passedict == prog->edicts)
+               passedict = NULL;
+       // precalculate prog value for passedict for comparisons
+       passedictprog = PRVM_EDICT_TO_PROG(passedict);
+       // precalculate passedict's owner edict pointer for comparisons
+       traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
+
+       // clip to entities
+       // because this uses World_EntitiestoBox, we know all entity boxes overlap
+       // the clip region, so we can skip culling checks in the loop below
+       numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
+       if (numtouchedicts > MAX_EDICTS)
+       {
+               // this never happens
+               Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+               numtouchedicts = MAX_EDICTS;
+       }
+       for (i = 0;i < numtouchedicts;i++)
+       {
+               touch = touchedicts[i];
+
+               if (touch->fields.server->solid < SOLID_BBOX)
+                       continue;
+               if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
+                       continue;
+
+               if (passedict)
+               {
+                       // don't clip against self
+                       if (passedict == touch)
+                               continue;
+                       // don't clip owned entities against owner
+                       if (traceowner == touch)
+                               continue;
+                       // don't clip owner against owned entities
+                       if (passedictprog == touch->fields.server->owner)
+                               continue;
+                       // don't clip points against points (they can't collide)
+                       if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
+                               continue;
+               }
+
+               bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
+
+               // might interact, so do an exact clip
+               model = NULL;
+               if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
+               {
+                       model = SV_GetModelFromEdict(touch);
+                       pitchsign = SV_GetPitchSign(touch);
+               }
+               if (model)
+                       Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
+               else
+                       Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
+               Matrix4x4_Invert_Simple(&imatrix, &matrix);
+               VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
+               VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
+               if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
+               else
+                       Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
+
+               Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
+       }
+
+finished:
+       return cliptrace;
+}
+
+/*
+==================
+SV_TraceLine
+==================
+*/
+#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
+trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+#else
+trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+#endif
+{
+       int i, bodysupercontents;
+       int passedictprog;
+       float pitchsign = 1;
+       prvm_edict_t *traceowner, *touch;
+       trace_t trace;
+       // bounding box of entire move area
+       vec3_t clipboxmins, clipboxmaxs;
+       // size when clipping against monsters
+       vec3_t clipmins2, clipmaxs2;
+       // start and end origin of move
+       vec3_t clipstart, clipend;
+       // trace results
+       trace_t cliptrace;
+       // matrices to transform into/out of other entity's space
+       matrix4x4_t matrix, imatrix;
+       // model of other entity
+       dp_model_t *model;
+       // list of entities to test for collisions
+       int numtouchedicts;
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
+#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
+       vec3_t end;
+       vec_t len = 0;
+
+       if (VectorCompare(start, pEnd))
+               return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
+
+       if(collision_endposnudge.value > 0)
+       {
+               // TRICK: make the trace 1 qu longer!
+               VectorSubtract(pEnd, start, end);
+               len = VectorNormalizeLength(end);
+               VectorMA(pEnd, collision_endposnudge.value, end, end);
+       }
+       else
+               VectorCopy(pEnd, end);
+#else
+       if (VectorCompare(start, end))
+               return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
+#endif
+
+       //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
+
+       VectorCopy(start, clipstart);
+       VectorCopy(end, clipend);
+       VectorClear(clipmins2);
+       VectorClear(clipmaxs2);
+#if COLLISIONPARANOID >= 3
+       Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
+#endif
+
+       // clip to world
+       Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
+       cliptrace.bmodelstartsolid = cliptrace.startsolid;
+       if (cliptrace.startsolid || cliptrace.fraction < 1)
+               cliptrace.ent = prog->edicts;
+       if (type == MOVE_WORLDONLY)
+               goto finished;
+
+       if (type == MOVE_MISSILE)
+       {
+               // LordHavoc: modified this, was = -15, now -= 15
+               for (i = 0;i < 3;i++)
+               {
+                       clipmins2[i] -= 15;
+                       clipmaxs2[i] += 15;
+               }
+       }
+
+       // create the bounding box of the entire move
+       for (i = 0;i < 3;i++)
+       {
+               clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
+               clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
+       }
+
+       // debug override to test against everything
+       if (sv_debugmove.integer)
+       {
+               clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
+               clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
+       }
+
+       // if the passedict is world, make it NULL (to avoid two checks each time)
+       if (passedict == prog->edicts)
+               passedict = NULL;
+       // precalculate prog value for passedict for comparisons
+       passedictprog = PRVM_EDICT_TO_PROG(passedict);
+       // precalculate passedict's owner edict pointer for comparisons
+       traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
+
+       // clip to entities
+       // because this uses World_EntitiestoBox, we know all entity boxes overlap
+       // the clip region, so we can skip culling checks in the loop below
+       numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
+       if (numtouchedicts > MAX_EDICTS)
+       {
+               // this never happens
+               Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+               numtouchedicts = MAX_EDICTS;
+       }
+       for (i = 0;i < numtouchedicts;i++)
+       {
+               touch = touchedicts[i];
+
+               if (touch->fields.server->solid < SOLID_BBOX)
+                       continue;
+               if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
+                       continue;
+
+               if (passedict)
+               {
+                       // don't clip against self
+                       if (passedict == touch)
+                               continue;
+                       // don't clip owned entities against owner
+                       if (traceowner == touch)
+                               continue;
+                       // don't clip owner against owned entities
+                       if (passedictprog == touch->fields.server->owner)
+                               continue;
+                       // don't clip points against points (they can't collide)
+                       if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
+                               continue;
+               }
+
+               bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
+
+               // might interact, so do an exact clip
+               model = NULL;
+               if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
+               {
+                       model = SV_GetModelFromEdict(touch);
+                       pitchsign = SV_GetPitchSign(touch);
+               }
+               if (model)
+                       Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
+               else
+                       Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
+               Matrix4x4_Invert_Simple(&imatrix, &matrix);
+               VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
+               VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
+               if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+               else
+                       Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, false);
+
+               Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
+       }
+
+finished:
+#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
+       if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
+               Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
+#endif
+       return cliptrace;
+}
+
 /*
 ==================
 SV_Move
@@ -84,15 +412,15 @@ SV_Move
 */
 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
 #if COLLISIONPARANOID >= 1
-trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
 #else
-trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
 #endif
 #else
 #if COLLISIONPARANOID >= 1
-trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
 #else
-trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
 #endif
 #endif
 {
@@ -119,20 +447,46 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const
        dp_model_t *model;
        // list of entities to test for collisions
        int numtouchedicts;
-       prvm_edict_t *touchedicts[MAX_EDICTS];
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
        vec3_t end;
        vec_t len = 0;
 
-       if(!VectorCompare(start, pEnd))
+       if (VectorCompare(mins, maxs))
+       {
+               vec3_t shiftstart, shiftend;
+               VectorAdd(start, mins, shiftstart);
+               VectorAdd(pEnd, mins, shiftend);
+               if (VectorCompare(start, pEnd))
+                       trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
+               else
+                       trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
+               VectorSubtract(trace.endpos, mins, trace.endpos);
+               return trace;
+       }
+
+       if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
        {
                // TRICK: make the trace 1 qu longer!
                VectorSubtract(pEnd, start, end);
                len = VectorNormalizeLength(end);
-               VectorAdd(pEnd, end, end);
+               VectorMA(pEnd, collision_endposnudge.value, end, end);
        }
        else
                VectorCopy(pEnd, end);
+#else
+       if (VectorCompare(mins, maxs))
+       {
+               vec3_t shiftstart, shiftend;
+               VectorAdd(start, mins, shiftstart);
+               VectorAdd(end, mins, shiftend);
+               if (VectorCompare(start, end))
+                       trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
+               else
+                       trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
+               VectorSubtract(trace.endpos, mins, trace.endpos);
+               return trace;
+       }
 #endif
 
        VectorCopy(start, clipstart);
@@ -237,56 +591,44 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const
                model = NULL;
                if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
                {
-                       unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = sv.models[(int)touch->fields.server->modelindex];
-                       //pitchsign = 1;
-                       if (
-                               ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
-                               ?
-                                       model->type == mod_alias
-                               :
-                                       (
-                                               (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
-                                               ||
-                                               ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
-                                       )
-                       )
-                               pitchsign = -1;
+                       model = SV_GetModelFromEdict(touch);
+                       pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
                        Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
-               if ((int)touch->fields.server->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+               VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
+               VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
+               if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
                else
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
 
                Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
        }
 
 finished:
 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
-       if(!VectorCompare(start, pEnd))
-               Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
+       if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
+               Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
 #endif
        return cliptrace;
 }
 
 #if COLLISIONPARANOID >= 1
-trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
+trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
 {
        int endstuck;
        trace_t trace;
        vec3_t temp;
-       trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
+       trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
        if (passedict)
        {
                VectorCopy(trace.endpos, temp);
-               endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
+               endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
 #if COLLISIONPARANOID < 3
                if (trace.startsolid || endstuck)
 #endif
@@ -306,11 +648,10 @@ int SV_PointSuperContents(const vec3_t point)
        matrix4x4_t matrix, imatrix;
        // model of other entity
        dp_model_t *model;
-       unsigned int modelindex;
        int frame;
        // list of entities to test for collisions
        int numtouchedicts;
-       prvm_edict_t *touchedicts[MAX_EDICTS];
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
 
        // get world supercontents at this point
        if (sv.worldmodel && sv.worldmodel->PointSuperContents)
@@ -337,10 +678,7 @@ int SV_PointSuperContents(const vec3_t point)
                        continue;
 
                // might interact, so do an exact clip
-               modelindex = (unsigned int)touch->fields.server->modelindex;
-               if (modelindex >= MAX_MODELS)
-                       continue;
-               model = sv.models[(int)touch->fields.server->modelindex];
+               model = SV_GetModelFromEdict(touch);
                if (!model || !model->PointSuperContents)
                        continue;
                Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
@@ -361,10 +699,41 @@ Linking entities into the world culling system
 ===============================================================================
 */
 
+void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
+{
+       prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
+       prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
+       prog->globals.server->time = sv.time;
+       prog->globals.server->trace_allsolid = false;
+       prog->globals.server->trace_startsolid = false;
+       prog->globals.server->trace_fraction = 1;
+       prog->globals.server->trace_inwater = false;
+       prog->globals.server->trace_inopen = true;
+       VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
+       VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
+       prog->globals.server->trace_plane_dist = 0;
+       prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
+       PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
+       PRVM_serverglobalfloat(trace_dphitcontents) = 0;
+       PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
+       PRVM_serverglobalstring(trace_dphittexturename) = 0;
+       PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
+}
+
 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
 {
        int i, numtouchedicts, old_self, old_other;
-       prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
+       prvm_edict_t *touch;
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
+
+       if (ent == prog->edicts)
+               return;         // don't add the world
+
+       if (ent->priv.server->free)
+               return;
+
+       if (ent->fields.server->solid == SOLID_NOT)
+               return;
 
        // build a list of edicts to touch, because the link loop can be corrupted
        // by IncreaseEdicts called during touch functions
@@ -383,44 +752,55 @@ void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
                touch = touchedicts[i];
                if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
                {
-                       prvm_eval_t *val;
-                       prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
-                       prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
-                       prog->globals.server->time = sv.time;
-                       prog->globals.server->trace_allsolid = false;
-                       prog->globals.server->trace_startsolid = false;
-                       prog->globals.server->trace_fraction = 1;
-                       prog->globals.server->trace_inwater = false;
-                       prog->globals.server->trace_inopen = true;
-                       VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
-                       VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
-                       prog->globals.server->trace_plane_dist = 0;
-                       prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
-                       if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
-                               val->_float = 0;
-                       if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
-                               val->_float = 0;
-                       if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
-                               val->_float = 0;
-                       if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
-                               val->string = 0;
-                       PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
+                       SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
                }
        }
        prog->globals.server->self = old_self;
        prog->globals.server->other = old_other;
 }
 
+static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
+{
+       vec3_t v, u;
+       matrix4x4_t m;
+       Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
+
+       v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
+               VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
+       v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+       v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+       v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+       v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+       v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+       v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+       v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
+               if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
+               if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
+}
+
 /*
 ===============
 SV_LinkEdict
 
 ===============
 */
-void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
+void SV_LinkEdict (prvm_edict_t *ent)
 {
        dp_model_t *model;
        vec3_t mins, maxs;
+       int modelindex;
 
        if (ent == prog->edicts)
                return;         // don't add the world
@@ -428,21 +808,34 @@ void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
        if (ent->priv.server->free)
                return;
 
+       modelindex = (int)ent->fields.server->modelindex;
+       if (modelindex < 0 || modelindex >= MAX_MODELS)
+       {
+               Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
+               modelindex = 0;
+       }
+       model = SV_GetModelByIndex(modelindex);
+
+       VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
+       VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
+
 // set the abs box
 
-       if (ent->fields.server->solid == SOLID_BSP)
+       if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
+       {
+               // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
+               // TODO special handling for spheres?
+               RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
+               VectorAdd(ent->fields.server->origin, mins, mins);
+               VectorAdd(ent->fields.server->origin, maxs, maxs);
+       }
+       else if (ent->fields.server->solid == SOLID_BSP)
        {
-               int modelindex = (int)ent->fields.server->modelindex;
-               if (modelindex < 0 || modelindex >= MAX_MODELS)
-               {
-                       Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
-                       modelindex = 0;
-               }
-               model = sv.models[modelindex];
                if (model != NULL)
                {
-                       if (!model->TraceBox && developer.integer >= 1)
-                               Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
+                       if (!model->TraceBox)
+                               Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
 
                        if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
                        {
@@ -502,10 +895,6 @@ void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
        VectorCopy(maxs, ent->fields.server->absmax);
 
        World_LinkEdict(&sv.world, ent, mins, maxs);
-
-       // if touch_triggers, call touch on all entities overlapping this box
-       if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
-               SV_LinkEdict_TouchAreaGrid(ent);
 }
 
 /*
@@ -530,7 +919,7 @@ static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
        trace_t trace;
        contents = SV_GenericHitSuperContentsMask(ent);
        VectorAdd(ent->fields.server->origin, offset, org);
-       trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
+       trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
        if (trace.startsupercontents & contents)
                return true;
        else
@@ -569,7 +958,7 @@ static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
 #else
                // verify if the endpos is REALLY outside solid
                VectorCopy(trace.endpos, org);
-               trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
+               trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
                if(trace.startsolid)
                        Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
                else
@@ -617,7 +1006,6 @@ returns true if entity had a valid contentstransition function call
 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
 {
        int bValidFunctionCall;
-       prvm_eval_t *contentstransition;
 
        // Default Valid Function Call to False
        bValidFunctionCall = false;
@@ -625,9 +1013,7 @@ int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
        if(ent->fields.server->watertype != nContents)
        { // Changed Contents
                // Acquire Contents Transition Function from QC
-               contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
-
-               if(contentstransition->function)
+               if(PRVM_serveredictfunction(ent, contentstransition))
                { // Valid Function; Execute
                        // Assign Valid Function
                        bValidFunctionCall = true;
@@ -639,7 +1025,7 @@ int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
                                // Assign Self
                                prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
                        // Execute VM Function
-                       PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
+                       PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
                }
        }
 
@@ -675,6 +1061,12 @@ void SV_CheckVelocity (prvm_edict_t *ent)
                }
        }
 
+       // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
+       // player_run/player_stand1 does not horribly malfunction if the
+       // velocity becomes a denormalized float
+       if (VectorLength2(ent->fields.server->velocity) < 0.0001)
+               VectorClear(ent->fields.server->velocity);
+
        // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
        wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
        if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
@@ -727,25 +1119,20 @@ qboolean SV_RunThink (prvm_edict_t *ent)
 SV_Impact
 
 Two entities have touched, so run their touch functions
-returns true if the impact kept the origin of the touching entity intact
 ==================
 */
 extern void VM_SetTraceGlobals(const trace_t *trace);
 extern sizebuf_t vm_tempstringsbuf;
-qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
+void SV_Impact (prvm_edict_t *e1, trace_t *trace)
 {
        int restorevm_tempstringsbuf_cursize;
        int old_self, old_other;
-       vec3_t org;
        prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
-       prvm_eval_t *val;
 
        old_self = prog->globals.server->self;
        old_other = prog->globals.server->other;
        restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
 
-       VectorCopy(e1->fields.server->origin, org);
-
        VM_SetTraceGlobals(trace);
 
        prog->globals.server->time = sv.time;
@@ -764,22 +1151,16 @@ qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
                VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
                prog->globals.server->trace_plane_dist = -trace->plane.dist;
                prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
-               if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
-                       val->_float = 0;
-               if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
-                       val->_float = 0;
-               if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
-                       val->_float = 0;
-               if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
-                       val->string = 0;
+               PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
+               PRVM_serverglobalfloat(trace_dphitcontents) = 0;
+               PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
+               PRVM_serverglobalstring(trace_dphittexturename) = 0;
                PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
        }
 
        prog->globals.server->self = old_self;
        prog->globals.server->other = old_other;
        vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
-
-       return VectorCompare(e1->fields.server->origin, org);
 }
 
 
@@ -822,7 +1203,7 @@ If stepnormal is not NULL, the plane normal of any vertical wall hit will be sto
 static float SV_Gravity (prvm_edict_t *ent);
 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
 #define MAX_CLIP_PLANES 5
-static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
+static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
 {
        int blocked, bumpcount;
        int i, j, numplanes;
@@ -835,6 +1216,11 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo
        if (time <= 0)
                return 0;
        gravity = 0;
+
+       if(sv_gameplayfix_nogravityonground.integer)
+               if((int)ent->fields.server->flags & FL_ONGROUND)
+                       applygravity = false;
+
        if (applygravity)
        {
                if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
@@ -859,9 +1245,6 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo
                        break;
 
                VectorScale(ent->fields.server->velocity, time_left, push);
-#if 0
-               VectorAdd(ent->fields.server->origin, push, end);
-#endif
                if(!SV_PushEntity(&trace, ent, push, false, false))
                {
                        // we got teleported by a touch function
@@ -870,64 +1253,6 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo
                        break;
                }
 
-#if 0
-               //if (trace.fraction < 0.002)
-               {
-#if 1
-                       vec3_t start;
-                       trace_t testtrace;
-                       VectorCopy(ent->fields.server->origin, start);
-                       start[2] += 3;//0.03125;
-                       VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
-                       end[2] += 3;//0.03125;
-                       testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
-                       if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
-                       {
-                               Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
-                               trace = testtrace;
-                       }
-#endif
-#if 0
-                       //j = -1;
-                       for (i = 0;i < numplanes;i++)
-                       {
-                               VectorCopy(ent->fields.server->origin, start);
-                               VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
-                               VectorMA(start, 3, planes[i], start);
-                               VectorMA(end, 3, planes[i], end);
-                               testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
-                               if (trace.fraction < testtrace.fraction)
-                               {
-                                       trace = testtrace;
-                                       VectorCopy(start, ent->fields.server->origin);
-                                       //j = i;
-                               }
-                       }
-                       //if (j >= 0)
-                       //      VectorAdd(ent->fields.server->origin, planes[j], start);
-#endif
-               }
-#endif
-
-#if 0
-               Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
-               if (trace.fraction < 1)
-                       Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
-               Con_Print("\n");
-#endif
-
-#if 0
-               if (trace.bmodelstartsolid)
-               {
-                       // LordHavoc: note: this code is what makes entities stick in place
-                       // if embedded in world only (you can walk through other objects if
-                       // stuck)
-                       // entity is trapped in another solid
-                       VectorClear(ent->fields.server->velocity);
-                       return 3;
-               }
-#endif
-
                if (trace.fraction == 1)
                        break;
                if (trace.plane.normal[2])
@@ -947,9 +1272,55 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo
                                ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
                        }
                }
+               else if (stepheight)
+               {
+                       // step - handle it immediately
+                       vec3_t org;
+                       vec3_t steppush;
+                       trace_t steptrace;
+                       trace_t steptrace2;
+                       trace_t steptrace3;
+                       //Con_Printf("step %f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
+                       VectorSet(steppush, 0, 0, stepheight);
+                       VectorCopy(ent->fields.server->origin, org);
+                       if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
+                       if(!SV_PushEntity(&steptrace2, ent, push, false, false))
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
+                       VectorSet(steppush, 0, 0, org[2] - ent->fields.server->origin[2]);
+                       if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
+                       {
+                               blocked |= 8;
+                               break;
+                       }
+                       //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
+                       // accept the new position if it made some progress...
+                       if (fabs(ent->fields.server->origin[0] - org[0]) >= 0.03125 || fabs(ent->fields.server->origin[1] - org[1]) >= 0.03125)
+                       {
+                               //Con_Printf("accepted (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
+                               trace = steptrace2;
+                               VectorCopy(ent->fields.server->origin, trace.endpos);
+                               time_left *= 1 - trace.fraction;
+                               numplanes = 0;
+                               continue;
+                       }
+                       else
+                       {
+                               //Con_Printf("REJECTED (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
+                               VectorCopy(org, ent->fields.server->origin);
+                       }
+               }
                else
                {
-                       // step
+                       // step - return it to caller
                        blocked |= 2;
                        // save the trace for player extrafriction
                        if (stepnormal)
@@ -987,47 +1358,42 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo
                VectorCopy(trace.plane.normal, planes[numplanes]);
                numplanes++;
 
-               if (sv_newflymove.integer)
-                       ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
-               else
+               // modify original_velocity so it parallels all of the clip planes
+               for (i = 0;i < numplanes;i++)
                {
-                       // modify original_velocity so it parallels all of the clip planes
-                       for (i = 0;i < numplanes;i++)
+                       ClipVelocity(original_velocity, planes[i], new_velocity, 1);
+                       for (j = 0;j < numplanes;j++)
                        {
-                               ClipVelocity(original_velocity, planes[i], new_velocity, 1);
-                               for (j = 0;j < numplanes;j++)
+                               if (j != i)
                                {
-                                       if (j != i)
-                                       {
-                                               // not ok
-                                               if (DotProduct(new_velocity, planes[j]) < 0)
-                                                       break;
-                                       }
+                                       // not ok
+                                       if (DotProduct(new_velocity, planes[j]) < 0)
+                                               break;
                                }
-                               if (j == numplanes)
-                                       break;
                        }
+                       if (j == numplanes)
+                               break;
+               }
 
-                       if (i != numplanes)
-                       {
-                               // go along this plane
-                               VectorCopy(new_velocity, ent->fields.server->velocity);
-                       }
-                       else
+               if (i != numplanes)
+               {
+                       // go along this plane
+                       VectorCopy(new_velocity, ent->fields.server->velocity);
+               }
+               else
+               {
+                       // go along the crease
+                       if (numplanes != 2)
                        {
-                               // go along the crease
-                               if (numplanes != 2)
-                               {
-                                       VectorClear(ent->fields.server->velocity);
-                                       blocked = 7;
-                                       break;
-                               }
-                               CrossProduct(planes[0], planes[1], dir);
-                               // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
-                               VectorNormalize(dir);
-                               d = DotProduct(dir, ent->fields.server->velocity);
-                               VectorScale(dir, d, ent->fields.server->velocity);
+                               VectorClear(ent->fields.server->velocity);
+                               blocked = 7;
+                               break;
                        }
+                       CrossProduct(planes[0], planes[1], dir);
+                       // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
+                       VectorNormalize(dir);
+                       d = DotProduct(dir, ent->fields.server->velocity);
+                       VectorScale(dir, d, ent->fields.server->velocity);
                }
 
                // if current velocity is against the original velocity,
@@ -1046,7 +1412,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo
        {
                // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
                // flag ONGROUND if there's ground under it
-               trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
+               trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
        }
        */
 
@@ -1067,13 +1433,10 @@ SV_Gravity
 static float SV_Gravity (prvm_edict_t *ent)
 {
        float ent_gravity;
-       prvm_eval_t *val;
 
-       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
-       if (val!=0 && val->_float)
-               ent_gravity = val->_float;
-       else
-               ent_gravity = 1.0;
+       ent_gravity = PRVM_serveredictfloat(ent, gravity);
+       if (!ent_gravity)
+               ent_gravity = 1.0f;
        return ent_gravity * sv_gravity.value * sv.frametime;
 }
 
@@ -1098,9 +1461,11 @@ Returns true if the push did not result in the entity being teleported by QC cod
 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
 {
        int type;
+       int bump;
+       vec3_t original, original_velocity;
        vec3_t end;
-       qboolean impact;
 
+       VectorCopy(ent->fields.server->origin, original);
        VectorAdd (ent->fields.server->origin, push, end);
 
        if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
@@ -1110,31 +1475,45 @@ static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, q
        else
                type = MOVE_NORMAL;
 
-       *trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
+       *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
+       bump = 0;
+       while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
+       {
+               vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
+               VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
+               *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
+               bump++;
+               if (bump > 10)
+               {
+                       VectorCopy(original, ent->fields.server->origin);
+                       break;
+               }
+       }
        if (trace->bmodelstartsolid && failonbmodelstartsolid)
                return true;
 
        VectorCopy (trace->endpos, ent->fields.server->origin);
 
+       VectorCopy(ent->fields.server->origin, original);
+       VectorCopy(ent->fields.server->velocity, original_velocity);
+
+       SV_LinkEdict(ent);
+
 #if 0
        if(!trace->startsolid)
-       if(SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
+       if(SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
        {
                Con_Printf("something eeeeevil happened\n");
        }
 #endif
 
-       impact = (ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent)));
+       if (dolink)
+               SV_LinkEdict_TouchAreaGrid(ent);
 
-       if(impact)
-       {
-               SV_LinkEdict (ent, dolink);
-               return SV_Impact (ent, trace);
-       }
-       else if(dolink)
-               SV_LinkEdict (ent, true);
+       if((ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent))))
+               SV_Impact (ent, trace);
 
-       return true;
+       return VectorCompare(ent->fields.server->origin, original) && VectorCompare(ent->fields.server->velocity, original_velocity);
 }
 
 
@@ -1158,7 +1537,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        dp_model_t *pushermodel;
        trace_t trace, trace2;
        matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
-       unsigned short moved_edicts[MAX_EDICTS];
+       static unsigned short moved_edicts[MAX_EDICTS];
 
        if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
        {
@@ -1183,7 +1562,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
                pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
                pusher->fields.server->ltime += movetime;
-               SV_LinkEdict (pusher, false);
+               SV_LinkEdict(pusher);
                return;
        default:
                Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
@@ -1195,7 +1574,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
                return;
        }
-       pushermodel = sv.models[index];
+       pushermodel = SV_GetModelByIndex(index);
        pusherowner = pusher->fields.server->owner;
        pusherprog = PRVM_EDICT_TO_PROG(pusher);
 
@@ -1265,11 +1644,9 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
        VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
        pusher->fields.server->ltime += movetime;
-       SV_LinkEdict (pusher, false);
+       SV_LinkEdict(pusher);
 
-       pushermodel = NULL;
-       if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
-               pushermodel = sv.models[(int)pusher->fields.server->modelindex];
+       pushermodel = SV_GetModelFromEdict(pusher);
        Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1);
        Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
 
@@ -1282,12 +1659,18 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        for (e = 0;e < numcheckentities;e++)
        {
                prvm_edict_t *check = checkentities[e];
-               if (check->fields.server->movetype == MOVETYPE_NONE
-                || check->fields.server->movetype == MOVETYPE_PUSH
-                || check->fields.server->movetype == MOVETYPE_FOLLOW
-                || check->fields.server->movetype == MOVETYPE_NOCLIP
-                || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
+               int movetype = (int)check->fields.server->movetype;
+               switch(movetype)
+               {
+               case MOVETYPE_NONE:
+               case MOVETYPE_PUSH:
+               case MOVETYPE_FOLLOW:
+               case MOVETYPE_NOCLIP:
+               case MOVETYPE_FAKEPUSH:
                        continue;
+               default:
+                       break;
+               }
 
                if (check->fields.server->owner == pusherprog)
                        continue;
@@ -1307,8 +1690,8 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                // final position, move it
                if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
                {
-                       Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
-                       //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
+                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+                       //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
                        if (!trace.startsolid)
                        {
                                //Con_Printf("- not in solid\n");
@@ -1335,6 +1718,15 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
                moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
 
+               // physics objects need better collisions than this code can do
+               if (movetype == MOVETYPE_PHYSICS)
+               {
+                       VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
+                       SV_LinkEdict(check);
+                       SV_LinkEdict_TouchAreaGrid(check);
+                       continue;
+               }
+
                // try moving the contacted entity
                pusher->fields.server->solid = SOLID_NOT;
                if(!SV_PushEntity (&trace, check, move, true, true))
@@ -1356,7 +1748,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                        check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
 
                // if it is still inside the pusher, block
-               Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                if (trace.startsolid)
                {
                        // try moving the contacted entity a tiny bit further to account for precision errors
@@ -1371,7 +1763,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                continue;
                        }
                        pusher->fields.server->solid = savesolid;
-                       Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                        if (trace.startsolid)
                        {
                                // try moving the contacted entity a tiny bit less to account for precision errors
@@ -1385,7 +1777,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                        continue;
                                }
                                pusher->fields.server->solid = savesolid;
-                               Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+                               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                                if (trace.startsolid)
                                {
                                        // still inside pusher, so it's really blocked
@@ -1404,7 +1796,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                        VectorCopy (pushorig, pusher->fields.server->origin);
                                        VectorCopy (pushang, pusher->fields.server->angles);
                                        pusher->fields.server->ltime = pushltime;
-                                       SV_LinkEdict (pusher, false);
+                                       SV_LinkEdict(pusher);
 
                                        // move back any entities we already moved
                                        for (i = 0;i < num_moved;i++)
@@ -1412,7 +1804,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                                prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
                                                VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
                                                VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
-                                               SV_LinkEdict (ed, false);
+                                               SV_LinkEdict(ed);
                                        }
 
                                        // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
@@ -1515,7 +1907,8 @@ unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
                if (!SV_TestEntityPosition(ent, unstickoffsets + i))
                {
                        VectorCopy(unstickoffsets + i, offset);
-                       SV_LinkEdict (ent, true);
+                       SV_LinkEdict(ent);
+                       //SV_LinkEdict_TouchAreaGrid(ent);
                        return UNSTICK_UNSTUCK;
                }
        }
@@ -1529,13 +1922,15 @@ unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
                offset[2] = -i;
                if (!SV_TestEntityPosition(ent, offset))
                {
-                       SV_LinkEdict (ent, true);
+                       SV_LinkEdict(ent);
+                       //SV_LinkEdict_TouchAreaGrid(ent);
                        return UNSTICK_UNSTUCK;
                }
                offset[2] = i;
                if (!SV_TestEntityPosition(ent, offset))
                {
-                       SV_LinkEdict (ent, true);
+                       SV_LinkEdict(ent);
+                       //SV_LinkEdict_TouchAreaGrid(ent);
                        return UNSTICK_UNSTUCK;
                }
        }
@@ -1554,8 +1949,8 @@ qboolean SV_UnstickEntity (prvm_edict_t *ent)
                        Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
                        return true;
                case UNSTICK_STUCK:
-                       if (developer.integer >= 100)
-                               Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
+                       if (developer_extra.integer)
+                               Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
                        return false;
                default:
                        Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
@@ -1588,7 +1983,8 @@ void SV_CheckStuck (prvm_edict_t *ent)
                        if (!SV_TestEntityPosition(ent, offset))
                        {
                                Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
-                               SV_LinkEdict (ent, true);
+                               SV_LinkEdict(ent);
+                               //SV_LinkEdict_TouchAreaGrid(ent);
                        }
                        else
                                Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
@@ -1743,7 +2139,13 @@ Only used by players
 */
 void SV_WalkMove (prvm_edict_t *ent)
 {
-       int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
+       int clip;
+       int oldonground;
+       //int originalmove_clip;
+       int originalmove_flags;
+       int originalmove_groundentity;
+       int hitsupercontentsmask;
+       int type;
        vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
        trace_t downtrace, trace;
        qboolean applygravity;
@@ -1767,14 +2169,34 @@ void SV_WalkMove (prvm_edict_t *ent)
        VectorCopy (ent->fields.server->origin, start_origin);
        VectorCopy (ent->fields.server->velocity, start_velocity);
 
-       clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
+       clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
+
+       if(sv_gameplayfix_downtracesupportsongroundflag.integer)
+       if(!(clip & 1))
+       {
+               // only try this if there was no floor in the way in the trace (no,
+               // this check seems to be not REALLY necessary, because if clip & 1,
+               // our trace will hit that thing too)
+               VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
+               VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
+               if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
+                       type = MOVE_MISSILE;
+               else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
+                       type = MOVE_NOMONSTERS; // only clip against bmodels
+               else
+                       type = MOVE_NORMAL;
+               trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
+               if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
+                       clip |= 1; // but we HAVE found a floor
+       }
 
        // if the move did not hit the ground at any point, we're not on ground
-       if (!(clip & 1))
+       if(!(clip & 1))
                ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
 
        SV_CheckVelocity(ent);
-       SV_LinkEdict (ent, true);
+       SV_LinkEdict(ent);
+       SV_LinkEdict_TouchAreaGrid(ent);
 
        if(clip & 8) // teleport
                return;
@@ -1787,7 +2209,7 @@ void SV_WalkMove (prvm_edict_t *ent)
 
        VectorCopy(ent->fields.server->origin, originalmove_origin);
        VectorCopy(ent->fields.server->velocity, originalmove_velocity);
-       originalmove_clip = clip;
+       //originalmove_clip = clip;
        originalmove_flags = (int)ent->fields.server->flags;
        originalmove_groundentity = ent->fields.server->groundentity;
 
@@ -1826,7 +2248,7 @@ void SV_WalkMove (prvm_edict_t *ent)
 
                // move forward
                ent->fields.server->velocity[2] = 0;
-               clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
+               clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
                ent->fields.server->velocity[2] += start_velocity[2];
                if(clip & 8)
                {
@@ -1836,7 +2258,8 @@ void SV_WalkMove (prvm_edict_t *ent)
                }
 
                SV_CheckVelocity(ent);
-               SV_LinkEdict (ent, true);
+               SV_LinkEdict(ent);
+               SV_LinkEdict_TouchAreaGrid(ent);
 
                // check for stuckness, possibly due to the limited precision of floats
                // in the clipping hulls
@@ -1903,7 +2326,8 @@ void SV_WalkMove (prvm_edict_t *ent)
        }
 
        SV_CheckVelocity(ent);
-       SV_LinkEdict (ent, true);
+       SV_LinkEdict(ent);
+       SV_LinkEdict_TouchAreaGrid(ent);
 }
 
 //============================================================================
@@ -1949,7 +2373,8 @@ void SV_Physics_Follow (prvm_edict_t *ent)
                ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
        }
        VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
-       SV_LinkEdict (ent, true);
+       SV_LinkEdict(ent);
+       //SV_LinkEdict_TouchAreaGrid(ent);
 }
 
 /*
@@ -2014,10 +2439,12 @@ void SV_Physics_Toss (prvm_edict_t *ent)
        vec3_t move;
        vec_t movetime;
        int bump;
+       prvm_edict_t *groundentity;
 
 // if onground, return without moving
        if ((int)ent->fields.server->flags & FL_ONGROUND)
        {
+               groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
                if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
                {
                        // don't stick to ground if onground and moving upward
@@ -2028,7 +2455,7 @@ void SV_Physics_Toss (prvm_edict_t *ent)
                        // we can trust FL_ONGROUND if groundentity is world because it never moves
                        return;
                }
-               else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
+               else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
                {
                        // if ent was supported by a brush model on previous frame,
                        // and groundentity is now freed, set groundentity to 0 (world)
@@ -2037,6 +2464,11 @@ void SV_Physics_Toss (prvm_edict_t *ent)
                        if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
                                return;
                }
+               else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
+               {
+                       // don't slide if still touching the groundentity
+                       return;
+               }
        }
        ent->priv.server->suspendedinairflag = false;
 
@@ -2072,31 +2504,33 @@ void SV_Physics_Toss (prvm_edict_t *ent)
                movetime *= 1 - min(1, trace.fraction);
                if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
                {
-                       ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
+                       float bouncefactor;
+                       bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
+                       if (!bouncefactor)
+                               bouncefactor = 1.0f;
+
+                       ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
                        ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
                }
                else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
                {
                        float d, ent_gravity;
-                       prvm_eval_t *val;
-                       float bouncefactor = 0.5f;
-                       float bouncestop = 60.0f / 800.0f;
+                       float bouncefactor;
+                       float bouncestop;
 
-                       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
-                       if (val!=0 && val->_float)
-                               bouncefactor = val->_float;
+                       bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
+                       if (!bouncefactor)
+                               bouncefactor = 0.5f;
 
-                       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
-                       if (val!=0 && val->_float)
-                               bouncestop = val->_float;
+                       bouncestop = PRVM_serveredictfloat(ent, bouncestop);
+                       if (!bouncestop)
+                               bouncestop = 60.0f / 800.0f;
 
                        ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
+                       ent_gravity = PRVM_serveredictfloat(ent, gravity);
+                       if (!ent_gravity)
+                               ent_gravity = 1.0f;
                        // LordHavoc: fixed grenades not bouncing when fired down a slope
-                       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
-                       if (val!=0 && val->_float)
-                               ent_gravity = val->_float;
-                       else
-                               ent_gravity = 1.0;
                        if (sv_gameplayfix_grenadebouncedownslopes.integer)
                        {
                                d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
@@ -2172,7 +2606,6 @@ void SV_Physics_Step (prvm_edict_t *ent)
        // DRESK
        // Backup Velocity in the event that movetypesteplandevent is called,
        // to provide a parameter with the entity's velocity at impact.
-       prvm_eval_t *movetypesteplandevent;
        vec3_t backupVelocity;
        VectorCopy(ent->fields.server->velocity, backupVelocity);
        // don't fall at all if fly/swim
@@ -2182,12 +2615,13 @@ void SV_Physics_Step (prvm_edict_t *ent)
                {
                        // freefall if onground and moving upward
                        // freefall if not standing on a world surface (it may be a lift or trap door)
-                       if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
+                       if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
                        {
                                ent->fields.server->flags -= FL_ONGROUND;
                                SV_CheckVelocity(ent);
-                               SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
-                               SV_LinkEdict(ent, true);
+                               SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
+                               SV_LinkEdict(ent);
+                               SV_LinkEdict_TouchAreaGrid(ent);
                                ent->priv.server->waterposition_forceupdate = true;
                        }
                }
@@ -2197,16 +2631,15 @@ void SV_Physics_Step (prvm_edict_t *ent)
                        int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
 
                        SV_CheckVelocity(ent);
-                       SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
-                       SV_LinkEdict(ent, true);
+                       SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
+                       SV_LinkEdict(ent);
+                       SV_LinkEdict_TouchAreaGrid(ent);
 
                        // just hit ground
                        if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
                        {
                                // DRESK - Check for Entity Land Event Function
-                               movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
-
-                               if(movetypesteplandevent->function)
+                               if(PRVM_serveredictfunction(ent, movetypesteplandevent))
                                { // Valid Function; Execute
                                        // Prepare Parameters
                                                // Assign Velocity at Impact
@@ -2216,7 +2649,7 @@ void SV_Physics_Step (prvm_edict_t *ent)
                                                // Assign Self
                                                prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
                                        // Execute VM Function
-                                       PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
+                                       PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
                                }
                                else
                                // Check for Engine Landing Sound
@@ -2274,7 +2707,7 @@ static void SV_Physics_Entity (prvm_edict_t *ent)
                        VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
                        VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
                }
-               SV_LinkEdict(ent, false);
+               SV_LinkEdict(ent);
                break;
        case MOVETYPE_STEP:
                SV_Physics_Step (ent);
@@ -2292,6 +2725,13 @@ static void SV_Physics_Entity (prvm_edict_t *ent)
                if (SV_RunThink (ent))
                        SV_Physics_Toss (ent);
                break;
+       case MOVETYPE_PHYSICS:
+               if (SV_RunThink(ent))
+               {
+                       SV_LinkEdict(ent);
+                       SV_LinkEdict_TouchAreaGrid(ent);
+               }
+               break;
        default:
                Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
                break;
@@ -2316,12 +2756,6 @@ void SV_Physics_ClientMove(void)
 
        // make sure the velocity is sane (not a NaN)
        SV_CheckVelocity(ent);
-       // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
-       // player_run/player_stand1 does not horribly malfunction if the
-       // velocity becomes a number that is both == 0 and != 0
-       // (sounds to me like NaN but to be absolutely safe...)
-       if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
-               VectorClear(ent->fields.server->velocity);
 
        // perform MOVETYPE_WALK behavior
        SV_WalkMove (ent);
@@ -2345,14 +2779,14 @@ void SV_Physics_ClientMove(void)
        }
 }
 
-void SV_Physics_ClientEntity(prvm_edict_t *ent)
+static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
 {
        // don't do physics on disconnected clients, FrikBot relies on this
        if (!host_client->spawned)
-       {
-               memset(&host_client->cmd, 0, sizeof(host_client->cmd));
                return;
-       }
+
+       // make sure the velocity is sane (not a NaN)
+       SV_CheckVelocity(ent);
 
        // don't run physics here if running asynchronously
        if (host_client->clmovement_inputtimeout <= 0)
@@ -2361,20 +2795,65 @@ void SV_Physics_ClientEntity(prvm_edict_t *ent)
                //host_client->cmd.time = max(host_client->cmd.time, sv.time);
        }
 
-       // make sure the velocity is sane (not a NaN)
+       // make sure the velocity is still sane (not a NaN)
        SV_CheckVelocity(ent);
-       // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
-       // player_run/player_stand1 does not horribly malfunction if the
-       // velocity becomes a number that is both == 0 and != 0
-       // (sounds to me like NaN but to be absolutely safe...)
-       if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
-               VectorClear(ent->fields.server->velocity);
 
        // call standard client pre-think
        prog->globals.server->time = sv.time;
        prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
-       PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
-       SV_CheckVelocity (ent);
+       PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
+
+       // make sure the velocity is still sane (not a NaN)
+       SV_CheckVelocity(ent);
+}
+
+static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
+{
+       // don't do physics on disconnected clients, FrikBot relies on this
+       if (!host_client->spawned)
+               return;
+
+       // make sure the velocity is sane (not a NaN)
+       SV_CheckVelocity(ent);
+
+       // call standard player post-think
+       prog->globals.server->time = sv.time;
+       prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+       PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
+
+       // make sure the velocity is still sane (not a NaN)
+       SV_CheckVelocity(ent);
+
+       if(ent->fields.server->fixangle)
+       {
+               // angle fixing was requested by physics code...
+               // so store the current angles for later use
+               memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
+               host_client->fixangle_angles_set = TRUE;
+
+               // and clear fixangle for the next frame
+               ent->fields.server->fixangle = 0;
+       }
+
+       // decrement the countdown variable used to decide when to go back to
+       // synchronous physics
+       if (host_client->clmovement_inputtimeout > sv.frametime)
+               host_client->clmovement_inputtimeout -= sv.frametime;
+       else
+               host_client->clmovement_inputtimeout = 0;
+}
+
+static void SV_Physics_ClientEntity(prvm_edict_t *ent)
+{
+       // don't do physics on disconnected clients, FrikBot relies on this
+       if (!host_client->spawned)
+       {
+               memset(&host_client->cmd, 0, sizeof(host_client->cmd));
+               return;
+       }
+
+       // make sure the velocity is sane (not a NaN)
+       SV_CheckVelocity(ent);
 
        switch ((int) ent->fields.server->movetype)
        {
@@ -2417,39 +2896,20 @@ void SV_Physics_ClientEntity(prvm_edict_t *ent)
                SV_RunThink (ent);
                SV_WalkMove (ent);
                break;
+       case MOVETYPE_PHYSICS:
+               SV_RunThink (ent);
+               break;
        default:
                Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
                break;
        }
 
-       // decrement the countdown variable used to decide when to go back to
-       // synchronous physics
-       if (host_client->clmovement_inputtimeout > sv.frametime)
-               host_client->clmovement_inputtimeout -= sv.frametime;
-       else
-               host_client->clmovement_inputtimeout = 0;
-
        SV_CheckVelocity (ent);
 
-       SV_LinkEdict (ent, true);
+       SV_LinkEdict(ent);
+       SV_LinkEdict_TouchAreaGrid(ent);
 
        SV_CheckVelocity (ent);
-
-       // call standard player post-think
-       prog->globals.server->time = sv.time;
-       prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
-       PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
-
-       if(ent->fields.server->fixangle)
-       {
-               // angle fixing was requested by physics code...
-               // so store the current angles for later use
-               memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
-               host_client->fixangle_angles_set = TRUE;
-
-               // and clear fixangle for the next frame
-               ent->fields.server->fixangle = 0;
-       }
 }
 
 /*
@@ -2470,6 +2930,9 @@ void SV_Physics (void)
        prog->globals.server->frametime = sv.frametime;
        PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
 
+       // run physics engine
+       World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
+
 //
 // treat each object in turn
 //
@@ -2478,12 +2941,36 @@ void SV_Physics (void)
        if (prog->globals.server->force_retouch > 0)
                for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
                        if (!ent->priv.server->free)
-                               SV_LinkEdict (ent, true);       // force retouch even for stationary
+                               SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
 
-       // run physics on the client entities
-       for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
-               if (!ent->priv.server->free)
+       if (sv_gameplayfix_consistentplayerprethink.integer)
+       {
+               // run physics on the client entities in 3 stages
+               for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
+                       if (!ent->priv.server->free)
+                               SV_Physics_ClientEntity_PreThink(ent);
+
+               for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
+                       if (!ent->priv.server->free)
+                               SV_Physics_ClientEntity(ent);
+
+               for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
+                       if (!ent->priv.server->free)
+                               SV_Physics_ClientEntity_PostThink(ent);
+       }
+       else
+       {
+               // run physics on the client entities
+               for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
+               {
+                       if (!ent->priv.server->free)
+                       {
+                               SV_Physics_ClientEntity_PreThink(ent);
                                SV_Physics_ClientEntity(ent);
+                               SV_Physics_ClientEntity_PostThink(ent);
+                       }
+               }
+       }
 
        // run physics on all the non-client entities
        if (!sv_freezenonclients.integer)
@@ -2503,12 +2990,12 @@ void SV_Physics (void)
                prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
 
        // LordHavoc: endframe support
-       if (prog->funcoffsets.EndFrame)
+       if (PRVM_serverfunction(EndFrame))
        {
                prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
                prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
                prog->globals.server->time = sv.time;
-               PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
+               PRVM_ExecuteProgram (PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
        }
 
        // decrement prog->num_edicts if the highest number entities died