extern cvar_t sv_gameplayfix_blowupfallenzombies;
extern cvar_t sv_gameplayfix_delayprojectiles;
extern cvar_t sv_gameplayfix_droptofloorstartsolid;
+extern cvar_t sv_gameplayfix_droptofloorstartsolid_nudgetocorrect;
extern cvar_t sv_gameplayfix_easierwaterjump;
extern cvar_t sv_gameplayfix_findradiusdistancetobox;
extern cvar_t sv_gameplayfix_grenadebouncedownslopes;
// if touchtriggers, calls prog functions for the intersected triggers
void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers);
+// 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
+qboolean 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
cvar_t sv_gameplayfix_blowupfallenzombies = {0, "sv_gameplayfix_blowupfallenzombies", "1", "causes findradius to detect SOLID_NOT entities such as zombies and corpses on the floor, allowing splash damage to apply to them"};
cvar_t sv_gameplayfix_delayprojectiles = {0, "sv_gameplayfix_delayprojectiles", "1", "causes entities to not move on the same frame they are spawned, meaning that projectiles wait until the next frame to perform their first move, giving proper interpolation and rocket trails, but making weapons harder to use at low framerates"};
cvar_t sv_gameplayfix_droptofloorstartsolid = {0, "sv_gameplayfix_droptofloorstartsolid", "1", "prevents items and monsters that start in a solid area from falling out of the level (makes droptofloor treat trace_startsolid as an acceptable outcome)"};
+cvar_t sv_gameplayfix_droptofloorstartsolid_nudgetocorrect = {0, "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect", "1", "tries to nudge stuck items and monsters out of walls before droptofloor is performed"};
cvar_t sv_gameplayfix_easierwaterjump = {0, "sv_gameplayfix_easierwaterjump", "1", "changes water jumping to make it easier to get out of water (exactly like in QuakeWorld)"};
cvar_t sv_gameplayfix_findradiusdistancetobox = {0, "sv_gameplayfix_findradiusdistancetobox", "1", "causes findradius to check the distance to the corner of a box rather than the center of the box, makes findradius detect bmodels such as very large doors that would otherwise be unaffected by splash damage"};
cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
Cvar_RegisterVariable (&sv_gameplayfix_delayprojectiles);
Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
+ Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid_nudgetocorrect);
Cvar_RegisterVariable (&sv_gameplayfix_easierwaterjump);
Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
{
// hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities.
Cvar_SetValueQuick (&sv_gameplayfix_blowupfallenzombies, 0);
+ // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue
+ Cvar_SetValueQuick (&sys_ticrate, 0.02);
}
if (gamemode == GAME_ROGUE)
{
1, 1, 0,
0, 0, -1,
0, 0, 1,
+ 0, 0, -2,
0, 0, 2,
+ 0, 0, -3,
0, 0, 3,
+ 0, 0, -4,
0, 0, 4,
+ 0, 0, -5,
0, 0, 5,
+ 0, 0, -6,
0, 0, 6,
+ 0, 0, -7,
0, 0, 7,
+ 0, 0, -8,
0, 0, 8,
+ 0, 0, -9,
0, 0, 9,
+ 0, 0, -10,
0, 0, 10,
+ 0, 0, -11,
0, 0, 11,
+ 0, 0, -12,
0, 0, 12,
+ 0, 0, -13,
0, 0, 13,
+ 0, 0, -14,
0, 0, 14,
+ 0, 0, -15,
0, 0, 15,
+ 0, 0, -16,
0, 0, 16,
+ 0, 0, -17,
0, 0, 17,
};
Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
}
-static void SV_UnstickEntity (prvm_edict_t *ent)
+qboolean SV_UnstickEntity (prvm_edict_t *ent)
{
int i;
// if not stuck in a bmodel, just return
if (!SV_TestEntityPosition(ent, vec3_origin))
- return;
+ return true;
for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
{
{
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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
SV_LinkEdict (ent, true);
- return;
+ return true;
}
}
if (developer.integer >= 100)
Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
+ return false;
}
VectorCopy (ent->fields.server->origin, end);
end[2] -= 256;
- trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
+ if (sv_gameplayfix_droptofloorstartsolid_nudgetocorrect.integer)
+ SV_UnstickEntity(ent);
- if (trace.fraction != 1 || (trace.startsolid && sv_gameplayfix_droptofloorstartsolid.integer))
- {
- if (trace.fraction < 1)
+ trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
+ if (trace.startsolid && sv_gameplayfix_droptofloorstartsolid.integer)
+ {
+ vec3_t offset, org;
+ VectorSet(offset, 0.5f * (ent->fields.server->mins[0] + ent->fields.server->maxs[0]), 0.5f * (ent->fields.server->mins[1] + ent->fields.server->maxs[1]), ent->fields.server->mins[2]);
+ VectorAdd(ent->fields.server->origin, offset, org);
+ trace = SV_Move (org, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
+ VectorSubtract(trace.endpos, offset, trace.endpos);
+ if (trace.startsolid)
+ {
+ Con_DPrintf("droptofloor at %f %f %f - COULD NOT FIX BADLY PLACED ENTITY\n", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
+ SV_UnstickEntity(ent);
+ SV_LinkEdict (ent, false);
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = 0;
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+ }
+ else if (trace.fraction < 1)
+ {
+ Con_DPrintf("droptofloor at %f %f %f - FIXED BADLY PLACED ENTITY\n", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
VectorCopy (trace.endpos, ent->fields.server->origin);
- SV_LinkEdict (ent, false);
- ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
- ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
- PRVM_G_FLOAT(OFS_RETURN) = 1;
- // if support is destroyed, keep suspended (gross hack for floating items in various maps)
- ent->priv.server->suspendedinairflag = true;
+ SV_UnstickEntity(ent);
+ SV_LinkEdict (ent, false);
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+ // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+ ent->priv.server->suspendedinairflag = true;
+ }
+ }
+ else
+ {
+ if (trace.fraction != 1)
+ {
+ if (trace.fraction < 1)
+ VectorCopy (trace.endpos, ent->fields.server->origin);
+ SV_LinkEdict (ent, false);
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+ // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+ ent->priv.server->suspendedinairflag = true;
+ }
}
}