]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
nudgeoutofsolid: implement fallback for when cliphulls are in use
authorbones_was_here <bones_was_here@xonotic.au>
Mon, 5 Aug 2024 15:50:34 +0000 (01:50 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Mon, 5 Aug 2024 17:01:58 +0000 (03:01 +1000)
Fixes sv_gameplayfix_droptofloorstartsolid_nudgetocorrect and
DP_QC_NUDGEOUTOFSOLID doing nothing on Q1BSP.

Fixes the Q1BSP condition in PHYS_NudgeOutOfSolid() to handle the exact
issue (so mod_q1bsp_polygoncollisions is properly supported).

Migrates unsticking code from server-specific to common.

Updates documentation.

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
dpdefs/dpextensions.qc
phys.c
phys.h
server.h
sv_main.c
sv_phys.c

index 42c8a2a2ab787a3c8771a799fa3fde93321dd23a..d72666dcb8de48757f9fd747843057f590cf7ca1 100644 (file)
@@ -2663,7 +2663,8 @@ float(entity ent) nudgeoutofsolid = #567;
 //description:
 //Attempts to move a stuck entity out of solid brushes, returning 1 if successful, 0 if it remains stuck, -1 if it wasn't stuck.
 //Note: makes only one tracebox call if the entity isn't stuck, so don't call tracebox just to see if you should call nudgeoutofsolid.
-//Currently has no effect on Q1BSP unless mod_q1bsp_polygoncollisions is enabled.
+//Uses a "smart" method considering surface properties and supporting multiple surfaces,
+//except on Q1BSP with mod_q1bsp_polygoncollisions 0 (there it falls back to the unsticking method).
 
 
 float(float dividend, float divisor) mod = #245;
diff --git a/phys.c b/phys.c
index 8d71b868be20e0defd5a6efa0a1cc9e6d8ab801d..9aca309ddd528d9905f9fe5035bc926fd024f893 100644 (file)
--- a/phys.c
+++ b/phys.c
@@ -6,7 +6,134 @@
 #include "cl_collision.h"
 
 
-int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
+// TODO handle this in a nicer way...
+static inline trace_t PHYS_TraceBox(prvm_prog_t *prog, 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 skipsupercontentsmask, int skipmaterialflagsmask, float extend, qbool hitnetworkbrushmodels, qbool hitnetworkplayers, int *hitnetworkentity, qbool hitcsqcentities)
+{
+       return (prog == SVVM_prog)
+               ? SV_TraceBox(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend)
+               : CL_TraceBox(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
+}
+
+/*
+============
+PHYS_TestEntityPosition
+
+returns true if the entity is in solid currently
+============
+*/
+qbool PHYS_TestEntityPosition (prvm_prog_t *prog, prvm_edict_t *ent, vec3_t offset)
+{
+       int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
+       int skipsupercontentsmask = 0;
+       int skipmaterialflagsmask = 0;
+       vec3_t org, entorigin, entmins, entmaxs;
+       trace_t trace;
+
+       VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
+       VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
+       VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
+       VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
+       trace = PHYS_TraceBox(prog, org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value, true, false, NULL, false);
+       if (trace.startsupercontents & hitsupercontentsmask)
+               return true;
+       else
+       {
+               if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
+               {
+                       // q1bsp/hlbsp use hulls and if the entity does not exactly match
+                       // a hull size it is incorrectly tested, so this code tries to
+                       // 'fix' it slightly...
+                       // FIXME: this breaks entities larger than the hull size
+                       int i;
+                       vec3_t v, m1, m2, s;
+                       VectorAdd(org, entmins, m1);
+                       VectorAdd(org, entmaxs, m2);
+                       VectorSubtract(m2, m1, s);
+#define EPSILON (1.0f / 32.0f)
+                       if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
+                       if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
+                       if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
+                       for (i = 0;i < 8;i++)
+                       {
+                               v[0] = (i & 1) ? m2[0] : m1[0];
+                               v[1] = (i & 2) ? m2[1] : m1[1];
+                               v[2] = (i & 4) ? m2[2] : m1[2];
+                               if (SV_PointSuperContents(v) & hitsupercontentsmask)
+                                       return true;
+                       }
+               }
+       }
+       // if the trace found a better position for the entity, move it there
+       if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
+       {
+#if 0
+               // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
+               VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
+#else
+               // verify if the endpos is REALLY outside solid
+               VectorCopy(trace.endpos, org);
+               trace = PHYS_TraceBox(prog, org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value, true, false, NULL, false);
+               if(trace.startsolid)
+                       Con_Printf("PHYS_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
+               else
+                       VectorCopy(org, PRVM_serveredictvector(ent, origin));
+#endif
+       }
+       return false;
+}
+
+static float unstickoffsets[] =
+{
+       // poutting -/+z changes first as they are least weird
+        0,  0,  -1,
+        0,  0,  1,
+        // x or y changes
+       -1,  0,  0,
+        1,  0,  0,
+        0, -1,  0,
+        0,  1,  0,
+        // x and y changes
+       -1, -1,  0,
+        1, -1,  0,
+       -1,  1,  0,
+        1,  1,  0,
+};
+
+unstickresult_t PHYS_UnstickEntityReturnOffset (prvm_prog_t *prog, prvm_edict_t *ent, vec3_t offset)
+{
+       int i, maxunstick;
+
+       // if not stuck in a bmodel, just return
+       if (!PHYS_TestEntityPosition(prog, ent, vec3_origin))
+               return UNSTICK_GOOD;
+
+       for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
+       {
+               if (!PHYS_TestEntityPosition(prog, ent, unstickoffsets + i))
+               {
+                       VectorCopy(unstickoffsets + i, offset);
+                       return UNSTICK_UNSTUCK;
+               }
+       }
+
+       maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
+       // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
+
+       for(i = 2; i <= maxunstick; ++i)
+       {
+               VectorClear(offset);
+               offset[2] = -i;
+               if (!PHYS_TestEntityPosition(prog, ent, offset))
+                       return UNSTICK_UNSTUCK;
+               offset[2] = i;
+               if (!PHYS_TestEntityPosition(prog, ent, offset))
+                       return UNSTICK_UNSTUCK;
+       }
+
+       return UNSTICK_STUCK;
+}
+
+unstickresult_t PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
 {
        int bump, pass;
        trace_t stucktrace;
@@ -30,8 +157,13 @@ int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
 
        VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
        VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
-       if (worldmodel && worldmodel->brushq1.numclipnodes)
+       if (worldmodel && worldmodel->TraceBox != Mod_CollisionBIH_TraceBox)
+       {
                separation = 0.0f; // when using hulls, it can not be enlarged
+
+               // FIXME: Mod_Q1BSP_TraceBox() doesn't support startdepth and startdepthnormal
+               return PHYS_UnstickEntityReturnOffset(prog, ent, testorigin); // fallback
+       }
        else
        {
                stuckmins[0] -= separation;
@@ -49,10 +181,7 @@ int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
                VectorCopy(PRVM_serveredictvector(ent, origin), testorigin);
                for (bump = 0;bump < 10;bump++)
                {
-                       if (prog == SVVM_prog) // TODO: can we refactor to use a shared TraceBox or at least a func ptr for these cases?
-                               stucktrace = SV_TraceBox(testorigin, stuckmins, stuckmaxs, testorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-                       else
-                               stucktrace = CL_TraceBox(testorigin, stuckmins, stuckmaxs, testorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, pass ? false : true, false, NULL, false);
+                       stucktrace = PHYS_TraceBox(prog, testorigin, stuckmins, stuckmaxs, testorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, pass ? false : true, false, NULL, false);
 
                        // Separation compared here to ensure a good location will be recognised reliably.
                        if (-stucktrace.startdepth <= separation
@@ -61,20 +190,17 @@ int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
                        {
                                // found a good location, use it
                                VectorCopy(testorigin, PRVM_serveredictvector(ent, origin));
-                               return bump || pass ? 1 : -1; // -1 means it wasn't stuck
+                               return bump || pass ? UNSTICK_UNSTUCK : UNSTICK_GOOD;
                        }
 
                        VectorMA(testorigin, -stucktrace.startdepth, stucktrace.startdepthnormal, targetorigin);
                        // Trace to targetorigin so we don't set it out of the world in complex cases.
-                       if (prog == SVVM_prog)
-                               stucktrace = SV_TraceBox(testorigin, stuckmins, stuckmaxs, targetorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-                       else
-                               stucktrace = CL_TraceBox(testorigin, stuckmins, stuckmaxs, targetorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, pass ? false : true, false, NULL, false);
+                       stucktrace = PHYS_TraceBox(prog, testorigin, stuckmins, stuckmaxs, targetorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, pass ? false : true, false, NULL, false);
                        if (stucktrace.fraction)
                                VectorCopy(stucktrace.endpos, testorigin);
                        else
                                break; // Can't move it so no point doing more iterations on this pass.
                }
        }
-       return 0;
+       return UNSTICK_STUCK;
 }
diff --git a/phys.h b/phys.h
index d18cfb92ffca06efde88811d015c3931441d6a74..cbb3d2cc71fb660e53e0d09ca19415b4640b7b69 100644 (file)
--- a/phys.h
+++ b/phys.h
@@ -4,10 +4,27 @@
 #include "quakedef.h"
 
 
+qbool PHYS_TestEntityPosition (prvm_prog_t *prog, prvm_edict_t *ent, vec3_t offset);
+
+typedef enum unstickresult_e
+{
+       // matching the DP_QC_NUDGEOUTOFSOLID return values
+       UNSTICK_STUCK = 0,
+       UNSTICK_GOOD = -1, ///< didn't need to be unstuck
+       UNSTICK_UNSTUCK = 1
+}
+unstickresult_t;
+/*! move an entity that is stuck by small amounts in various directions to try to nudge it back into the collision hull
+ * returns 1 if it found a better place, 0 if it remains stuck, -1 if it wasn't stuck.
+ * Replaces SV_TryUnstick() and SV_CheckStuck() which in Quake applied to players only.
+ */
+unstickresult_t PHYS_UnstickEntityReturnOffset (prvm_prog_t *prog, prvm_edict_t *ent, vec3_t offset);
 /*! move an entity that is stuck out of the surface it is stuck in (can move large amounts)
+ * with consideration to the properties of the surface and support for multiple surfaces.
  * returns 1 if it found a better place, 0 if it remains stuck, -1 if it wasn't stuck.
+ * Replaces PHYS_UnstickEntityReturnOffset() but falls back to it when using cliphulls.
  */
-int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent);
+unstickresult_t PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent);
 extern cvar_t cl_gameplayfix_nudgeoutofsolid_separation;
 
 
index 4378e55814717e856aede3b4fc808807eeda64b6..26ba7e696d858c8be4a285989b7a42ba2393f47e 100644 (file)
--- a/server.h
+++ b/server.h
@@ -563,12 +563,6 @@ void SV_LinkEdict(prvm_edict_t *ent);
 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent);
 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent); // if we detected a touch from another source
 
-/*! move an entity that is stuck by small amounts in various directions to try to nudge it back into the collision hull
- * returns true if it found a better place
- * Replaces SV_TryUnstick() and SV_CheckStuck() which in Quake applied to players only.
- */
-qbool SV_UnstickEntity (prvm_edict_t *ent);
-
 /// calculates hitsupercontentsmask for a generic qc entity
 int SV_GenericHitSuperContentsMask(const prvm_edict_t *edict);
 /// traces a box move against worldmodel and all entities in the specified area
index 23649b5110da324fa7e44ba95eb9e37904ed2f7f..0743e0ed233229d8a1d7de5b6793e6598aedcaac 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -115,7 +115,7 @@ cvar_t sv_gameplayfix_impactbeforeonground = {CF_SERVER, "sv_gameplayfix_impactb
 cvar_t sv_gameplayfix_multiplethinksperframe = {CF_SERVER, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
 cvar_t sv_gameplayfix_noairborncorpse = {CF_SERVER, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
 cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {CF_SERVER, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
-cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors where an object ended up in solid for some reason, better than sv_gameplayfix_unstick* but currently has no effect on Q1BSP (unless mod_q1bsp_polygoncollisions is enabled)"};
+cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors where an object ended up in solid for some reason, smarter than sv_gameplayfix_unstick* except on Q1BSP with mod_q1bsp_polygoncollisions disabled (there it falls back to the unsticking method)"};
 cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
 cvar_t sv_gameplayfix_q2airaccelerate = {CF_SERVER, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
 cvar_t sv_gameplayfix_nogravityonground = {CF_SERVER, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"};
index a61bc2f893a999c25f0d8ce30d55b884ee9965c3..dbfc1baaa7cd37e70586d4ad025d102fa660b26f 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -915,74 +915,6 @@ Utility functions
 ===============================================================================
 */
 
-/*
-============
-SV_TestEntityPosition
-
-returns true if the entity is in solid currently
-============
-*/
-static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
-       int skipsupercontentsmask = 0;
-       int skipmaterialflagsmask = 0;
-       vec3_t org, entorigin, entmins, entmaxs;
-       trace_t trace;
-       VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
-       VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
-       VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
-       VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
-       trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
-       if (trace.startsupercontents & hitsupercontentsmask)
-               return true;
-       else
-       {
-               if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
-               {
-                       // q1bsp/hlbsp use hulls and if the entity does not exactly match
-                       // a hull size it is incorrectly tested, so this code tries to
-                       // 'fix' it slightly...
-                       // FIXME: this breaks entities larger than the hull size
-                       int i;
-                       vec3_t v, m1, m2, s;
-                       VectorAdd(org, entmins, m1);
-                       VectorAdd(org, entmaxs, m2);
-                       VectorSubtract(m2, m1, s);
-#define EPSILON (1.0f / 32.0f)
-                       if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
-                       if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
-                       if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
-                       for (i = 0;i < 8;i++)
-                       {
-                               v[0] = (i & 1) ? m2[0] : m1[0];
-                               v[1] = (i & 2) ? m2[1] : m1[1];
-                               v[2] = (i & 4) ? m2[2] : m1[2];
-                               if (SV_PointSuperContents(v) & hitsupercontentsmask)
-                                       return true;
-                       }
-               }
-       }
-       // if the trace found a better position for the entity, move it there
-       if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
-       {
-#if 0
-               // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
-               VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
-#else
-               // verify if the endpos is REALLY outside solid
-               VectorCopy(trace.endpos, org);
-               trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
-               if(trace.startsolid)
-                       Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
-               else
-                       VectorCopy(org, PRVM_serveredictvector(ent, origin));
-#endif
-       }
-       return false;
-}
-
 // DRESK - Support for Entity Contents Transition Event
 /*
 ================
@@ -1585,6 +1517,7 @@ The trace struct is filled with the trace that has been done.
 Returns true if the push did not result in the entity being teleported by QC code.
 ============
 */
+static qbool SV_UnstickEntity (prvm_edict_t *ent);
 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dotouch, qbool checkstuck)
 {
        prvm_prog_t *prog = SVVM_prog;
@@ -2007,67 +1940,6 @@ CLIENT MOVEMENT
 ===============================================================================
 */
 
-static float unstickoffsets[] =
-{
-       // poutting -/+z changes first as they are least weird
-        0,  0,  -1,
-        0,  0,  1,
-        // x or y changes
-       -1,  0,  0,
-        1,  0,  0,
-        0, -1,  0,
-        0,  1,  0,
-        // x and y changes
-       -1, -1,  0,
-        1, -1,  0,
-       -1,  1,  0,
-        1,  1,  0,
-};
-
-typedef enum unstickresult_e
-{
-       // matching the DP_QC_NUDGEOUTOFSOLID return values
-       UNSTICK_STUCK = 0,
-       UNSTICK_GOOD = -1, ///< didn't need to be unstuck
-       UNSTICK_UNSTUCK = 1
-}
-unstickresult_t;
-
-static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int i, maxunstick;
-
-       // if not stuck in a bmodel, just return
-       if (!SV_TestEntityPosition(ent, vec3_origin))
-               return UNSTICK_GOOD;
-
-       for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
-       {
-               if (!SV_TestEntityPosition(ent, unstickoffsets + i))
-               {
-                       VectorCopy(unstickoffsets + i, offset);
-                       return UNSTICK_UNSTUCK;
-               }
-       }
-
-       maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
-       // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
-
-       for(i = 2; i <= maxunstick; ++i)
-       {
-               VectorClear(offset);
-               offset[2] = -i;
-               if (!SV_TestEntityPosition(ent, offset))
-                       return UNSTICK_UNSTUCK;
-               offset[2] = i;
-               if (!SV_TestEntityPosition(ent, offset))
-                       return UNSTICK_UNSTUCK;
-       }
-
-       return UNSTICK_STUCK;
-}
-
 /*
 =============
 SV_CheckStuck
@@ -2076,13 +1948,12 @@ This is a big hack to try and fix the rare case of getting stuck in the world
 clipping hull.
 =============
 */
-qbool SV_UnstickEntity (prvm_edict_t *ent)
+static qbool SV_UnstickEntity (prvm_edict_t *ent)
 {
        prvm_prog_t *prog = SVVM_prog;
        vec3_t offset;
 
-       if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0
-       && sv.worldmodel->TraceBox == Mod_CollisionBIH_TraceBox) // Mod_Q1BSP_TraceBox doesn't support startdepth
+       if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
        {
                VectorCopy(PRVM_serveredictvector(ent, origin), offset);
                switch (PHYS_NudgeOutOfSolid(prog, ent))
@@ -2105,7 +1976,7 @@ qbool SV_UnstickEntity (prvm_edict_t *ent)
        if (!(PRVM_NUM_FOR_EDICT(ent) <= svs.maxclients ? sv_gameplayfix_unstickplayers : sv_gameplayfix_unstickentities).integer)
                return false;
 
-       switch(SV_UnstickEntityReturnOffset(ent, offset))
+       switch(PHYS_UnstickEntityReturnOffset(prog, ent, offset))
        {
                case UNSTICK_GOOD:
                        return true;
@@ -2114,7 +1985,7 @@ qbool SV_UnstickEntity (prvm_edict_t *ent)
                        return true;
                case UNSTICK_STUCK:
                        VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
-                       if (!SV_TestEntityPosition(ent, offset))
+                       if (!PHYS_TestEntityPosition(prog, ent, offset))
                        {
                                Con_DPrintf("Unstuck entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
                                return true;
@@ -2122,7 +1993,7 @@ qbool SV_UnstickEntity (prvm_edict_t *ent)
                        Con_DPrintf(CON_WARN "Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
                        return false;
                default:
-                       Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
+                       Con_Printf("UnstickEntityReturnOffset returned a value outside its enum.\n");
                        return false;
        }
 }