This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
-onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
+onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
*/
-cvar_t sv_friction = {"sv_friction","4",false,true};
-cvar_t sv_stopspeed = {"sv_stopspeed","100"};
-cvar_t sv_gravity = {"sv_gravity","800",false,true};
-cvar_t sv_maxvelocity = {"sv_maxvelocity","2000"};
-cvar_t sv_nostep = {"sv_nostep","0"};
+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_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
+
+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 (edict_t *ent);
+void SV_Physics_Toss (prvm_edict_t *ent);
+
+void SV_Phys_Init (void)
+{
+ Cvar_RegisterVariable(&sv_stepheight);
+ Cvar_RegisterVariable(&sv_jumpstep);
+ Cvar_RegisterVariable(&sv_wallfriction);
+ Cvar_RegisterVariable(&sv_newflymove);
+ Cvar_RegisterVariable(&sv_freezenonclients);
+ Cvar_RegisterVariable(&sv_playerphysicsqc);
+ Cvar_RegisterVariable(&sv_debugmove);
+
+ Cvar_RegisterVariable(&sv_sound_watersplash);
+ Cvar_RegisterVariable(&sv_sound_land);
+}
+
+/*
+===============================================================================
+
+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 SV_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)
+{
+ 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)
+ 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);
+
+ World_LinkEdict(&sv.world, ent, mins, maxs);
+
+ // 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)
+{
+ trace_t trace = SV_Move (ent->fields.server->origin, 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.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...
+ int i;
+ vec3_t v, m1, m2, s;
+ VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
+ VectorAdd(ent->fields.server->origin, 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) ? 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;
+ }
+}
/*
================
*/
void SV_CheckAllEnts (void)
{
- int e;
- edict_t *check;
+ int e;
+ prvm_edict_t *check;
-// see if any solid entities are inside the final position
- check = NEXT_EDICT(sv.edicts);
- for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+ // see if any solid entities are inside the final position
+ check = PRVM_NEXT_EDICT(prog->edicts);
+ for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
{
- if (check->free)
+ if (check->priv.server->free)
continue;
- if (check->v.movetype == MOVETYPE_PUSH
- || check->v.movetype == MOVETYPE_NONE
- || check->v.movetype == MOVETYPE_FOLLOW
- || check->v.movetype == MOVETYPE_NOCLIP)
+ if (check->fields.server->movetype == MOVETYPE_PUSH
+ || check->fields.server->movetype == MOVETYPE_NONE
+ || check->fields.server->movetype == MOVETYPE_FOLLOW
+ || check->fields.server->movetype == MOVETYPE_NOCLIP)
continue;
if (SV_TestEntityPosition (check))
- Con_Printf ("entity in invalid position\n");
+ Con_Print("entity in invalid position\n");
+ }
+}
+
+// DRESK - Support for Entity Contents Transition Event
+/*
+================
+SV_CheckContentsTransition
+
+returns true if entity had a valid contentstransition function call
+================
+*/
+int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
+{
+ int bValidFunctionCall;
+ prvm_eval_t *contentstransition;
+
+ // Default Valid Function Call to False
+ bValidFunctionCall = false;
+
+ if(ent->fields.server->watertype != nContents)
+ { // Changed Contents
+ // Acquire Contents Transition Function from QC
+ contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
+
+ if(contentstransition->function)
+ { // Valid Function; Execute
+ // Assign Valid Function
+ bValidFunctionCall = true;
+ // Prepare Parameters (Original Contents, New Contents)
+ // Original Contents
+ PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
+ // New Contents
+ PRVM_G_FLOAT(OFS_PARM1) = nContents;
+ // Execute VM Function
+ PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
+ }
}
+
+ // Return if Function Call was Valid
+ return bValidFunctionCall;
}
+
/*
================
SV_CheckVelocity
================
*/
-void SV_CheckVelocity (edict_t *ent)
+void SV_CheckVelocity (prvm_edict_t *ent)
{
- int i;
- float wishspeed;
+ int i;
+ float wishspeed;
//
// bound velocity
//
for (i=0 ; i<3 ; i++)
{
- if (IS_NAN(ent->v.velocity[i]))
+ if (IS_NAN(ent->fields.server->velocity[i]))
{
- Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname);
- ent->v.velocity[i] = 0;
+ Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
+ ent->fields.server->velocity[i] = 0;
}
- if (IS_NAN(ent->v.origin[i]))
+ if (IS_NAN(ent->fields.server->origin[i]))
{
- Con_Printf ("Got a NaN origin on %s\n", pr_strings + ent->v.classname);
- ent->v.origin[i] = 0;
+ Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
+ ent->fields.server->origin[i] = 0;
}
- // LordHavoc: maxvelocity fix, see below
-/*
- if (ent->v.velocity[i] > sv_maxvelocity.value)
- ent->v.velocity[i] = sv_maxvelocity.value;
- else if (ent->v.velocity[i] < -sv_maxvelocity.value)
- ent->v.velocity[i] = -sv_maxvelocity.value;
-*/
}
// LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
- wishspeed = DotProduct(ent->v.velocity, ent->v.velocity);
+ wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
{
wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
- ent->v.velocity[0] *= wishspeed;
- ent->v.velocity[1] *= wishspeed;
- ent->v.velocity[2] *= wishspeed;
- wishspeed = sv_maxvelocity.value;
+ ent->fields.server->velocity[0] *= wishspeed;
+ ent->fields.server->velocity[1] *= wishspeed;
+ ent->fields.server->velocity[2] *= wishspeed;
}
}
Returns false if the entity removed itself.
=============
*/
-qboolean SV_RunThink (edict_t *ent)
+qboolean SV_RunThink (prvm_edict_t *ent)
{
- float thinktime;
+ float thinktime;
- thinktime = ent->v.nextthink;
- if (thinktime <= 0 || thinktime > sv.time + host_frametime)
+ thinktime = ent->fields.server->nextthink;
+ if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
return true;
-
+
+ // 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; // don't let things stay in the past.
- // it is possible to start that way
- // by a trigger with a local time.
- ent->v.nextthink = 0;
- pr_global_struct->time = thinktime;
- pr_global_struct->self = EDICT_TO_PROG(ent);
- pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
- PR_ExecuteProgram (ent->v.think);
- return !ent->free;
+ thinktime = sv.time;
+
+ 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");
+ return !ent->priv.server->free;
}
/*
Two entities have touched, so run their touch functions
==================
*/
-void SV_Impact (edict_t *e1, edict_t *e2)
+extern void VM_SetTraceGlobals(const trace_t *trace);
+void SV_Impact (prvm_edict_t *e1, trace_t *trace)
{
- int old_self, old_other;
-
- old_self = pr_global_struct->self;
- old_other = pr_global_struct->other;
-
- pr_global_struct->time = sv.time;
- if (e1->v.touch && e1->v.solid != SOLID_NOT)
+ 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;
+
+ 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)
{
- pr_global_struct->self = EDICT_TO_PROG(e1);
- pr_global_struct->other = EDICT_TO_PROG(e2);
- PR_ExecuteProgram (e1->v.touch);
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
+ prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
+ PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
}
-
- if (e2->v.touch && e2->v.solid != SOLID_NOT)
+
+ if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
{
- pr_global_struct->self = EDICT_TO_PROG(e2);
- pr_global_struct->other = EDICT_TO_PROG(e1);
- PR_ExecuteProgram (e2->v.touch);
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
+ prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
+ 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_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 (e2->fields.server->touch, "QC function self.touch is missing");
}
- pr_global_struct->self = old_self;
- pr_global_struct->other = old_other;
+ prog->globals.server->self = old_self;
+ prog->globals.server->other = old_other;
}
returns the blocked flags (1 = floor, 2 = step / wall)
==================
*/
-#define STOP_EPSILON 0.1
-
-int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
+#define STOP_EPSILON 0.1
+void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
{
- float backoff;
- float change;
- int i, blocked;
-
- blocked = 0;
- if (normal[2] > 0)
- blocked |= 1; // floor
- if (!normal[2])
- blocked |= 2; // step
-
- backoff = DotProduct (in, normal) * overbounce;
+ int i;
+ float backoff;
- for (i=0 ; i<3 ; i++)
- {
- change = normal[i]*backoff;
- out[i] = in[i] - change;
+ backoff = -DotProduct (in, normal) * overbounce;
+ VectorMA(in, backoff, normal, out);
+
+ for (i = 0;i < 3;i++)
if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
out[i] = 0;
- }
-
- return blocked;
}
1 = floor
2 = wall / step
4 = dead stop
-If steptrace is not NULL, the trace of any vertical wall hit will be stored
+If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
============
*/
-#define MAX_CLIP_PLANES 5
-int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
+// LordHavoc: increased from 5 to 32
+#define MAX_CLIP_PLANES 32
+int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
{
- int bumpcount, numbumps;
- vec3_t dir;
- float d;
- int numplanes;
- vec3_t planes[MAX_CLIP_PLANES];
- vec3_t primal_velocity, original_velocity, new_velocity;
- int i, j;
- trace_t trace;
- vec3_t end;
- float time_left;
- int blocked;
-
- numbumps = 4;
-
+ 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->v.velocity, original_velocity);
- VectorCopy (ent->v.velocity, primal_velocity);
+ VectorCopy(ent->fields.server->velocity, original_velocity);
+ VectorCopy(ent->fields.server->velocity, primal_velocity);
numplanes = 0;
-
time_left = time;
-
- for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
+ for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
{
- if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2])
+ if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
break;
- for (i=0 ; i<3 ; i++)
- end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];
+ 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, hitsupercontentsmask);
+#if 0
+ //if (trace.fraction < 0.002)
+ {
+#if 1
+ vec3_t start;
+ trace_t testtrace;
+ VectorCopy(ent->fields.server->origin, start);
+ 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, 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);
+ trace = testtrace;
+ }
+#endif
+#if 0
+ //j = -1;
+ for (i = 0;i < numplanes;i++)
+ {
+ VectorCopy(ent->fields.server->origin, start);
+ 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, hitsupercontentsmask);
+ if (trace.fraction < testtrace.fraction)
+ {
+ trace = testtrace;
+ VectorCopy(start, ent->fields.server->origin);
+ //j = i;
+ }
+ }
+ //if (j >= 0)
+ // VectorAdd(ent->fields.server->origin, planes[j], start);
+#endif
+ }
+#endif
- trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+#if 0
+ Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
+ if (trace.fraction < 1)
+ Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
+ Con_Print("\n");
+#endif
- if (trace.allsolid)
- { // entity is trapped in another solid
- VectorClear(ent->v.velocity);
+#if 0
+ if (trace.bmodelstartsolid)
+ {
+ // LordHavoc: note: this code is what makes entities stick in place
+ // if embedded in world only (you can walk through other objects if
+ // stuck)
+ // entity is trapped in another solid
+ VectorClear(ent->fields.server->velocity);
return 3;
}
+#endif
- if (trace.fraction > 0)
- { // actually covered some distance
- VectorCopy (trace.endpos, ent->v.origin);
- VectorCopy (ent->v.velocity, original_velocity);
- numplanes = 0;
- }
-
+ // break if it moved the entire distance
if (trace.fraction == 1)
- break; // moved the entire distance
+ {
+ VectorCopy(trace.endpos, ent->fields.server->origin);
+ break;
+ }
if (!trace.ent)
- Host_Error ("SV_FlyMove: !trace.ent");
+ {
+ Con_Printf ("SV_FlyMove: !trace.ent");
+ trace.ent = prog->edicts;
+ }
+
+ impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
- if (trace.plane.normal[2] > 0.7)
+ if (trace.plane.normal[2])
{
- blocked |= 1; // floor
- if (trace.ent->v.solid == SOLID_BSP)
+ if (trace.plane.normal[2] > 0.7)
{
- ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
- ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+ // floor
+ blocked |= 1;
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
}
}
- if (!trace.plane.normal[2])
+ else
{
- blocked |= 2; // step
- if (steptrace)
- *steptrace = trace; // save for player extrafriction
+ // step
+ blocked |= 2;
+ // save the trace for player extrafriction
+ if (stepnormal)
+ VectorCopy(trace.plane.normal, stepnormal);
}
-//
-// run the impact function
-//
- SV_Impact (ent, trace.ent);
- if (ent->free)
- break; // removed by the impact function
-
-
- time_left -= time_left * trace.fraction;
-
- // cliped to another plane
+ if (trace.fraction >= 0.001)
+ {
+ // actually covered some distance
+ VectorCopy(trace.endpos, ent->fields.server->origin);
+ VectorCopy(ent->fields.server->velocity, original_velocity);
+ numplanes = 0;
+ }
+
+ // run the impact function
+ if (impact)
+ {
+ SV_Impact(ent, &trace);
+
+ // break if removed by the impact function
+ if (ent->priv.server->free)
+ break;
+ }
+
+ time_left *= 1 - trace.fraction;
+
+ // clipped to another plane
if (numplanes >= MAX_CLIP_PLANES)
- { // this shouldn't really happen
- VectorClear(ent->v.velocity);
- return 3;
+ {
+ // this shouldn't really happen
+ VectorClear(ent->fields.server->velocity);
+ blocked = 3;
+ break;
+ }
+
+ /*
+ for (i = 0;i < numplanes;i++)
+ if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
+ break;
+ if (i < numplanes)
+ {
+ VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
+ continue;
}
+ */
- VectorCopy (trace.plane.normal, planes[numplanes]);
+ VectorCopy(trace.plane.normal, planes[numplanes]);
numplanes++;
-//
-// modify original_velocity so it parallels all of the clip planes
-//
- for (i=0 ; i<numplanes ; i++)
+ if (sv_newflymove.integer)
+ ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
+ else
{
- ClipVelocity (original_velocity, planes[i], new_velocity, 1);
- for (j=0 ; j<numplanes ; j++)
- if (j != i)
+ // modify original_velocity so it parallels all of the clip planes
+ for (i = 0;i < numplanes;i++)
+ {
+ ClipVelocity(original_velocity, planes[i], new_velocity, 1);
+ for (j = 0;j < numplanes;j++)
{
- if (DotProduct (new_velocity, planes[j]) < 0)
- break; // not ok
+ if (j != i)
+ {
+ // not ok
+ if (DotProduct(new_velocity, planes[j]) < 0)
+ break;
+ }
}
- if (j == numplanes)
- break;
- }
-
- if (i != numplanes)
- { // go along this plane
- VectorCopy (new_velocity, ent->v.velocity);
- }
- else
- { // go along the crease
- if (numplanes != 2)
+ if (j == numplanes)
+ break;
+ }
+
+ if (i != numplanes)
{
-// Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
- VectorClear(ent->v.velocity);
- return 7;
+ // go along this plane
+ VectorCopy(new_velocity, ent->fields.server->velocity);
+ }
+ else
+ {
+ // go along the crease
+ if (numplanes != 2)
+ {
+ VectorClear(ent->fields.server->velocity);
+ blocked = 7;
+ break;
+ }
+ CrossProduct(planes[0], planes[1], dir);
+ // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
+ VectorNormalize(dir);
+ d = DotProduct(dir, ent->fields.server->velocity);
+ VectorScale(dir, d, ent->fields.server->velocity);
}
- CrossProduct (planes[0], planes[1], dir);
- d = DotProduct (dir, ent->v.velocity);
- VectorScale (dir, d, ent->v.velocity);
}
-//
-// if original velocity is against the original velocity, stop dead
-// to avoid tiny occilations in sloping corners
-//
- if (DotProduct (ent->v.velocity, primal_velocity) <= 0)
+ // if current velocity is against the original velocity,
+ // stop dead to avoid tiny occilations in sloping corners
+ if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
{
- VectorClear(ent->v.velocity);
- return blocked;
+ VectorClear(ent->fields.server->velocity);
+ break;
}
}
+ //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
+
+ /*
+ if ((blocked & 1) == 0 && bumpcount > 1)
+ {
+ // 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, hitsupercontentsmask);
+ }
+ */
+
+ // LordHavoc: this came from QW and allows you to get out of water more easily
+ if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
+ VectorCopy(primal_velocity, ent->fields.server->velocity);
return blocked;
}
-
/*
============
SV_AddGravity
============
*/
-void SV_AddGravity (edict_t *ent)
+void SV_AddGravity (prvm_edict_t *ent)
{
- float ent_gravity;
+ float ent_gravity;
+ prvm_eval_t *val;
- eval_t *val;
-
- val = GETEDICTFIELDVALUE(ent, eval_gravity);
+ val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
if (val!=0 && val->_float)
ent_gravity = val->_float;
else
ent_gravity = 1.0;
- ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime;
+ ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
}
Does not change the entities velocity at all
============
*/
-trace_t SV_PushEntity (edict_t *ent, vec3_t push)
+static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
{
- trace_t trace;
- vec3_t end;
-
- VectorAdd (ent->v.origin, push, end);
-
- if (ent->v.movetype == MOVETYPE_FLYMISSILE)
- trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
- else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)
- // only clip against bmodels
- trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
+ int type;
+ trace_t trace;
+ vec3_t end;
+
+ VectorAdd (ent->fields.server->origin, push, end);
+
+ if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
+ type = MOVE_MISSILE;
+ else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
+ type = MOVE_NOMONSTERS; // only clip against bmodels
else
- trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
-
- VectorCopy (trace.endpos, ent->v.origin);
- SV_LinkEdict (ent, true);
+ type = MOVE_NORMAL;
+
+ 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;
- if (trace.ent)
- SV_Impact (ent, trace.ent);
+ VectorCopy (trace.endpos, ent->fields.server->origin);
+ SV_LinkEdict (ent, true);
+ if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
+ SV_Impact (ent, &trace);
return trace;
-}
+}
/*
============
*/
-void SV_PushMove (edict_t *pusher, float movetime)
+void SV_PushMove (prvm_edict_t *pusher, float movetime)
{
- int i, e;
- edict_t *check;
- vec3_t mins, maxs, move;
- vec3_t entorig, pushorig;
- int num_moved;
- edict_t *moved_edict[MAX_EDICTS];
- vec3_t moved_from[MAX_EDICTS];
- float savesolid;
-
- switch ((int) pusher->v.solid)
+ int i, e, index;
+ float savesolid, movetime2, pushltime;
+ vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
+ int num_moved;
+ int numcheckentities;
+ static prvm_edict_t *checkentities[MAX_EDICTS];
+ model_t *pushermodel;
+ trace_t trace;
+ matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
+
+ 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;
+ return;
+ }
+
+ switch ((int) pusher->fields.server->solid)
{
// LordHavoc: valid pusher types
case SOLID_BSP:
// LordHavoc: no collisions
case SOLID_NOT:
case SOLID_TRIGGER:
- VectorMA (pusher->v.origin, movetime, pusher->v.velocity, pusher->v.origin);
- pusher->v.ltime += movetime;
+ VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
+ VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
+ pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
+ pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
+ pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
+ pusher->fields.server->ltime += movetime;
SV_LinkEdict (pusher, false);
return;
default:
- Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v.solid);
+ Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
+ return;
}
- if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
+ index = (int) pusher->fields.server->modelindex;
+ if (index < 1 || index >= MAX_MODELS)
{
- pusher->v.ltime += movetime;
+ Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
return;
}
+ pushermodel = sv.models[index];
- for (i=0 ; i<3 ; i++)
+ movetime2 = movetime;
+ VectorScale(pusher->fields.server->velocity, movetime2, move1);
+ VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
+ if (moveangle[0] || moveangle[2])
{
- move[i] = pusher->v.velocity[i] * movetime;
- mins[i] = pusher->v.absmin[i] + move[i];
- maxs[i] = pusher->v.absmax[i] + move[i];
- }
-
- VectorCopy (pusher->v.origin, pushorig);
-
-// move the pusher to it's final position
-
- VectorAdd (pusher->v.origin, move, pusher->v.origin);
- pusher->v.ltime += movetime;
- SV_LinkEdict (pusher, false);
-
-
-// see if any solid entities are inside the final position
- num_moved = 0;
- check = NEXT_EDICT(sv.edicts);
- for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
- {
- if (check->free)
- continue;
- if (check->v.movetype == MOVETYPE_PUSH
- || check->v.movetype == MOVETYPE_NONE
- || check->v.movetype == MOVETYPE_FOLLOW
- || check->v.movetype == MOVETYPE_NOCLIP)
- continue;
-
- // if the entity is standing on the pusher, it will definitely be moved
- if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher))
+ for (i = 0;i < 3;i++)
{
- if (check->v.absmin[0] >= maxs[0]
- || check->v.absmin[1] >= maxs[1]
- || check->v.absmin[2] >= maxs[2]
- || check->v.absmax[0] <= mins[0]
- || check->v.absmax[1] <= mins[1]
- || check->v.absmax[2] <= mins[2])
- continue;
-
- // see if the ent's bbox is inside the pusher's final position
- if (!SV_TestEntityPosition (check))
- continue;
- }
-
- // remove the onground flag for non-players
- if (check->v.movetype != MOVETYPE_WALK)
- check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
-
- VectorCopy (check->v.origin, entorig);
- VectorCopy (check->v.origin, moved_from[num_moved]);
- moved_edict[num_moved] = check;
- num_moved++;
-
- // LordHavoc: pusher fixes (teleport train bug, etc)
- savesolid = pusher->v.solid;
- if (savesolid == SOLID_BSP || savesolid == SOLID_BBOX || savesolid == SOLID_SLIDEBOX)
- {
- // try moving the contacted entity
- pusher->v.solid = SOLID_NOT;
- SV_PushEntity (check, move);
- pusher->v.solid = savesolid; // was SOLID_BSP
-
- // if it is still inside the pusher, block
- if (SV_TestEntityPosition (check))
- { // fail the move
- if (check->v.mins[0] == check->v.maxs[0])
- continue;
- if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
- { // corpse
- check->v.mins[0] = check->v.mins[1] = 0;
- VectorCopy (check->v.mins, check->v.maxs);
- continue;
- }
-
- VectorCopy (entorig, check->v.origin);
- SV_LinkEdict (check, true);
-
- VectorCopy (pushorig, pusher->v.origin);
- SV_LinkEdict (pusher, false);
- pusher->v.ltime -= movetime;
-
- // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
- if (pusher->v.blocked)
- {
- pr_global_struct->self = EDICT_TO_PROG(pusher);
- pr_global_struct->other = EDICT_TO_PROG(check);
- PR_ExecuteProgram (pusher->v.blocked);
- }
-
- // move back any entities we already moved
- num_moved--; // LordHavoc: pop off check, because it was already restored
- for (i=0 ; i<num_moved ; i++)
- {
- VectorCopy (moved_from[i], moved_edict[i]->v.origin);
- SV_LinkEdict (moved_edict[i], false);
- }
- return;
- }
+ if (move1[i] > 0)
+ {
+ mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
+ maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
+ }
+ else
+ {
+ mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
+ maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
+ }
}
}
-
-
-}
-
-/*
-============
-SV_PushRotate
-
-============
-*/
-void SV_PushRotate (edict_t *pusher, float movetime)
-{
- int i, e;
- edict_t *check;
- vec3_t move, a, amove;
- vec3_t entorigin, entangles, pushorigin, pushangles;
- int num_moved;
- edict_t *moved_edict[MAX_EDICTS];
- vec3_t moved_from[MAX_EDICTS];
- vec3_t angled_from[MAX_EDICTS];
- vec3_t org, org2;
- vec3_t forward, right, up;
- float savesolid;
-
- switch ((int) pusher->v.solid)
+ else if (moveangle[1])
{
- // LordHavoc: valid pusher types
- case SOLID_BSP:
- case SOLID_BBOX:
- case SOLID_SLIDEBOX:
- case SOLID_CORPSE: // LordHavoc: this would be weird...
- break;
- // LordHavoc: no collisions
- case SOLID_NOT:
- case SOLID_TRIGGER:
- VectorMA (pusher->v.angles, movetime, pusher->v.avelocity, pusher->v.angles);
- pusher->v.ltime += movetime;
- SV_LinkEdict (pusher, false);
- return;
- default:
- Host_Error("SV_PushRotate: unrecognized solid type %f\n", pusher->v.solid);
+ for (i = 0;i < 3;i++)
+ {
+ if (move1[i] > 0)
+ {
+ mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
+ maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
+ }
+ else
+ {
+ mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
+ maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
+ }
+ }
}
- if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
+ else
{
- pusher->v.ltime += movetime;
- return;
+ for (i = 0;i < 3;i++)
+ {
+ if (move1[i] > 0)
+ {
+ mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
+ maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
+ }
+ else
+ {
+ mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
+ maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
+ }
+ }
}
- for (i=0 ; i<3 ; i++)
- amove[i] = pusher->v.avelocity[i] * movetime;
+ VectorNegate (moveangle, a);
+ AngleVectorsFLU (a, forward, left, up);
- VectorNegate (amove, a);
- AngleVectors (a, forward, right, up);
+ VectorCopy (pusher->fields.server->origin, pushorig);
+ VectorCopy (pusher->fields.server->angles, pushang);
+ pushltime = pusher->fields.server->ltime;
- VectorCopy (pusher->v.origin, pushorigin);
- VectorCopy (pusher->v.angles, pushangles);
-
-// move the pusher to it's final position
+// move the pusher to its final position
- VectorAdd (pusher->v.angles, amove, pusher->v.angles);
- pusher->v.ltime += movetime;
+ VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
+ VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
+ 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;
- check = NEXT_EDICT(sv.edicts);
- for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+
+ numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
+ for (e = 0;e < numcheckentities;e++)
{
- if (check->free)
- continue;
- if (check->v.movetype == MOVETYPE_PUSH
- || check->v.movetype == MOVETYPE_NONE
- || check->v.movetype == MOVETYPE_FOLLOW
- || check->v.movetype == MOVETYPE_NOCLIP)
+ prvm_edict_t *check = checkentities[e];
+ if (check->fields.server->movetype == MOVETYPE_NONE
+ || check->fields.server->movetype == MOVETYPE_PUSH
+ || check->fields.server->movetype == MOVETYPE_FOLLOW
+ || check->fields.server->movetype == MOVETYPE_NOCLIP
+ || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
continue;
- // if the entity is standing on the pusher, it will definately be moved
- if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher))
+ // if the entity is standing on the pusher, it will definitely be moved
+ // 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 (check->v.absmin[0] >= pusher->v.absmax[0]
- || check->v.absmin[1] >= pusher->v.absmax[1]
- || check->v.absmin[2] >= pusher->v.absmax[2]
- || check->v.absmax[0] <= pusher->v.absmin[0]
- || check->v.absmax[1] <= pusher->v.absmin[1]
- || check->v.absmax[2] <= pusher->v.absmin[2])
- continue;
-
- // see if the ent's bbox is inside the pusher's final position
- if (!SV_TestEntityPosition (check))
+ 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+ if (!trace.startsolid)
continue;
}
- // remove the onground flag for non-players
- if (check->v.movetype != MOVETYPE_WALK)
- check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
-
- VectorCopy (check->v.origin, entorigin);
- VectorCopy (check->v.origin, moved_from[num_moved]);
- VectorCopy (check->v.angles, entangles);
- VectorCopy (check->v.angles, angled_from[num_moved]);
- moved_edict[num_moved] = check;
- num_moved++;
-
- // calculate destination position
- VectorSubtract (check->v.origin, pusher->v.origin, org);
- org2[0] = DotProduct (org, forward);
- org2[1] = -DotProduct (org, right);
- org2[2] = DotProduct (org, up);
- VectorSubtract (org2, org, move);
-
- // try moving the contacted entity
- savesolid = pusher->v.solid; // LordHavoc: restore to correct solid type
- pusher->v.solid = SOLID_NOT;
- SV_PushEntity (check, move);
- pusher->v.solid = savesolid; // LordHavoc: restore to correct solid type
-
- VectorAdd (check->v.angles, amove, check->v.angles);
-
- // if it is still inside the pusher, block
- if (SV_TestEntityPosition (check))
- { // fail the move
- if (check->v.mins[0] == check->v.maxs[0])
- continue;
- if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
- { // corpse
- check->v.mins[0] = check->v.mins[1] = 0;
- VectorCopy (check->v.mins, check->v.maxs);
- continue;
- }
-
- VectorCopy (entorigin, check->v.origin);
- VectorCopy (entangles, check->v.angles);
- SV_LinkEdict (check, true);
-
- VectorCopy (pushorigin, pusher->v.origin);
- VectorCopy (pushangles, pusher->v.angles);
- SV_LinkEdict (pusher, false);
- pusher->v.ltime -= movetime;
-
- // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
- if (pusher->v.blocked)
- {
- pr_global_struct->self = EDICT_TO_PROG(pusher);
- pr_global_struct->other = EDICT_TO_PROG(check);
- PR_ExecuteProgram (pusher->v.blocked);
- }
-
- // move back any entities we already moved
- num_moved--; // LordHavoc: pop off check, because it was already restored
- for (i=0 ; i<num_moved ; i++)
+
+ if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
+ {
+ vec3_t org2;
+ VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
+ org2[0] = DotProduct (org, forward);
+ org2[1] = DotProduct (org, left);
+ org2[2] = DotProduct (org, up);
+ VectorSubtract (org2, org, move);
+ VectorAdd (move, move1, move);
+ }
+ else
+ VectorCopy (move1, move);
+
+ 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;
+
+ // try moving the contacted entity
+ pusher->fields.server->solid = SOLID_NOT;
+ trace = SV_PushEntity (check, move, true);
+ // FIXME: turn players specially
+ check->fields.server->angles[1] += trace.fraction * moveangle[1];
+ 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 check is for items riding platforms that are passing under (or
+ // through) walls intended to knock the items off
+ if (trace.fraction < 1 && check->fields.server->movetype != MOVETYPE_WALK)
+ check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
+
+ // if it is still inside the pusher, block
+ 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+ if (trace.startsolid)
+ {
+ // try moving the contacted entity a tiny bit further to account for precision errors
+ vec3_t move2;
+ pusher->fields.server->solid = SOLID_NOT;
+ VectorScale(move, 1.1, move2);
+ VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
+ VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
+ SV_PushEntity (check, move2, true);
+ pusher->fields.server->solid = savesolid;
+ 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+ if (trace.startsolid)
{
- VectorCopy (moved_from[i], moved_edict[i]->v.origin);
- VectorCopy (angled_from[i], moved_edict[i]->v.angles);
- SV_LinkEdict (moved_edict[i], false);
+ // try moving the contacted entity a tiny bit less to account for precision errors
+ pusher->fields.server->solid = SOLID_NOT;
+ VectorScale(move, 0.9, move2);
+ VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
+ VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
+ SV_PushEntity (check, move2, true);
+ pusher->fields.server->solid = savesolid;
+ 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+ if (trace.startsolid)
+ {
+ // still inside pusher, so it's really blocked
+
+ // fail the move
+ if (check->fields.server->mins[0] == check->fields.server->maxs[0])
+ continue;
+ if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
+ {
+ // corpse
+ check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
+ VectorCopy (check->fields.server->mins, check->fields.server->maxs);
+ continue;
+ }
+
+ VectorCopy (pushorig, pusher->fields.server->origin);
+ VectorCopy (pushang, pusher->fields.server->angles);
+ pusher->fields.server->ltime = pushltime;
+ SV_LinkEdict (pusher, false);
+
+ // move back any entities we already moved
+ for (i = 0;i < num_moved;i++)
+ {
+ prvm_edict_t *ed = sv.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);
+ }
+
+ // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
+ if (pusher->fields.server->blocked)
+ {
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
+ prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
+ PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
+ }
+ break;
+ }
}
- return;
}
}
+ pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
+ pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
+ pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
}
/*
================
*/
-void SV_Physics_Pusher (edict_t *ent)
+void SV_Physics_Pusher (prvm_edict_t *ent)
{
- float thinktime;
- float oldltime;
- float movetime;
-
- oldltime = ent->v.ltime;
-
- thinktime = ent->v.nextthink;
- if (thinktime < ent->v.ltime + host_frametime)
+ float thinktime, oldltime, movetime;
+
+ oldltime = ent->fields.server->ltime;
+
+ thinktime = ent->fields.server->nextthink;
+ if (thinktime < ent->fields.server->ltime + sv.frametime)
{
- movetime = thinktime - ent->v.ltime;
+ movetime = thinktime - ent->fields.server->ltime;
if (movetime < 0)
movetime = 0;
}
else
- movetime = host_frametime;
+ movetime = sv.frametime;
if (movetime)
+ // advances ent->fields.server->ltime if not blocked
+ SV_PushMove (ent, movetime);
+
+ if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
{
- if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
- SV_PushRotate (ent, movetime);
- else
- SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked
- }
-
- if (thinktime > oldltime && thinktime <= ent->v.ltime)
- {
- ent->v.nextthink = 0;
- pr_global_struct->time = sv.time;
- pr_global_struct->self = EDICT_TO_PROG(ent);
- pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
- PR_ExecuteProgram (ent->v.think);
- if (ent->free)
- return;
+ ent->fields.server->nextthink = 0;
+ prog->globals.server->time = sv.time;
+ 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");
}
-
}
clipping hull.
=============
*/
-void SV_CheckStuck (edict_t *ent)
+void SV_CheckStuck (prvm_edict_t *ent)
{
- int i, j;
- int z;
- vec3_t org;
+ int i, j, z;
+ vec3_t org;
if (!SV_TestEntityPosition(ent))
{
- VectorCopy (ent->v.origin, ent->v.oldorigin);
+ VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
return;
}
- VectorCopy (ent->v.origin, org);
- VectorCopy (ent->v.oldorigin, ent->v.origin);
+ VectorCopy (ent->fields.server->origin, org);
+
+ 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 (ent->fields.server->oldorigin, ent->fields.server->origin);
if (!SV_TestEntityPosition(ent))
{
- Con_DPrintf ("Unstuck.\n");
+ 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=0 ; z< 18 ; z++)
+
+ 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;
+
+ // if not stuck in a bmodel, just return
+ if (!SV_TestEntityPosition(ent))
+ 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->v.origin[0] = org[0] + i;
- ent->v.origin[1] = org[1] + j;
- ent->v.origin[2] = org[2] + z;
+ 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.\n");
+ 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;
}
}
-
- VectorCopy (org, ent->v.origin);
- Con_DPrintf ("player is stuck.\n");
+
+ 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));
}
SV_CheckWater
=============
*/
-qboolean SV_CheckWater (edict_t *ent)
+qboolean SV_CheckWater (prvm_edict_t *ent)
{
- vec3_t point;
- int cont;
-
- point[0] = ent->v.origin[0];
- point[1] = ent->v.origin[1];
- point[2] = ent->v.origin[2] + ent->v.mins[2] + 1;
-
- ent->v.waterlevel = 0;
- ent->v.watertype = CONTENTS_EMPTY;
- cont = SV_PointContents (point);
- if (cont <= CONTENTS_WATER)
+ int cont;
+ int nNativeContents;
+ vec3_t point;
+
+ point[0] = ent->fields.server->origin[0];
+ point[1] = ent->fields.server->origin[1];
+ point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
+
+ // DRESK - Support for Entity Contents Transition Event
+ // NOTE: Some logic needed to be slightly re-ordered
+ // to not affect performance and allow for the feature.
+
+ // Acquire Super Contents Prior to Resets
+ cont = SV_PointSuperContents(point);
+ // Acquire Native Contents Here
+ nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
+
+ // DRESK - Support for Entity Contents Transition Event
+ if(ent->fields.server->watertype)
+ // Entity did NOT Spawn; Check
+ SV_CheckContentsTransition(ent, nNativeContents);
+
+
+ ent->fields.server->waterlevel = 0;
+ ent->fields.server->watertype = CONTENTS_EMPTY;
+ cont = SV_PointSuperContents(point);
+ if (cont & (SUPERCONTENTS_LIQUIDSMASK))
{
- ent->v.watertype = cont;
- ent->v.waterlevel = 1;
- point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5;
- cont = SV_PointContents (point);
- if (cont <= CONTENTS_WATER)
+ ent->fields.server->watertype = nNativeContents;
+ ent->fields.server->waterlevel = 1;
+ point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
+ if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
{
- ent->v.waterlevel = 2;
- point[2] = ent->v.origin[2] + ent->v.view_ofs[2];
- cont = SV_PointContents (point);
- if (cont <= CONTENTS_WATER)
- ent->v.waterlevel = 3;
+ ent->fields.server->waterlevel = 2;
+ point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
+ if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
+ ent->fields.server->waterlevel = 3;
}
}
-
- return ent->v.waterlevel > 1;
+
+ return ent->fields.server->waterlevel > 1;
}
/*
============
*/
-void SV_WallFriction (edict_t *ent, trace_t *trace)
+void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
{
- vec3_t forward, right, up;
- float d, i;
- vec3_t into, side;
-
- AngleVectors (ent->v.v_angle, forward, right, up);
- d = DotProduct (trace->plane.normal, forward);
-
- d += 0.5;
- if (d >= 0)
- return;
-
-// cut the tangential velocity
- i = DotProduct (trace->plane.normal, ent->v.velocity);
- VectorScale (trace->plane.normal, i, into);
- VectorSubtract (ent->v.velocity, into, side);
-
- ent->v.velocity[0] = side[0] * (1 + d);
- ent->v.velocity[1] = side[1] * (1 + d);
+ float d, i;
+ vec3_t forward, into, side;
+
+ AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
+ if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
+ {
+ // cut the tangential velocity
+ i = DotProduct (stepnormal, ent->fields.server->velocity);
+ VectorScale (stepnormal, i, into);
+ VectorSubtract (ent->fields.server->velocity, into, side);
+ ent->fields.server->velocity[0] = side[0] * (1 + d);
+ ent->fields.server->velocity[1] = side[1] * (1 + d);
+ }
}
+#if 0
/*
=====================
SV_TryUnstick
This is a hack, but in the interest of good gameplay...
======================
*/
-int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
+int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
{
- int i;
- vec3_t oldorg;
- vec3_t dir;
- int clip;
- trace_t steptrace;
-
- VectorCopy (ent->v.origin, oldorg);
+ int i, clip;
+ vec3_t oldorg, dir;
+
+ VectorCopy (ent->fields.server->origin, oldorg);
VectorClear (dir);
for (i=0 ; i<8 ; i++)
{
-// try pushing a little in an axial direction
+ // try pushing a little in an axial direction
switch (i)
{
- case 0: dir[0] = 2; dir[1] = 0; break;
- case 1: dir[0] = 0; dir[1] = 2; break;
- case 2: dir[0] = -2; dir[1] = 0; break;
- case 3: dir[0] = 0; dir[1] = -2; break;
- case 4: dir[0] = 2; dir[1] = 2; break;
- case 5: dir[0] = -2; dir[1] = 2; break;
- case 6: dir[0] = 2; dir[1] = -2; break;
- case 7: dir[0] = -2; dir[1] = -2; break;
+ case 0: dir[0] = 2; dir[1] = 0; break;
+ case 1: dir[0] = 0; dir[1] = 2; break;
+ case 2: dir[0] = -2; dir[1] = 0; break;
+ case 3: dir[0] = 0; dir[1] = -2; break;
+ case 4: dir[0] = 2; dir[1] = 2; break;
+ case 5: dir[0] = -2; dir[1] = 2; break;
+ case 6: dir[0] = 2; dir[1] = -2; break;
+ case 7: dir[0] = -2; dir[1] = -2; break;
}
-
- SV_PushEntity (ent, dir);
-// retry the original move
- ent->v.velocity[0] = oldvel[0];
- ent->v. velocity[1] = oldvel[1];
- ent->v. velocity[2] = 0;
- clip = SV_FlyMove (ent, 0.1, &steptrace);
+ SV_PushEntity (ent, dir, false);
+
+ // retry the original move
+ 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, SV_GenericHitSuperContentsMask(ent));
- if ( fabs(oldorg[1] - ent->v.origin[1]) > 4
- || fabs(oldorg[0] - ent->v.origin[0]) > 4 )
+ if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
+ || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
{
-//Con_DPrintf ("unstuck!\n");
+ Con_DPrint("TryUnstick - success.\n");
return clip;
}
-
-// go back to the original pos and try again
- VectorCopy (oldorg, ent->v.origin);
+
+ // go back to the original pos and try again
+ VectorCopy (oldorg, ent->fields.server->origin);
}
-
- VectorClear (ent->v.velocity);
- return 7; // still not moving
+
+ // still not moving
+ VectorClear (ent->fields.server->velocity);
+ Con_DPrint("TryUnstick - failure.\n");
+ return 7;
}
+#endif
/*
=====================
Only used by players
======================
*/
-#define STEPSIZE 18
-void SV_WalkMove (edict_t *ent)
+void SV_WalkMove (prvm_edict_t *ent)
{
- vec3_t upmove, downmove;
- vec3_t oldorg, oldvel;
- vec3_t nosteporg, nostepvel;
- int clip;
- int oldonground;
- trace_t steptrace, downtrace;
-
-//
-// do a regular slide move unless it looks like you ran into a step
-//
- oldonground = (int)ent->v.flags & FL_ONGROUND;
- ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
-
- VectorCopy (ent->v.origin, oldorg);
- VectorCopy (ent->v.velocity, oldvel);
-
- clip = SV_FlyMove (ent, host_frametime, &steptrace);
-
- if ( !(clip & 2) )
- return; // move didn't block on a step
-
- if (!oldonground && ent->v.waterlevel == 0)
- return; // don't stair up while jumping
-
- if (ent->v.movetype != MOVETYPE_WALK)
- return; // gibbed by a trigger
-
- if (sv_nostep.value)
- return;
-
- if ( (int)sv_player->v.flags & FL_WATERJUMP )
- return;
+ 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;
- VectorCopy (ent->v.origin, nosteporg);
- VectorCopy (ent->v.velocity, nostepvel);
-
-//
-// try moving up and forward to go up a step
-//
- VectorCopy (oldorg, ent->v.origin); // back to start pos
+ // if frametime is 0 (due to client sending the same timestamp twice),
+ // don't move
+ if (sv.frametime <= 0)
+ return;
- VectorClear (upmove);
- VectorClear (downmove);
- upmove[2] = STEPSIZE;
- downmove[2] = -STEPSIZE + oldvel[2]*host_frametime;
+ hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
-// move up
- SV_PushEntity (ent, upmove); // FIXME: don't link?
+ SV_CheckVelocity(ent);
-// move forward
- ent->v.velocity[0] = oldvel[0];
- ent->v. velocity[1] = oldvel[1];
- ent->v. velocity[2] = 0;
- clip = SV_FlyMove (ent, host_frametime, &steptrace);
+ // do a regular slide move unless it looks like you ran into a step
+ oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
-// check for stuckness, possibly due to the limited precision of floats
-// in the clipping hulls
- if (clip)
- {
- if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125
- && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )
- { // stepping up didn't make any progress
- clip = SV_TryUnstick (ent, oldvel);
- }
- }
-
-// extra friction based on view angle
- if ( clip & 2 )
- SV_WallFriction (ent, &steptrace);
+ VectorCopy (ent->fields.server->origin, start_origin);
+ VectorCopy (ent->fields.server->velocity, start_velocity);
-// move down
- downtrace = SV_PushEntity (ent, downmove); // FIXME: don't link?
+ clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
- if (downtrace.plane.normal[2] > 0.7)
- {
- if (ent->v.solid == SOLID_BSP)
- {
- ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
- ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
- }
- }
- else
- {
-// if the push down didn't end up on good ground, use the move without
-// the step up. This happens near wall / slope combinations, and can
-// cause the player to hop up higher on a slope too steep to climb
- VectorCopy (nosteporg, ent->v.origin);
- VectorCopy (nostepvel, ent->v.velocity);
- }
-}
+ // 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);
-/*
-================
-SV_Physics_Client
+ VectorCopy(ent->fields.server->origin, originalmove_origin);
+ VectorCopy(ent->fields.server->velocity, originalmove_velocity);
+ originalmove_clip = clip;
+ originalmove_flags = (int)ent->fields.server->flags;
+ originalmove_groundentity = ent->fields.server->groundentity;
-Player character actions
-================
-*/
-void SV_Physics_Client (edict_t *ent, int num)
-{
- if ( ! svs.clients[num-1].active )
- return; // unconnected slot
+ if ((int)ent->fields.server->flags & FL_WATERJUMP)
+ return;
-//
-// call standard client pre-think
-//
- pr_global_struct->time = sv.time;
- pr_global_struct->self = EDICT_TO_PROG(ent);
- PR_ExecuteProgram (pr_global_struct->PlayerPreThink);
-
-//
-// do a move
-//
- SV_CheckVelocity (ent);
+ if (sv_nostep.integer)
+ return;
-//
-// decide which move function to call
-//
- switch ((int)ent->v.movetype)
+ // if move didn't block on a step, return
+ if (clip & 2)
{
- case MOVETYPE_NONE:
- if (!SV_RunThink (ent))
+ // if move was not trying to move into the step, return
+ if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
return;
- break;
- case MOVETYPE_WALK:
- if (!SV_RunThink (ent))
- return;
- if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
- SV_AddGravity (ent);
- SV_CheckStuck (ent);
- SV_WalkMove (ent);
- break;
-
- case MOVETYPE_TOSS:
- case MOVETYPE_BOUNCE:
- SV_Physics_Toss (ent);
- break;
+ if (ent->fields.server->movetype != MOVETYPE_FLY)
+ {
+ // return if gibbed by a trigger
+ if (ent->fields.server->movetype != MOVETYPE_WALK)
+ return;
- case MOVETYPE_FLY:
- if (!SV_RunThink (ent))
- return;
- SV_CheckWater (ent);
- SV_FlyMove (ent, host_frametime, NULL);
- break;
-
- case MOVETYPE_NOCLIP:
- if (!SV_RunThink (ent))
+ // only step up while jumping if that is enabled
+ if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
+ if (!oldonground && ent->fields.server->waterlevel == 0)
+ return;
+ }
+
+ // try moving up and forward to go up a step
+ // back to start pos
+ VectorCopy (start_origin, ent->fields.server->origin);
+ VectorCopy (start_velocity, ent->fields.server->velocity);
+
+ // move up
+ VectorClear (upmove);
+ upmove[2] = sv_stepheight.value;
+ // FIXME: don't link?
+ SV_PushEntity(ent, upmove, false);
+
+ // move forward
+ ent->fields.server->velocity[2] = 0;
+ clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
+ ent->fields.server->velocity[2] += start_velocity[2];
+
+ SV_CheckVelocity(ent);
+
+ // check for stuckness, possibly due to the limited precision of floats
+ // in the clipping hulls
+ if (clip
+ && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
+ && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
+ {
+ //Con_Printf("wall\n");
+ // stepping up didn't make any progress, revert to original move
+ VectorCopy(originalmove_origin, ent->fields.server->origin);
+ VectorCopy(originalmove_velocity, ent->fields.server->velocity);
+ //clip = originalmove_clip;
+ ent->fields.server->flags = originalmove_flags;
+ ent->fields.server->groundentity = originalmove_groundentity;
+ // now try to unstick if needed
+ //clip = SV_TryUnstick (ent, oldvel);
return;
- SV_CheckWater (ent);
- VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
- break;
-
- default:
- Host_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
- }
+ }
-//
-// call standard player post-think
-//
- SV_LinkEdict (ent, true);
+ //Con_Printf("step - ");
- pr_global_struct->time = sv.time;
- pr_global_struct->self = EDICT_TO_PROG(ent);
- PR_ExecuteProgram (pr_global_struct->PlayerPostThink);
-}
+ // extra friction based on view angle
+ if (clip & 2 && sv_wallfriction.integer)
+ SV_WallFriction (ent, stepnormal);
+ }
+ // 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
+ VectorClear (downmove);
+ downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
+ // FIXME: don't link?
+ downtrace = SV_PushEntity (ent, downmove, false);
-/*
-=============
-SV_Physics_None
+ 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 double jump bug)
+#if 0
+ // LordHavoc: disabled this check so you can walk on monsters/players
+ //if (ent->fields.server->solid == SOLID_BSP)
+ {
+ //Con_Printf("onground\n");
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
+ }
+#endif
+ }
+ else
+ {
+ //Con_Printf("slope\n");
+ // if the push down didn't end up on good ground, use the move without
+ // the step up. This happens near wall / slope combinations, and can
+ // cause the player to hop up higher on a slope too steep to climb
+ VectorCopy(originalmove_origin, ent->fields.server->origin);
+ VectorCopy(originalmove_velocity, ent->fields.server->velocity);
+ //clip = originalmove_clip;
+ ent->fields.server->flags = originalmove_flags;
+ ent->fields.server->groundentity = originalmove_groundentity;
+ }
-Non moving objects can only think
-=============
-*/
-void SV_Physics_None (edict_t *ent)
-{
-// regular thinking
- SV_RunThink (ent);
+ SV_CheckVelocity(ent);
}
+//============================================================================
+
/*
=============
SV_Physics_Follow
Entities that are "stuck" to another entity
=============
*/
-void SV_Physics_Follow (edict_t *ent)
+void SV_Physics_Follow (prvm_edict_t *ent)
{
- vec3_t vf, vr, vu, angles;
- edict_t *e;
-// regular thinking
+ vec3_t vf, vr, vu, angles, v;
+ prvm_edict_t *e;
+
+ // regular thinking
if (!SV_RunThink (ent))
return;
+
// LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
- e = PROG_TO_EDICT(ent->v.aiment);
- if (e->v.angles[0] == ent->v.punchangle[0] && e->v.angles[1] == ent->v.punchangle[1] && e->v.angles[2] == ent->v.punchangle[2])
+ e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
+ if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
{
// quick case for no rotation
- VectorAdd(e->v.origin, ent->v.view_ofs, ent->v.origin);
+ VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
}
else
{
- angles[0] = -(e->v.angles[0] - ent->v.punchangle[0]);
- angles[1] = e->v.angles[1] - ent->v.punchangle[1];
- angles[2] = e->v.angles[2] - ent->v.punchangle[2];
+ angles[0] = -ent->fields.server->punchangle[0];
+ angles[1] = ent->fields.server->punchangle[1];
+ angles[2] = ent->fields.server->punchangle[2];
AngleVectors (angles, vf, vr, vu);
- ent->v.origin[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[2] * vu[0] + e->v.origin[0];
- ent->v.origin[1] = ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[2] * vu[1] + e->v.origin[1];
- ent->v.origin[2] = ent->v.view_ofs[0] * vf[2] + ent->v.view_ofs[1] * vr[2] + ent->v.view_ofs[2] * vu[2] + e->v.origin[2];
- /*
- ent->v.origin[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[0] * vf[2] + e->v.origin[0];
- ent->v.origin[1] = ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[1] * vr[2] + e->v.origin[1];
- ent->v.origin[2] = ent->v.view_ofs[2] * vu[0] + ent->v.view_ofs[2] * vu[1] + ent->v.view_ofs[2] * vu[2] + e->v.origin[2];
- */
+ v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
+ v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
+ v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
+ angles[0] = -e->fields.server->angles[0];
+ angles[1] = e->fields.server->angles[1];
+ angles[2] = e->fields.server->angles[2];
+ AngleVectors (angles, vf, vr, vu);
+ ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
+ ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
+ ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
}
- VectorAdd (e->v.angles, ent->v.v_angle, ent->v.angles);
-// VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin);
+ VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
SV_LinkEdict (ent, true);
}
-/*
-=============
-SV_Physics_Noclip
-
-A moving object that doesn't obey physics
-=============
-*/
-void SV_Physics_Noclip (edict_t *ent)
-{
-// regular thinking
- if (!SV_RunThink (ent))
- return;
-
- VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
- VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
-
- SV_LinkEdict (ent, false);
-}
-
/*
==============================================================================
=============
*/
-void SV_CheckWaterTransition (edict_t *ent)
+void SV_CheckWaterTransition (prvm_edict_t *ent)
{
- int cont;
- cont = SV_PointContents (ent->v.origin);
- if (!ent->v.watertype)
- { // just spawned here
- ent->v.watertype = cont;
- ent->v.waterlevel = 1;
+ int cont;
+ cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
+ if (!ent->fields.server->watertype)
+ {
+ // just spawned here
+ ent->fields.server->watertype = cont;
+ ent->fields.server->waterlevel = 1;
return;
}
-
+
+ // DRESK - Support for Entity Contents Transition Event
+ // NOTE: Call here BEFORE updating the watertype below,
+ // and suppress watersplash sound if a valid function
+ // call was made to allow for custom "splash" sounds.
+ if( !SV_CheckContentsTransition(ent, cont) )
+ { // Contents Transition Function Invalid; Potentially Play Water Sound
+ // check if the entity crossed into or out of water
+ if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
+ SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
+ }
+
if (cont <= CONTENTS_WATER)
{
- if (ent->v.watertype == CONTENTS_EMPTY)
- { // just crossed into water
- SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
- }
- ent->v.watertype = cont;
- ent->v.waterlevel = 1;
+ ent->fields.server->watertype = cont;
+ ent->fields.server->waterlevel = 1;
}
else
{
- if (ent->v.watertype != CONTENTS_EMPTY)
- { // just crossed into water
- SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
- }
- ent->v.watertype = CONTENTS_EMPTY;
- ent->v.waterlevel = cont;
+ ent->fields.server->watertype = CONTENTS_EMPTY;
+ ent->fields.server->waterlevel = 0;
}
}
Toss, bounce, and fly movement. When onground, do nothing.
=============
*/
-void SV_Physics_Toss (edict_t *ent)
+void SV_Physics_Toss (prvm_edict_t *ent)
{
- trace_t trace;
- vec3_t move;
- float backoff;
- // regular thinking
- if (!SV_RunThink (ent))
- return;
+ trace_t trace;
+ vec3_t move;
// if onground, return without moving
- if ( ((int)ent->v.flags & FL_ONGROUND) )
- return;
+ if ((int)ent->fields.server->flags & FL_ONGROUND)
+ {
+ if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
+ {
+ // don't stick to ground if onground and moving upward
+ ent->fields.server->flags -= FL_ONGROUND;
+ }
+ else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
+ {
+ // we can trust FL_ONGROUND if groundentity is world because it never moves
+ return;
+ }
+ else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
+ {
+ // if ent was supported by a brush model on previous frame,
+ // and groundentity is now freed, set groundentity to 0 (world)
+ // which leaves it suspended in the air
+ ent->fields.server->groundentity = 0;
+ return;
+ }
+ }
+ ent->priv.server->suspendedinairflag = false;
SV_CheckVelocity (ent);
// add gravity
- if (ent->v.movetype != MOVETYPE_FLY
- && ent->v.movetype != MOVETYPE_BOUNCEMISSILE // LordHavoc: enabled MOVETYPE_BOUNCEMISSILE
- && ent->v.movetype != MOVETYPE_FLYMISSILE)
+ if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
SV_AddGravity (ent);
// move angles
- VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
+ VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
// move origin
- VectorScale (ent->v.velocity, host_frametime, move);
- trace = SV_PushEntity (ent, move);
- if (trace.fraction == 1)
+ VectorScale (ent->fields.server->velocity, sv.frametime, move);
+ trace = SV_PushEntity (ent, move, true);
+ if (ent->priv.server->free)
return;
- if (ent->free)
- return;
-
- if (ent->v.movetype == MOVETYPE_BOUNCE)
- backoff = 1.5;
- else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE)
- backoff = 2.0;
- else
- backoff = 1;
-
- ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
+ if (trace.bmodelstartsolid)
+ {
+ // try to unstick the entity
+ SV_UnstickEntity(ent);
+ trace = SV_PushEntity (ent, move, false);
+ if (ent->priv.server->free)
+ return;
+ }
-// stop if on ground
- if (trace.plane.normal[2] > 0.7)
- {
- if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE))
+ if (trace.fraction < 1)
+ {
+ if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
+ {
+ ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
+ ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
+ }
+ else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
+ {
+ float d;
+ ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
+ // LordHavoc: fixed grenades not bouncing when fired down a slope
+ if (sv_gameplayfix_grenadebouncedownslopes.integer)
+ {
+ d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
+ if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
+ {
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
+ VectorClear (ent->fields.server->velocity);
+ VectorClear (ent->fields.server->avelocity);
+ }
+ else
+ ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
+ }
+ else
+ {
+ if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
+ {
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
+ VectorClear (ent->fields.server->velocity);
+ VectorClear (ent->fields.server->avelocity);
+ }
+ else
+ ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
+ }
+ }
+ else
{
- ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
- ent->v.groundentity = EDICT_TO_PROG(trace.ent);
- VectorClear (ent->v.velocity);
- VectorClear (ent->v.avelocity);
+ ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
+ if (trace.plane.normal[2] > 0.7)
+ {
+ ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
+ ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
+ if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
+ ent->priv.server->suspendedinairflag = true;
+ VectorClear (ent->fields.server->velocity);
+ VectorClear (ent->fields.server->avelocity);
+ }
+ else
+ ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
}
}
-
+
// check for in water
SV_CheckWaterTransition (ent);
}
will fall if the floor is pulled out from under them.
=============
*/
-void SV_Physics_Step (edict_t *ent)
+void SV_Physics_Step (prvm_edict_t *ent)
{
- qboolean hitsound;
-
-// freefall if not onground
- if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )
+ int flags = (int)ent->fields.server->flags;
+ // don't fall at all if fly/swim
+ if (!(flags & (FL_FLY | FL_SWIM)))
{
- if (ent->v.velocity[2] < sv_gravity.value*-0.1)
- hitsound = true;
+ if (flags & FL_ONGROUND)
+ {
+ // freefall if onground and moving upward
+ // freefall if not standing on a world surface (it may be a lift or trap door)
+ if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
+ {
+ ent->fields.server->flags -= FL_ONGROUND;
+ SV_AddGravity(ent);
+ SV_CheckVelocity(ent);
+ SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
+ SV_LinkEdict(ent, true);
+ }
+ }
else
- hitsound = false;
+ {
+ // freefall if not onground
+ int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
- SV_AddGravity (ent);
- SV_CheckVelocity (ent);
- SV_FlyMove (ent, host_frametime, NULL);
- SV_LinkEdict (ent, true);
+ SV_AddGravity(ent);
+ SV_CheckVelocity(ent);
+ SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
+ SV_LinkEdict(ent, true);
- if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground
- {
- if (hitsound)
- SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
+ // just hit ground
+ if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
+ SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
}
}
// regular thinking
- SV_RunThink (ent);
-
- SV_CheckWaterTransition (ent);
+ SV_RunThink(ent);
+
+ SV_CheckWaterTransition(ent);
}
//============================================================================
+static void SV_Physics_Entity (prvm_edict_t *ent)
+{
+ // don't run a move on newly spawned projectiles as it messes up movement
+ // interpolation and rocket trails
+ qboolean runmove = ent->priv.server->move;
+ ent->priv.server->move = true;
+ switch ((int) ent->fields.server->movetype)
+ {
+ case MOVETYPE_PUSH:
+ case MOVETYPE_FAKEPUSH:
+ SV_Physics_Pusher (ent);
+ break;
+ case MOVETYPE_NONE:
+ // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
+ if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
+ SV_RunThink (ent);
+ break;
+ case MOVETYPE_FOLLOW:
+ 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_LinkEdict(ent, false);
+ break;
+ case MOVETYPE_STEP:
+ SV_Physics_Step (ent);
+ break;
+ case MOVETYPE_WALK:
+ if (SV_RunThink (ent))
+ {
+ if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
+ SV_AddGravity (ent);
+ SV_CheckStuck (ent);
+ SV_WalkMove (ent);
+ SV_LinkEdict (ent, true);
+ }
+ break;
+ case MOVETYPE_TOSS:
+ case MOVETYPE_BOUNCE:
+ case MOVETYPE_BOUNCEMISSILE:
+ case MOVETYPE_FLYMISSILE:
+ case MOVETYPE_FLY:
+ // regular thinking
+ if (SV_RunThink (ent) && runmove)
+ SV_Physics_Toss (ent);
+ break;
+ default:
+ Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
+ break;
+ }
+}
+
+void SV_Physics_ClientEntity (prvm_edict_t *ent)
+{
+ SV_ApplyClientMove();
+ // make sure the velocity is sane (not a NaN)
+ SV_CheckVelocity(ent);
+ // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
+ if (prog->funcoffsets.SV_PlayerPhysics && sv_playerphysicsqc.integer)
+ {
+ prog->globals.server->time = sv.time;
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+ PRVM_ExecuteProgram (prog->funcoffsets.SV_PlayerPhysics, "QC function SV_PlayerPhysics is missing");
+ }
+ else
+ 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
+ // 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);
+ // call standard client pre-think
+ prog->globals.server->time = sv.time;
+ prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+ PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
+ SV_CheckVelocity (ent);
+
+ switch ((int) ent->fields.server->movetype)
+ {
+ case MOVETYPE_PUSH:
+ case MOVETYPE_FAKEPUSH:
+ SV_Physics_Pusher (ent);
+ break;
+ case MOVETYPE_NONE:
+ // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
+ if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
+ SV_RunThink (ent);
+ break;
+ case MOVETYPE_FOLLOW:
+ 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);
+ }
+ break;
+ case MOVETYPE_STEP:
+ SV_Physics_Step (ent);
+ break;
+ case MOVETYPE_WALK:
+ if (SV_RunThink (ent))
+ {
+ if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
+ SV_AddGravity (ent);
+ SV_CheckStuck (ent);
+ SV_WalkMove (ent);
+ }
+ break;
+ case MOVETYPE_TOSS:
+ case MOVETYPE_BOUNCE:
+ case MOVETYPE_BOUNCEMISSILE:
+ case MOVETYPE_FLYMISSILE:
+ // regular thinking
+ if (SV_RunThink (ent))
+ SV_Physics_Toss (ent);
+ break;
+ case MOVETYPE_FLY:
+ if (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;
+ }
+
+ SV_CheckVelocity (ent);
+
+ // call standard player post-think
+ SV_LinkEdict (ent, true);
+
+ SV_CheckVelocity (ent);
+
+ 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");
+}
+
/*
================
SV_Physics
================
*/
-extern dfunction_t *EndFrameQC;
void SV_Physics (void)
{
- int i;
- edict_t *ent;
+ int i;
+ prvm_edict_t *ent;
// let the progs know that a new frame has started
- pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
- pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
- pr_global_struct->time = sv.time;
- PR_ExecuteProgram (pr_global_struct->StartFrame);
-
-//SV_CheckAllEnts ();
+ 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;
+ prog->globals.server->frametime = sv.frametime;
+ PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
//
// treat each object in turn
//
- ent = sv.edicts;
- for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
- {
- if (ent->free)
- continue;
-
- if (pr_global_struct->force_retouch)
- SV_LinkEdict (ent, true); // force retouch even for stationary
- if (i > 0 && i <= svs.maxclients)
- {
- SV_Physics_Client (ent, i);
- continue;
- }
+ // if force_retouch, relink all the entities
+ if (prog->globals.server->force_retouch > 0)
+ for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
+ if (!ent->priv.server->free)
+ SV_LinkEdict (ent, true); // force retouch even for stationary
- switch ((int) ent->v.movetype)
+ // 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)
{
- case MOVETYPE_PUSH:
- SV_Physics_Pusher (ent);
- break;
- case MOVETYPE_NONE:
- SV_Physics_None (ent);
- break;
- case MOVETYPE_FOLLOW:
- SV_Physics_Follow (ent);
- break;
- case MOVETYPE_NOCLIP:
- SV_Physics_Noclip (ent);
- break;
- case MOVETYPE_STEP:
- SV_Physics_Step (ent);
- break;
- // LordHavoc: added support for MOVETYPE_WALK on normal entities! :)
- case MOVETYPE_WALK:
- if (SV_RunThink (ent))
- {
- if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
- SV_AddGravity (ent);
- SV_CheckStuck (ent);
- SV_WalkMove (ent);
- }
- break;
- case MOVETYPE_TOSS:
- case MOVETYPE_BOUNCE:
- case MOVETYPE_BOUNCEMISSILE:
- case MOVETYPE_FLY:
- case MOVETYPE_FLYMISSILE:
- SV_Physics_Toss (ent);
- break;
- default:
- Host_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);
- break;
+ // 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);
}
}
-
- if (pr_global_struct->force_retouch)
- pr_global_struct->force_retouch--;
+
+ // run physics on all the non-client entities
+ if (!sv_freezenonclients.integer)
+ for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
+ if (!ent->priv.server->free)
+ SV_Physics_Entity(ent);
+
+ if (prog->globals.server->force_retouch > 0)
+ prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
// LordHavoc: endframe support
- if (EndFrameQC)
+ if (prog->funcoffsets.EndFrame)
{
- pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
- pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
- pr_global_struct->time = sv.time;
- PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions));
+ 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 (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
}
- sv.time += host_frametime;
-}
-
-
-trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore)
-{
- int i;
- edict_t tempent, *tent;
- trace_t trace;
- vec3_t move;
- vec3_t end;
- double save_frametime;
-
- save_frametime = host_frametime;
- host_frametime = 0.05;
-
- memcpy(&tempent, ent, sizeof(edict_t));
- tent = &tempent;
+ // decrement prog->num_edicts if the highest number entities died
+ for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
- for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
- {
- SV_CheckVelocity (tent);
- SV_AddGravity (tent);
- VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles);
- VectorScale (tent->v.velocity, host_frametime, move);
- VectorAdd (tent->v.origin, move, end);
- trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent);
- VectorCopy (trace.endpos, tent->v.origin);
-
- if (trace.ent)
- if (trace.ent != ignore)
- break;
- }
- host_frametime = save_frametime;
- return trace;
+ if (!sv_freezenonclients.integer)
+ sv.time += sv.frametime;
}