mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-27 14:42:23 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
1448 lines
39 KiB
C++
1448 lines
39 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "physics/Physics_Actor.h"
|
|
#include "Entity.h"
|
|
#include "Player.h"
|
|
#include "Moveable.h"
|
|
#include "Game_local.h"
|
|
|
|
#include "physics/Push.h"
|
|
|
|
/*
|
|
============
|
|
idPush::InitSavingPushedEntityPositions
|
|
============
|
|
*/
|
|
void idPush::InitSavingPushedEntityPositions( void ) {
|
|
numPushed = 0;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::SaveEntityPosition
|
|
============
|
|
*/
|
|
void idPush::SaveEntityPosition( idEntity *ent ) {
|
|
int i;
|
|
|
|
// if already saved the physics state for this entity
|
|
for ( i = 0; i < numPushed; i++ ) {
|
|
if ( pushed[i].ent == ent ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// don't overflow
|
|
if ( numPushed >= MAX_GENTITIES ) {
|
|
gameLocal.Error( "more than MAX_GENTITIES pushed entities" );
|
|
return;
|
|
}
|
|
|
|
pushed[numPushed].ent = ent;
|
|
|
|
// if the entity is an actor
|
|
if ( ent->IsType( idActor::Type ) ) {
|
|
// save the delta view angles
|
|
pushed[numPushed].deltaViewAngles = static_cast<idActor *>(ent)->GetDeltaViewAngles();
|
|
}
|
|
|
|
// save the physics state
|
|
ent->GetPhysics()->SaveState();
|
|
|
|
numPushed++;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::RestorePushedEntityPositions
|
|
============
|
|
*/
|
|
void idPush::RestorePushedEntityPositions( void ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < numPushed; i++ ) {
|
|
|
|
// if the entity is an actor
|
|
if ( pushed[i].ent->IsType( idActor::Type ) ) {
|
|
// set back the delta view angles
|
|
static_cast<idActor *>(pushed[i].ent)->SetDeltaViewAngles( pushed[i].deltaViewAngles );
|
|
}
|
|
|
|
// restore the physics state
|
|
pushed[i].ent->GetPhysics()->RestoreState();
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::RotateEntityToAxial
|
|
============
|
|
*/
|
|
bool idPush::RotateEntityToAxial( idEntity *ent, idVec3 rotationPoint ) {
|
|
int i;
|
|
trace_t trace;
|
|
idRotation rotation;
|
|
idMat3 axis;
|
|
idPhysics *physics;
|
|
|
|
physics = ent->GetPhysics();
|
|
axis = physics->GetAxis();
|
|
if ( !axis.IsRotated() ) {
|
|
return true;
|
|
}
|
|
// try to rotate the bbox back to axial with at most four rotations
|
|
for ( i = 0; i < 4; i++ ) {
|
|
axis = physics->GetAxis();
|
|
rotation = axis.ToRotation();
|
|
rotation.Scale( -1 );
|
|
rotation.SetOrigin( rotationPoint );
|
|
// tiny float numbers in the clip axis, this can get the entity stuck
|
|
if ( rotation.GetAngle() == 0.0f ) {
|
|
physics->SetAxis( mat3_identity );
|
|
return true;
|
|
}
|
|
//
|
|
ent->GetPhysics()->ClipRotation( trace, rotation, NULL );
|
|
// if the full rotation is possible
|
|
if ( trace.fraction >= 1.0f ) {
|
|
// set bbox in final axial position
|
|
physics->SetOrigin( trace.endpos );
|
|
physics->SetAxis( mat3_identity );
|
|
return true;
|
|
}
|
|
// if partial rotation was possible
|
|
else if ( trace.fraction > 0.0f ) {
|
|
// partial rotation
|
|
physics->SetOrigin( trace.endpos );
|
|
physics->SetAxis( trace.endAxis );
|
|
}
|
|
// next rotate around collision point
|
|
rotationPoint = trace.c.point;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef NEW_PUSH
|
|
|
|
/*
|
|
============
|
|
idPush::CanPushEntity
|
|
============
|
|
*/
|
|
bool idPush::CanPushEntity( idEntity *ent, idEntity *pusher, idEntity *initialPusher, const int flags ) {
|
|
|
|
// if the physics object is not pushable
|
|
if ( !ent->GetPhysics()->IsPushable() ) {
|
|
return false;
|
|
}
|
|
|
|
// if the entity doesn't clip with this pusher
|
|
if ( !( ent->GetPhysics()->GetClipMask() & pusher->GetPhysics()->GetContents() ) ) {
|
|
return false;
|
|
}
|
|
|
|
// don't push players in noclip mode
|
|
if ( ent->client && ent->client->noclip ) {
|
|
return false;
|
|
}
|
|
|
|
// if we should only push idMoveable entities
|
|
if ( ( flags & PUSHFL_ONLYMOVEABLE ) && !ent->IsType( idMoveable::Type ) ) {
|
|
return false;
|
|
}
|
|
|
|
// if we shouldn't push entities the original pusher rests upon
|
|
if ( flags & PUSHFL_NOGROUNDENTITIES ) {
|
|
if ( initialPusher->GetPhysics()->IsGroundEntity( ent->entityNumber ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::AddEntityToPushedGroup
|
|
============
|
|
*/
|
|
void idPush::AddEntityToPushedGroup( idEntity *ent, float fraction, bool groundContact ) {
|
|
int i, j;
|
|
|
|
for ( i = 0; i < pushedGroupSize; i++ ) {
|
|
if ( ent == pushedGroup[i].ent ) {
|
|
if ( fraction > pushedGroup[i].fraction ) {
|
|
pushedGroup[i].fraction = fraction;
|
|
pushedGroup[i].groundContact &= groundContact;
|
|
pushedGroup[i].test = true;
|
|
}
|
|
return;
|
|
}
|
|
else if ( fraction > pushedGroup[i].fraction ) {
|
|
for ( j = pushedGroupSize; j > i; j-- ) {
|
|
pushedGroup[j] = pushedGroup[j-1];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// put the entity in the group
|
|
pushedGroupSize++;
|
|
pushedGroup[i].ent = ent;
|
|
pushedGroup[i].fraction = fraction;
|
|
pushedGroup[i].groundContact = groundContact;
|
|
pushedGroup[i].test = true;
|
|
|
|
// remove any further occurances of the same entity in the group
|
|
for ( i++; i < pushedGroupSize; i++ ) {
|
|
if ( ent == pushedGroup[i].ent ) {
|
|
for ( j = i+1; j < pushedGroupSize; j++ ) {
|
|
pushedGroup[j-1] = pushedGroup[j];
|
|
}
|
|
pushedGroupSize--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::IsFullyPushed
|
|
============
|
|
*/
|
|
bool idPush::IsFullyPushed( idEntity *ent ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < pushedGroupSize; i++ ) {
|
|
if ( pushedGroup[i].fraction < 1.0f ) {
|
|
return false;
|
|
}
|
|
if ( ent == pushedGroup[i].ent ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipTranslationAgainstPusher
|
|
============
|
|
*/
|
|
bool idPush::ClipTranslationAgainstPusher( trace_t &results, idEntity *ent, idEntity *pusher, const idVec3 &translation ) {
|
|
int i, n;
|
|
trace_t t;
|
|
|
|
results.fraction = 1.0f;
|
|
|
|
n = pusher->GetPhysics()->GetNumClipModels();
|
|
for ( i = 0; i < n; i++ ) {
|
|
ent->GetPhysics()->ClipTranslation( t, translation, pusher->GetPhysics()->GetClipModel( i ) );
|
|
if ( t.fraction < results.fraction ) {
|
|
results = t;
|
|
}
|
|
}
|
|
return ( results.fraction < 1.0f );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::GetPushableEntitiesForTranslation
|
|
============
|
|
*/
|
|
int idPush::GetPushableEntitiesForTranslation( idEntity *pusher, idEntity *initialPusher, const int flags,
|
|
const idVec3 &translation, idEntity *entityList[], int maxEntities ) {
|
|
int i, n, l;
|
|
idBounds bounds, pushBounds;
|
|
idPhysics *physics;
|
|
|
|
// get bounds for the whole movement
|
|
physics = pusher->GetPhysics();
|
|
bounds = physics->GetBounds();
|
|
pushBounds.FromBoundsTranslation( bounds, physics->GetOrigin(), physics->GetAxis(), translation );
|
|
pushBounds.ExpandSelf( 2.0f );
|
|
|
|
// get all entities within the push bounds
|
|
n = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
|
|
|
|
for ( l = i = 0; i < n; i++ ) {
|
|
if ( entityList[i] == pusher || entityList[i] == initialPusher ) {
|
|
continue;
|
|
}
|
|
if ( CanPushEntity( entityList[i], pusher, initialPusher, flags ) ) {
|
|
entityList[l++] = entityList[i];
|
|
}
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipTranslationalPush
|
|
|
|
Try to push other entities by translating the given entity.
|
|
============
|
|
*/
|
|
float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags,
|
|
const idVec3 &newOrigin, const idVec3 &translation ) {
|
|
int i, j, numListedEntities;
|
|
idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ];
|
|
float fraction;
|
|
bool groundContact, blocked = false;
|
|
float totalMass;
|
|
trace_t trace;
|
|
idVec3 realTranslation, partialTranslation;
|
|
|
|
totalMass = 0.0f;
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = newOrigin;
|
|
results.endAxis = pusher->GetPhysics()->GetAxis();
|
|
memset( results.c, 0, sizeof( results.c ) );
|
|
|
|
if ( translation == vec3_origin ) {
|
|
return totalMass;
|
|
}
|
|
|
|
// clip against all non-pushable physics objects
|
|
if ( flags & PUSHFL_CLIP ) {
|
|
|
|
numListedEntities = GetPushableEntitiesForTranslation( pusher, pusher, flags, translation, entityList, MAX_GENTITIES );
|
|
// disable pushable entities for collision detection
|
|
for ( i = 0; i < numListedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->DisableClip();
|
|
}
|
|
// clip translation
|
|
pusher->GetPhysics()->ClipTranslation( results, translation, NULL );
|
|
// enable pushable entities
|
|
for ( i = 0; i < numListedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->EnableClip();
|
|
}
|
|
if ( results.fraction == 0.0f ) {
|
|
return totalMass;
|
|
}
|
|
realTranslation = results.fraction * translation;
|
|
}
|
|
else {
|
|
realTranslation = translation;
|
|
}
|
|
|
|
// put the pusher in the group of pushed physics objects
|
|
pushedGroup[0].ent = pusher;
|
|
pushedGroup[0].fraction = 1.0f;
|
|
pushedGroup[0].groundContact = true;
|
|
pushedGroup[0].test = true;
|
|
pushedGroupSize = 1;
|
|
|
|
// get all physics objects that need to be pushed
|
|
for ( i = 0; i < pushedGroupSize; ) {
|
|
if ( !pushedGroup[i].test ) {
|
|
i++;
|
|
continue;
|
|
}
|
|
pushedGroup[i].test = false;
|
|
curPusher = pushedGroup[i].ent;
|
|
fraction = pushedGroup[i].fraction;
|
|
groundContact = pushedGroup[i].groundContact;
|
|
i = 0;
|
|
|
|
numListedEntities = GetPushableEntitiesForTranslation( curPusher, pusher, flags, realTranslation, entityList, MAX_GENTITIES );
|
|
|
|
for ( j = 0; j < numListedEntities; j++ ) {
|
|
ent = entityList[ j ];
|
|
|
|
if ( IsFullyPushed( ent ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !CanPushEntity( ent, curPusher, pusher, flags ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) {
|
|
AddEntityToPushedGroup( ent, 1.0f * fraction, false );
|
|
}
|
|
else if ( ClipTranslationAgainstPusher( trace, ent, curPusher, -fraction * realTranslation ) ) {
|
|
AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact );
|
|
}
|
|
}
|
|
}
|
|
|
|
// save physics states and disable physics objects for collision detection
|
|
for ( i = 0; i < pushedGroupSize; i++ ) {
|
|
SaveEntityPosition( pushedGroup[i].ent );
|
|
pushedGroup[i].ent->GetPhysics()->DisableClip();
|
|
}
|
|
|
|
// clip all pushed physics objects
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
partialTranslation = realTranslation * pushedGroup[i].fraction;
|
|
|
|
pushedGroup[i].ent->GetPhysics()->ClipTranslation( trace, partialTranslation, NULL );
|
|
|
|
if ( trace.fraction < 1.0f ) {
|
|
blocked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// enable all physics objects for collision detection
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
pushedGroup[i].ent->GetPhysics()->EnableClip();
|
|
}
|
|
|
|
// push all or nothing
|
|
if ( blocked ) {
|
|
if ( flags & PUSHFL_CLIP ) {
|
|
pusher->GetPhysics()->ClipTranslation( results, realTranslation, NULL );
|
|
}
|
|
else {
|
|
results.fraction = 0.0f;
|
|
results.endpos = pusher->GetPhysics()->GetOrigin();
|
|
results.endAxis = pusher->GetPhysics()->GetAxis();
|
|
}
|
|
}
|
|
else {
|
|
// translate all pushed physics objects
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
partialTranslation = realTranslation * pushedGroup[i].fraction;
|
|
pushedGroup[i].ent->GetPhysics()->Translate( partialTranslation );
|
|
totalMass += pushedGroup[i].ent->GetPhysics()->GetMass();
|
|
}
|
|
// translate the clip models of the pusher
|
|
for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) {
|
|
pusher->GetPhysics()->GetClipModel(i)->Translate( results.fraction * realTranslation );
|
|
pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip );
|
|
}
|
|
}
|
|
|
|
return totalMass;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipRotationAgainstPusher
|
|
============
|
|
*/
|
|
bool idPush::ClipRotationAgainstPusher( trace_t &results, idEntity *ent, idEntity *pusher, const idRotation &rotation ) {
|
|
int i, n;
|
|
trace_t t;
|
|
|
|
results.fraction = 1.0f;
|
|
|
|
n = pusher->GetPhysics()->GetNumClipModels();
|
|
for ( i = 0; i < n; i++ ) {
|
|
ent->GetPhysics()->ClipRotation( t, rotation, pusher->GetPhysics()->GetClipModel( i ) );
|
|
if ( t.fraction < results.fraction ) {
|
|
results = t;
|
|
}
|
|
}
|
|
return ( results.fraction < 1.0f );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::GetPushableEntitiesForRotation
|
|
============
|
|
*/
|
|
int idPush::GetPushableEntitiesForRotation( idEntity *pusher, idEntity *initialPusher, const int flags,
|
|
const idRotation &rotation, idEntity *entityList[], int maxEntities ) {
|
|
int i, n, l;
|
|
idBounds bounds, pushBounds;
|
|
idPhysics *physics;
|
|
|
|
// get bounds for the whole movement
|
|
physics = pusher->GetPhysics();
|
|
bounds = physics->GetBounds();
|
|
pushBounds.FromBoundsRotation( bounds, physics->GetOrigin(), physics->GetAxis(), rotation );
|
|
pushBounds.ExpandSelf( 2.0f );
|
|
|
|
// get all entities within the push bounds
|
|
n = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
|
|
|
|
for ( l = i = 0; i < n; i++ ) {
|
|
if ( entityList[i] == pusher || entityList[i] == initialPusher ) {
|
|
continue;
|
|
}
|
|
if ( CanPushEntity( entityList[i], pusher, initialPusher, flags ) ) {
|
|
entityList[l++] = entityList[i];
|
|
}
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipRotationalPush
|
|
|
|
Try to push other entities by rotating the given entity.
|
|
============
|
|
*/
|
|
float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags,
|
|
const idMat3 &newAxis, const idRotation &rotation ) {
|
|
int i, j, numListedEntities;
|
|
idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ];
|
|
float fraction;
|
|
bool groundContact, blocked = false;
|
|
float totalMass;
|
|
trace_t trace;
|
|
idRotation realRotation, partialRotation;
|
|
idMat3 oldAxis;
|
|
|
|
totalMass = 0.0f;
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = pusher->GetPhysics()->GetOrigin();
|
|
results.endAxis = newAxis;
|
|
memset( results.c, 0, sizeof( results.c ) );
|
|
|
|
if ( !rotation.GetAngle() ) {
|
|
return totalMass;
|
|
}
|
|
|
|
// clip against all non-pushable physics objects
|
|
if ( flags & PUSHFL_CLIP ) {
|
|
|
|
numListedEntities = GetPushableEntitiesForRotation( pusher, pusher, flags, rotation, entityList, MAX_GENTITIES );
|
|
// disable pushable entities for collision detection
|
|
for ( i = 0; i < numListedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->DisableClip();
|
|
}
|
|
// clip rotation
|
|
pusher->GetPhysics()->ClipRotation( results, rotation, NULL );
|
|
// enable pushable entities
|
|
for ( i = 0; i < numListedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->EnableClip();
|
|
}
|
|
if ( results.fraction == 0.0f ) {
|
|
return totalMass;
|
|
}
|
|
realRotation = results.fraction * rotation;
|
|
}
|
|
else {
|
|
realRotation = rotation;
|
|
}
|
|
|
|
// put the pusher in the group of pushed physics objects
|
|
pushedGroup[0].ent = pusher;
|
|
pushedGroup[0].fraction = 1.0f;
|
|
pushedGroup[0].groundContact = true;
|
|
pushedGroup[0].test = true;
|
|
pushedGroupSize = 1;
|
|
|
|
// get all physics objects that need to be pushed
|
|
for ( i = 0; i < pushedGroupSize; ) {
|
|
if ( !pushedGroup[i].test ) {
|
|
i++;
|
|
continue;
|
|
}
|
|
pushedGroup[i].test = false;
|
|
curPusher = pushedGroup[i].ent;
|
|
fraction = pushedGroup[i].fraction;
|
|
groundContact = pushedGroup[i].groundContact;
|
|
i = 0;
|
|
|
|
numListedEntities = GetPushableEntitiesForRotation( curPusher, pusher, flags, realRotation, entityList, MAX_GENTITIES );
|
|
|
|
for ( j = 0; j < numListedEntities; j++ ) {
|
|
ent = entityList[ j ];
|
|
|
|
if ( IsFullyPushed( ent ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) {
|
|
AddEntityToPushedGroup( ent, 1.0f * fraction, false );
|
|
}
|
|
else if ( ClipRotationAgainstPusher( trace, ent, curPusher, -fraction * realRotation ) ) {
|
|
AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact );
|
|
}
|
|
}
|
|
}
|
|
|
|
// save physics states and disable physics objects for collision detection
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
SaveEntityPosition( pushedGroup[i].ent );
|
|
pushedGroup[i].ent->GetPhysics()->DisableClip();
|
|
}
|
|
|
|
// clip all pushed physics objects
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
partialRotation = realRotation * pushedGroup[i].fraction;
|
|
|
|
pushedGroup[i].ent->GetPhysics()->ClipRotation( trace, partialRotation, NULL );
|
|
|
|
if ( trace.fraction < 1.0f ) {
|
|
blocked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// enable all physics objects for collision detection
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
pushedGroup[i].ent->GetPhysics()->EnableClip();
|
|
}
|
|
|
|
// push all or nothing
|
|
if ( blocked ) {
|
|
if ( flags & PUSHFL_CLIP ) {
|
|
pusher->GetPhysics()->ClipRotation( results, realRotation, NULL );
|
|
}
|
|
else {
|
|
results.fraction = 0.0f;
|
|
results.endpos = pusher->GetPhysics()->GetOrigin();
|
|
results.endAxis = pusher->GetPhysics()->GetAxis();
|
|
}
|
|
}
|
|
else {
|
|
// rotate all pushed physics objects
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
partialRotation = realRotation * pushedGroup[i].fraction;
|
|
pushedGroup[i].ent->GetPhysics()->Rotate( partialRotation );
|
|
totalMass += pushedGroup[i].ent->GetPhysics()->GetMass();
|
|
}
|
|
// rotate the clip models of the pusher
|
|
for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) {
|
|
pusher->GetPhysics()->GetClipModel(i)->Rotate( realRotation );
|
|
pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip );
|
|
pusher->GetPhysics()->GetClipModel(i)->Enable();
|
|
}
|
|
// rotate any actors back to axial
|
|
for ( i = 1; i < pushedGroupSize; i++ ) {
|
|
// if the entity is using actor physics
|
|
if ( pushedGroup[i].ent->GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
|
|
|
|
// rotate the collision model back to axial
|
|
if ( !RotateEntityToAxial( pushedGroup[i].ent, pushedGroup[i].ent->GetPhysics()->GetOrigin() ) ) {
|
|
// don't allow rotation if the bbox is no longer axial
|
|
results.fraction = 0.0f;
|
|
results.endpos = pusher->GetPhysics()->GetOrigin();
|
|
results.endAxis = pusher->GetPhysics()->GetAxis();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return totalMass;
|
|
}
|
|
|
|
#else /* !NEW_PUSH */
|
|
|
|
enum {
|
|
PUSH_NO, // not pushed
|
|
PUSH_OK, // pushed ok
|
|
PUSH_BLOCKED // blocked
|
|
};
|
|
|
|
/*
|
|
============
|
|
idPush::ClipEntityRotation
|
|
============
|
|
*/
|
|
void idPush::ClipEntityRotation( trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idRotation &rotation ) {
|
|
|
|
if ( skip ) {
|
|
skip->Disable();
|
|
}
|
|
|
|
ent->GetPhysics()->ClipRotation( trace, rotation, clipModel );
|
|
|
|
if ( skip ) {
|
|
skip->Enable();
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipEntityTranslation
|
|
============
|
|
*/
|
|
void idPush::ClipEntityTranslation( trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idVec3 &translation ) {
|
|
|
|
if ( skip ) {
|
|
skip->Disable();
|
|
}
|
|
|
|
ent->GetPhysics()->ClipTranslation( trace, translation, clipModel );
|
|
|
|
if ( skip ) {
|
|
skip->Enable();
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::TryRotatePushEntity
|
|
============
|
|
*/
|
|
#ifdef _DEBUG
|
|
// #define ROTATIONAL_PUSH_DEBUG
|
|
#endif
|
|
|
|
int idPush::TryRotatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags,
|
|
const idMat3 &newAxis, const idRotation &rotation ) {
|
|
trace_t trace;
|
|
idVec3 rotationPoint;
|
|
idRotation newRotation;
|
|
float checkAngle;
|
|
idPhysics *physics;
|
|
|
|
physics = check->GetPhysics();
|
|
|
|
#ifdef ROTATIONAL_PUSH_DEBUG
|
|
bool startsolid = false;
|
|
if ( physics->ClipContents( clipModel ) ) {
|
|
startsolid = true;
|
|
}
|
|
#endif
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = clipModel->GetOrigin();
|
|
results.endAxis = newAxis;
|
|
memset( &results.c, 0, sizeof( results.c ) );
|
|
|
|
// always pushed when standing on the pusher
|
|
if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) {
|
|
// rotate the entity colliding with all other entities except the pusher itself
|
|
ClipEntityRotation( trace, check, NULL, clipModel, rotation );
|
|
// if there is a collision
|
|
if ( trace.fraction < 1.0f ) {
|
|
// angle along which the entity is pushed
|
|
checkAngle = rotation.GetAngle() * trace.fraction;
|
|
// test if the entity can stay at it's partly pushed position by rotating
|
|
// the entity in reverse only colliding with pusher
|
|
newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), -(rotation.GetAngle() - checkAngle) );
|
|
ClipEntityRotation( results, check, clipModel, NULL, newRotation );
|
|
// if there is a collision
|
|
if ( results.fraction < 1.0f ) {
|
|
|
|
// FIXME: try to push the blocking entity as well or try to slide along collision plane(s)?
|
|
|
|
results.c.normal = -results.c.normal;
|
|
results.c.dist = -results.c.dist;
|
|
|
|
// the entity will be crushed between the pusher and some other entity
|
|
return PUSH_BLOCKED;
|
|
}
|
|
}
|
|
else {
|
|
// angle along which the entity is pushed
|
|
checkAngle = rotation.GetAngle();
|
|
}
|
|
// point to rotate entity bbox around back to axial
|
|
rotationPoint = physics->GetOrigin();
|
|
}
|
|
else {
|
|
// rotate entity in reverse only colliding with pusher
|
|
newRotation = rotation;
|
|
newRotation.Scale( -1 );
|
|
//
|
|
ClipEntityRotation( results, check, clipModel, NULL, newRotation );
|
|
// if no collision with the pusher then the entity is not pushed by the pusher
|
|
if ( results.fraction >= 1.0f ) {
|
|
#ifdef ROTATIONAL_PUSH_DEBUG
|
|
// set pusher into final position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis );
|
|
if ( physics->ClipContents( clipModel ) ) {
|
|
if ( !startsolid ) {
|
|
int bah = 1;
|
|
}
|
|
}
|
|
#endif
|
|
return PUSH_NO;
|
|
}
|
|
// get point to rotate bbox around back to axial
|
|
rotationPoint = results.c.point;
|
|
// angle along which the entity will be pushed
|
|
checkAngle = rotation.GetAngle() * (1.0f - results.fraction);
|
|
// rotate the entity colliding with all other entities except the pusher itself
|
|
newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle );
|
|
ClipEntityRotation( trace, check, NULL, clipModel, newRotation );
|
|
// if there is a collision
|
|
if ( trace.fraction < 1.0f ) {
|
|
|
|
// FIXME: try to push the blocking entity as well or try to slide along collision plane(s)?
|
|
|
|
results.c.normal = -results.c.normal;
|
|
results.c.dist = -results.c.dist;
|
|
|
|
// the entity will be crushed between the pusher and some other entity
|
|
return PUSH_BLOCKED;
|
|
}
|
|
}
|
|
|
|
SaveEntityPosition( check );
|
|
|
|
newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle );
|
|
|
|
newRotation.RotatePoint( rotationPoint );
|
|
|
|
// rotate the entity
|
|
physics->Rotate( newRotation );
|
|
|
|
// set pusher into final position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis );
|
|
|
|
#ifdef ROTATIONAL_PUSH_DEBUG
|
|
if ( physics->ClipContents( clipModel ) ) {
|
|
if ( !startsolid ) {
|
|
int bah = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if the entity uses actor physics
|
|
if ( physics->IsType( idPhysics_Actor::Type ) ) {
|
|
|
|
// rotate the collision model back to axial
|
|
if ( !RotateEntityToAxial( check, rotationPoint ) ) {
|
|
// don't allow rotation if the bbox is no longer axial
|
|
return PUSH_BLOCKED;
|
|
}
|
|
}
|
|
|
|
#ifdef ROTATIONAL_PUSH_DEBUG
|
|
if ( physics->ClipContents( clipModel ) ) {
|
|
if ( !startsolid ) {
|
|
int bah = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if the entity is an actor using actor physics
|
|
if ( check->IsType( idActor::Type ) && physics->IsType( idPhysics_Actor::Type ) ) {
|
|
|
|
// if the entity is standing ontop of the pusher
|
|
if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) {
|
|
// rotate actor view
|
|
idActor *actor = static_cast<idActor *>(check);
|
|
idAngles delta = actor->GetDeltaViewAngles();
|
|
delta.yaw += newRotation.ToMat3()[0].ToYaw();
|
|
actor->SetDeltaViewAngles( delta );
|
|
}
|
|
}
|
|
|
|
return PUSH_OK;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::TryTranslatePushEntity
|
|
============
|
|
*/
|
|
#ifdef _DEBUG
|
|
// #define TRANSLATIONAL_PUSH_DEBUG
|
|
#endif
|
|
|
|
int idPush::TryTranslatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags,
|
|
const idVec3 &newOrigin, const idVec3 &move ) {
|
|
trace_t trace;
|
|
idVec3 checkMove;
|
|
idVec3 oldOrigin;
|
|
idPhysics *physics;
|
|
|
|
physics = check->GetPhysics();
|
|
|
|
#ifdef TRANSLATIONAL_PUSH_DEBUG
|
|
bool startsolid = false;
|
|
if ( physics->ClipContents( clipModel ) ) {
|
|
startsolid = true;
|
|
}
|
|
#endif
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = newOrigin;
|
|
results.endAxis = clipModel->GetAxis();
|
|
memset( &results.c, 0, sizeof( results.c ) );
|
|
|
|
// always pushed when standing on the pusher
|
|
if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) {
|
|
// move the entity colliding with all other entities except the pusher itself
|
|
ClipEntityTranslation( trace, check, NULL, clipModel, move );
|
|
// if there is a collision
|
|
if ( trace.fraction < 1.0f ) {
|
|
// vector along which the entity is pushed
|
|
checkMove = move * trace.fraction;
|
|
// test if the entity can stay at it's partly pushed position by moving the entity in reverse only colliding with pusher
|
|
ClipEntityTranslation( results, check, clipModel, NULL, -(move - checkMove) );
|
|
// if there is a collision
|
|
if ( results.fraction < 1.0f ) {
|
|
|
|
// FIXME: try to push the blocking entity as well or try to slide along collision plane(s)?
|
|
|
|
results.c.normal = -results.c.normal;
|
|
results.c.dist = -results.c.dist;
|
|
|
|
// the entity will be crushed between the pusher and some other entity
|
|
return PUSH_BLOCKED;
|
|
}
|
|
}
|
|
else {
|
|
// vector along which the entity is pushed
|
|
checkMove = move;
|
|
}
|
|
}
|
|
else {
|
|
// move entity in reverse only colliding with pusher
|
|
ClipEntityTranslation( results, check, clipModel, NULL, -move );
|
|
// if no collision with the pusher then the entity is not pushed by the pusher
|
|
if ( results.fraction >= 1.0f ) {
|
|
return PUSH_NO;
|
|
}
|
|
// vector along which the entity is pushed
|
|
checkMove = move * (1.0f - results.fraction);
|
|
// move the entity colliding with all other entities except the pusher itself
|
|
ClipEntityTranslation( trace, check, NULL, clipModel, checkMove );
|
|
// if there is a collisions
|
|
if ( trace.fraction < 1.0f ) {
|
|
|
|
results.c.normal = -results.c.normal;
|
|
results.c.dist = -results.c.dist;
|
|
|
|
// FIXME: try to push the blocking entity as well ?
|
|
// FIXME: handle sliding along more than one collision plane ?
|
|
// FIXME: this code has issues, player pushing box into corner in "maps/mre/aaron/test.map"
|
|
|
|
/*
|
|
oldOrigin = physics->GetOrigin();
|
|
|
|
// movement still remaining
|
|
checkMove *= (1.0f - trace.fraction);
|
|
|
|
// project the movement along the collision plane
|
|
if ( !checkMove.ProjectAlongPlane( trace.c.normal, 0.1f, 1.001f ) ) {
|
|
return PUSH_BLOCKED;
|
|
}
|
|
checkMove *= 1.001f;
|
|
|
|
// move entity from collision point along the collision plane
|
|
physics->SetOrigin( trace.endpos );
|
|
ClipEntityTranslation( trace, check, NULL, NULL, checkMove );
|
|
|
|
if ( trace.fraction < 1.0f ) {
|
|
physics->SetOrigin( oldOrigin );
|
|
return PUSH_BLOCKED;
|
|
}
|
|
|
|
checkMove = trace.endpos - oldOrigin;
|
|
|
|
// move entity in reverse only colliding with pusher
|
|
physics->SetOrigin( trace.endpos );
|
|
ClipEntityTranslation( trace, check, clipModel, NULL, -move );
|
|
|
|
physics->SetOrigin( oldOrigin );
|
|
*/
|
|
if ( trace.fraction < 1.0f ) {
|
|
return PUSH_BLOCKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
SaveEntityPosition( check );
|
|
|
|
// translate the entity
|
|
physics->Translate( checkMove );
|
|
|
|
#ifdef TRANSLATIONAL_PUSH_DEBUG
|
|
// set the pusher in the translated position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() );
|
|
if ( physics->ClipContents( clipModel ) ) {
|
|
if ( !startsolid ) {
|
|
int bah = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return PUSH_OK;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::DiscardEntities
|
|
============
|
|
*/
|
|
int idPush::DiscardEntities( idEntity *entityList[], int numEntities, int flags, idEntity *pusher ) {
|
|
int i, num;
|
|
idEntity *check;
|
|
|
|
// remove all entities we cannot or should not push from the list
|
|
for ( num = i = 0; i < numEntities; i++ ) {
|
|
check = entityList[ i ];
|
|
|
|
// if the physics object is not pushable
|
|
if ( !check->GetPhysics()->IsPushable() ) {
|
|
continue;
|
|
}
|
|
|
|
// if the entity doesn't clip with this pusher
|
|
if ( !( check->GetPhysics()->GetClipMask() & pusher->GetPhysics()->GetContents() ) ) {
|
|
continue;
|
|
}
|
|
|
|
// don't push players in noclip mode
|
|
if ( check->IsType( idPlayer::Type ) && static_cast<idPlayer *>(check)->noclip ) {
|
|
continue;
|
|
}
|
|
|
|
// if we should only push idMoveable entities
|
|
if ( ( flags & PUSHFL_ONLYMOVEABLE ) && !check->IsType( idMoveable::Type ) ) {
|
|
continue;
|
|
}
|
|
|
|
// if we shouldn't push entities the clip model rests upon
|
|
if ( flags & PUSHFL_NOGROUNDENTITIES ) {
|
|
if ( pusher->GetPhysics()->IsGroundEntity( check->entityNumber ) ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// keep entity in list
|
|
entityList[ num++ ] = entityList[i];
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipTranslationalPush
|
|
|
|
Try to push other entities by moving the given entity.
|
|
============
|
|
*/
|
|
float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags,
|
|
const idVec3 &newOrigin, const idVec3 &translation ) {
|
|
int i, listedEntities, res;
|
|
idEntity *check, *entityList[ MAX_GENTITIES ];
|
|
idBounds bounds, pushBounds;
|
|
idVec3 clipMove, clipOrigin, oldOrigin, dir, impulse;
|
|
trace_t pushResults;
|
|
bool wasEnabled;
|
|
float totalMass;
|
|
idClipModel *clipModel;
|
|
|
|
clipModel = pusher->GetPhysics()->GetClipModel();
|
|
|
|
totalMass = 0.0f;
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = newOrigin;
|
|
results.endAxis = clipModel->GetAxis();
|
|
memset( &results.c, 0, sizeof( results.c ) );
|
|
|
|
if ( translation == vec3_origin ) {
|
|
return totalMass;
|
|
}
|
|
|
|
dir = translation;
|
|
dir.Normalize();
|
|
dir.z += 1.0f;
|
|
dir *= 10.0f;
|
|
|
|
// get bounds for the whole movement
|
|
bounds = clipModel->GetBounds();
|
|
if ( bounds[0].x >= bounds[1].x ) {
|
|
return totalMass;
|
|
}
|
|
pushBounds.FromBoundsTranslation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), translation );
|
|
|
|
wasEnabled = clipModel->IsEnabled();
|
|
|
|
// make sure we don't get the pushing clip model in the list
|
|
clipModel->Disable();
|
|
|
|
listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
|
|
|
|
// discard entities we cannot or should not push
|
|
listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher );
|
|
|
|
if ( flags & PUSHFL_CLIP ) {
|
|
|
|
// can only clip movement of a trace model
|
|
assert( clipModel->IsTraceModel() );
|
|
|
|
// disable to be pushed entities for collision detection
|
|
for ( i = 0; i < listedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->DisableClip();
|
|
}
|
|
|
|
gameLocal.clip.Translation( results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL );
|
|
|
|
// enable to be pushed entities for collision detection
|
|
for ( i = 0; i < listedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->EnableClip();
|
|
}
|
|
|
|
if ( results.fraction == 0.0f ) {
|
|
if ( wasEnabled ) {
|
|
clipModel->Enable();
|
|
}
|
|
return totalMass;
|
|
}
|
|
|
|
clipMove = results.endpos - clipModel->GetOrigin();
|
|
clipOrigin = results.endpos;
|
|
|
|
}
|
|
else {
|
|
|
|
clipMove = translation;
|
|
clipOrigin = newOrigin;
|
|
}
|
|
|
|
// we have to enable the clip model because we use it during pushing
|
|
clipModel->Enable();
|
|
|
|
// save pusher old position
|
|
oldOrigin = clipModel->GetOrigin();
|
|
|
|
// try to push the entities
|
|
for ( i = 0; i < listedEntities; i++ ) {
|
|
|
|
check = entityList[ i ];
|
|
|
|
idPhysics *physics = check->GetPhysics();
|
|
|
|
// disable the entity for collision detection
|
|
physics->DisableClip();
|
|
|
|
res = TryTranslatePushEntity( pushResults, check, clipModel, flags, clipOrigin, clipMove );
|
|
|
|
// enable the entity for collision detection
|
|
physics->EnableClip();
|
|
|
|
// if the entity is pushed
|
|
if ( res == PUSH_OK ) {
|
|
// set the pusher in the translated position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() );
|
|
// the entity might be pushed off the ground
|
|
physics->EvaluateContacts();
|
|
// put pusher back in old position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), oldOrigin, clipModel->GetAxis() );
|
|
|
|
// wake up this object
|
|
if ( flags & PUSHFL_APPLYIMPULSE ) {
|
|
impulse = physics->GetMass() * dir;
|
|
} else {
|
|
impulse.Zero();
|
|
}
|
|
check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), impulse );
|
|
|
|
// add mass of pushed entity
|
|
totalMass += physics->GetMass();
|
|
}
|
|
|
|
// if the entity is not blocking
|
|
if ( res != PUSH_BLOCKED ) {
|
|
continue;
|
|
}
|
|
|
|
// if the blocking entity is a projectile
|
|
if ( check->IsType( idProjectile::Type ) ) {
|
|
check->ProcessEvent( &EV_Explode );
|
|
continue;
|
|
}
|
|
|
|
// if blocking entities should be crushed
|
|
if ( flags & PUSHFL_CRUSH ) {
|
|
check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) );
|
|
continue;
|
|
}
|
|
|
|
// if the entity is an active articulated figure and gibs
|
|
if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) {
|
|
if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) {
|
|
check->ProcessEvent( &EV_Gib, "damage_Gib" );
|
|
}
|
|
}
|
|
|
|
// if the entity is a moveable item and gibs
|
|
if ( check->IsType( idMoveableItem::Type ) && check->spawnArgs.GetBool( "gib" ) ) {
|
|
check->ProcessEvent( &EV_Gib, "damage_Gib" );
|
|
}
|
|
|
|
// blocked
|
|
results = pushResults;
|
|
results.fraction = 0.0f;
|
|
results.endAxis = clipModel->GetAxis();
|
|
results.endpos = clipModel->GetOrigin();
|
|
results.c.entityNum = check->entityNumber;
|
|
results.c.id = 0;
|
|
|
|
if ( !wasEnabled ) {
|
|
clipModel->Disable();
|
|
}
|
|
|
|
return totalMass;
|
|
}
|
|
|
|
if ( !wasEnabled ) {
|
|
clipModel->Disable();
|
|
}
|
|
|
|
return totalMass;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPush::ClipRotationalPush
|
|
|
|
Try to push other entities by moving the given entity.
|
|
============
|
|
*/
|
|
float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags,
|
|
const idMat3 &newAxis, const idRotation &rotation ) {
|
|
int i, listedEntities, res;
|
|
idEntity *check, *entityList[ MAX_GENTITIES ];
|
|
idBounds bounds, pushBounds;
|
|
idRotation clipRotation;
|
|
idMat3 clipAxis, oldAxis;
|
|
trace_t pushResults;
|
|
bool wasEnabled;
|
|
float totalMass;
|
|
idClipModel *clipModel;
|
|
|
|
clipModel = pusher->GetPhysics()->GetClipModel();
|
|
|
|
totalMass = 0.0f;
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = clipModel->GetOrigin();
|
|
results.endAxis = newAxis;
|
|
memset( &results.c, 0, sizeof( results.c ) );
|
|
|
|
if ( !rotation.GetAngle() ) {
|
|
return totalMass;
|
|
}
|
|
|
|
// get bounds for the whole movement
|
|
bounds = clipModel->GetBounds();
|
|
if ( bounds[0].x >= bounds[1].x ) {
|
|
return totalMass;
|
|
}
|
|
pushBounds.FromBoundsRotation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), rotation );
|
|
|
|
wasEnabled = clipModel->IsEnabled();
|
|
|
|
// make sure we don't get the pushing clip model in the list
|
|
clipModel->Disable();
|
|
|
|
listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
|
|
|
|
// discard entities we cannot or should not push
|
|
listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher );
|
|
|
|
if ( flags & PUSHFL_CLIP ) {
|
|
|
|
// can only clip movement of a trace model
|
|
assert( clipModel->IsTraceModel() );
|
|
|
|
// disable to be pushed entities for collision detection
|
|
for ( i = 0; i < listedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->DisableClip();
|
|
}
|
|
|
|
gameLocal.clip.Rotation( results, clipModel->GetOrigin(), rotation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL );
|
|
|
|
// enable to be pushed entities for collision detection
|
|
for ( i = 0; i < listedEntities; i++ ) {
|
|
entityList[i]->GetPhysics()->EnableClip();
|
|
}
|
|
|
|
if ( results.fraction == 0.0f ) {
|
|
if ( wasEnabled ) {
|
|
clipModel->Enable();
|
|
}
|
|
return totalMass;
|
|
}
|
|
|
|
clipRotation = rotation * results.fraction;
|
|
clipAxis = results.endAxis;
|
|
}
|
|
else {
|
|
|
|
clipRotation = rotation;
|
|
clipAxis = newAxis;
|
|
}
|
|
|
|
// we have to enable the clip model because we use it during pushing
|
|
clipModel->Enable();
|
|
|
|
// save pusher old position
|
|
oldAxis = clipModel->GetAxis();
|
|
|
|
// try to push all the entities
|
|
for ( i = 0; i < listedEntities; i++ ) {
|
|
|
|
check = entityList[ i ];
|
|
|
|
idPhysics *physics = check->GetPhysics();
|
|
|
|
// disable the entity for collision detection
|
|
physics->DisableClip();
|
|
|
|
res = TryRotatePushEntity( pushResults, check, clipModel, flags, clipAxis, clipRotation );
|
|
|
|
// enable the entity for collision detection
|
|
physics->EnableClip();
|
|
|
|
// if the entity is pushed
|
|
if ( res == PUSH_OK ) {
|
|
// set the pusher in the rotated position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis );
|
|
// the entity might be pushed off the ground
|
|
physics->EvaluateContacts();
|
|
// put pusher back in old position
|
|
clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), oldAxis );
|
|
|
|
// wake up this object
|
|
check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), vec3_origin );
|
|
|
|
// add mass of pushed entity
|
|
totalMass += physics->GetMass();
|
|
}
|
|
|
|
// if the entity is not blocking
|
|
if ( res != PUSH_BLOCKED ) {
|
|
continue;
|
|
}
|
|
|
|
// if the blocking entity is a projectile
|
|
if ( check->IsType( idProjectile::Type ) ) {
|
|
check->ProcessEvent( &EV_Explode );
|
|
continue;
|
|
}
|
|
|
|
// if blocking entities should be crushed
|
|
if ( flags & PUSHFL_CRUSH ) {
|
|
check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) );
|
|
continue;
|
|
}
|
|
|
|
// if the entity is an active articulated figure and gibs
|
|
if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) {
|
|
if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) {
|
|
check->ProcessEvent( &EV_Gib, "damage_Gib" );
|
|
}
|
|
}
|
|
|
|
// blocked
|
|
results = pushResults;
|
|
results.fraction = 0.0f;
|
|
results.endAxis = clipModel->GetAxis();
|
|
results.endpos = clipModel->GetOrigin();
|
|
results.c.entityNum = check->entityNumber;
|
|
results.c.id = 0;
|
|
|
|
if ( !wasEnabled ) {
|
|
clipModel->Disable();
|
|
}
|
|
|
|
return totalMass;
|
|
}
|
|
|
|
if ( !wasEnabled ) {
|
|
clipModel->Disable();
|
|
}
|
|
|
|
return totalMass;
|
|
}
|
|
|
|
#endif /* !NEW_PUSH */
|
|
|
|
|
|
/*
|
|
============
|
|
idPush::ClipPush
|
|
|
|
Try to push other entities by moving the given entity.
|
|
============
|
|
*/
|
|
float idPush::ClipPush( trace_t &results, idEntity *pusher, const int flags,
|
|
const idVec3 &oldOrigin, const idMat3 &oldAxis,
|
|
idVec3 &newOrigin, idMat3 &newAxis ) {
|
|
idVec3 translation;
|
|
idRotation rotation;
|
|
float mass;
|
|
|
|
mass = 0.0f;
|
|
|
|
results.fraction = 1.0f;
|
|
results.endpos = newOrigin;
|
|
results.endAxis = newAxis;
|
|
memset( &results.c, 0, sizeof( results.c ) );
|
|
|
|
// translational push
|
|
translation = newOrigin - oldOrigin;
|
|
|
|
// if the pusher translates
|
|
if ( translation != vec3_origin ) {
|
|
|
|
mass += ClipTranslationalPush( results, pusher, flags, newOrigin, translation );
|
|
if ( results.fraction < 1.0f ) {
|
|
newOrigin = oldOrigin;
|
|
newAxis = oldAxis;
|
|
return mass;
|
|
}
|
|
} else {
|
|
newOrigin = oldOrigin;
|
|
}
|
|
|
|
// rotational push
|
|
rotation = ( oldAxis.Transpose() * newAxis ).ToRotation();
|
|
rotation.SetOrigin( newOrigin );
|
|
rotation.Normalize180();
|
|
rotation.ReCalculateMatrix(); // recalculate the rotation matrix to avoid accumulating rounding errors
|
|
|
|
// if the pusher rotates
|
|
if ( rotation.GetAngle() != 0.0f ) {
|
|
|
|
// recalculate new axis to avoid floating point rounding problems
|
|
newAxis = oldAxis * rotation.ToMat3();
|
|
newAxis.OrthoNormalizeSelf();
|
|
newAxis.FixDenormals();
|
|
newAxis.FixDegeneracies();
|
|
|
|
pusher->GetPhysics()->GetClipModel()->SetPosition( newOrigin, oldAxis );
|
|
|
|
mass += ClipRotationalPush( results, pusher, flags, newAxis, rotation );
|
|
if ( results.fraction < 1.0f ) {
|
|
newOrigin = oldOrigin;
|
|
newAxis = oldAxis;
|
|
return mass;
|
|
}
|
|
} else {
|
|
newAxis = oldAxis;
|
|
}
|
|
|
|
return mass;
|
|
}
|