// sv_phys.c
#include "quakedef.h"
-// used only for VM_GetTempString
-#include "prvm_cmds.h"
/*
*/
-cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
-cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
-cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
-cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
-cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
-cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
-cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
-cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
-cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
-cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
-cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
-cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
-
-cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
-cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
-
-// TODO: move this extern to server.h
-extern cvar_t sv_clmovement_waitforinput;
-
#define MOVE_EPSILON 0.01
void SV_Physics_Toss (prvm_edict_t *ent);
-void SV_Phys_Init (void)
+/*
+===============================================================================
+
+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;
+ else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
+ {
+ if ((int)passedict->fields.server->flags & FL_MONSTER)
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
+ else
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
+ }
+ else if (passedict->fields.server->solid == SOLID_CORPSE)
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
+ else if (passedict->fields.server->solid == SOLID_TRIGGER)
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
+ else
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
+ }
+ else
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
+}
+
+/*
+==================
+SV_Move
+==================
+*/
+#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)
+#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)
+#endif
+{
+ vec3_t hullmins, hullmaxs;
+ int i, bodysupercontents;
+ int passedictprog;
+ qboolean pointtrace;
+ prvm_edict_t *traceowner, *touch;
+ trace_t trace;
+ // bounding box of entire move area
+ vec3_t clipboxmins, clipboxmaxs;
+ // size of the moving object
+ vec3_t clipmins, clipmaxs;
+ // 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
+ model_t *model;
+ // list of entities to test for collisions
+ int numtouchedicts;
+ prvm_edict_t *touchedicts[MAX_EDICTS];
+
+ VectorCopy(start, clipstart);
+ VectorCopy(end, clipend);
+ VectorCopy(mins, clipmins);
+ VectorCopy(maxs, clipmaxs);
+ VectorCopy(mins, clipmins2);
+ VectorCopy(maxs, 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_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
+ cliptrace.bmodelstartsolid = cliptrace.startsolid;
+ if (cliptrace.startsolid || cliptrace.fraction < 1)
+ cliptrace.ent = prog->edicts;
+ if (type == MOVE_WORLDONLY)
+ return cliptrace;
+
+ if (type == MOVE_MISSILE)
+ {
+ // LordHavoc: modified this, was = -15, now -= 15
+ for (i = 0;i < 3;i++)
+ {
+ clipmins2[i] -= 15;
+ clipmaxs2[i] += 15;
+ }
+ }
+
+ // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
+ if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
+ sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
+ else
+ {
+ VectorCopy(clipmins, hullmins);
+ VectorCopy(clipmaxs, hullmaxs);
+ }
+
+ // create the bounding box of the entire move
+ for (i = 0;i < 3;i++)
+ {
+ clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
+ clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[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);
+ // figure out whether this is a point trace for comparisons
+ pointtrace = VectorCompare(clipmins, clipmaxs);
+ // 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 (pointtrace && 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)
+ {
+ 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];
+ }
+ if (model)
+ 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);
+ 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, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+ else
+ Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, 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);
+ }
+
+ 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)
+{
+ int endstuck;
+ trace_t trace;
+ vec3_t temp;
+ trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
+ if (passedict)
+ {
+ VectorCopy(trace.endpos, temp);
+ endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
+#if COLLISIONPARANOID < 3
+ if (trace.startsolid || endstuck)
+#endif
+ Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, passedict->fields.server->origin[0], passedict->fields.server->origin[1], passedict->fields.server->origin[2], end[0] - passedict->fields.server->origin[0], end[1] - passedict->fields.server->origin[1], end[2] - passedict->fields.server->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.server->origin[0], trace.endpos[1] - passedict->fields.server->origin[1], trace.endpos[2] - passedict->fields.server->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
+ }
+ return trace;
+}
+#endif
+
+/*
+===============================================================================
+
+Linking entities into the world culling system
+
+===============================================================================
+*/
+
+void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
+{
+ int i, numtouchedicts, old_self, old_other;
+ prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
+
+ // build a list of edicts to touch, because the link loop can be corrupted
+ // by IncreaseEdicts called during touch functions
+ numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, 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;
+ }
+
+ old_self = prog->globals.server->self;
+ old_other = prog->globals.server->other;
+ for (i = 0;i < numtouchedicts;i++)
+ {
+ 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");
+ }
+ }
+ prog->globals.server->self = old_self;
+ prog->globals.server->other = old_other;
+}
+
+/*
+===============
+SV_LinkEdict
+
+===============
+*/
+void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
{
- Cvar_RegisterVariable(&sv_stepheight);
- Cvar_RegisterVariable(&sv_jumpstep);
- Cvar_RegisterVariable(&sv_wallfriction);
- Cvar_RegisterVariable(&sv_newflymove);
- Cvar_RegisterVariable(&sv_freezenonclients);
+ model_t *model;
+ vec3_t mins, maxs;
+
+ if (ent == prog->edicts)
+ return; // don't add the world
+
+ if (ent->priv.server->free)
+ return;
+
+// set the abs box
+
+ 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 (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
+ {
+ VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
+ VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
+ }
+ else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
+ {
+ VectorAdd(ent->fields.server->origin, model->yawmins, mins);
+ VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
+ }
+ else
+ {
+ VectorAdd(ent->fields.server->origin, model->normalmins, mins);
+ VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
+ }
+ }
+ else
+ {
+ // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
+ VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
+ VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
+ }
+ }
+ else
+ {
+ VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
+ VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
+ }
+
+//
+// to make items easier to pick up and allow them to be grabbed off
+// of shelves, the abs sizes are expanded
+//
+ if ((int)ent->fields.server->flags & FL_ITEM)
+ {
+ mins[0] -= 15;
+ mins[1] -= 15;
+ mins[2] -= 1;
+ maxs[0] += 15;
+ maxs[1] += 15;
+ maxs[2] += 1;
+ }
+ else
+ {
+ // because movement is clipped an epsilon away from an actual edge,
+ // we must fully check even when bounding boxes don't quite touch
+ mins[0] -= 1;
+ mins[1] -= 1;
+ mins[2] -= 1;
+ maxs[0] += 1;
+ maxs[1] += 1;
+ maxs[2] += 1;
+ }
+
+ VectorCopy(mins, ent->fields.server->absmin);
+ VectorCopy(maxs, ent->fields.server->absmax);
- Cvar_RegisterVariable(&sv_playerphysicsqc);
+ World_LinkEdict(&sv.world, ent, mins, maxs);
- Cvar_RegisterVariable(&sv_sound_watersplash);
- Cvar_RegisterVariable(&sv_sound_land);
+ // if touch_triggers, call touch on all entities overlapping this box
+ if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
+ SV_LinkEdict_TouchAreaGrid(ent);
}
+/*
+===============================================================================
+
+Utility functions
+
+===============================================================================
+*/
+
/*
============
SV_TestEntityPosition
returns true if the entity is in solid currently
============
*/
-static int SV_TestEntityPosition (prvm_edict_t *ent)
+static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
{
- trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
+ vec3_t org;
+ trace_t trace;
+ 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, SUPERCONTENTS_SOLID);
if (trace.startsupercontents & SUPERCONTENTS_SOLID)
return true;
else
{
- if (sv.worldmodel->brushq1.hulls && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
+ if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->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;
+ vec3_t v, m1, m2, s;
+ VectorAdd(org, ent->fields.server->mins, m1);
+ VectorAdd(org, ent->fields.server->maxs, 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) ? ent->fields.server->maxs[0] : ent->fields.server->mins[0];
- v[1] = (i & 2) ? ent->fields.server->maxs[1] : ent->fields.server->mins[1];
- v[2] = (i & 4) ? ent->fields.server->maxs[2] : ent->fields.server->mins[2];
+ 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) & SUPERCONTENTS_SOLID)
return true;
}
}
- return false;
}
+ // if the trace found a better position for the entity, move it there
+ if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
+ VectorCopy(trace.endpos, ent->fields.server->origin);
+ return false;
}
/*
|| check->fields.server->movetype == MOVETYPE_NOCLIP)
continue;
- if (SV_TestEntityPosition (check))
+ if (SV_TestEntityPosition (check, vec3_origin))
Con_Print("entity in invalid position\n");
}
}
if(ent->fields.server->watertype != nContents)
{ // Changed Contents
// Acquire Contents Transition Function from QC
- contentstransition = PRVM_GETEDICTFIELDVALUE(ent, eval_contentstransition);
+ contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
if(contentstransition->function)
{ // Valid Function; Execute
PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
// New Contents
PRVM_G_FLOAT(OFS_PARM1) = nContents;
+ // Assign Self
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
// Execute VM Function
PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
}
*/
qboolean SV_RunThink (prvm_edict_t *ent)
{
- float thinktime;
-
- thinktime = ent->fields.server->nextthink;
- if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
- return true;
+ int iterations;
// don't let things stay in the past.
// it is possible to start that way by a trigger with a local time.
- if (thinktime < sv.time)
- thinktime = sv.time;
+ if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
+ return true;
- ent->fields.server->nextthink = 0;
- prog->globals.server->time = thinktime;
- prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
- prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
- PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
+ for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
+ {
+ prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
+ ent->fields.server->nextthink = 0;
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+ prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
+ PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
+ // mods often set nextthink to time to cause a think every frame,
+ // we don't want to loop in that case, so exit if the new nextthink is
+ // <= the time the qc was told, also exit if it is past the end of the
+ // frame
+ if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
+ break;
+ }
return !ent->priv.server->free;
}
Two entities have touched, so run their touch functions
==================
*/
+extern void VM_SetTraceGlobals(const trace_t *trace);
+extern sizebuf_t vm_tempstringsbuf;
void SV_Impact (prvm_edict_t *e1, trace_t *trace)
{
+ int restorevm_tempstringsbuf_cursize;
int old_self, old_other;
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;
+
+ VM_SetTraceGlobals(trace);
prog->globals.server->time = sv.time;
if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
{
prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
- prog->globals.server->trace_allsolid = trace->allsolid;
- prog->globals.server->trace_startsolid = trace->startsolid;
- prog->globals.server->trace_fraction = trace->fraction;
- prog->globals.server->trace_inwater = trace->inwater;
- prog->globals.server->trace_inopen = trace->inopen;
- VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
- VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
- prog->globals.server->trace_plane_dist = trace->plane.dist;
- if (trace->ent)
- prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
- else
- prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
- val->_float = trace->startsupercontents;
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
- val->_float = trace->hitsupercontents;
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
- val->_float = trace->hitq3surfaceflags;
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
- {
- if (trace->hittexture)
- {
- char *s = VM_GetTempString();
- strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
- val->string = PRVM_SetEngineString(s);
- }
- else
- val->string = 0;
- }
PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
}
{
prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
- 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 (e2->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;
+ VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
+ 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_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
val->_float = 0;
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
val->_float = 0;
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
val->_float = 0;
- if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
val->string = 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;
}
*/
// LordHavoc: increased from 5 to 32
#define MAX_CLIP_PLANES 32
-int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
+int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
{
int blocked, bumpcount;
int i, j, impact, numplanes;
float d, time_left;
vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
trace_t trace;
+ if (time <= 0)
+ return 0;
blocked = 0;
VectorCopy(ent->fields.server->velocity, original_velocity);
VectorCopy(ent->fields.server->velocity, primal_velocity);
break;
VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
- trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
+ trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
#if 0
//if (trace.fraction < 0.002)
{
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);
+ 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);
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);
+ testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
if (trace.fraction < testtrace.fraction)
{
trace = testtrace;
trace.ent = prog->edicts;
}
- if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
- impact = false;
- else
- {
- ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
- impact = true;
- }
+ impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
if (trace.plane.normal[2])
{
{
// 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);
+ trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
}
*/
float ent_gravity;
prvm_eval_t *val;
- val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
+ val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
if (val!=0 && val->_float)
ent_gravity = val->_float;
else
else
type = MOVE_NORMAL;
- trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
+ trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
if (trace.bmodelstartsolid && failonbmodelstartsolid)
return trace;
static prvm_edict_t *checkentities[MAX_EDICTS];
model_t *pushermodel;
trace_t trace;
+ matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
+ 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])
{
pusher->fields.server->ltime += movetime;
SV_LinkEdict (pusher, false);
+ pushermodel = NULL;
+ if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
+ pushermodel = sv.models[(int)pusher->fields.server->modelindex];
+ 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);
+
savesolid = pusher->fields.server->solid;
// see if any solid entities are inside the final position
num_moved = 0;
- numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
+ numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
for (e = 0;e < numcheckentities;e++)
{
prvm_edict_t *check = checkentities[e];
+ int checkcontents = SV_GenericHitSuperContentsMask(check);
if (check->fields.server->movetype == MOVETYPE_NONE
|| check->fields.server->movetype == MOVETYPE_PUSH
|| check->fields.server->movetype == MOVETYPE_FOLLOW
continue;
// if the entity is standing on the pusher, it will definitely be moved
- if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
- {
- // remove the onground flag for non-players
- if (check->fields.server->movetype != MOVETYPE_WALK)
- check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
- }
- else
+ // if the entity is not standing on the pusher, but is in the pusher's
+ // final position, move it
+ if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
{
- // if the entity is not inside the pusher's final position, leave it alone
- if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
+ Collision_ClipToGenericEntity(&trace, pushermodel, 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);
+ if (!trace.startsolid)
continue;
}
VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
- sv.moved_edicts[num_moved++] = check;
+ moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
// try moving the contacted entity
pusher->fields.server->solid = SOLID_NOT;
pusher->fields.server->solid = savesolid; // was SOLID_BSP
//Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
+ // this trace.fraction < 1 check causes items to fall off of pushers
+ // if they pass under or through a wall
+ // the groundentity check causes items to fall off of ledges
+ if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
+ check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
+
// if it is still inside the pusher, block
- if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
+ Collision_ClipToGenericEntity(&trace, pushermodel, 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);
+ if (trace.startsolid)
{
// try moving the contacted entity a tiny bit further to account for precision errors
vec3_t move2;
VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
SV_PushEntity (check, move2, true);
pusher->fields.server->solid = savesolid;
- if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
+ Collision_ClipToGenericEntity(&trace, pushermodel, 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);
+ if (trace.startsolid)
{
// try moving the contacted entity a tiny bit less to account for precision errors
pusher->fields.server->solid = SOLID_NOT;
VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
SV_PushEntity (check, move2, true);
pusher->fields.server->solid = savesolid;
- if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
+ Collision_ClipToGenericEntity(&trace, pushermodel, 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);
+ if (trace.startsolid)
{
// still inside pusher, so it's really blocked
// move back any entities we already moved
for (i = 0;i < num_moved;i++)
{
- prvm_edict_t *ed = sv.moved_edicts[i];
+ 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);
===============================================================================
*/
+static float unstickoffsets[] =
+{
+ -1, 0, 0,
+ 1, 0, 0,
+ 0, -1, 0,
+ 0, 1, 0,
+ -1, -1, 0,
+ 1, -1, 0,
+ -1, 1, 0,
+ 1, 1, 0,
+ 0, 0, -1,
+ 0, 0, 1,
+ 0, 0, 2,
+ 0, 0, 3,
+ 0, 0, 4,
+ 0, 0, 5,
+ 0, 0, 6,
+ 0, 0, 7,
+ 0, 0, 8,
+ 0, 0, 9,
+ 0, 0, 10,
+ 0, 0, 11,
+ 0, 0, 12,
+ 0, 0, 13,
+ 0, 0, 14,
+ 0, 0, 15,
+ 0, 0, 16,
+ 0, 0, 17,
+};
+
/*
=============
SV_CheckStuck
*/
void SV_CheckStuck (prvm_edict_t *ent)
{
- int i, j, z;
- vec3_t org;
+ int i;
+ vec3_t offset;
- if (!SV_TestEntityPosition(ent))
+ if (!SV_TestEntityPosition(ent, vec3_origin))
{
VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
return;
}
- VectorCopy (ent->fields.server->origin, org);
- VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
- if (!SV_TestEntityPosition(ent))
+ for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
+ {
+ if (!SV_TestEntityPosition(ent, unstickoffsets + i))
+ {
+ Con_DPrintf("Unstuck player 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;
+ }
+ }
+
+ VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
+ 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);
return;
}
- for (z=-1 ; z< 18 ; z++)
- for (i=-1 ; i <= 1 ; i++)
- for (j=-1 ; j <= 1 ; j++)
- {
- ent->fields.server->origin[0] = org[0] + i;
- ent->fields.server->origin[1] = org[1] + j;
- ent->fields.server->origin[2] = org[2] + z;
- if (!SV_TestEntityPosition(ent))
- {
- Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
- SV_LinkEdict (ent, true);
- return;
- }
- }
-
- VectorCopy (org, ent->fields.server->origin);
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)
{
- int i, j, z;
- vec3_t org;
+ int i;
// if not stuck in a bmodel, just return
- if (!SV_TestEntityPosition(ent))
+ if (!SV_TestEntityPosition(ent, vec3_origin))
return;
- VectorCopy (ent->fields.server->origin, org);
-
- for (z=-1 ; z< 18 ; z += 6)
- for (i=-1 ; i <= 1 ; i++)
- for (j=-1 ; j <= 1 ; j++)
- {
- ent->fields.server->origin[0] = org[0] + i;
- ent->fields.server->origin[1] = org[1] + j;
- ent->fields.server->origin[2] = org[2] + z;
- if (!SV_TestEntityPosition(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), (float)i, (float)j, (float)z);
- SV_LinkEdict (ent, true);
- return;
- }
- }
+ for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
+ {
+ if (!SV_TestEntityPosition(ent, unstickoffsets + i))
+ {
+ 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;
+ }
+ }
- VectorCopy (org, ent->fields.server->origin);
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 0
/*
=====================
SV_TryUnstick
ent->fields.server->velocity[0] = oldvel[0];
ent->fields.server->velocity[1] = oldvel[1];
ent->fields.server->velocity[2] = 0;
- clip = SV_FlyMove (ent, 0.1, NULL);
+ clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
|| fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
Con_DPrint("TryUnstick - failure.\n");
return 7;
}
+#endif
/*
=====================
*/
void SV_WalkMove (prvm_edict_t *ent)
{
- int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
+ int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
trace_t downtrace;
+ // if frametime is 0 (due to client sending the same timestamp twice),
+ // don't move
+ if (sv.frametime <= 0)
+ return;
+
+ hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
+
SV_CheckVelocity(ent);
// do a regular slide move unless it looks like you ran into a step
oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
- ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
VectorCopy (ent->fields.server->origin, start_origin);
VectorCopy (ent->fields.server->velocity, start_velocity);
- clip = SV_FlyMove (ent, sv.frametime, NULL);
+ clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
+
+ // if the move did not hit the ground at any point, we're not on ground
+ if (!(clip & 1))
+ ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
SV_CheckVelocity(ent);
// move forward
ent->fields.server->velocity[2] = 0;
- clip = SV_FlyMove (ent, sv.frametime, stepnormal);
+ clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
ent->fields.server->velocity[2] += start_velocity[2];
SV_CheckVelocity(ent);
if (clip & 2 && sv_wallfriction.integer)
SV_WallFriction (ent, stepnormal);
}
- // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
- else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
+ // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
+ else if (!sv_gameplayfix_stepdown.integer || ent->fields.server->waterlevel >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)ent->fields.server->flags & FL_ONGROUND))
return;
// move down
if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
{
// this has been disabled so that you can't jump when you are stepping
- // up while already jumping (also known as the Quake2 stair jump bug)
+ // up while already jumping (also known as the Quake2 double jump bug)
#if 0
// LordHavoc: disabled this check so you can walk on monsters/players
//if (ent->fields.server->solid == SOLID_BSP)
ent->fields.server->flags -= FL_ONGROUND;
SV_AddGravity(ent);
SV_CheckVelocity(ent);
- SV_FlyMove(ent, sv.frametime, NULL);
+ SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
SV_LinkEdict(ent, true);
}
}
SV_AddGravity(ent);
SV_CheckVelocity(ent);
- SV_FlyMove(ent, sv.frametime, NULL);
+ SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
SV_LinkEdict(ent, true);
// just hit ground
case MOVETYPE_FLYMISSILE:
case MOVETYPE_FLY:
// regular thinking
- if (SV_RunThink (ent) && runmove)
+ if (SV_RunThink (ent) && (runmove || !sv_gameplayfix_delayprojectiles.integer))
SV_Physics_Toss (ent);
break;
default:
}
}
-void SV_ApplyClientMove (void);
-void SV_Physics_ClientEntity (prvm_edict_t *ent)
+void SV_Physics_ClientMove(void)
{
- SV_ApplyClientMove();
+ prvm_edict_t *ent;
+ ent = host_client->edict;
+
+ // call player physics, this needs the proper frametime
+ prog->globals.server->frametime = sv.frametime;
+ SV_ClientThink();
+
+ // call standard client pre-think, with frametime = 0
+ prog->globals.server->time = sv.time;
+ prog->globals.server->frametime = 0;
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+ PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
+ prog->globals.server->frametime = sv.frametime;
+
// make sure the velocity is sane (not a NaN)
SV_CheckVelocity(ent);
- // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
- if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
+ // 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
+ if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
+ SV_AddGravity (ent);
+ SV_CheckStuck (ent);
+ SV_WalkMove (ent);
+
+ SV_CheckVelocity (ent);
+
+ SV_LinkEdict (ent, true);
+
+ SV_CheckVelocity (ent);
+
+ // call standard player post-think, with frametime = 0
+ prog->globals.server->time = sv.time;
+ prog->globals.server->frametime = 0;
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+ PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
+ prog->globals.server->frametime = sv.frametime;
+
+ if(ent->fields.server->fixangle)
{
- prog->globals.server->time = sv.time;
- prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
- PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
+ // 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;
}
- else
- SV_ClientThink ();
+}
+
+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;
+ }
+
+ // don't run physics here if running asynchronously
+ if (host_client->clmovement_skipphysicsframes <= 0)
+ SV_ClientThink();
+
// make sure the velocity is sane (not a NaN)
SV_CheckVelocity(ent);
// LordHavoc: a hack to ensure that the (rather silly) id1 quakec
// (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);
SV_Physics_Follow (ent);
break;
case MOVETYPE_NOCLIP:
- if (SV_RunThink(ent))
- {
- SV_CheckWater(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_RunThink(ent);
+ SV_CheckWater(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);
break;
case MOVETYPE_STEP:
SV_Physics_Step (ent);
break;
case MOVETYPE_WALK:
- if (SV_RunThink (ent))
+ SV_RunThink (ent);
+ // don't run physics here if running asynchronously
+ if (host_client->clmovement_skipphysicsframes <= 0)
{
if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
SV_AddGravity (ent);
case MOVETYPE_BOUNCEMISSILE:
case MOVETYPE_FLYMISSILE:
// regular thinking
- if (SV_RunThink (ent))
- SV_Physics_Toss (ent);
+ SV_RunThink (ent);
+ SV_Physics_Toss (ent);
break;
case MOVETYPE_FLY:
- if (SV_RunThink (ent))
- {
- SV_CheckWater (ent);
- SV_WalkMove (ent);
- }
+ SV_RunThink (ent);
+ SV_CheckWater (ent);
+ SV_WalkMove (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_skipphysicsframes > 0)
+ host_client->clmovement_skipphysicsframes--;
+
SV_CheckVelocity (ent);
- // call standard player post-think
SV_LinkEdict (ent, true);
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;
+ }
}
/*
// 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)
- {
- // don't do physics on disconnected clients, FrikBot relies on this
- if (!host_client->spawned)
- memset(&host_client->cmd, 0, sizeof(host_client->cmd));
- // don't run physics here if running asynchronously
- else if (host_client->clmovement_skipphysicsframes > 0)
- host_client->clmovement_skipphysicsframes--;
- else
SV_Physics_ClientEntity(ent);
- }
- }
// run physics on all the non-client entities
if (!sv_freezenonclients.integer)
prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
// LordHavoc: endframe support
- if (EndFrameQC)
+ if (prog->funcoffsets.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 ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
+ PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
}
// decrement prog->num_edicts if the highest number entities died
if (!sv_freezenonclients.integer)
sv.time += sv.frametime;
}
-
-
-trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
-{
- int i;
- float gravity;
- vec3_t move, end;
- vec3_t original_origin;
- vec3_t original_velocity;
- vec3_t original_angles;
- vec3_t original_avelocity;
- prvm_eval_t *val;
- trace_t trace;
-
- VectorCopy(tossent->fields.server->origin , original_origin );
- VectorCopy(tossent->fields.server->velocity , original_velocity );
- VectorCopy(tossent->fields.server->angles , original_angles );
- VectorCopy(tossent->fields.server->avelocity, original_avelocity);
-
- val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
- if (val != NULL && val->_float != 0)
- gravity = val->_float;
- else
- gravity = 1.0;
- gravity *= sv_gravity.value * 0.05;
-
- for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
- {
- SV_CheckVelocity (tossent);
- tossent->fields.server->velocity[2] -= gravity;
- VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
- VectorScale (tossent->fields.server->velocity, 0.05, move);
- VectorAdd (tossent->fields.server->origin, move, end);
- trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
- VectorCopy (trace.endpos, tossent->fields.server->origin);
-
- if (trace.fraction < 1)
- break;
- }
-
- VectorCopy(original_origin , tossent->fields.server->origin );
- VectorCopy(original_velocity , tossent->fields.server->velocity );
- VectorCopy(original_angles , tossent->fields.server->angles );
- VectorCopy(original_avelocity, tossent->fields.server->avelocity);
-
- return trace;
-}
-