// sv_phys.c
#include "quakedef.h"
+// used only for VM_GetTempString
+#include "prvm_cmds.h"
/*
*/
cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
+cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
-cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
+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_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
+// TODO: move this extern to server.h
+extern cvar_t sv_clmovement_waitforinput;
+
#define MOVE_EPSILON 0.01
void SV_Physics_Toss (prvm_edict_t *ent);
returns true if the entity is in solid currently
============
*/
-static int SV_TestEntityPosition (prvm_edict_t *ent, int movemode)
+static int SV_TestEntityPosition (prvm_edict_t *ent)
{
- return SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, movemode, ent).startsolid;
+ trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
+ if (trace.startsupercontents & SUPERCONTENTS_SOLID)
+ return true;
+ else
+ return false;
}
/*
|| check->fields.server->movetype == MOVETYPE_NOCLIP)
continue;
- if (SV_TestEntityPosition (check, MOVE_NORMAL))
+ if (SV_TestEntityPosition (check))
Con_Print("entity in invalid position\n");
}
}
Two entities have touched, so run their touch functions
==================
*/
-void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
+void SV_Impact (prvm_edict_t *e1, trace_t *trace)
{
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;
prog->globals.server->time = sv.time;
- if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
+ if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
{
prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
+ prog->globals.server->trace_allsolid = trace->allsolid;
+ prog->globals.server->trace_startsolid = trace->startsolid;
+ prog->globals.server->trace_fraction = trace->fraction;
+ prog->globals.server->trace_inwater = trace->inwater;
+ prog->globals.server->trace_inopen = trace->inopen;
+ VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
+ VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
+ prog->globals.server->trace_plane_dist = trace->plane.dist;
+ if (trace->ent)
+ prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
+ else
+ prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
+ val->_float = trace->startsupercontents;
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
+ val->_float = trace->hitsupercontents;
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
+ val->_float = trace->hitq3surfaceflags;
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
+ {
+ if (trace->hittexture)
+ {
+ char *s = VM_GetTempString();
+ strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
+ val->string = PRVM_SetEngineString(s);
+ }
+ else
+ val->string = 0;
+ }
PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
}
- if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
+ if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
{
prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
+ prog->globals.server->trace_allsolid = false;
+ prog->globals.server->trace_startsolid = false;
+ prog->globals.server->trace_fraction = 1;
+ prog->globals.server->trace_inwater = false;
+ prog->globals.server->trace_inopen = true;
+ VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
+ VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
+ prog->globals.server->trace_plane_dist = 0;
+ prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
+ val->_float = 0;
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
+ val->_float = 0;
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
+ val->_float = 0;
+ if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
+ val->string = 0;
PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
}
Con_Print("\n");
#endif
- /*
- if (trace.startsolid)
+ if (trace.bmodelstartsolid)
{
- // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
+ // 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;
}
- */
// break if it moved the entire distance
if (trace.fraction == 1)
// run the impact function
if (impact)
{
- SV_Impact(ent, (prvm_edict_t *)trace.ent);
+ SV_Impact(ent, &trace);
// break if removed by the impact function
if (ent->priv.server->free)
trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
}
*/
+
+ // 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_LinkEdict (ent, true);
if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
- SV_Impact (ent, (prvm_edict_t *)trace.ent);
+ SV_Impact (ent, &trace);
return trace;
}
SV_LinkEdict (pusher, false);
return;
default:
- Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
+ Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
return;
}
index = (int) pusher->fields.server->modelindex;
if (index < 1 || index >= MAX_MODELS)
{
- Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
+ 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];
continue;
// if the entity is standing on the pusher, it will definitely be moved
- if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
+ if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
{
- // if the entity is not inside the pusher's final position, leave it alone
- if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
- continue;
// remove the onground flag for non-players
if (check->fields.server->movetype != MOVETYPE_WALK)
check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
}
+ else
+ {
+ // if the entity is not inside the pusher's final position, leave it alone
+ if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
+ continue;
+ }
if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
pusher->fields.server->solid = savesolid; // was SOLID_BSP
// if it is still inside the pusher, block
- if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
+ if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
{
// try moving the contacted entity a tiny bit further to account for precision errors
vec3_t move2;
VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
SV_PushEntity (check, move2);
pusher->fields.server->solid = savesolid;
- if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
+ if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
{
// try moving the contacted entity a tiny bit less to account for precision errors
pusher->fields.server->solid = SOLID_NOT;
VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
SV_PushEntity (check, move2);
pusher->fields.server->solid = savesolid;
- if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
+ if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
{
// still inside pusher, so it's really blocked
int i, j, z;
vec3_t org;
- if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
+ if (!SV_TestEntityPosition(ent))
{
VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
return;
VectorCopy (ent->fields.server->origin, org);
VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
- if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
+ if (!SV_TestEntityPosition(ent))
{
- Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
+ 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++)
+ 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, MOVE_NORMAL))
+ if (!SV_TestEntityPosition(ent))
{
- Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
+ Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
SV_LinkEdict (ent, true);
return;
}
}
VectorCopy (org, ent->fields.server->origin);
- Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
+ 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)
vec3_t org;
// if not stuck in a bmodel, just return
- if (!SV_TestEntityPosition(ent, MOVE_NOMONSTERS))
+ if (!SV_TestEntityPosition(ent))
return;
VectorCopy (ent->fields.server->origin, org);
- for (z=0 ; z< 18 ; z += 6)
+ for (z=-1 ; z< 18 ; z += 6)
for (i=-1 ; i <= 1 ; i++)
for (j=-1 ; j <= 1 ; j++)
{
ent->fields.server->origin[0] = org[0] + i;
ent->fields.server->origin[1] = org[1] + j;
ent->fields.server->origin[2] = org[2] + z;
- if (!SV_TestEntityPosition(ent, MOVE_NOMONSTERS))
+ if (!SV_TestEntityPosition(ent))
{
- Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
+ 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;
}
if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
{
+ // this has been disabled so that you can't jump when you are stepping
+ // up while already jumping (also known as the Quake2 stair jump bug)
+#if 0
// LordHavoc: disabled this check so you can walk on monsters/players
//if (ent->fields.server->solid == SOLID_BSP)
{
ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
}
+#endif
}
else
{
// if onground, return without moving
if ((int)ent->fields.server->flags & FL_ONGROUND)
{
- // don't stick to ground if onground and moving upward
- if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
+ 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
+ }
+ 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)
{
- prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
- if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
- return;
// if ent was supported by a brush model on previous frame,
- // and groundentity is now freed, set groundentity to 0 (floating)
- if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
- {
- // leave it suspended in the air
- ent->fields.server->groundentity = 0;
- return;
- }
+ // 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;
trace = SV_PushEntity (ent, move);
if (ent->priv.server->free)
return;
- if (trace.startsolid)
+ if (trace.bmodelstartsolid)
{
// try to unstick the entity
SV_UnstickEntity(ent);
if (flags & FL_ONGROUND)
{
// freefall if onground and moving upward
- // freefall if not standing on a world surface (it may be a lift)
- prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
- if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
+ // 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);
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->movesequence)
+ else if (host_client->clmovement_skipphysicsframes > 0)
+ host_client->clmovement_skipphysicsframes--;
+ else
SV_Physics_ClientEntity(ent);
}
}
trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
{
int i;
- float gravity, savesolid;
+ float gravity;
vec3_t move, end;
- prvm_edict_t tempent, *tent;
- entvars_t vars;
+ vec3_t original_origin;
+ vec3_t original_velocity;
+ vec3_t original_angles;
+ vec3_t original_avelocity;
prvm_eval_t *val;
trace_t trace;
- // copy the vars over
- memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
- // set up the temp entity to point to the copied vars
- tent = &tempent;
- tent->fields.server = &vars;
+ VectorCopy(tossent->fields.server->origin , original_origin );
+ VectorCopy(tossent->fields.server->velocity , original_velocity );
+ VectorCopy(tossent->fields.server->angles , original_angles );
+ VectorCopy(tossent->fields.server->avelocity, original_avelocity);
- savesolid = tossent->fields.server->solid;
- tossent->fields.server->solid = SOLID_NOT;
-
- // this has to fetch the field from the original edict, since our copy is truncated
val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
if (val != NULL && val->_float != 0)
gravity = val->_float;
for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
{
- SV_CheckVelocity (tent);
- tent->fields.server->velocity[2] -= gravity;
- VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
- VectorScale (tent->fields.server->velocity, 0.05, move);
- VectorAdd (tent->fields.server->origin, move, end);
- trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
- VectorCopy (trace.endpos, tent->fields.server->origin);
-
- if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
+ SV_CheckVelocity (tossent);
+ tossent->fields.server->velocity[2] -= gravity;
+ VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
+ VectorScale (tossent->fields.server->velocity, 0.05, move);
+ VectorAdd (tossent->fields.server->origin, move, end);
+ trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
+ VectorCopy (trace.endpos, tossent->fields.server->origin);
+
+ if (trace.fraction < 1)
break;
}
- tossent->fields.server->solid = savesolid;
- trace.fraction = 0; // not relevant
+
+ VectorCopy(original_origin , tossent->fields.server->origin );
+ VectorCopy(original_velocity , tossent->fields.server->velocity );
+ VectorCopy(original_angles , tossent->fields.server->angles );
+ VectorCopy(original_avelocity, tossent->fields.server->avelocity);
+
return trace;
}