From bca5b75201307881fb7fcf0b28f8beceadc8b903 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Tue, 6 Aug 2024 01:50:34 +1000 Subject: [PATCH] nudgeoutofsolid: implement fallback for when cliphulls are in use 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 --- dpdefs/dpextensions.qc | 3 +- phys.c | 150 +++++++++++++++++++++++++++++++++++++---- phys.h | 19 +++++- server.h | 6 -- sv_main.c | 2 +- sv_phys.c | 141 ++------------------------------------ 6 files changed, 165 insertions(+), 156 deletions(-) diff --git a/dpdefs/dpextensions.qc b/dpdefs/dpextensions.qc index 42c8a2a2..d72666dc 100644 --- a/dpdefs/dpextensions.qc +++ b/dpdefs/dpextensions.qc @@ -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 8d71b868..9aca309d 100644 --- 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 d18cfb92..cbb3d2cc 100644 --- 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; diff --git a/server.h b/server.h index 4378e558..26ba7e69 100644 --- 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 diff --git a/sv_main.c b/sv_main.c index 23649b51..0743e0ed 100644 --- 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)"}; diff --git a/sv_phys.c b/sv_phys.c index a61bc2f8..dbfc1baa 100644 --- 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; } } -- 2.39.2