]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_phys.c
fix movement loss tracking
[xonotic/darkplaces.git] / sv_phys.c
index db221566c2a5b20b007f420be6ffc236d9a74a67..b3d981005a66eb79d49c55510bd56a583d14daa6 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -46,9 +46,8 @@ void SV_Physics_Toss (prvm_edict_t *ent);
 int SV_GetPitchSign(prvm_edict_t *ent)
 {
        dp_model_t *model;
-       int modelindex;
        if (
-                       ((modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[modelindex]))
+                       (model = SV_GetModelFromEdict(ent))
                        ?
                        model->type == mod_alias
                        :
@@ -122,7 +121,7 @@ trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
        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];
 
        //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
 
@@ -214,10 +213,7 @@ trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
                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];
+                       model = SV_GetModelFromEdict(touch);
                        pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
@@ -225,10 +221,13 @@ trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
                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, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, 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, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
                else
-                       Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
+                       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);
        }
@@ -267,17 +266,17 @@ trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
        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(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);
@@ -377,10 +376,7 @@ trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
                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];
+                       model = SV_GetModelFromEdict(touch);
                        pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
@@ -388,18 +384,21 @@ trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
                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, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, 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, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
                else
-                       Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
+                       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);
 
                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;
 }
@@ -446,17 +445,17 @@ trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
        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(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);
@@ -577,11 +576,7 @@ trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
                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;
+                       model = SV_GetModelFromEdict(touch);
                        pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
@@ -589,18 +584,21 @@ trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
                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, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, 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, 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;
 }
@@ -635,11 +633,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)
@@ -666,10 +663,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);
@@ -690,10 +684,37 @@ Linking entities into the world culling system
 ===============================================================================
 */
 
+void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
+{
+       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");
+}
+
 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
@@ -721,34 +742,44 @@ 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
@@ -759,6 +790,7 @@ 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
@@ -766,17 +798,30 @@ void SV_LinkEdict (prvm_edict_t *ent)
        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)
@@ -1438,8 +1483,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;
        vec3_t end;
 
+       VectorCopy(ent->fields.server->origin, original);
        VectorAdd (ent->fields.server->origin, push, end);
 
        if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
@@ -1450,9 +1498,23 @@ static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, q
                type = MOVE_NORMAL;
 
        *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->startsolid && 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);
        SV_LinkEdict(ent);
 
@@ -1494,7 +1556,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])
        {
@@ -1531,7 +1593,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);
 
@@ -1603,9 +1665,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        pusher->fields.server->ltime += movetime;
        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);
 
@@ -1649,7 +1709,7 @@ 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);
+                       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)
                        {
@@ -1707,7 +1767,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
@@ -1722,7 +1782,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
@@ -1736,7 +1796,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
@@ -2098,7 +2158,7 @@ Only used by players
 */
 void SV_WalkMove (prvm_edict_t *ent)
 {
-       int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
+       int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask, type;
        vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
        trace_t downtrace, trace;
        qboolean applygravity;
@@ -2124,8 +2184,27 @@ void SV_WalkMove (prvm_edict_t *ent)
 
        clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
 
+       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);
@@ -2373,10 +2452,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
@@ -2387,7 +2468,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)
@@ -2396,6 +2477,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;
 
@@ -2547,7 +2633,7 @@ 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);