This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/server/sv_phys.c

1778 lines
50 KiB
C
Raw Normal View History

2007-09-17 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2007 <20>
// sv_physics.c - server physic
//=======================================================================
2008-06-09 22:00:00 +02:00
#include "common.h"
2007-09-17 22:00:00 +02:00
#include "server.h"
2008-11-25 22:00:00 +01:00
#include "matrix_lib.h"
2008-08-30 22:00:00 +02:00
#include "const.h"
2007-09-28 22:00:00 +02:00
2007-09-17 22:00:00 +02:00
/*
2008-07-30 22:00:00 +02:00
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.
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
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
corpses are SOLID_NOT and MOVETYPE_TOSS
crates are SOLID_BBOX and MOVETYPE_TOSS
walking monsters are SOLID_BBOX and MOVETYPE_STEP
flying/floating monsters are SOLID_BBOX and MOVETYPE_FLY
solid_edge items only clip against bsp models.
2007-09-17 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
#define MOVE_EPSILON 0.01
#define MAX_CLIP_PLANES 32
2007-09-17 22:00:00 +02:00
2007-09-28 22:00:00 +02:00
/*
2008-07-30 22:00:00 +02:00
===============================================================================
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
LINE TESTING IN HULLS
===============================================================================
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
int SV_ContentsMask( const edict_t *passedict )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( passedict )
2007-09-28 22:00:00 +02:00
{
2008-12-21 22:00:00 +01:00
if( passedict->v.flags & FL_MONSTER )
2008-07-30 22:00:00 +02:00
return MASK_MONSTERSOLID;
2008-12-21 22:00:00 +01:00
else if( passedict->v.flags & FL_CLIENT )
2008-07-30 22:00:00 +02:00
return MASK_PLAYERSOLID;
2008-12-15 22:00:00 +01:00
else if( passedict->v.solid == SOLID_TRIGGER )
2008-07-30 22:00:00 +02:00
return CONTENTS_SOLID|CONTENTS_BODY;
return MASK_SOLID;
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
return MASK_SOLID;
2007-09-28 22:00:00 +02:00
}
/*
2008-07-30 22:00:00 +02:00
==================
SV_Trace
==================
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
trace_t SV_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, edict_t *passedict, int contentsmask )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
vec3_t hullmins, hullmaxs;
int i, bodycontents;
bool pointtrace;
edict_t *traceowner, *touch;
2007-09-28 22:00:00 +02:00
trace_t trace;
2008-07-30 22:00:00 +02:00
vec3_t clipboxmins, clipboxmaxs;// bounding box of entire move area
vec3_t clipmins, clipmaxs;// size of the moving object
vec3_t clipmins2, clipmaxs2;// size when clipping against monsters
vec3_t clipstart, clipend;// start and end origin of move
trace_t cliptrace;// trace results
matrix4x4 matrix, imatrix;// matrices to transform into/out of other entity's space
cmodel_t *model;// model of other entity
int numtouchedicts;// list of entities to test for collisions
edict_t *touchedicts[MAX_EDICTS];
VectorCopy( start, clipstart );
VectorCopy( end, clipend );
VectorCopy( mins, clipmins );
VectorCopy( maxs, clipmaxs );
VectorCopy( mins, clipmins2 );
VectorCopy( maxs, clipmaxs2 );
// clip to world
pe->ClipToWorld( &cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, contentsmask );
cliptrace.startstuck = cliptrace.startsolid;
if( cliptrace.startsolid || cliptrace.fraction < 1 )
2008-12-20 22:00:00 +01:00
cliptrace.ent = EDICT_NUM( 0 );
2008-07-30 22:00:00 +02:00
if( type == MOVE_WORLDONLY )
return cliptrace;
if( type == MOVE_MISSILE )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
// LordHavoc: modified this, was = -15, now -= 15
for( i = 0; i < 3; i++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
clipmins2[i] -= 15;
clipmaxs2[i] += 15;
2007-09-28 22:00:00 +02:00
}
}
2008-07-30 22:00:00 +02:00
VectorCopy( clipmins, hullmins );
VectorCopy( clipmaxs, hullmaxs );
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// 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;
}
2007-09-28 22:00:00 +02:00
2008-12-20 22:00:00 +01:00
// if the passedict is world, make it NULL (to avoid two checks each time)
if( passedict == EDICT_NUM( 0 )) passedict = NULL;
2008-07-30 22:00:00 +02:00
// figure out whether this is a point trace for comparisons
pointtrace = VectorCompare(clipmins, clipmaxs);
// precalculate passedict's owner edict pointer for comparisons
2008-12-17 22:00:00 +01:00
if( passedict && passedict->v.owner )
traceowner = passedict->v.owner;
else traceowner = NULL;
2008-07-30 22:00:00 +02:00
// clip to entities
2008-07-31 22:00:00 +02:00
// because this uses SV_AreaEdicts, we know all entity boxes overlap
2008-07-30 22:00:00 +02:00
// the clip region, so we can skip culling checks in the loop below
2009-09-24 22:00:00 +02:00
numtouchedicts = SV_AreaEdicts( clipboxmins, clipboxmaxs, touchedicts, GI->max_edicts, AREA_SOLID );
if( numtouchedicts > GI->max_edicts )
2008-07-30 22:00:00 +02:00
{
// this never happens
2009-09-24 22:00:00 +02:00
MsgDev( D_WARN, "SV_AreaEdicts returned %i edicts, max was %i\n", numtouchedicts, GI->max_edicts );
numtouchedicts = GI->max_edicts;
2008-07-30 22:00:00 +02:00
}
for( i = 0; i < numtouchedicts; i++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
touch = touchedicts[i];
2008-12-15 22:00:00 +01:00
if( touch->v.solid < SOLID_BBOX ) continue;
if( type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP )
2008-07-30 22:00:00 +02:00
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
2008-12-15 22:00:00 +01:00
if( passedict == touch->v.owner ) continue;
2008-07-30 22:00:00 +02:00
// don't clip points against points (they can't collide)
2008-12-17 22:00:00 +01:00
if( pointtrace && VectorCompare( touch->v.mins, touch->v.maxs ) && (type != MOVE_MISSILE || !(touch->v.flags & FL_MONSTER)))
2008-07-30 22:00:00 +02:00
continue;
}
bodycontents = CONTENTS_BODY;
// might interact, so do an exact clip
model = NULL;
2008-12-17 22:00:00 +01:00
if( touch->v.solid == SOLID_BSP || type == MOVE_HITMODEL )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
uint modelindex = (uint)touch->v.modelindex;
2008-07-30 22:00:00 +02:00
// if the modelindex is 0, it shouldn't be SOLID_BSP!
if( modelindex > 0 && modelindex < MAX_MODELS )
2008-12-25 22:00:00 +01:00
model = sv.models[touch->v.modelindex];
2007-09-28 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
if( model ) Matrix4x4_CreateFromEntity( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2], touch->v.angles[0], touch->v.angles[1], touch->v.angles[2], 1 );
else Matrix4x4_CreateTranslate( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2] );
2008-07-30 22:00:00 +02:00
Matrix4x4_Invert_Simple( imatrix, matrix );
2008-12-15 22:00:00 +01:00
if((int)touch->v.flags & FL_MONSTER)
pe->ClipToGenericEntity(&trace, model, touch->v.mins, touch->v.maxs, bodycontents, matrix, imatrix, clipstart, clipmins2, clipmaxs2, clipend, contentsmask );
else pe->ClipToGenericEntity(&trace, model, touch->v.mins, touch->v.maxs, bodycontents, matrix, imatrix, clipstart, clipmins, clipmaxs, clipend, contentsmask );
2008-07-30 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
pe->CombineTraces( &cliptrace, &trace, touch, touch->v.solid == SOLID_BSP );
2008-07-30 22:00:00 +02:00
}
return cliptrace;
}
int SV_PointContents( const vec3_t point )
{
int i, contents = 0;
edict_t *touch;
vec3_t transformed;
matrix4x4 matrix, imatrix; // matrices to transform into/out of other entity's space
cmodel_t *model; // model of other entity
uint modelindex;
int numtouchedicts; // list of entities to test for collisions
edict_t *touchedicts[MAX_EDICTS];
// get world supercontents at this point
if( sv.worldmodel && sv.worldmodel->PointContents )
contents = sv.worldmodel->PointContents( point, sv.worldmodel );
// get list of entities at this point
2009-09-24 22:00:00 +02:00
numtouchedicts = SV_AreaEdicts( point, point, touchedicts, GI->max_edicts, AREA_SOLID );
if( numtouchedicts > GI->max_edicts )
2008-07-30 22:00:00 +02:00
{
// this never happens
2009-09-24 22:00:00 +02:00
MsgDev( D_WARN, "SV_AreaEdicts returned %i edicts, max was %i\n", numtouchedicts, GI->max_edicts );
numtouchedicts = GI->max_edicts;
2008-07-30 22:00:00 +02:00
}
for( i = 0; i < numtouchedicts; i++ )
{
touch = touchedicts[i];
// we only care about SOLID_BSP for pointcontents
2008-12-15 22:00:00 +01:00
if( touch->v.solid != SOLID_BSP ) continue;
2008-07-30 22:00:00 +02:00
// might interact, so do an exact clip
2008-12-15 22:00:00 +01:00
modelindex = (uint)touch->v.modelindex;
2008-07-30 22:00:00 +02:00
if( modelindex >= MAX_MODELS ) continue;
2008-12-15 22:00:00 +01:00
model = sv.models[(int)touch->v.modelindex];
2008-07-30 22:00:00 +02:00
if( !model || !model->PointContents ) continue;
2008-12-15 22:00:00 +01:00
Matrix4x4_CreateFromEntity( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2], touch->v.angles[0], touch->v.angles[1], touch->v.angles[2], 1 );
2008-07-30 22:00:00 +02:00
Matrix4x4_Invert_Simple( imatrix, matrix );
2009-08-30 22:00:00 +02:00
Matrix4x4_VectorTransform( imatrix, point, transformed);
2008-07-30 22:00:00 +02:00
contents |= model->PointContents( transformed, model );
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
return contents;
2007-09-28 22:00:00 +02:00
}
/*
2008-07-30 22:00:00 +02:00
===============================================================================
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
Utility functions
===============================================================================
*/
/*
============
SV_TestEntityPosition
returns true if the entity is in solid currently
============
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
static int SV_TestEntityPosition( edict_t *ent, vec3_t offset )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
vec3_t org;
trace_t trace;
2008-12-15 22:00:00 +01:00
VectorAdd( ent->v.origin, offset, org );
trace = SV_Trace( org, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NOMONSTERS, ent, CONTENTS_SOLID );
2008-07-30 22:00:00 +02:00
if( trace.startcontents & CONTENTS_SOLID )
return true;
// if the trace found a better position for the entity, move it there
2008-12-15 22:00:00 +01:00
if( VectorDistance2( trace.endpos, ent->v.origin ) >= 0.0001 )
VectorCopy( trace.endpos, ent->v.origin );
2008-07-30 22:00:00 +02:00
return false;
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
/*
================
SV_CheckAllEnts
================
*/
void SV_CheckAllEnts( void )
{
int e;
edict_t *check;
// see if any solid entities are inside the final position
2008-12-17 22:00:00 +01:00
check = EDICT_NUM( 1 );
2008-12-26 22:00:00 +01:00
for( e = 1; e < svgame.globals->numEntities; e++, check++ )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if( check->free ) continue;
switch( check->v.movetype )
2008-07-30 22:00:00 +02:00
{
case MOVETYPE_PUSH:
case MOVETYPE_NONE:
case MOVETYPE_FOLLOW:
case MOVETYPE_NOCLIP:
continue;
default: break;
}
if(SV_TestEntityPosition( check, vec3_origin ))
MsgDev( D_INFO, "entity in invalid position\n" );
}
}
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
/*
================
SV_CheckVelocity
================
*/
void SV_CheckVelocity( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
int i;
float wishspeed;
// bound velocity
2008-07-30 22:00:00 +02:00
for( i = 0; i < 3; i++ )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if(IS_NAN(ent->v.velocity[i]))
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
MsgDev( D_INFO, "Got a NaN velocity on entity #%i (%s)\n", NUM_FOR_EDICT( ent ), STRING( ent->v.classname ));
ent->v.velocity[i] = 0;
2007-09-28 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
if (IS_NAN(ent->v.origin[i]))
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
MsgDev( D_INFO, "Got a NaN origin on entity #%i (%s)\n", NUM_FOR_EDICT( ent ), STRING( ent->v.classname ));
ent->v.origin[i] = 0;
2007-09-28 22:00:00 +02:00
}
}
// LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
2008-12-15 22:00:00 +01:00
wishspeed = DotProduct( ent->v.velocity, ent->v.velocity );
2008-07-30 22:00:00 +02:00
if( wishspeed > sv_maxvelocity->value * sv_maxvelocity->value )
2007-09-28 22:00:00 +02:00
{
wishspeed = sv_maxvelocity->value / sqrt(wishspeed);
2008-12-15 22:00:00 +01:00
ent->v.velocity[0] *= wishspeed;
ent->v.velocity[1] *= wishspeed;
ent->v.velocity[2] *= wishspeed;
2007-09-28 22:00:00 +02:00
}
}
/*
2008-07-30 22:00:00 +02:00
=============
SV_RunThink
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
Runs thinking code if time. There is some play in the exact time the think
function will be called, because it is called before any movement is done
in a frame. Not used for pushmove objects, because they must be exact.
Returns false if the entity removed itself.
=============
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
bool SV_RunThink( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2009-01-14 22:00:00 +01:00
float thinktime;
2007-09-28 22:00:00 +02:00
2009-01-14 22:00:00 +01:00
thinktime = ent->v.nextthink;
2009-09-17 22:00:00 +02:00
if( thinktime <= 0 || thinktime > (sv.time * 0.001f) + svgame.globals->frametime )
2008-07-30 22:00:00 +02:00
return true;
2009-09-17 22:00:00 +02:00
if( thinktime < (sv.time * 0.001f)) // don't let things stay in the past.
thinktime = sv.time * 0.001f; // it is possible to start that way
2009-02-03 22:00:00 +01:00
// by a trigger with a local time.
2009-01-14 22:00:00 +01:00
svgame.globals->time = thinktime;
ent->v.nextthink = 0;
svgame.dllFuncs.pfnThink( ent );
2008-12-15 22:00:00 +01:00
return !ent->free;
2007-09-28 22:00:00 +02:00
}
/*
==================
SV_Impact
Two entities have touched, so run their touch functions
==================
*/
2008-07-30 22:00:00 +02:00
void SV_Impact( edict_t *e1, trace_t *trace )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
edict_t *e2 = trace->ent;
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
SV_CopyTraceToGlobal( trace );
2007-09-28 22:00:00 +02:00
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-15 22:00:00 +01:00
if( !e1->free && !e2->free && e1->v.solid != SOLID_NOT )
2007-09-28 22:00:00 +02:00
{
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnTouch( e1, e2 );
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
if( !e1->free && !e2->free && e2->v.solid != SOLID_NOT )
2007-09-28 22:00:00 +02:00
{
2008-12-26 22:00:00 +01:00
VectorCopy( e2->v.origin, svgame.globals->trace_endpos );
VectorNegate( trace->plane.normal, svgame.globals->trace_plane_normal );
svgame.globals->trace_plane_dist = -trace->plane.dist;
svgame.globals->trace_ent = e1;
svgame.dllFuncs.pfnTouch( e2, e1 );
2007-09-28 22:00:00 +02:00
}
}
/*
============
2008-07-31 22:00:00 +02:00
SV_TouchTriggers
2007-09-28 22:00:00 +02:00
2008-07-31 22:00:00 +02:00
called by player or monster
2007-09-28 22:00:00 +02:00
============
*/
2008-07-31 22:00:00 +02:00
void SV_TouchTriggers( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-07-31 22:00:00 +02:00
int i, num;
edict_t *touch[MAX_EDICTS];
// dead things don't activate triggers!
2008-12-15 22:00:00 +01:00
if(!(ent->v.flags & FL_CLIENT) && (ent->v.health <= 0))
2008-07-31 22:00:00 +02:00
return;
2009-09-24 22:00:00 +02:00
num = SV_AreaEdicts( ent->v.absmin, ent->v.absmax, touch, GI->max_edicts, AREA_TRIGGERS );
2008-07-31 22:00:00 +02:00
// be careful, it is possible to have an entity in this
// list removed before we get to it (killtriggered)
for( i = 0; i < num; i++ )
{
2008-12-15 22:00:00 +01:00
if( touch[i]->free ) continue;
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnTouch( touch[i], ent );
2008-07-31 22:00:00 +02:00
}
2007-09-28 22:00:00 +02:00
}
2008-07-31 22:00:00 +02:00
/*
============
SV_ClampMove
clamp the move to 1/8 units, so the position will
be accurate for client side prediction
============
*/
2008-07-30 22:00:00 +02:00
void SV_ClampAngle( vec3_t angle )
2007-09-28 22:00:00 +02:00
{
2008-07-31 22:00:00 +02:00
angle[0] -= 360.0 * floor(angle[0] * (1.0 / 360.0));
angle[1] -= 360.0 * floor(angle[1] * (1.0 / 360.0));
angle[2] -= 360.0 * floor(angle[2] * (1.0 / 360.0));
2007-09-28 22:00:00 +02:00
}
/*
2008-07-30 22:00:00 +02:00
==================
SV_ClipVelocity
Slide off of the impacting object
returns the blocked flags (1 = floor, 2 = step / wall)
==================
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
void SV_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
int i;
float backoff;
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
backoff = -DotProduct (in, normal) * overbounce;
VectorMA( in, backoff, normal, out );
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
for( i = 0; i < 3; i++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON )
out[i] = 0;
2007-09-28 22:00:00 +02:00
}
}
/*
============
SV_FlyMove
The basic solid body movement clip that slides along multiple planes
Returns the clipflags if the velocity was modified (hit something solid)
1 = floor
2 = wall / step
4 = dead stop
If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
============
*/
2008-07-30 22:00:00 +02:00
int SV_FlyMove( edict_t *ent, float time, float *stepnormal, int contentsmask )
2007-09-28 22:00:00 +02:00
{
int blocked = 0, bumpcount;
2008-07-30 22:00:00 +02:00
int i, j, impact, numplanes;
2007-09-28 22:00:00 +02:00
float d, time_left;
vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
trace_t trace;
2008-07-30 22:00:00 +02:00
if( time <= 0 ) return 0;
2008-12-21 22:00:00 +01:00
VectorCopy( ent->v.velocity, original_velocity );
VectorCopy( ent->v.velocity, primal_velocity );
2007-09-28 22:00:00 +02:00
numplanes = 0;
time_left = time;
2008-07-30 22:00:00 +02:00
for( bumpcount = 0; bumpcount < MAX_CLIP_PLANES; bumpcount++ )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if(!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2])
2007-09-28 22:00:00 +02:00
break;
2008-12-15 22:00:00 +01:00
VectorMA( ent->v.origin, time_left, ent->v.velocity, end );
trace = SV_Trace( ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, contentsmask );
2007-09-28 22:00:00 +02:00
// break if it moved the entire distance
2008-07-30 22:00:00 +02:00
if( trace.fraction == 1 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
VectorCopy( trace.endpos, ent->v.origin );
2007-09-28 22:00:00 +02:00
break;
}
2008-07-30 22:00:00 +02:00
if( !trace.ent )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
MsgDev( D_WARN, "SV_FlyMove: trace.ent == NULL\n" );
2008-12-17 22:00:00 +01:00
trace.ent = EDICT_NUM( 0 );
2007-09-28 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
impact = !(ent->v.flags & FL_ONGROUND) || ent->v.groundentity != trace.ent;
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
if( trace.plane.normal[2] )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( trace.plane.normal[2] > 0.7 )
2007-09-28 22:00:00 +02:00
{
// floor
blocked |= 1;
2008-12-15 22:00:00 +01:00
ent->v.flags |= FL_ONGROUND;
ent->v.groundentity = trace.ent;
2007-09-28 22:00:00 +02:00
}
}
else
{
// step
blocked |= 2;
// save the trace for player extrafriction
2008-07-30 22:00:00 +02:00
if( stepnormal ) VectorCopy( trace.plane.normal, stepnormal );
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
if( trace.fraction >= 0.001 )
2007-09-28 22:00:00 +02:00
{
// actually covered some distance
2008-12-15 22:00:00 +01:00
VectorCopy( trace.endpos, ent->v.origin );
VectorCopy( ent->v.velocity, original_velocity );
2007-09-28 22:00:00 +02:00
numplanes = 0;
}
// run the impact function
2008-07-30 22:00:00 +02:00
if( impact )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
SV_Impact( ent, &trace );
2007-09-28 22:00:00 +02:00
// break if removed by the impact function
2008-12-15 22:00:00 +01:00
if( ent->free ) break;
2007-09-28 22:00:00 +02:00
}
time_left *= 1 - trace.fraction;
// clipped to another plane
2008-07-30 22:00:00 +02:00
if( numplanes >= MAX_CLIP_PLANES )
2007-09-28 22:00:00 +02:00
{
// this shouldn't really happen
2008-12-15 22:00:00 +01:00
VectorClear( ent->v.velocity );
2007-09-28 22:00:00 +02:00
blocked = 3;
break;
}
2008-07-30 22:00:00 +02:00
VectorCopy( trace.plane.normal, planes[numplanes] );
2007-09-28 22:00:00 +02:00
numplanes++;
// modify original_velocity so it parallels all of the clip planes
2008-07-30 22:00:00 +02:00
for( i = 0; i < numplanes; i++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
SV_ClipVelocity( original_velocity, planes[i], new_velocity, 1 );
for( j = 0; j < numplanes; j++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( j != i )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
// not ok
if(DotProduct( new_velocity, planes[j] ) < 0 )
2007-09-28 22:00:00 +02:00
break;
}
}
2008-07-30 22:00:00 +02:00
if( j == numplanes ) break;
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
if( i != numplanes )
2007-09-28 22:00:00 +02:00
{
// go along this plane
2008-12-15 22:00:00 +01:00
VectorCopy(new_velocity, ent->v.velocity);
2007-09-28 22:00:00 +02:00
}
else
{
// go along the crease
2008-07-30 22:00:00 +02:00
if( numplanes != 2 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
VectorClear( ent->v.velocity );
2007-09-28 22:00:00 +02:00
blocked = 7;
break;
}
2008-07-30 22:00:00 +02:00
CrossProduct( planes[0], planes[1], dir );
VectorNormalize( dir );
2008-12-15 22:00:00 +01:00
d = DotProduct( dir, ent->v.velocity );
VectorScale( dir, d, ent->v.velocity );
2007-09-28 22:00:00 +02:00
}
// if current velocity is against the original velocity,
// stop dead to avoid tiny occilations in sloping corners
2008-12-15 22:00:00 +01:00
if( DotProduct(ent->v.velocity, primal_velocity ) <= 0 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
VectorClear( ent->v.velocity );
2007-09-28 22:00:00 +02:00
break;
}
}
2008-07-30 22:00:00 +02:00
// this came from QW and allows you to get out of water more easily
2008-12-21 22:00:00 +01:00
if( ent->v.flags & FL_WATERJUMP )
2008-12-15 22:00:00 +01:00
VectorCopy( primal_velocity, ent->v.velocity );
2008-07-31 22:00:00 +02:00
2007-09-28 22:00:00 +02:00
return blocked;
}
/*
============
SV_AddGravity
============
*/
2008-07-30 22:00:00 +02:00
void SV_AddGravity( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-12-26 22:00:00 +01:00
if( ent->pvServerData->stuck )
{
VectorClear( ent->v.velocity );
return;
}
2008-12-21 22:00:00 +01:00
if( ent->v.gravity ) // gravity modifier
2008-12-26 22:00:00 +01:00
ent->v.velocity[2] -= sv_gravity->value * ent->v.gravity * svgame.globals->frametime;
else ent->v.velocity[2] -= sv_gravity->value * svgame.globals->frametime;
2007-09-28 22:00:00 +02:00
}
/*
2008-07-30 22:00:00 +02:00
===============================================================================
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
PUSHMOVE
===============================================================================
*/
/*
============
SV_PushEntity
Does not change the entities velocity at all
============
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
static trace_t SV_PushEntity( edict_t *ent, vec3_t push, bool failonstartstuck )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
int type;
trace_t trace;
vec3_t end;
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
VectorAdd( ent->v.origin, push, end );
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
if( ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT )
2008-07-30 22:00:00 +02:00
type = MOVE_NOMONSTERS; // only clip against bmodels
else type = MOVE_NORMAL;
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
trace = SV_Trace( ent->v.origin, ent->v.mins, ent->v.maxs, end, type, ent, SV_ContentsMask( ent ));
2008-07-30 22:00:00 +02:00
if( trace.startstuck && failonstartstuck )
return trace;
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
VectorCopy( trace.endpos, ent->v.origin );
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
2008-12-15 22:00:00 +01:00
if( ent->v.solid >= SOLID_TRIGGER && ent->v.solid < SOLID_BOX && trace.ent && (!(ent->v.flags & FL_ONGROUND) || ent->v.groundentity != trace.ent ))
2008-07-30 22:00:00 +02:00
SV_Impact( ent, &trace );
return trace;
2007-09-28 22:00:00 +02:00
}
/*
2008-07-30 22:00:00 +02:00
============
SV_PushMove
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
============
2007-09-28 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
void SV_PushMove( edict_t *pusher, float movetime )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
int i, e, index;
int checkcontents;
bool rotated;
float savesolid, movetime2, pushltime;
vec3_t mins, maxs, move, move1, moveangle;
vec3_t pushorig, pushang, a, forward, left, up, org;
int num_moved, numcheckentities;
static edict_t *checkentities[MAX_EDICTS];
cmodel_t *pushermodel;
2007-09-28 22:00:00 +02:00
trace_t trace;
2008-07-30 22:00:00 +02:00
matrix4x4 pusherfinalmatrix, pusherfinalimatrix;
word moved_edicts[MAX_EDICTS];
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2] && !pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
pusher->v.ltime += movetime;
2008-07-30 22:00:00 +02:00
return;
}
2008-12-15 22:00:00 +01:00
switch((int) pusher->v.solid )
2008-07-30 22:00:00 +02:00
{
case SOLID_BSP:
case SOLID_BBOX:
break;
case SOLID_NOT:
case SOLID_TRIGGER:
2008-12-15 22:00:00 +01:00
VectorMA (pusher->v.origin, movetime, pusher->v.velocity, pusher->v.origin);
VectorMA (pusher->v.angles, movetime, pusher->v.avelocity, pusher->v.angles);
SV_ClampAngle( pusher->v.angles );
pusher->v.ltime += movetime;
2008-07-30 22:00:00 +02:00
SV_LinkEdict( pusher );
return;
default:
2008-12-15 22:00:00 +01:00
MsgDev( D_WARN, "SV_PushMove: entity #%i, unrecognized solid type %f\n", NUM_FOR_EDICT( pusher ), pusher->v.solid );
2008-07-30 22:00:00 +02:00
return;
}
2008-12-15 22:00:00 +01:00
index = (int)pusher->v.modelindex;
2008-07-30 22:00:00 +02:00
if( index < 1 || index >= MAX_MODELS )
{
2008-12-15 22:00:00 +01:00
MsgDev( D_WARN, "SV_PushMove: entity #%i has an invalid modelindex %f\n", NUM_FOR_EDICT( pusher ), pusher->v.modelindex );
2008-07-30 22:00:00 +02:00
return;
}
pushermodel = sv.models[index];
2008-12-15 22:00:00 +01:00
rotated = VectorLength2(pusher->v.angles) + VectorLength2(pusher->v.avelocity) > 0;
2008-07-30 22:00:00 +02:00
movetime2 = movetime;
2008-12-15 22:00:00 +01:00
VectorScale( pusher->v.velocity, movetime2, move1 );
VectorScale( pusher->v.avelocity, movetime2, moveangle );
2008-07-30 22:00:00 +02:00
if( moveangle[0] || moveangle[2] )
{
for( i = 0; i < 3; i++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( move1[i] > 0 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
mins[i] = pushermodel->rotatedmins[i] + pusher->v.origin[i] - 1;
maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v.origin[i] + 1;
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
else
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v.origin[i] - 1;
maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v.origin[i] + 1;
2007-09-28 22:00:00 +02:00
}
}
}
2008-07-30 22:00:00 +02:00
else if( moveangle[1] )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
for( i = 0; i < 3; i++ )
{
if( move1[i] > 0 )
{
2008-12-15 22:00:00 +01:00
mins[i] = pushermodel->yawmins[i] + pusher->v.origin[i] - 1;
maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v.origin[i] + 1;
2008-07-30 22:00:00 +02:00
}
else
{
2008-12-15 22:00:00 +01:00
mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v.origin[i] - 1;
maxs[i] = pushermodel->yawmaxs[i] + pusher->v.origin[i] + 1;
2008-07-30 22:00:00 +02:00
}
}
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
else
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
for( i = 0; i < 3; i++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( move1[i] > 0 )
{
2008-12-15 22:00:00 +01:00
mins[i] = pushermodel->normalmins[i] + pusher->v.origin[i] - 1;
maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v.origin[i] + 1;
2008-07-30 22:00:00 +02:00
}
else
{
2008-12-15 22:00:00 +01:00
mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v.origin[i] - 1;
maxs[i] = pushermodel->normalmaxs[i] + pusher->v.origin[i] + 1;
2008-07-30 22:00:00 +02:00
}
2007-09-28 22:00:00 +02:00
}
}
2008-07-30 22:00:00 +02:00
VectorNegate( moveangle, a );
AngleVectorsFLU( a, forward, left, up );
2008-12-15 22:00:00 +01:00
VectorCopy( pusher->v.origin, pushorig );
VectorCopy( pusher->v.angles, pushang );
pushltime = pusher->v.ltime;
2008-07-30 22:00:00 +02:00
// move the pusher to its final position
2008-12-15 22:00:00 +01:00
VectorMA( pusher->v.origin, movetime, pusher->v.velocity, pusher->v.origin );
VectorMA( pusher->v.angles, movetime, pusher->v.avelocity, pusher->v.angles );
pusher->v.ltime += movetime;
2008-07-30 22:00:00 +02:00
SV_LinkEdict( pusher );
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
pushermodel = NULL;
2008-12-15 22:00:00 +01:00
if( pusher->v.modelindex >= 1 && pusher->v.modelindex < MAX_MODELS )
pushermodel = sv.models[(int)pusher->v.modelindex];
Matrix4x4_CreateFromEntity( pusherfinalmatrix, pusher->v.origin[0], pusher->v.origin[1], pusher->v.origin[2], pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2], 1 );
2008-07-30 22:00:00 +02:00
Matrix4x4_Invert_Simple( pusherfinalimatrix, pusherfinalmatrix );
2008-12-15 22:00:00 +01:00
savesolid = pusher->v.solid;
2008-07-30 22:00:00 +02:00
// see if any solid entities are inside the final position
num_moved = 0;
2008-11-14 22:00:00 +01:00
numcheckentities = SV_AreaEdicts( mins, maxs, checkentities, MAX_EDICTS, AREA_SOLID );
2008-07-30 22:00:00 +02:00
for( e = 0; e < numcheckentities; e++ )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
edict_t *check = checkentities[e];
2008-12-15 22:00:00 +01:00
switch( check->v.movetype )
2008-07-30 22:00:00 +02:00
{
case MOVETYPE_NONE:
case MOVETYPE_PUSH:
case MOVETYPE_FOLLOW:
case MOVETYPE_NOCLIP:
continue;
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
// tell any MOVETYPE_STEP entity that it may need to check for water transitions
2008-12-26 22:00:00 +01:00
check->pvServerData->forceupdate = true;
2008-07-30 22:00:00 +02:00
checkcontents = SV_ContentsMask( check );
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// 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
2008-12-15 22:00:00 +01:00
if(!( check->v.flags & FL_ONGROUND) || check->v.groundentity != pusher )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
pe->ClipToGenericEntity( &trace, pushermodel, pusher->v.mins, pusher->v.maxs, CONTENTS_BODY, pusherfinalmatrix, pusherfinalimatrix, check->v.origin, check->v.mins, check->v.maxs, check->v.origin, checkcontents );
2008-07-30 22:00:00 +02:00
if( !trace.startsolid ) continue;
}
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
if( rotated )
{
vec3_t org2;
2008-12-15 22:00:00 +01:00
VectorSubtract( check->v.origin, pusher->v.origin, org );
2008-07-30 22:00:00 +02:00
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 );
}
//Msg("- pushing %f %f %f\n", move[0], move[1], move[2]);
2008-12-26 22:00:00 +01:00
VectorCopy (check->v.origin, check->pvServerData->moved_origin );
VectorCopy (check->v.angles, check->pvServerData->moved_angles );
2008-12-15 22:00:00 +01:00
moved_edicts[num_moved++] = NUM_FOR_EDICT( check );
2008-07-30 22:00:00 +02:00
// try moving the contacted entity
2008-12-15 22:00:00 +01:00
pusher->v.solid = SOLID_NOT;
2008-07-30 22:00:00 +02:00
trace = SV_PushEntity( check, move, true );
// FIXME: turn players specially
2008-12-15 22:00:00 +01:00
check->v.angles[1] += trace.fraction * moveangle[1];
pusher->v.solid = savesolid; // was SOLID_BSP
2008-07-30 22:00:00 +02:00
// this trace.fraction < 1 check causes items to fall off of pushers
// if they pass under or through a wall
// the groundentity check causes items to fall off of ledges
2008-12-15 22:00:00 +01:00
if( check->v.movetype != MOVETYPE_WALK && (trace.fraction < 1 || check->v.groundentity != pusher ))
check->v.flags &= ~FL_ONGROUND;
2008-07-30 22:00:00 +02:00
// if it is still inside the pusher, block
2008-12-15 22:00:00 +01:00
pe->ClipToGenericEntity( &trace, pushermodel, pusher->v.mins, pusher->v.maxs, CONTENTS_BODY, pusherfinalmatrix, pusherfinalimatrix, check->v.origin, check->v.mins, check->v.maxs, check->v.origin, checkcontents );
2008-07-30 22:00:00 +02:00
if (trace.startsolid)
{
// try moving the contacted entity a tiny bit further to account for precision errors
vec3_t move2;
2008-12-15 22:00:00 +01:00
pusher->v.solid = SOLID_NOT;
2008-07-30 22:00:00 +02:00
VectorScale( move, 1.1f, move2 );
2008-12-26 22:00:00 +01:00
VectorCopy( check->pvServerData->moved_origin, check->v.origin );
VectorCopy( check->pvServerData->moved_angles, check->v.angles );
2008-07-30 22:00:00 +02:00
SV_PushEntity( check, move2, true );
2008-12-15 22:00:00 +01:00
pusher->v.solid = savesolid;
pe->ClipToGenericEntity( &trace, pushermodel, pusher->v.mins, pusher->v.maxs, CONTENTS_BODY, pusherfinalmatrix, pusherfinalimatrix, check->v.origin, check->v.mins, check->v.maxs, check->v.origin, checkcontents );
2008-07-30 22:00:00 +02:00
if( trace.startsolid )
{
// try moving the contacted entity a tiny bit less to account for precision errors
2008-12-15 22:00:00 +01:00
pusher->v.solid = SOLID_NOT;
2008-07-30 22:00:00 +02:00
VectorScale( move, 0.9, move2 );
2008-12-26 22:00:00 +01:00
VectorCopy( check->pvServerData->moved_origin, check->v.origin );
VectorCopy( check->pvServerData->moved_angles, check->v.angles );
2008-07-30 22:00:00 +02:00
SV_PushEntity( check, move2, true );
2008-12-15 22:00:00 +01:00
pusher->v.solid = savesolid;
pe->ClipToGenericEntity( &trace, pushermodel, pusher->v.mins, pusher->v.maxs, CONTENTS_BODY, pusherfinalmatrix, pusherfinalimatrix, check->v.origin, check->v.mins, check->v.maxs, check->v.origin, checkcontents );
2008-07-30 22:00:00 +02:00
if( trace.startsolid )
{
// still inside pusher, so it's really blocked
// fail the move
2008-12-15 22:00:00 +01:00
if( check->v.mins[0] == check->v.maxs[0] ) continue;
if( check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER )
2008-07-30 22:00:00 +02:00
{
// corpse
2008-12-15 22:00:00 +01:00
check->v.mins[0] = check->v.mins[1] = 0;
VectorCopy( check->v.mins, check->v.maxs );
2008-07-30 22:00:00 +02:00
continue;
}
2008-12-15 22:00:00 +01:00
VectorCopy( pushorig, pusher->v.origin );
VectorCopy( pushang, pusher->v.angles );
pusher->v.ltime = pushltime;
2008-07-30 22:00:00 +02:00
SV_LinkEdict( pusher );
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// move back any entities we already moved
for( i = 0; i < num_moved; i++ )
{
2008-12-15 22:00:00 +01:00
edict_t *ed = EDICT_NUM( moved_edicts[i] );
2008-12-26 22:00:00 +01:00
VectorCopy( ed->pvServerData->moved_origin, ed->v.origin );
VectorCopy( ed->pvServerData->moved_angles, ed->v.angles );
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ed );
}
// if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnBlocked( pusher, check );
2008-07-30 22:00:00 +02:00
break;
}
}
}
}
2008-12-15 22:00:00 +01:00
SV_ClampAngle( pusher->v.angles );
2008-07-30 22:00:00 +02:00
}
2007-09-28 22:00:00 +02:00
2007-09-17 22:00:00 +02:00
/*
2008-07-30 22:00:00 +02:00
================
SV_Physics_Pusher
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
================
2007-09-17 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_Pusher( edict_t *ent )
2007-09-17 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
float thinktime, oldltime, movetime;
2007-09-17 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
oldltime = ent->v.ltime;
thinktime = ent->v.nextthink;
2007-09-28 22:00:00 +02:00
2008-12-26 22:00:00 +01:00
if( thinktime < ent->v.ltime + svgame.globals->frametime )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
movetime = thinktime - ent->v.ltime;
2008-07-30 22:00:00 +02:00
if( movetime < 0 ) movetime = 0;
}
2008-12-26 22:00:00 +01:00
else movetime = svgame.globals->frametime;
2007-09-17 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
// advances ent->v.ltime if not blocked
2008-07-30 22:00:00 +02:00
if( movetime ) SV_PushMove( ent, movetime );
2008-12-15 22:00:00 +01:00
if( thinktime > oldltime && thinktime <= ent->v.ltime )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent->v.nextthink = 0;
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnThink( ent );
2008-07-30 22:00:00 +02:00
}
2007-09-17 22:00:00 +02:00
}
/*
2008-07-30 22:00:00 +02:00
===============================================================================
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
CLIENT MOVEMENT
===============================================================================
2007-09-17 22:00:00 +02:00
*/
2008-07-30 22:00:00 +02:00
static float unstickoffsets[] =
2007-09-17 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
-1, 0, 0, 1, 0, 0, 0, -1, 0, 0, 1, 0, -1, -1, 0, 1, -1, 0, -1, 1,
0, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 0, -2, 0, 0, 2, 0, 0, -3, 0, 0, 3,
0, 0, -4, 0, 0, 4, 0, 0, -5, 0, 0, 5, 0, 0, -6, 0, 0, 6, 0, 0, -7,
0, 0, 7, 0, 0, -8, 0, 0, 8, 0, 0, -9, 0, 0, 9, 0, 0, -10, 0, 0, 10,
0, 0, -11, 0, 0, 11, 0, 0, -12, 0, 0, 12, 0, 0, -13, 0, 0, 13, 0, 0,
-14, 0, 0, 14, 0, 0, -15, 0, 0, 15, 0, 0, -16, 0, 0, 16, 0, 0, -17,
0, 0, 17,
};
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
/*
=============
SV_CheckStuck
This is a big hack to try and fix the rare case of getting stuck in the world
clipping hull.
=============
*/
void SV_CheckStuck( edict_t *ent )
{
int i;
vec3_t offset;
2008-08-10 22:00:00 +02:00
VectorClear( offset );
2008-12-21 22:00:00 +01:00
if( ent->v.flags & FL_DUCKING )
2008-08-10 22:00:00 +02:00
{
offset[0] += 1;
offset[1] += 1;
offset[2] += 1;
}
2008-12-26 22:00:00 +01:00
if( !SV_TestEntityPosition( ent, offset ))
2007-09-17 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
VectorCopy( ent->v.origin, ent->v.oldorigin );
2008-12-26 22:00:00 +01:00
ent->pvServerData->stuck = false;
2007-09-17 22:00:00 +02:00
return;
}
2008-07-30 22:00:00 +02:00
for( i = 0; i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0])); i += 3 )
2007-09-17 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if(!SV_TestEntityPosition( ent, unstickoffsets + i))
{
2008-12-20 22:00:00 +01:00
MsgDev( D_NOTE, "Unstuck player with offset %g %g %g.\n", unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
2008-12-26 22:00:00 +01:00
ent->pvServerData->stuck = false;
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
return;
}
2007-09-17 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
VectorSubtract( ent->v.oldorigin, ent->v.origin, offset );
2008-07-30 22:00:00 +02:00
if(!SV_TestEntityPosition( ent, offset ))
2007-09-17 22:00:00 +02:00
{
2008-12-20 22:00:00 +01:00
MsgDev( D_NOTE, "Unstuck player by restoring oldorigin.\n" );
2008-12-26 22:00:00 +01:00
ent->pvServerData->stuck = false;
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
2007-09-17 22:00:00 +02:00
return;
}
2008-12-26 22:00:00 +01:00
if( !ent->pvServerData->stuck )
MsgDev( D_ERROR, "Stuck player\n" ); // fire once
ent->pvServerData->stuck = true;
2008-07-30 22:00:00 +02:00
}
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
bool SV_UnstickEntity( edict_t *ent )
{
int i;
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// if not stuck in a bmodel, just return
2008-12-26 22:00:00 +01:00
if( !SV_TestEntityPosition( ent, vec3_origin ))
{
ent->pvServerData->stuck = false;
2008-07-30 22:00:00 +02:00
return true;
2008-12-26 22:00:00 +01:00
}
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
for( i = 0; i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0])); i += 3 )
2007-09-17 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if(!SV_TestEntityPosition( ent, unstickoffsets + i ))
{
2008-12-26 22:00:00 +01:00
MsgDev( D_NOTE, "Unstuck entity \"%s\" with offset %g %g %g.\n", STRING( ent->v.classname ), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2] );
ent->pvServerData->stuck = false;
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
return true;
}
2007-09-17 22:00:00 +02:00
}
2008-12-26 22:00:00 +01:00
if( !ent->pvServerData->stuck )
MsgDev( D_ERROR, "Stuck entity \"%s\".\n", STRING( ent->v.classname ));
ent->pvServerData->stuck = true;
2008-07-30 22:00:00 +02:00
return false;
}
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
/*
=============
SV_CheckWater
=============
*/
bool SV_CheckWater( edict_t *ent )
{
int cont;
vec3_t point;
2007-09-17 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
point[0] = ent->v.origin[0];
point[1] = ent->v.origin[1];
point[2] = ent->v.origin[2] + ent->v.mins[2] + 1;
2007-09-17 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
ent->v.waterlevel = 0;
2008-12-21 22:00:00 +01:00
ent->v.watertype = CONTENTS_NONE;
2008-07-30 22:00:00 +02:00
cont = SV_PointContents( point );
2008-12-21 22:00:00 +01:00
2008-07-30 22:00:00 +02:00
if( cont & (MASK_WATER))
{
2008-12-15 22:00:00 +01:00
ent->v.watertype = cont;
ent->v.waterlevel = 1;
2009-01-11 22:00:00 +01:00
point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2]) * 0.5;
2008-12-21 22:00:00 +01:00
if( SV_PointContents( point ) & MASK_WATER )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent->v.waterlevel = 2;
point[2] = ent->v.origin[2] + ent->v.view_ofs[2];
2008-12-21 22:00:00 +01:00
if( SV_PointContents( point ) & MASK_WATER )
2008-12-06 22:00:00 +01:00
{
2008-12-15 22:00:00 +01:00
ent->v.waterlevel = 3;
2008-12-06 22:00:00 +01:00
}
2008-07-30 22:00:00 +02:00
}
}
2008-12-15 22:00:00 +01:00
return ent->v.waterlevel > 1;
2008-07-30 22:00:00 +02:00
}
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
/*
============
SV_WallFriction
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
============
*/
void SV_WallFriction( edict_t *ent, float *stepnormal )
{
float d, i;
vec3_t forward, into, side;
2009-01-06 22:00:00 +01:00
AngleVectors( ent->v.viewangles, forward, NULL, NULL );
if(( d = DotProduct( stepnormal, forward ) + 0.5 ) < 0 )
2007-09-17 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
// cut the tangential velocity
2009-01-06 22:00:00 +01:00
i = DotProduct( stepnormal, ent->v.velocity );
VectorScale( stepnormal, i, into );
VectorSubtract( ent->v.velocity, into, side );
2008-12-15 22:00:00 +01:00
ent->v.velocity[0] = side[0] * (1 + d);
ent->v.velocity[1] = side[1] * (1 + d);
2008-07-30 22:00:00 +02:00
}
}
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
/*
=====================
SV_WalkMove
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
Only used by players
======================
*/
void SV_WalkMove( edict_t *ent )
{
2008-12-15 22:00:00 +01:00
int contentsmask;
2008-12-21 22:00:00 +01:00
int clip, oldonground, originalmove_clip, originalmove_flags;
2008-07-30 22:00:00 +02:00
vec3_t upmove, downmove, start_origin, start_velocity, stepnormal;
vec3_t originalmove_origin, originalmove_velocity;
2008-12-15 22:00:00 +01:00
edict_t *originalmove_groundentity;
2008-07-30 22:00:00 +02:00
trace_t downtrace;
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// if frametime is 0 (due to client sending the same timestamp twice), don't move
2008-12-26 22:00:00 +01:00
if( svgame.globals->frametime <= 0 ) return;
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
contentsmask = SV_ContentsMask( ent );
2008-07-31 22:00:00 +02:00
SV_CheckVelocity( ent );
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// do a regular slide move unless it looks like you ran into a step
2008-12-15 22:00:00 +01:00
oldonground = (ent->v.flags & FL_ONGROUND);
2008-07-30 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
VectorCopy( ent->v.origin, start_origin );
VectorCopy( ent->v.velocity, start_velocity );
2008-12-26 22:00:00 +01:00
clip = SV_FlyMove( ent, svgame.globals->frametime, NULL, contentsmask );
2008-07-30 22:00:00 +02:00
// if the move did not hit the ground at any point, we're not on ground
2008-12-15 22:00:00 +01:00
if(!(clip & 1)) ent->v.flags &= ~FL_ONGROUND;
2008-07-30 22:00:00 +02:00
SV_CheckVelocity( ent );
2008-12-15 22:00:00 +01:00
VectorCopy( ent->v.origin, originalmove_origin );
VectorCopy( ent->v.velocity, originalmove_velocity );
2008-07-30 22:00:00 +02:00
originalmove_clip = clip;
2008-12-21 22:00:00 +01:00
originalmove_flags = ent->v.flags;
2008-12-15 22:00:00 +01:00
originalmove_groundentity = ent->v.groundentity;
2008-07-30 22:00:00 +02:00
2008-12-21 22:00:00 +01:00
if( ent->v.flags & FL_WATERJUMP )
2008-07-30 22:00:00 +02:00
return;
// if move didn't block on a step, return
if( clip & 2 )
{
// 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;
2008-12-15 22:00:00 +01:00
if( ent->v.movetype != MOVETYPE_FLY )
2007-09-17 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
// return if gibbed by a trigger
2008-12-15 22:00:00 +01:00
if( ent->v.movetype != MOVETYPE_WALK )
2008-07-30 22:00:00 +02:00
return;
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// only step up while jumping if that is enabled
2008-12-15 22:00:00 +01:00
if( !oldonground && ent->v.waterlevel == 0 )
2008-07-30 22:00:00 +02:00
return;
}
// try moving up and forward to go up a step
// back to start pos
2008-12-15 22:00:00 +01:00
VectorCopy( start_origin, ent->v.origin );
VectorCopy( start_velocity, ent->v.velocity );
2008-07-30 22:00:00 +02:00
// move up
VectorClear( upmove );
upmove[2] = sv_stepheight->value;
SV_PushEntity( ent, upmove, false ); // FIXME: don't link?
// move forward
2008-12-15 22:00:00 +01:00
ent->v.velocity[2] = 0;
2008-12-26 22:00:00 +01:00
clip = SV_FlyMove( ent, svgame.globals->frametime, stepnormal, contentsmask );
2008-12-15 22:00:00 +01:00
ent->v.velocity[2] += start_velocity[2];
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
SV_CheckVelocity( ent );
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// check for stuckness, possibly due to the limited precision of floats
// in the clipping hulls
2008-12-15 22:00:00 +01:00
if( clip && fabs(originalmove_origin[1] - ent->v.origin[1]) < 0.03125 && fabs(originalmove_origin[0] - ent->v.origin[0]) < 0.03125 )
2008-07-30 22:00:00 +02:00
{
// stepping up didn't make any progress, revert to original move
2008-12-15 22:00:00 +01:00
VectorCopy( originalmove_origin, ent->v.origin );
VectorCopy( originalmove_velocity, ent->v.velocity );
2008-12-21 22:00:00 +01:00
ent->v.flags = originalmove_flags;
2008-12-15 22:00:00 +01:00
ent->v.groundentity = originalmove_groundentity;
2008-07-30 22:00:00 +02:00
return;
}
2007-09-17 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
// extra friction based on view angle
if( clip & 2 ) 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
2008-12-15 22:00:00 +01:00
else if( ent->v.waterlevel >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || (ent->v.flags & FL_ONGROUND))
2008-07-30 22:00:00 +02:00
return;
// move down
VectorClear( downmove );
2008-12-26 22:00:00 +01:00
downmove[2] = -sv_stepheight->value + start_velocity[2] * svgame.globals->frametime;
2008-07-30 22:00:00 +02:00
downtrace = SV_PushEntity( ent, downmove, false ); // FIXME: don't link?
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
2008-12-15 22:00:00 +01:00
//if (ent->v.solid == SOLID_BSP)
2008-07-30 22:00:00 +02:00
{
//Con_Printf("onground\n");
2008-12-15 22:00:00 +01:00
ent->v.flags |= FL_ONGROUND;
ent->v.groundentity = downtrace.ent;
2007-09-17 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
#endif
2007-09-17 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
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
2008-12-15 22:00:00 +01:00
VectorCopy( originalmove_origin, ent->v.origin );
VectorCopy( originalmove_velocity, ent->v.velocity );
2008-12-21 22:00:00 +01:00
ent->v.flags = originalmove_flags;
2008-12-15 22:00:00 +01:00
ent->v.groundentity = originalmove_groundentity;
2008-07-30 22:00:00 +02:00
}
SV_CheckVelocity( ent );
2007-09-17 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
//============================================================================
2007-09-28 22:00:00 +02:00
/*
=============
2008-07-30 22:00:00 +02:00
SV_Physics_Follow
2007-09-28 22:00:00 +02:00
Entities that are "stuck" to another entity
=============
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_Follow( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
vec3_t vf, vr, vu, angles, v;
edict_t *e;
2007-09-28 22:00:00 +02:00
// regular thinking
2008-07-30 22:00:00 +02:00
if(!SV_RunThink( ent )) return;
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
e = ent->v.aiment;
2009-09-28 22:00:00 +02:00
if( !e || e->free ) return;
2008-12-15 22:00:00 +01:00
if(VectorCompare( e->v.angles, ent->v.punchangle ))
2007-09-28 22:00:00 +02:00
{
// quick case for no rotation
2008-12-15 22:00:00 +01:00
VectorAdd( e->v.origin, ent->v.view_ofs, ent->v.origin );
2007-09-28 22:00:00 +02:00
}
else
{
2008-12-15 22:00:00 +01:00
angles[0] = -ent->v.punchangle[0];
angles[1] = ent->v.punchangle[1];
angles[2] = ent->v.punchangle[2];
2008-07-30 22:00:00 +02:00
AngleVectors( angles, vf, vr, vu );
2008-12-15 22:00:00 +01:00
v[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[2] * vu[0];
v[1] = ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[2] * vu[1];
v[2] = ent->v.view_ofs[0] * vf[2] + ent->v.view_ofs[1] * vr[2] + ent->v.view_ofs[2] * vu[2];
angles[0] = -e->v.angles[0];
angles[1] = e->v.angles[1];
angles[2] = e->v.angles[2];
2008-07-30 22:00:00 +02:00
AngleVectors( angles, vf, vr, vu );
2008-12-15 22:00:00 +01:00
ent->v.origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v.origin[0];
ent->v.origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v.origin[1];
ent->v.origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v.origin[2];
2007-09-28 22:00:00 +02:00
}
2009-01-06 22:00:00 +01:00
VectorAdd( e->v.angles, ent->v.viewangles, ent->v.angles );
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
2007-09-28 22:00:00 +02:00
}
/*
==============================================================================
TOSS / BOUNCE
==============================================================================
*/
/*
=============
SV_CheckWaterTransition
=============
*/
2008-07-30 22:00:00 +02:00
void SV_CheckWaterTransition( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
int cont = SV_PointContents( ent->v.origin );
2007-09-28 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
if( !ent->v.watertype )
2007-09-28 22:00:00 +02:00
{
// just spawned here
2008-12-15 22:00:00 +01:00
ent->v.watertype = cont;
ent->v.waterlevel = 1;
2007-09-28 22:00:00 +02:00
return;
}
// check if the entity crossed into or out of water
2008-12-15 22:00:00 +01:00
if( ent->v.watertype & MASK_WATER )
2007-09-28 22:00:00 +02:00
{
2008-12-21 22:00:00 +01:00
//Msg( "water splash!\n" );
2007-09-28 22:00:00 +02:00
//SV_StartSound (ent, 0, "", 255, 1);
}
2008-07-30 22:00:00 +02:00
if( cont <= CONTENTS_WATER )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent->v.watertype = cont;
ent->v.waterlevel = 1;
2007-09-28 22:00:00 +02:00
}
else
{
2008-12-15 22:00:00 +01:00
ent->v.watertype = 0;
ent->v.waterlevel = 0;
2007-09-28 22:00:00 +02:00
}
}
/*
=============
2008-07-30 22:00:00 +02:00
SV_Physics_Toss
2007-09-28 22:00:00 +02:00
Toss, bounce, and fly movement. When onground, do nothing.
=============
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_Toss( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
trace_t trace;
vec3_t move;
// if onground, return without moving
2008-12-15 22:00:00 +01:00
if( ent->v.flags & FL_ONGROUND )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if( ent->v.velocity[2] >= (1.0 / 32.0))
2008-07-30 22:00:00 +02:00
{
// don't stick to ground if onground and moving upward
2008-12-15 22:00:00 +01:00
ent->v.flags &= FL_ONGROUND;
2008-07-30 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
else if( !ent->v.groundentity )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
// we can trust FL_ONGROUND if groundentity is world because it never moves
2008-07-30 22:00:00 +02:00
return;
}
2008-12-26 22:00:00 +01:00
else if( ent->pvServerData->suspended && ent->v.groundentity->free )
2008-07-30 22:00:00 +02:00
{
// 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
2008-12-17 22:00:00 +01:00
ent->v.groundentity = EDICT_NUM( 0 );
2008-07-30 22:00:00 +02:00
return;
}
}
2008-12-26 22:00:00 +01:00
ent->pvServerData->suspended = false;
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
SV_CheckVelocity( ent );
2007-09-28 22:00:00 +02:00
// add gravity
2008-12-15 22:00:00 +01:00
if( ent->v.movetype == MOVETYPE_TOSS || ent->v.movetype == MOVETYPE_BOUNCE )
2008-07-30 22:00:00 +02:00
SV_AddGravity( ent );
2007-09-28 22:00:00 +02:00
// move angles
2008-12-26 22:00:00 +01:00
VectorMA( ent->v.angles, svgame.globals->frametime, ent->v.avelocity, ent->v.angles );
2007-09-28 22:00:00 +02:00
// move origin
2008-12-26 22:00:00 +01:00
VectorScale( ent->v.velocity, svgame.globals->frametime, move );
2008-07-30 22:00:00 +02:00
trace = SV_PushEntity( ent, move, true );
2008-12-15 22:00:00 +01:00
if( ent->free ) return;
2007-09-28 22:00:00 +02:00
2008-07-30 22:00:00 +02:00
if( trace.startstuck )
2007-09-28 22:00:00 +02:00
{
// try to unstick the entity
2008-07-30 22:00:00 +02:00
SV_UnstickEntity( ent );
trace = SV_PushEntity( ent, move, false );
2008-12-15 22:00:00 +01:00
if( ent->free )
2008-07-30 22:00:00 +02:00
return;
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
if( trace.fraction < 1 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if( ent->v.movetype == MOVETYPE_BOUNCE )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
float d;
2008-12-15 22:00:00 +01:00
SV_ClipVelocity( ent->v.velocity, trace.plane.normal, ent->v.velocity, 1.5 );
d = DotProduct( trace.plane.normal, ent->v.velocity );
2008-07-30 22:00:00 +02:00
if( trace.plane.normal[2] > 0.7 && fabs(d) < 60 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent->v.flags |= FL_ONGROUND;
ent->v.groundentity = trace.ent;
VectorClear( ent->v.velocity );
VectorClear( ent->v.avelocity );
2007-09-28 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
else ent->v.flags &= ~FL_ONGROUND;
2007-09-28 22:00:00 +02:00
}
else
{
2008-12-15 22:00:00 +01:00
SV_ClipVelocity( ent->v.velocity, trace.plane.normal, ent->v.velocity, 1.0 );
2008-07-30 22:00:00 +02:00
if( trace.plane.normal[2] > 0.7 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent->v.flags |= FL_ONGROUND;
ent->v.groundentity = trace.ent;
2008-12-17 22:00:00 +01:00
if( trace.ent && trace.ent->v.solid == SOLID_BSP )
2008-12-26 22:00:00 +01:00
ent->pvServerData->suspended = true;
2008-12-15 22:00:00 +01:00
VectorClear( ent->v.velocity );
VectorClear( ent->v.avelocity );
2007-09-28 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
else ent->v.flags &= ~FL_ONGROUND;
2007-09-28 22:00:00 +02:00
}
}
// check for in water
2008-07-30 22:00:00 +02:00
SV_CheckWaterTransition( ent );
2007-09-28 22:00:00 +02:00
}
/*
===============================================================================
STEPPING MOVEMENT
===============================================================================
*/
/*
=============
2008-07-30 22:00:00 +02:00
SV_Physics_Step
2007-09-28 22:00:00 +02:00
Monsters freefall when they don't have a ground entity, otherwise
all movement is done with discrete steps.
This is also used for objects that have become still on the ground, but
will fall if the floor is pulled out from under them.
=============
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_Step( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-12-21 22:00:00 +01:00
int flags = ent->v.flags;
2007-09-28 22:00:00 +02:00
2008-12-21 22:00:00 +01:00
if(!(flags & (FL_FLY|FL_SWIM)))
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if( ent->v.flags & FL_ONGROUND )
2007-09-28 22:00:00 +02:00
{
// freefall if onground and moving upward
// freefall if not standing on a world surface (it may be a lift or trap door)
2008-12-15 22:00:00 +01:00
if(ent->v.velocity[2] >= (1.0 / 32.0) || ent->v.groundentity)
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent->v.flags &= ~FL_ONGROUND;
2008-07-30 22:00:00 +02:00
SV_AddGravity( ent );
SV_CheckVelocity( ent );
2008-12-26 22:00:00 +01:00
SV_FlyMove( ent, svgame.globals->frametime, NULL, SV_ContentsMask( ent ));
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
2008-12-26 22:00:00 +01:00
ent->pvServerData->forceupdate = true;
2007-09-28 22:00:00 +02:00
}
}
else
{
2008-07-30 22:00:00 +02:00
SV_AddGravity( ent );
SV_CheckVelocity( ent );
2008-12-26 22:00:00 +01:00
SV_FlyMove( ent, svgame.globals->frametime, NULL, SV_ContentsMask( ent ));
2008-07-30 22:00:00 +02:00
SV_LinkEdict( ent );
2007-09-28 22:00:00 +02:00
// just hit ground
2008-12-26 22:00:00 +01:00
ent->pvServerData->forceupdate = true;
2007-09-28 22:00:00 +02:00
}
}
// regular thinking
2008-07-30 22:00:00 +02:00
if(!SV_RunThink( ent )) return;
2007-09-28 22:00:00 +02:00
2008-12-26 22:00:00 +01:00
if( ent->pvServerData->forceupdate || !VectorCompare( ent->v.origin, ent->pvServerData->water_origin))
2007-09-28 22:00:00 +02:00
{
2008-12-26 22:00:00 +01:00
ent->pvServerData->forceupdate = false;
VectorCopy( ent->v.origin, ent->pvServerData->water_origin );
2008-07-30 22:00:00 +02:00
SV_CheckWaterTransition( ent );
2007-09-28 22:00:00 +02:00
}
}
/*
====================
SV_Physics_Conveyor
REAL simple - all we do is check for player riders and adjust their position.
Only gotcha here is we have to make sure we don't end up embedding player in
*another* object that's being moved by the conveyor.
====================
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_Conveyor( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
edict_t *player;
int i;
trace_t tr;
vec3_t v, move;
vec3_t point, end;
2008-12-15 22:00:00 +01:00
VectorScale( ent->v.movedir, ent->v.speed, v );
2008-07-30 22:00:00 +02:00
VectorScale( v, 0.1f, move );
2007-09-28 22:00:00 +02:00
2009-09-25 22:00:00 +02:00
for( i = 0; i < sv_maxclients->integer; i++ )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
player = EDICT_NUM(i) + 1;
if( player->free ) continue;
if( !player->v.groundentity ) continue;
if( player->v.groundentity != ent )
2008-07-30 22:00:00 +02:00
continue;
2007-09-28 22:00:00 +02:00
// Look below player; make sure he's on a conveyor
2008-12-15 22:00:00 +01:00
VectorCopy( player->v.origin, point );
2007-09-28 22:00:00 +02:00
point[2] += 1;
2008-07-30 22:00:00 +02:00
VectorCopy( point, end );
2007-09-28 22:00:00 +02:00
end[2] -= 256;
2008-12-15 22:00:00 +01:00
tr = SV_Trace( point, player->v.mins, player->v.maxs, end, MOVE_NORMAL, player, MASK_SOLID );
2007-09-28 22:00:00 +02:00
// tr.ent HAS to be conveyor, but just in case we screwed something up:
2008-07-30 22:00:00 +02:00
if( tr.ent == ent )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( tr.plane.normal[2] > 0 )
2007-09-28 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
v[2] = ent->v.speed * com.sqrt( 1.0 - tr.plane.normal[2]*tr.plane.normal[2]) / tr.plane.normal[2];
if(DotProduct( ent->v.movedir, tr.plane.normal) > 0)
2008-07-30 22:00:00 +02:00
v[2] = -v[2]; // then we're moving down
2007-09-28 22:00:00 +02:00
move[2] = v[2] * 0.1f;
}
2008-12-15 22:00:00 +01:00
VectorAdd( player->v.origin, move, end );
2008-12-26 22:00:00 +01:00
tr = SV_Trace( player->v.origin, player->v.mins, player->v.maxs, end, MOVE_NORMAL, player, player->pvServerData->clipmask );
2008-12-15 22:00:00 +01:00
VectorCopy( tr.endpos, player->v.origin );
2008-07-30 22:00:00 +02:00
SV_LinkEdict( player );
2007-09-28 22:00:00 +02:00
}
}
}
/*
=============
SV_PhysicsNoclip
A moving object that doesn't obey physics
=============
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_Noclip( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
// regular thinking
2009-01-13 22:00:00 +01:00
if( SV_RunThink( ent ))
2008-07-30 22:00:00 +02:00
{
SV_CheckWater( ent );
2008-12-26 22:00:00 +01:00
VectorMA( ent->v.angles, svgame.globals->frametime, ent->v.avelocity, ent->v.angles );
VectorMA( ent->v.origin, svgame.globals->frametime, ent->v.velocity, ent->v.origin );
2008-07-30 22:00:00 +02:00
}
2008-07-31 22:00:00 +02:00
SV_LinkEdict( ent );
2007-09-28 22:00:00 +02:00
}
/*
=============
SV_PhysicsNone
Non moving objects can only think
=============
*/
2008-07-30 22:00:00 +02:00
void SV_Physics_None( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2009-09-17 22:00:00 +02:00
if( ent->v.nextthink > 0 && ent->v.nextthink <= (sv.time * 0.001f) + svgame.globals->frametime )
2009-01-13 22:00:00 +01:00
SV_RunThink( ent );
2007-09-28 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
//============================================================================
static void SV_Physics_Entity( edict_t *ent )
2007-09-28 22:00:00 +02:00
{
2008-12-17 22:00:00 +01:00
switch( ent->v.movetype )
2007-09-28 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
case MOVETYPE_PUSH:
SV_Physics_Pusher( ent );
break;
2007-09-28 22:00:00 +02:00
case MOVETYPE_NONE:
2007-11-28 22:00:00 +01:00
case MOVETYPE_PHYSIC:
2008-07-30 22:00:00 +02:00
SV_Physics_None( ent );
2007-09-28 22:00:00 +02:00
break;
2008-07-30 22:00:00 +02:00
case MOVETYPE_FOLLOW:
SV_Physics_Follow( ent );
2007-09-28 22:00:00 +02:00
break;
case MOVETYPE_NOCLIP:
2008-07-30 22:00:00 +02:00
SV_Physics_Noclip( ent );
2007-09-28 22:00:00 +02:00
break;
case MOVETYPE_STEP:
2008-07-30 22:00:00 +02:00
SV_Physics_Step( ent );
2007-09-28 22:00:00 +02:00
break;
case MOVETYPE_WALK:
2008-07-16 22:00:00 +02:00
if(SV_RunThink( ent ))
2007-09-28 22:00:00 +02:00
{
2008-12-21 22:00:00 +01:00
if(!SV_CheckWater( ent ) && !( ent->v.flags & FL_WATERJUMP ))
2008-07-30 22:00:00 +02:00
SV_AddGravity( ent );
2008-07-16 22:00:00 +02:00
SV_CheckStuck( ent );
SV_WalkMove( ent );
SV_LinkEdict( ent );
}
2007-09-28 22:00:00 +02:00
break;
2008-07-30 22:00:00 +02:00
case MOVETYPE_TOSS:
case MOVETYPE_BOUNCE:
case MOVETYPE_FLY:
2009-01-13 22:00:00 +01:00
if( SV_RunThink( ent )) SV_Physics_Toss( ent );
2008-07-30 22:00:00 +02:00
break;
2007-09-28 22:00:00 +02:00
case MOVETYPE_CONVEYOR:
2008-07-30 22:00:00 +02:00
SV_Physics_Conveyor( ent );
break;
default:
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnFrame( ent );
2008-07-30 22:00:00 +02:00
break;
}
}
void SV_Physics_ClientEntity( edict_t *ent )
{
2008-12-26 22:00:00 +01:00
sv_client_t *client = ent->pvServerData->client;
2008-07-30 22:00:00 +02:00
if( !client ) return;//Host_Error( "SV_Physics_ClientEntity: tired to apply physic to a non-client entity\n" );
// don't do physics on disconnected clients, FrikBot relies on this
if( client->state != cs_spawned )
{
2009-06-24 22:00:00 +02:00
memset( &client->lastcmd, 0, sizeof( client->lastcmd ));
2008-07-30 22:00:00 +02:00
return;
}
// don't run physics here if running asynchronously
2009-06-24 22:00:00 +02:00
if( client->skipframes <= 0 ) SV_ClientThink( client, &client->lastcmd );
2008-07-30 22:00:00 +02:00
// make sure the velocity is sane (not a NaN)
SV_CheckVelocity( ent );
2009-09-17 22:00:00 +02:00
if( DotProduct( ent->v.velocity, ent->v.velocity ) < 0.0001f )
2008-12-15 22:00:00 +01:00
VectorClear( ent->v.velocity );
2008-07-30 22:00:00 +02:00
// call standard client pre-think
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnPlayerPreThink( ent );
2008-07-30 22:00:00 +02:00
SV_CheckVelocity( ent );
2009-01-13 22:00:00 +01:00
switch( ent->v.movetype )
2008-07-30 22:00:00 +02:00
{
case MOVETYPE_PUSH:
SV_Physics_Pusher( ent );
break;
case MOVETYPE_NONE:
case MOVETYPE_PHYSIC:
SV_Physics_None( ent );
break;
case MOVETYPE_FOLLOW:
SV_Physics_Follow( ent );
break;
case MOVETYPE_NOCLIP:
SV_RunThink( ent );
SV_CheckWater( ent );
2008-12-26 22:00:00 +01:00
VectorMA( ent->v.origin, svgame.globals->frametime, ent->v.velocity, ent->v.origin );
VectorMA( ent->v.angles, svgame.globals->frametime, ent->v.avelocity, ent->v.angles );
2008-07-30 22:00:00 +02:00
break;
case MOVETYPE_STEP:
SV_Physics_Step( ent );
break;
case MOVETYPE_WALK:
SV_RunThink( ent );
// don't run physics here if running asynchronously
if( client->skipframes <= 0 )
{
2008-12-21 22:00:00 +01:00
if(!SV_CheckWater( ent ) && !( ent->v.flags & FL_WATERJUMP) )
2008-07-30 22:00:00 +02:00
SV_AddGravity (ent);
SV_CheckStuck (ent);
SV_WalkMove (ent);
}
break;
case MOVETYPE_TOSS:
case MOVETYPE_BOUNCE:
// regular thinking
SV_RunThink( ent );
SV_Physics_Toss( ent );
break;
case MOVETYPE_FLY:
SV_RunThink( ent );
SV_CheckWater( ent );
SV_WalkMove( ent );
2007-09-28 22:00:00 +02:00
break;
default:
2008-12-21 22:00:00 +01:00
MsgDev( D_ERROR, "SV_Physics_ClientEntity: bad movetype %i\n", ent->v.movetype );
2008-07-30 22:00:00 +02:00
break;
}
// decrement the countdown variable used to decide when to go back to synchronous physics
if( client->skipframes > 0 ) client->skipframes--;
SV_CheckVelocity( ent );
SV_LinkEdict( ent );
SV_CheckVelocity( ent );
2008-12-15 22:00:00 +01:00
if( ent->v.movetype != MOVETYPE_NOCLIP )
2008-07-31 22:00:00 +02:00
SV_TouchTriggers( ent );
2008-07-30 22:00:00 +02:00
// call standard player post-think
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnPlayerPostThink( ent );
2008-07-30 22:00:00 +02:00
}
void SV_Physics_ClientMove( sv_client_t *client, usercmd_t *cmd )
{
edict_t *ent = client->edict;
// call player physics, this needs the proper frametime
2009-09-17 22:00:00 +02:00
svgame.globals->frametime = sv.frametime * 0.001f;
2008-07-30 22:00:00 +02:00
SV_ClientThink( client, cmd );
// call standard client pre-think, with frametime = 0
2009-09-20 22:00:00 +02:00
svgame.globals->time = cmd->servertime * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.globals->frametime = 0;
svgame.dllFuncs.pfnPlayerPreThink( ent );
2009-09-20 22:00:00 +02:00
svgame.globals->frametime = cmd->msec * 0.001f;
2008-07-30 22:00:00 +02:00
2008-11-28 22:00:00 +01:00
if( !sv_physics->integer )
{
// make sure the velocity is sane (not a NaN)
SV_CheckVelocity( ent );
2009-02-03 22:00:00 +01:00
if( DotProduct(ent->v.velocity, ent->v.velocity) < 0.0001 )
2008-12-15 22:00:00 +01:00
VectorClear( ent->v.velocity );
2008-12-03 22:00:00 +01:00
2008-12-20 22:00:00 +01:00
switch( ent->v.movetype )
2008-12-03 22:00:00 +01:00
{
case MOVETYPE_WALK:
// perform MOVETYPE_WALK behavior
2008-12-21 22:00:00 +01:00
if(!SV_CheckWater( ent ) && !( ent->v.flags & FL_WATERJUMP ))
2008-12-03 22:00:00 +01:00
SV_AddGravity( ent );
SV_CheckStuck( ent );
SV_WalkMove( ent );
break;
case MOVETYPE_NOCLIP:
SV_CheckWater( ent );
2008-12-26 22:00:00 +01:00
VectorMA( ent->v.origin, svgame.globals->frametime, ent->v.velocity, ent->v.origin );
VectorMA( ent->v.angles, svgame.globals->frametime, ent->v.avelocity, ent->v.angles );
2008-12-03 22:00:00 +01:00
break;
}
2008-11-28 22:00:00 +01:00
SV_CheckVelocity( ent );
SV_LinkEdict( ent );
SV_CheckVelocity( ent );
}
else SV_LinkEdict( ent );
2008-07-30 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
if( ent->v.movetype != MOVETYPE_NOCLIP )
2008-07-31 22:00:00 +02:00
SV_TouchTriggers( ent );
2008-07-30 22:00:00 +02:00
// call standard player post-think, with frametime = 0
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.globals->frametime = 0;
svgame.dllFuncs.pfnPlayerPostThink( ent );
2009-09-17 22:00:00 +02:00
svgame.globals->frametime = sv.frametime * 0.001f;
2008-07-30 22:00:00 +02:00
}
/*
================
SV_Physics
================
*/
void SV_Physics( void )
{
int i;
edict_t *ent;
// let the progs know that a new frame has started
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
svgame.globals->frametime = sv.frametime * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnStartFrame();
2008-07-30 22:00:00 +02:00
// treat each object in turn
2008-12-26 22:00:00 +01:00
for( i = 1; i < svgame.globals->numEntities; i++ )
2008-07-30 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( i );
if( ent->free ) continue;
2008-07-30 22:00:00 +02:00
2009-07-15 22:00:00 +02:00
if( ent->pvServerData->s.ed_type != ED_PORTAL )
VectorCopy( ent->v.origin, ent->v.oldorigin );
2009-09-25 22:00:00 +02:00
if( i <= sv_maxclients->integer );// SV_Physics_ClientEntity( ent );
2008-12-15 22:00:00 +01:00
else if( !sv_playersonly->integer ) SV_Physics_Entity( ent );
2008-07-30 22:00:00 +02:00
}
2008-08-01 22:00:00 +02:00
// let everything in the world think and move
2009-01-05 22:00:00 +01:00
pe->Frame( svgame.globals->frametime );
2008-12-20 22:00:00 +01:00
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-07-30 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
// at end of frame kill all entities which supposed to it
2008-12-26 22:00:00 +01:00
for( i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ )
2008-12-15 22:00:00 +01:00
{
2008-12-17 22:00:00 +01:00
ent = EDICT_NUM( i );
if( ent->free ) continue;
2008-12-15 22:00:00 +01:00
if( ent->v.flags & FL_KILLME )
SV_FreeEdict( EDICT_NUM( i ));
}
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnEndFrame();
2008-12-15 22:00:00 +01:00
2008-12-26 22:00:00 +01:00
// decrement svgame.globals->numEntities if the highest number entities died
for( ; EDICT_NUM( svgame.globals->numEntities - 1)->free; svgame.globals->numEntities-- );
2009-09-16 22:00:00 +02:00
if( !sv_playersonly->integer ) sv.time += sv.frametime;
2007-09-17 22:00:00 +02:00
}