1275 lines
35 KiB
C++
1275 lines
35 KiB
C++
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
#include "../precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
||
|
#define new DEBUG_NEW
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#include "Physics_Monster.h"
|
||
|
#include "../Entity.h"
|
||
|
#include "../Actor.h"
|
||
|
#include "../Player.h"
|
||
|
#include "../../decllib/DeclSurfaceType.h"
|
||
|
|
||
|
CLASS_DECLARATION( idPhysics_Actor, idPhysics_Monster )
|
||
|
END_CLASS
|
||
|
|
||
|
const float PM_OVERCLIP = 1.001f;
|
||
|
const float MIN_WALK_NORMAL = 0.7f; // can't walk on very steep slopes
|
||
|
const float CONST_PM_STEPSCALE = 1.0f;
|
||
|
|
||
|
idCVar g_drawContacts( "g_drawContacts", "0", CVAR_BOOL | CVAR_GAME, "draw physics object contacts" );
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
sdMonsterPhysicsNetworkData::MakeDefault
|
||
|
=====================
|
||
|
*/
|
||
|
void sdMonsterPhysicsNetworkData::MakeDefault( void ) {
|
||
|
origin = vec3_origin;
|
||
|
velocity = vec3_zero;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
sdMonsterPhysicsNetworkData::Write
|
||
|
=====================
|
||
|
*/
|
||
|
void sdMonsterPhysicsNetworkData::Write( idFile* file ) const {
|
||
|
file->WriteVec3( origin );
|
||
|
file->WriteVec3( velocity );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
sdMonsterPhysicsNetworkData::Read
|
||
|
=====================
|
||
|
*/
|
||
|
void sdMonsterPhysicsNetworkData::Read( idFile* file ) {
|
||
|
file->ReadVec3( origin );
|
||
|
file->ReadVec3( velocity );
|
||
|
|
||
|
origin.FixDenormals();
|
||
|
velocity.FixDenormals();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
sdMonsterPhysicsBroadcastData::MakeDefault
|
||
|
=====================
|
||
|
*/
|
||
|
void sdMonsterPhysicsBroadcastData::MakeDefault( void ) {
|
||
|
pushVelocity = vec3_zero;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
sdMonsterPhysicsBroadcastData::Write
|
||
|
=====================
|
||
|
*/
|
||
|
void sdMonsterPhysicsBroadcastData::Write( idFile* file ) const {
|
||
|
file->WriteVec3( pushVelocity );
|
||
|
file->WriteInt( atRest );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
sdMonsterPhysicsBroadcastData::Read
|
||
|
=====================
|
||
|
*/
|
||
|
void sdMonsterPhysicsBroadcastData::Read( idFile* file ) {
|
||
|
file->ReadVec3( pushVelocity );
|
||
|
file->ReadInt( atRest );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
idPhysics_Monster::CheckGround
|
||
|
=====================
|
||
|
*/
|
||
|
void idPhysics_Monster::CheckGround( void ) {
|
||
|
int i;//, contents;
|
||
|
idVec3 point;
|
||
|
bool hadGroundContacts = HasGroundContacts();
|
||
|
|
||
|
// set the clip model origin before getting the contacts
|
||
|
clipModel->SetPosition( current.origin, clipModel->GetAxis(), gameLocal.clip );
|
||
|
|
||
|
EvaluateContacts( CLIP_DEBUG_PARMS_ONLY );
|
||
|
|
||
|
// setup a ground trace from the contacts
|
||
|
groundTrace.endpos = current.origin;
|
||
|
groundTrace.endAxis = clipModel->GetAxis();
|
||
|
if ( contacts.Num() ) {
|
||
|
groundTrace.fraction = 0.0f;
|
||
|
groundTrace.c = contacts[0];
|
||
|
for ( i = 1; i < contacts.Num(); i++ ) {
|
||
|
groundTrace.c.normal += contacts[i].normal;
|
||
|
}
|
||
|
groundTrace.c.normal.Normalize();
|
||
|
} else {
|
||
|
groundTrace.fraction = 1.0f;
|
||
|
}
|
||
|
|
||
|
// if the trace didn't hit anything, we are in free fall
|
||
|
if ( groundTrace.fraction == 1.0f ) {
|
||
|
current.onGround = false;
|
||
|
walking = false;
|
||
|
groundEntityPtr = NULL;
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
groundEntityPtr = gameLocal.entities[ groundTrace.c.entityNum ];
|
||
|
|
||
|
// check if getting thrown off the ground
|
||
|
if ( (current.velocity * -gravityNormal) > 0.0f && ( current.velocity * groundTrace.c.normal ) > 10.0f ) {
|
||
|
current.onGround = false;
|
||
|
walking = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
current.onGround = true;
|
||
|
walking = true;
|
||
|
|
||
|
// let the entity know about the collision
|
||
|
groundEntityPtr->Hit( groundTrace, current.velocity, self );
|
||
|
self->Collide( groundTrace, current.velocity, -1 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
idPhysics_Monster::SlideMove
|
||
|
=====================
|
||
|
*/
|
||
|
#define MAX_CLIP_PLANES 5
|
||
|
#define MAX_BUMPS 4
|
||
|
|
||
|
bool idPhysics_Monster::SlideMove( bool gravity, bool stepUp, bool stepDown, bool push, int vehiclePush ) {
|
||
|
int i, j, k, pushFlags;
|
||
|
int bumpcount, numplanes;
|
||
|
float d, time_left, into, totalMass;
|
||
|
idVec3 dir, planes[MAX_CLIP_PLANES];
|
||
|
idVec3 end, stepEnd, primal_velocity, endVelocity, endClipVelocity, clipVelocity;
|
||
|
trace_t trace, stepTrace, downTrace;
|
||
|
bool nearGround, stepped, pushed, vehiclePushed;
|
||
|
|
||
|
primal_velocity = current.velocity;
|
||
|
|
||
|
if ( gravity ) {
|
||
|
endVelocity = current.velocity + gravityVector * frametime;
|
||
|
current.velocity = ( current.velocity + endVelocity ) * 0.5f;
|
||
|
primal_velocity = endVelocity;
|
||
|
if ( current.onGround ) {
|
||
|
// slide along the ground plane
|
||
|
current.velocity.ProjectOntoPlane( groundTrace.c.normal, PM_OVERCLIP );
|
||
|
}
|
||
|
} else {
|
||
|
endVelocity = current.velocity;
|
||
|
}
|
||
|
|
||
|
time_left = frametime;
|
||
|
|
||
|
// never turn against the ground plane
|
||
|
if ( current.onGround ) {
|
||
|
numplanes = 1;
|
||
|
planes[0] = groundTrace.c.normal;
|
||
|
} else {
|
||
|
numplanes = 0;
|
||
|
}
|
||
|
|
||
|
// never turn against original velocity
|
||
|
planes[numplanes] = current.velocity;
|
||
|
planes[numplanes].Normalize();
|
||
|
numplanes++;
|
||
|
|
||
|
for ( bumpcount = 0; bumpcount < MAX_BUMPS; bumpcount++ ) {
|
||
|
|
||
|
// calculate position we are trying to move to
|
||
|
end = current.origin + time_left * current.velocity;
|
||
|
|
||
|
// see if we can make it there
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
|
||
|
|
||
|
time_left -= time_left * trace.fraction;
|
||
|
current.origin = trace.endpos;
|
||
|
|
||
|
// if moved the entire distance
|
||
|
if ( trace.fraction >= 1.0f ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
stepped = pushed = vehiclePushed = false;
|
||
|
|
||
|
// if we are allowed to step up
|
||
|
if ( stepUp && ( trace.c.normal * -gravityNormal ) < MIN_WALK_NORMAL ) {
|
||
|
|
||
|
nearGround = current.onGround;
|
||
|
|
||
|
if ( !nearGround ) {
|
||
|
// trace down to see if the player is near the ground
|
||
|
// step checking when near the ground allows the player to move up stairs smoothly while jumping
|
||
|
stepEnd = current.origin + maxStepHeight * gravityNormal;
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
|
||
|
nearGround = ( downTrace.fraction < 1.0f && (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL );
|
||
|
}
|
||
|
|
||
|
// may only step up if near the ground
|
||
|
if ( nearGround ) {
|
||
|
|
||
|
// step up
|
||
|
stepEnd = current.origin - maxStepHeight * gravityNormal;
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
|
||
|
|
||
|
// trace along velocity
|
||
|
stepEnd = downTrace.endpos + time_left * current.velocity;
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS stepTrace, downTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
|
||
|
|
||
|
// step down
|
||
|
stepEnd = stepTrace.endpos + maxStepHeight * gravityNormal;
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS downTrace, stepTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
|
||
|
|
||
|
if ( downTrace.fraction >= 1.0f || (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL ) {
|
||
|
|
||
|
// if moved the entire distance
|
||
|
if ( stepTrace.fraction >= 1.0f ) {
|
||
|
time_left = 0.0f;
|
||
|
current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
|
||
|
current.origin = downTrace.endpos;
|
||
|
current.velocity *= CONST_PM_STEPSCALE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// if the move is further when stepping up
|
||
|
if ( stepTrace.fraction > trace.fraction ) {
|
||
|
time_left -= time_left * stepTrace.fraction;
|
||
|
current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
|
||
|
current.origin = downTrace.endpos;
|
||
|
current.velocity *= CONST_PM_STEPSCALE;
|
||
|
trace = stepTrace;
|
||
|
stepped = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if we can push other entities and not blocked by the world
|
||
|
if ( push && trace.c.entityNum != ENTITYNUM_WORLD ) {
|
||
|
|
||
|
clipModel->SetPosition( current.origin, clipModel->GetAxis(), gameLocal.clip );
|
||
|
|
||
|
// clip movement, only push idMoveables, don't push entities the player is standing on
|
||
|
// apply impact to pushed objects
|
||
|
pushFlags = PUSHFL_CLIP|PUSHFL_ONLYMOVEABLE|PUSHFL_NOGROUNDENTITIES|PUSHFL_APPLYIMPULSE;
|
||
|
|
||
|
// clip & push
|
||
|
totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin, clipModel );
|
||
|
|
||
|
if ( totalMass > 0.0f ) {
|
||
|
// decrease velocity based on the total mass of the objects being pushed ?
|
||
|
current.velocity *= 1.0f - idMath::ClampFloat( 0.0f, 1000.0f, totalMass - 20.0f ) * ( 1.0f / 950.0f );
|
||
|
pushed = true;
|
||
|
}
|
||
|
|
||
|
current.origin = trace.endpos;
|
||
|
time_left -= time_left * trace.fraction;
|
||
|
|
||
|
// if moved the entire distance
|
||
|
if ( trace.fraction >= 1.0f ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// try to vehiclepush things out of the way
|
||
|
if ( !stepped && !pushed && vehiclePush && trace.c.entityNum != ENTITYNUM_WORLD ) {
|
||
|
idEntity* other = gameLocal.entities[ trace.c.entityNum ];
|
||
|
idPhysics* otherPhysics = other->GetPhysics();
|
||
|
idPhysics_Actor* actorPhysics = otherPhysics->Cast< idPhysics_Actor >();
|
||
|
|
||
|
if ( actorPhysics != NULL ) {
|
||
|
clipModel->Disable();
|
||
|
idVec3 move = end - trace.endpos;
|
||
|
if ( actorPhysics->VehiclePush( false, time_left, move, clipModel, vehiclePush ) == VPUSH_OK ) {
|
||
|
vehiclePushed = true;
|
||
|
}
|
||
|
clipModel->Enable();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !stepped && !vehiclePushed && !vehiclePush ) {
|
||
|
// let the entity know about the collision
|
||
|
idEntity *ent = gameLocal.entities[ trace.c.entityNum ];
|
||
|
ent->Hit( trace, current.velocity, self );
|
||
|
self->Collide( trace, current.velocity, -1 );
|
||
|
}
|
||
|
|
||
|
if ( numplanes >= MAX_CLIP_PLANES ) {
|
||
|
// MrElusive: I think we have some relatively high poly LWO models with a lot of slanted tris
|
||
|
// where it may hit the max clip planes
|
||
|
current.velocity = vec3_origin;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if this is the same plane we hit before, nudge velocity out along it,
|
||
|
// which fixes some epsilon issues with non-axial planes
|
||
|
//
|
||
|
for ( i = 0; i < numplanes; i++ ) {
|
||
|
if ( ( trace.c.normal * planes[i] ) > 0.999f ) {
|
||
|
// clip into the trace normal just in case this normal is almost but not exactly the same as the groundTrace normal
|
||
|
current.velocity.ProjectOntoPlane( trace.c.normal, PM_OVERCLIP );
|
||
|
// also add the normal to nudge the velocity out
|
||
|
current.velocity += trace.c.normal;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( i < numplanes ) {
|
||
|
continue;
|
||
|
}
|
||
|
planes[numplanes] = trace.c.normal;
|
||
|
numplanes++;
|
||
|
|
||
|
//
|
||
|
// modify velocity so it parallels all of the clip planes
|
||
|
//
|
||
|
|
||
|
// find a plane that it enters
|
||
|
for ( i = 0; i < numplanes; i++ ) {
|
||
|
into = current.velocity * planes[i];
|
||
|
if ( into >= 0.1f ) {
|
||
|
continue; // move doesn't interact with the plane
|
||
|
}
|
||
|
|
||
|
// slide along the plane
|
||
|
clipVelocity = current.velocity;
|
||
|
clipVelocity.ProjectOntoPlane( planes[i], PM_OVERCLIP );
|
||
|
|
||
|
// slide along the plane
|
||
|
endClipVelocity = endVelocity;
|
||
|
endClipVelocity.ProjectOntoPlane( planes[i], PM_OVERCLIP );
|
||
|
|
||
|
// see if there is a second plane that the new move enters
|
||
|
for ( j = 0; j < numplanes; j++ ) {
|
||
|
if ( j == i ) {
|
||
|
continue;
|
||
|
}
|
||
|
if ( ( clipVelocity * planes[j] ) >= 0.1f ) {
|
||
|
continue; // move doesn't interact with the plane
|
||
|
}
|
||
|
|
||
|
// try clipping the move to the plane
|
||
|
clipVelocity.ProjectOntoPlane( planes[j], PM_OVERCLIP );
|
||
|
endClipVelocity.ProjectOntoPlane( planes[j], PM_OVERCLIP );
|
||
|
|
||
|
// see if it goes back into the first clip plane
|
||
|
if ( ( clipVelocity * planes[i] ) >= 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// slide the original velocity along the crease
|
||
|
dir = planes[i].Cross( planes[j] );
|
||
|
dir.Normalize();
|
||
|
d = dir * current.velocity;
|
||
|
clipVelocity = d * dir;
|
||
|
|
||
|
dir = planes[i].Cross( planes[j] );
|
||
|
dir.Normalize();
|
||
|
d = dir * endVelocity;
|
||
|
endClipVelocity = d * dir;
|
||
|
|
||
|
// see if there is a third plane the the new move enters
|
||
|
for ( k = 0; k < numplanes; k++ ) {
|
||
|
if ( k == i || k == j ) {
|
||
|
continue;
|
||
|
}
|
||
|
if ( ( clipVelocity * planes[k] ) >= 0.1f ) {
|
||
|
continue; // move doesn't interact with the plane
|
||
|
}
|
||
|
|
||
|
// stop dead at a tripple plane interaction
|
||
|
current.velocity = vec3_origin;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if we have fixed all interactions, try another move
|
||
|
current.velocity = clipVelocity;
|
||
|
endVelocity = endClipVelocity;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// step down
|
||
|
if ( stepDown && current.onGround ) {
|
||
|
stepEnd = current.origin + gravityNormal * maxStepHeight;
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
|
||
|
if ( downTrace.fraction > 1e-4f && downTrace.fraction < 1.0f ) {
|
||
|
current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
|
||
|
current.origin = downTrace.endpos;
|
||
|
current.velocity *= CONST_PM_STEPSCALE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gravity ) {
|
||
|
current.velocity = endVelocity;
|
||
|
}
|
||
|
|
||
|
// come to a dead stop when the velocity orthogonal to the gravity flipped
|
||
|
clipVelocity = current.velocity - gravityNormal * current.velocity * gravityNormal;
|
||
|
endClipVelocity = endVelocity - gravityNormal * endVelocity * gravityNormal;
|
||
|
if ( clipVelocity * endClipVelocity < 0.0f ) {
|
||
|
current.velocity = gravityNormal * current.velocity * gravityNormal;
|
||
|
}
|
||
|
|
||
|
return bumpcount == 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::idPhysics_Monster
|
||
|
================
|
||
|
*/
|
||
|
idPhysics_Monster::idPhysics_Monster( void ) {
|
||
|
|
||
|
memset( ¤t, 0, sizeof( current ) );
|
||
|
saved = current;
|
||
|
|
||
|
delta.Zero();
|
||
|
maxStepHeight = 18.0f;
|
||
|
useVelocityMove = false;
|
||
|
noImpact = false;
|
||
|
blockingEntity = NULL;
|
||
|
waterLevel = 0.0f;
|
||
|
|
||
|
isStable = true;
|
||
|
|
||
|
atRest = -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::SetDelta
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SetDelta( const idVec3 &d ) {
|
||
|
delta = d;
|
||
|
if ( delta != vec3_origin ) {
|
||
|
Activate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::SetMaxStepHeight
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SetMaxStepHeight( const float newMaxStepHeight ) {
|
||
|
maxStepHeight = newMaxStepHeight;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::GetMaxStepHeight
|
||
|
================
|
||
|
*/
|
||
|
float idPhysics_Monster::GetMaxStepHeight( void ) const {
|
||
|
return maxStepHeight;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::OnGround
|
||
|
================
|
||
|
*/
|
||
|
bool idPhysics_Monster::OnGround( void ) const {
|
||
|
return current.onGround;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::GetSlideMoveEntity
|
||
|
================
|
||
|
*/
|
||
|
idEntity *idPhysics_Monster::GetSlideMoveEntity( void ) const {
|
||
|
return blockingEntity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::EnableImpact
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::EnableImpact( void ) {
|
||
|
noImpact = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::DisableImpact
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::DisableImpact( void ) {
|
||
|
noImpact = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::Activate
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::Activate( void ) {
|
||
|
atRest = -1;
|
||
|
self->BecomeActive( TH_PHYSICS );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::PutToRest
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::PutToRest( void ) {
|
||
|
atRest = gameLocal.time;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::IsAtRest
|
||
|
================
|
||
|
*/
|
||
|
bool idPhysics_Monster::IsAtRest( void ) {
|
||
|
return atRest >= 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::Evaluate
|
||
|
================
|
||
|
*/
|
||
|
bool idPhysics_Monster::Evaluate( int timeStepMSec, int endTimeMSec ) {
|
||
|
DebugDraw();
|
||
|
|
||
|
idVec3 masterOrigin, oldOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
current.origin.FixDenormals();
|
||
|
current.velocity.FixDenormals();
|
||
|
current.pushVelocity.FixDenormals();
|
||
|
|
||
|
// determine the time
|
||
|
framemsec = timeStepMSec;
|
||
|
frametime = MS2SEC( framemsec );
|
||
|
|
||
|
if ( IsAtRest() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( current.onGround ) {
|
||
|
if ( ( current.velocity - current.pushVelocity ).IsZero() && delta.IsZero() ) {
|
||
|
if ( ( groundTrace.c.normal * -gravityNormal ) >= MIN_WALK_NORMAL ) {
|
||
|
PutToRest();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( timeStepMSec == 0 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
walking = false;
|
||
|
current.onGround = false;
|
||
|
|
||
|
blockingEntity = NULL;
|
||
|
oldOrigin = current.origin;
|
||
|
|
||
|
current.stepUp = 0.0f;
|
||
|
|
||
|
ActivateContactEntities();
|
||
|
|
||
|
// move the monster velocity into the frame of a pusher
|
||
|
current.velocity -= current.pushVelocity;
|
||
|
|
||
|
clipModel->Unlink( gameLocal.clip );
|
||
|
|
||
|
CheckWater();
|
||
|
|
||
|
// check if on the ground
|
||
|
CheckGround();
|
||
|
|
||
|
idVec3 newVelocity = current.velocity - current.pushVelocity;
|
||
|
|
||
|
if ( !isStable ) {
|
||
|
current.onGround = false;
|
||
|
walking = false;
|
||
|
}
|
||
|
|
||
|
if ( walking ) {
|
||
|
newVelocity = ( ( newVelocity * gravityNormal ) * gravityNormal );
|
||
|
newVelocity += ( delta / frametime );
|
||
|
}
|
||
|
|
||
|
current.velocity = newVelocity;
|
||
|
|
||
|
if ( walking ) {
|
||
|
WalkMove();
|
||
|
} else {
|
||
|
AirMove();
|
||
|
}
|
||
|
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
|
||
|
|
||
|
// get all the ground contacts
|
||
|
EvaluateContacts( CLIP_DEBUG_PARMS_ONLY );
|
||
|
|
||
|
// move the monster velocity back into the world frame
|
||
|
current.velocity += current.pushVelocity;
|
||
|
current.pushVelocity.Zero();
|
||
|
|
||
|
return ( current.origin != oldOrigin );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::UpdateTime
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::UpdateTime( int endTimeMSec ) {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::GetTime
|
||
|
================
|
||
|
*/
|
||
|
int idPhysics_Monster::GetTime( void ) const {
|
||
|
return gameLocal.time;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::GetImpactInfo
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const {
|
||
|
info->invMass = invMass;
|
||
|
info->invInertiaTensor.Zero();
|
||
|
info->position.Zero();
|
||
|
info->velocity = current.velocity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::ApplyImpulse
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
|
||
|
if ( noImpact ) {
|
||
|
return;
|
||
|
}
|
||
|
current.velocity += impulse * invMass;
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::SaveState
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SaveState( void ) {
|
||
|
saved = current;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::RestoreState
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::RestoreState( void ) {
|
||
|
current = saved;
|
||
|
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
|
||
|
|
||
|
EvaluateContacts( CLIP_DEBUG_PARMS_ONLY );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Player::SetOrigin
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SetOrigin( const idVec3 &newOrigin, int id ) {
|
||
|
idVec3 masterOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
if ( masterEntity ) {
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
current.origin = masterOrigin + newOrigin * masterAxis;
|
||
|
}
|
||
|
else {
|
||
|
current.origin = newOrigin;
|
||
|
}
|
||
|
clipModel->Link( gameLocal.clip, self, 0, newOrigin, clipModel->GetAxis() );
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Player::SetAxis
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SetAxis( const idMat3 &newAxis, int id ) {
|
||
|
clipModel->Link( gameLocal.clip, self, 0, clipModel->GetOrigin(), newAxis );
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::Translate
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::Translate( const idVec3 &translation, int id ) {
|
||
|
|
||
|
current.origin += translation;
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::Rotate
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::Rotate( const idRotation &rotation, int id ) {
|
||
|
idVec3 masterOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
current.origin *= rotation;
|
||
|
if ( masterEntity ) {
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
}
|
||
|
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() );
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::SetLinearVelocity
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
|
||
|
current.velocity = newLinearVelocity;
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::GetLinearVelocity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &idPhysics_Monster::GetLinearVelocity( int id ) const {
|
||
|
return current.velocity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::SetPushed
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::SetPushed( int deltaTime ) {
|
||
|
// velocity with which the monster is pushed
|
||
|
current.pushVelocity += ( current.origin - saved.origin ) / ( deltaTime * idMath::M_MS2SEC );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::GetPushedLinearVelocity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &idPhysics_Monster::GetPushedLinearVelocity( const int id ) const {
|
||
|
return current.pushVelocity;
|
||
|
}
|
||
|
|
||
|
const float MONSTER_ORIGIN_MAX = 32767.0f;
|
||
|
const int MONSTER_ORIGIN_TOTAL_BITS = 24;
|
||
|
const int MONSTER_ORIGIN_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( MONSTER_ORIGIN_MAX ) ) + 1;
|
||
|
const int MONSTER_ORIGIN_MANTISSA_BITS = MONSTER_ORIGIN_TOTAL_BITS - 1 - MONSTER_ORIGIN_EXPONENT_BITS;
|
||
|
|
||
|
const float MONSTER_VELOCITY_MAX = 4000;
|
||
|
const int MONSTER_VELOCITY_TOTAL_BITS = 16;
|
||
|
const int MONSTER_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( MONSTER_VELOCITY_MAX ) ) + 1;
|
||
|
const int MONSTER_VELOCITY_MANTISSA_BITS = MONSTER_VELOCITY_TOTAL_BITS - 1 - MONSTER_VELOCITY_EXPONENT_BITS;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::CheckNetworkStateChanges
|
||
|
================
|
||
|
*/
|
||
|
bool idPhysics_Monster::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_BASE( sdMonsterPhysicsNetworkData );
|
||
|
|
||
|
if ( !baseData.origin.Compare( current.origin, 0.01f ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( !baseData.velocity.Compare( current.velocity, 0.01f ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_BASE( sdMonsterPhysicsBroadcastData );
|
||
|
|
||
|
if ( baseData.pushVelocity != current.pushVelocity ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.atRest != atRest ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::WriteNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_STATES( sdMonsterPhysicsNetworkData );
|
||
|
|
||
|
newData.origin = current.origin;
|
||
|
newData.velocity = current.velocity;
|
||
|
|
||
|
msg.WriteDeltaVector( baseData.origin, newData.origin, MONSTER_ORIGIN_EXPONENT_BITS, MONSTER_ORIGIN_MANTISSA_BITS );
|
||
|
msg.WriteDeltaVector( baseData.velocity, newData.velocity, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_STATES( sdMonsterPhysicsBroadcastData );
|
||
|
|
||
|
newData.pushVelocity = current.pushVelocity;
|
||
|
newData.atRest = atRest;
|
||
|
|
||
|
msg.WriteDeltaVector( baseData.pushVelocity, newData.pushVelocity, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
|
||
|
msg.WriteDeltaLong( baseData.atRest, newData.atRest );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::ApplyNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
|
||
|
traceCollection.ForceNextUpdate();
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_NEW( sdMonsterPhysicsNetworkData );
|
||
|
|
||
|
current.origin = newData.origin;
|
||
|
current.velocity = newData.velocity;
|
||
|
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
|
||
|
self->UpdateVisuals();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_NEW( sdMonsterPhysicsBroadcastData );
|
||
|
|
||
|
current.pushVelocity = newData.pushVelocity;
|
||
|
atRest = newData.atRest;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::ReadNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_STATES( sdMonsterPhysicsNetworkData );
|
||
|
|
||
|
newData.origin = msg.ReadDeltaVector( baseData.origin, MONSTER_ORIGIN_EXPONENT_BITS, MONSTER_ORIGIN_MANTISSA_BITS );
|
||
|
newData.velocity = msg.ReadDeltaVector( baseData.velocity, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
|
||
|
|
||
|
newData.origin.FixDenormals();
|
||
|
newData.velocity.FixDenormals();
|
||
|
|
||
|
self->OnNewOriginRead( newData.origin );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_STATES( sdMonsterPhysicsBroadcastData );
|
||
|
|
||
|
newData.pushVelocity = msg.ReadDeltaVector( baseData.pushVelocity, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
|
||
|
newData.atRest = msg.ReadDeltaLong( baseData.atRest );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::CreateNetworkStructure
|
||
|
================
|
||
|
*/
|
||
|
sdEntityStateNetworkData* idPhysics_Monster::CreateNetworkStructure( networkStateMode_t mode ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
return new sdMonsterPhysicsNetworkData();
|
||
|
}
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
return new sdMonsterPhysicsBroadcastData();
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::CheckWater
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::CheckWater( void ) {
|
||
|
waterLevel = 0.0f;
|
||
|
|
||
|
const idBounds& absBounds = GetAbsBounds( -1 );
|
||
|
|
||
|
const idClipModel* clipModel;
|
||
|
int count = gameLocal.clip.ClipModelsTouchingBounds( CLIP_DEBUG_PARMS absBounds, CONTENTS_WATER, &clipModel, 1, NULL );
|
||
|
if ( !count ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !clipModel->GetNumCollisionModels() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idCollisionModel* model = clipModel->GetCollisionModel( 0 );
|
||
|
int numPlanes = model->GetNumBrushPlanes();
|
||
|
if ( !numPlanes ) {
|
||
|
return;
|
||
|
}
|
||
|
const idBounds& modelBounds = model->GetBounds();
|
||
|
|
||
|
self->CheckWater( clipModel->GetOrigin(), clipModel->GetAxis(), model );
|
||
|
|
||
|
idVec3 waterCurrent;
|
||
|
clipModel->GetEntity()->GetWaterCurrent( waterCurrent );
|
||
|
|
||
|
float scratch[ MAX_TRACEMODEL_WATER_POINTS ];
|
||
|
float totalVolume = 0.0f;
|
||
|
float waterVolume = 0.0f;
|
||
|
|
||
|
{
|
||
|
idClipModel* bodyClip = this->clipModel;
|
||
|
const traceModelWater_t* waterPoints = bodyClip->GetWaterPoints();
|
||
|
if ( waterPoints == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
float volume = bodyClip->GetTraceModelVolume();
|
||
|
totalVolume += volume;
|
||
|
|
||
|
for ( int i = 0; i < MAX_TRACEMODEL_WATER_POINTS; i++ ) {
|
||
|
scratch[ i ] = waterPoints[ i ].weight * volume;
|
||
|
}
|
||
|
|
||
|
for ( int l = 0; l < numPlanes; l++ ) {
|
||
|
idPlane plane = model->GetBrushPlane( l );
|
||
|
plane.TranslateSelf( clipModel->GetOrigin() - bodyClip->GetOrigin() );
|
||
|
plane.Normal() *= clipModel->GetAxis();
|
||
|
|
||
|
for ( int i = 0; i < MAX_TRACEMODEL_WATER_POINTS; i++ ) {
|
||
|
if ( plane.Distance( waterPoints[ i ].xyz ) > 0 ) {
|
||
|
scratch[ i ] = 0.f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float height = clipModel->GetOrigin().z - bodyClip->GetOrigin().z + modelBounds.GetMaxs().z;
|
||
|
idPlane plane( mat3_identity[ 2 ], height );
|
||
|
|
||
|
for ( int i = 0; i < MAX_TRACEMODEL_WATER_POINTS; i++ ) {
|
||
|
if ( !scratch[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
scratch[ i ] *= Min( -plane.Distance( waterPoints[ i ].xyz ) / 16.f, 1.f );
|
||
|
/*
|
||
|
idVec3 impulse = scratch[ i ] * ( ( -gravityNormal * buoyancy ) + ( waterCurrent * inverseMass ) );
|
||
|
|
||
|
idVec3 org = bodyClip->GetOrigin() + ( bodyClip->GetAxis() * waterPoints[ i ].xyz );
|
||
|
|
||
|
current.i.linearMomentum += impulse;
|
||
|
current.i.angularMomentum += ( org - ( current.i.position + centerOfMass * current.i.orientation ) ).Cross( impulse );
|
||
|
*/
|
||
|
waterVolume += scratch[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( totalVolume > idMath::FLT_EPSILON ) {
|
||
|
waterLevel = waterVolume / totalVolume;
|
||
|
}
|
||
|
|
||
|
/* if ( waterLevel ) {
|
||
|
Activate();
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::WalkMove
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::WalkMove( void ) {
|
||
|
idVec3 oldVelocity, vel;
|
||
|
float oldVel, newVel;
|
||
|
|
||
|
oldVelocity = current.velocity;
|
||
|
|
||
|
// slide along the ground plane
|
||
|
current.velocity.ProjectOntoPlane( groundTrace.c.normal, PM_OVERCLIP );
|
||
|
|
||
|
// if not clipped into the opposite direction
|
||
|
if ( oldVelocity * current.velocity > 0.0f ) {
|
||
|
newVel = current.velocity.LengthSqr();
|
||
|
if ( newVel > 1.0f ) {
|
||
|
oldVel = oldVelocity.LengthSqr();
|
||
|
if ( oldVel > 1.0f ) {
|
||
|
// don't decrease velocity when going up or down a slope
|
||
|
current.velocity *= idMath::Sqrt( oldVel / newVel );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// don't do anything if standing still
|
||
|
vel = current.velocity - (current.velocity * gravityNormal) * gravityNormal;
|
||
|
if ( !vel.LengthSqr() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gameLocal.push.InitSavingPushedEntityPositions();
|
||
|
|
||
|
SlideMove( false, true, true, true, 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::AirMove
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::AirMove( void ) {
|
||
|
// we may have a ground plane that is very steep, even
|
||
|
// though we don't have a groundentity
|
||
|
// slide along the steep plane
|
||
|
if ( current.onGround ) {
|
||
|
current.velocity.ProjectOntoPlane( groundTrace.c.normal, PM_OVERCLIP );
|
||
|
}
|
||
|
|
||
|
SlideMove( true, false, false, false, 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::DebugDraw
|
||
|
================
|
||
|
*/
|
||
|
void idPhysics_Monster::DebugDraw( void ) {
|
||
|
if ( g_drawContacts.GetBool() ) {
|
||
|
for( int i = 0; i < contacts.Num(); i++ ) {
|
||
|
contactInfo_t& contact = contacts[ i ];
|
||
|
|
||
|
idVec3 x, y;
|
||
|
contact.normal.NormalVectors( x, y );
|
||
|
gameRenderWorld->DebugLine( colorBlue, contact.point, contact.point + 12.0f * contact.normal );
|
||
|
gameRenderWorld->DebugLine( colorBlue, contact.point - 4.0f * x, contact.point + 4.0f * x );
|
||
|
gameRenderWorld->DebugLine( colorBlue, contact.point - 4.0f * y, contact.point + 4.0f * y );
|
||
|
if( contact.surfaceType ) {
|
||
|
gameRenderWorld->DrawText( contact.surfaceType->GetName(), contact.point, 0.2f, colorWhite, gameLocal.GetLocalViewPlayer()->GetRenderView()->viewaxis );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics_Monster::VehiclePush
|
||
|
================
|
||
|
*/
|
||
|
#define VEHICLE_PUSH_EPSILON 0.5f
|
||
|
|
||
|
int idPhysics_Monster::VehiclePush( bool stuck, float timeDelta, idVec3& move, idClipModel* pusher, int pushCount ) {
|
||
|
|
||
|
if ( pushCount > 3 ) {
|
||
|
move.Zero();
|
||
|
return VPUSH_BLOCKED;
|
||
|
}
|
||
|
|
||
|
// remove components into the ground
|
||
|
if ( current.onGround ) {
|
||
|
float groundMove = move * groundTrace.c.normal;
|
||
|
if ( groundMove < 0.0f ) {
|
||
|
move -= groundMove * groundTrace.c.normal;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( !stuck ) {
|
||
|
// change the velocity so it satisfies the push
|
||
|
// note it has to be changed, then restored back
|
||
|
// could combine it and the required velocity of the move now, but it'd
|
||
|
// mean that you get double-moved by the current velocity - exploits++
|
||
|
idVec3 oldVelocity = current.velocity;
|
||
|
idVec3 oldOrigin = current.origin;
|
||
|
frametime = timeDelta;
|
||
|
framemsec = SEC2MS( timeDelta );
|
||
|
if ( framemsec < 1 ) {
|
||
|
framemsec = 1;
|
||
|
frametime = MS2SEC( 1 );
|
||
|
}
|
||
|
current.velocity = move / frametime;
|
||
|
|
||
|
if ( framemsec >= 1 ) {
|
||
|
// pusher->Disable();
|
||
|
SlideMove( false, true, false, false, pushCount + 1 );
|
||
|
// pusher->Enable();
|
||
|
|
||
|
CheckWater();
|
||
|
CheckGround();
|
||
|
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
|
||
|
self->UpdateVisuals();
|
||
|
}
|
||
|
|
||
|
// see if we ended up moving the right way
|
||
|
idVec3 moveDirection = move;
|
||
|
float moveNeeded = moveDirection.Normalize();
|
||
|
idVec3 movedDelta = current.origin - oldOrigin;
|
||
|
float movedAmount = moveDirection * movedDelta;
|
||
|
|
||
|
current.velocity = oldVelocity;
|
||
|
float oldMoveDirSpeed = current.velocity * moveDirection;
|
||
|
current.pushVelocity = ( moveNeeded - oldMoveDirSpeed ) * moveDirection;
|
||
|
// remove pushvelocity into the ground so it doesn't think its falling damage
|
||
|
if ( groundTrace.fraction < 1.0f ) {
|
||
|
float intoGroundVel = -current.pushVelocity * groundTrace.c.normal;
|
||
|
if ( intoGroundVel > 0.0f ) {
|
||
|
current.pushVelocity += intoGroundVel * groundTrace.c.normal;
|
||
|
}
|
||
|
}
|
||
|
current.velocity += current.pushVelocity ;
|
||
|
|
||
|
|
||
|
// gameRenderWorld->DebugArrow( colorYellow, current.origin + idVec3(0,0,64), current.origin + idVec3(0,0,64) + moveDirection * 128.0f, 4 );
|
||
|
if ( movedAmount < moveNeeded - VEHICLE_PUSH_EPSILON ) {
|
||
|
// didn't move all the way!
|
||
|
// update the amount that we move
|
||
|
move = move * movedAmount / moveNeeded;
|
||
|
return VPUSH_BLOCKED;
|
||
|
}
|
||
|
} else {
|
||
|
// that means we're inside the vehicle and its trying to push us out
|
||
|
// this is normally caused because we're being pushed by the vehicle
|
||
|
// and we try to walk towards it
|
||
|
|
||
|
// try to find a point on the outside of the vehicle that we can teleport to
|
||
|
// start by finding the normal of a nearby surface
|
||
|
contactInfo_t contacts[ 2 ];
|
||
|
int numContacts = gameLocal.clip.ContactsModel( CLIP_DEBUG_PARMS contacts, 2, current.origin, NULL, 4.0f, clipModel, clipModel->GetAxis(), -1, pusher, pusher->GetOrigin(), pusher->GetAxis() );
|
||
|
idVec3 foundNormal = vec3_origin;
|
||
|
|
||
|
if ( numContacts ) {
|
||
|
// average out the normals of the contacts - this provides a good approximation
|
||
|
for ( int i = 0; i < numContacts; i++ ) {
|
||
|
// gameRenderWorld->DebugSphere( colorGreen, idSphere( contacts[ i ].point, 4.0f ) );
|
||
|
// gameRenderWorld->DebugArrow( colorGreen, contacts[ i ].point, contacts[ i ].point + contacts[ i ].normal * 128.0f, 4 );
|
||
|
|
||
|
foundNormal += contacts[ i ].normal;
|
||
|
}
|
||
|
foundNormal /= numContacts;
|
||
|
} else {
|
||
|
// embedded so far it doesn't find any contacts O_o
|
||
|
// use a trace to try to find the normal
|
||
|
idVec3 traceDir = pusher->GetAbsBounds().GetCenter() - current.origin;
|
||
|
float traceDirLength = traceDir.Normalize();
|
||
|
if ( traceDirLength == 0.0f ) {
|
||
|
// well and truly embedded - nothing we can do here
|
||
|
move.Zero();
|
||
|
return VPUSH_BLOCKED;
|
||
|
}
|
||
|
|
||
|
idVec3 normalFinderStart = current.origin - traceDir * 128.0f;
|
||
|
idVec3 normalFinderEnd = current.origin + traceDir * 128.0f;
|
||
|
|
||
|
trace_t normalFinder;
|
||
|
gameLocal.clip.TranslationModel( CLIP_DEBUG_PARMS normalFinder, normalFinderStart, normalFinderEnd, clipModel,
|
||
|
clipModel->GetAxis(), GetClipMask(), pusher,
|
||
|
pusher->GetOrigin(), pusher->GetAxis() );
|
||
|
if ( normalFinder.fraction == 1.0f ) {
|
||
|
// well and truly embedded - nothing we can do here
|
||
|
move.Zero();
|
||
|
return VPUSH_BLOCKED;
|
||
|
}
|
||
|
|
||
|
foundNormal = normalFinder.c.normal;
|
||
|
}
|
||
|
|
||
|
// gameRenderWorld->DebugArrow( colorYellow, current.origin, current.origin + foundNormal * 128.0f, 4 );
|
||
|
idVec3 start = current.origin + foundNormal * 32.0f;
|
||
|
idVec3 end = current.origin - foundNormal * 32.0f;
|
||
|
|
||
|
trace_t tr;
|
||
|
gameLocal.clip.TranslationModel( CLIP_DEBUG_PARMS tr, start, end, clipModel, clipModel->GetAxis(), GetClipMask(), pusher,
|
||
|
pusher->GetOrigin(), pusher->GetAxis() );
|
||
|
|
||
|
if ( tr.fraction < 1.0f ) {
|
||
|
// push to the new spot
|
||
|
idEntity* other = pusher->GetEntity();
|
||
|
if ( other != NULL ) {
|
||
|
other->DisableClip( false );
|
||
|
} else {
|
||
|
pusher->Disable();
|
||
|
}
|
||
|
|
||
|
idVec3 pushOutMove = tr.endpos - current.origin;
|
||
|
int pushOutResult = VehiclePush( false, timeDelta, pushOutMove, pusher, pushCount );
|
||
|
|
||
|
if ( other != NULL ) {
|
||
|
other->EnableClip();
|
||
|
} else {
|
||
|
pusher->Enable();
|
||
|
}
|
||
|
|
||
|
if ( pushOutResult == VPUSH_OK ) {
|
||
|
// heres a point thats not inside the vehicle, and not inside the world! move there, use it as our new origin
|
||
|
// proceed with the move, adjust it for the new origin
|
||
|
return VehiclePush( false, timeDelta, move, pusher, pushCount );
|
||
|
} else {
|
||
|
return VPUSH_BLOCKED;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return VPUSH_OK;
|
||
|
}
|