You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
16 KiB
481 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "pushentity.h" |
|
#include "tf_player.h" |
|
#include "collisionutils.h" |
|
#include "tf_gamerules.h" |
|
#include "func_respawnroom.h" |
|
//#include "mathlib/mathlib.h" |
|
|
|
class CTFPhysicsPushEntities : public CPhysicsPushedEntities |
|
{ |
|
public: |
|
|
|
DECLARE_CLASS( CTFPhysicsPushEntities, CPhysicsPushedEntities ); |
|
|
|
// Constructor/Destructor. |
|
CTFPhysicsPushEntities(); |
|
~CTFPhysicsPushEntities(); |
|
|
|
protected: |
|
|
|
// Speculatively checks to see if all entities in this list can be pushed |
|
virtual bool SpeculativelyCheckRotPush( const RotatingPushMove_t &rotPushMove, CBaseEntity *pRoot ); |
|
virtual bool SpeculativelyCheckLinearPush( const Vector &vecAbsPush ); |
|
virtual void FinishRotPushedEntity( CBaseEntity *pPushedEntity, const RotatingPushMove_t &rotPushMove ); |
|
|
|
private: |
|
|
|
bool RotationPushTFPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, const RotatingPushMove_t &rotPushMove, bool bRotationalPush ); |
|
bool RotationCheckPush( PhysicsPushedInfo_t &info ); |
|
bool LinearPushTFPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, bool bRotationalPush ); |
|
bool LinearCheckPush( PhysicsPushedInfo_t &info ); |
|
|
|
bool IsPlayerAABBIntersetingPusherOBB( CBaseEntity *pEntity, CBaseEntity *pRootEntity ); |
|
|
|
void MovePlayer( CBaseEntity *pBlocker, PhysicsPushedInfo_t &info, float flMoveScale, bool bPusherIsTrain ); |
|
void FindNewPushDirection( Vector &vecCurrent, Vector &vecNormal, Vector &vecOutput ); |
|
|
|
float m_flPushDist; |
|
Vector m_vecPushVector; |
|
}; |
|
|
|
CTFPhysicsPushEntities s_TFPushedEntities; |
|
CPhysicsPushedEntities *g_pPushedEntities = &s_TFPushedEntities; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. |
|
//----------------------------------------------------------------------------- |
|
CTFPhysicsPushEntities::CTFPhysicsPushEntities() |
|
{ |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. |
|
//----------------------------------------------------------------------------- |
|
CTFPhysicsPushEntities::~CTFPhysicsPushEntities() |
|
{ |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::SpeculativelyCheckRotPush( const RotatingPushMove_t &rotPushMove, CBaseEntity *pRoot ) |
|
{ |
|
// Only do this for "payload" or "escort" maps. |
|
if ( !( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT ) ) |
|
return BaseClass::SpeculativelyCheckRotPush( rotPushMove, pRoot ); |
|
|
|
Vector vecAbsPush( 0.0f, 0.0f, 0.0f ); |
|
m_nBlocker = -1; |
|
int nMovedCount = m_rgMoved.Count(); |
|
for ( int i = ( nMovedCount - 1 ); i >= 0; --i ) |
|
{ |
|
// Is the entity and TF Player? |
|
CTFPlayer *pTFPlayer = NULL; |
|
if ( m_rgMoved[i].m_pEntity && m_rgMoved[i].m_pEntity->IsPlayer() ) |
|
{ |
|
pTFPlayer = ToTFPlayer( m_rgMoved[i].m_pEntity ); |
|
} |
|
|
|
// Special code to move the player away from the func_train. |
|
if ( pTFPlayer ) |
|
{ |
|
// Rotationally push the player! |
|
RotationPushTFPlayer( m_rgMoved[i], vecAbsPush, rotPushMove, true ); |
|
} |
|
else |
|
{ |
|
ComputeRotationalPushDirection( m_rgMoved[i].m_pEntity, rotPushMove, &vecAbsPush, pRoot ); |
|
if (!SpeculativelyCheckPush( m_rgMoved[i], vecAbsPush, true )) |
|
{ |
|
m_nBlocker = i; |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Speculatively checks to see if all entities in this list can be pushed |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::SpeculativelyCheckLinearPush( const Vector &vecAbsPush ) |
|
{ |
|
// Only do this for "payload" or "escort" maps. |
|
if ( !( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT ) ) |
|
return BaseClass::SpeculativelyCheckLinearPush( vecAbsPush ); |
|
|
|
m_nBlocker = -1; |
|
int nMovedCount = m_rgMoved.Count(); |
|
for ( int i = ( nMovedCount - 1 ); i >= 0; --i ) |
|
{ |
|
// Is the entity and TF Player? |
|
CTFPlayer *pTFPlayer = NULL; |
|
if ( m_rgMoved[i].m_pEntity && m_rgMoved[i].m_pEntity->IsPlayer() ) |
|
{ |
|
pTFPlayer = ToTFPlayer( m_rgMoved[i].m_pEntity ); |
|
} |
|
|
|
// Special code to move the player away from the func_train. |
|
if ( pTFPlayer ) |
|
{ |
|
// Linearly push the player! |
|
LinearPushTFPlayer( m_rgMoved[i], vecAbsPush, false ); |
|
} |
|
else |
|
{ |
|
if (!SpeculativelyCheckPush( m_rgMoved[i], vecAbsPush, false )) |
|
{ |
|
m_nBlocker = i; |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::RotationPushTFPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, const RotatingPushMove_t &rotPushMove, bool bRotationalPush ) |
|
{ |
|
// Clear out the collision entity so that if we early out we don't send bogus collision data to the physics system. |
|
info.m_Trace.m_pEnt = NULL; |
|
|
|
// Look into doing a full engine->CM_Clear( trace) |
|
|
|
// Get the player. |
|
CTFPlayer *pPlayer = ToTFPlayer( info.m_pEntity ); |
|
if ( !pPlayer ) |
|
return false; |
|
|
|
info.m_vecStartAbsOrigin = pPlayer->GetAbsOrigin(); |
|
|
|
// Get the player collision data. |
|
CCollisionProperty *pCollisionPlayer = info.m_pEntity->CollisionProp(); |
|
if ( !pCollisionPlayer ) |
|
return false; |
|
|
|
// Find the root object if in hierarchy. |
|
CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent(); |
|
if ( !pRootEntity ) |
|
return false; |
|
|
|
// Get the pusher collision data. |
|
CCollisionProperty *pCollisionPusher = pRootEntity->CollisionProp(); |
|
if ( !pCollisionPusher ) |
|
return false; |
|
|
|
// Do we have a collision. |
|
if ( !IsOBBIntersectingOBB( pCollisionPlayer->GetCollisionOrigin(), pCollisionPlayer->GetCollisionAngles(), pCollisionPlayer->OBBMins(), pCollisionPlayer->OBBMaxs(), |
|
pCollisionPusher->GetCollisionOrigin(), pCollisionPusher->GetCollisionAngles(), pCollisionPusher->OBBMins(), pCollisionPusher->OBBMaxs(), |
|
0.0f ) ) |
|
return false; |
|
|
|
// For speed use spheres to approximate push distance. |
|
Vector vecPlayerOrigin = pCollisionPlayer->GetCollisionOrigin(); |
|
float flPlayerRadius = pCollisionPlayer->BoundingRadius(); |
|
|
|
Vector vecPusherOrigin = pCollisionPusher->GetCollisionOrigin(); |
|
float flPusherRadius = pCollisionPusher->BoundingRadius(); |
|
|
|
Vector vecDeltaOrigin; |
|
VectorSubtract( vecPlayerOrigin, vecPusherOrigin, vecDeltaOrigin ); |
|
|
|
float flRadiusTotal = flPlayerRadius + flPusherRadius; |
|
float flLength = vecDeltaOrigin.Length(); |
|
float flDistanceDelta = fabs( flRadiusTotal - flLength ); |
|
|
|
// Put special code in if we are riding the pusher - only push upward. |
|
if ( pPlayer->GetGroundEntity() == pRootEntity ) |
|
{ |
|
// Set the push direction and distance. |
|
m_vecPushVector.Init( 0.0f, 0.0f, 1.0f ); |
|
if ( rotPushMove.amove[0] != 0.0f ) |
|
{ |
|
m_flPushDist = fabs( tan( DEG2RAD( rotPushMove.amove[0] ) ) * flPusherRadius ); |
|
float flPushAdd = m_flPushDist * 0.1f; |
|
m_flPushDist += flPushAdd; |
|
} |
|
else |
|
{ |
|
m_flPushDist = 0.0f; |
|
} |
|
} |
|
else |
|
{ |
|
// Set the push direction and distance. |
|
m_vecPushVector = vecDeltaOrigin; |
|
m_vecPushVector.NormalizeInPlace(); |
|
m_flPushDist = flDistanceDelta; |
|
float flPushAdd = m_flPushDist * 0.1f; |
|
m_flPushDist += flPushAdd; |
|
} |
|
|
|
return RotationCheckPush( info ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::RotationCheckPush( PhysicsPushedInfo_t &info ) |
|
{ |
|
// Get the blocking and pushing entities. |
|
CBaseEntity *pBlocker = info.m_pEntity; |
|
CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent(); |
|
if ( !pBlocker || !pRootEntity ) |
|
return true; |
|
|
|
int *pPusherHandles = ( int* )stackalloc( m_rgPusher.Count() * sizeof( int ) ); |
|
UnlinkPusherList( pPusherHandles ); |
|
for ( int iPushTry = 0; iPushTry < 3; ++iPushTry ) |
|
{ |
|
MovePlayer( pBlocker, info, 0.35f, pRootEntity->IsBaseTrain() ); |
|
if ( !IsPlayerAABBIntersetingPusherOBB( pBlocker, pRootEntity ) ) |
|
break; |
|
} |
|
RelinkPusherList( pPusherHandles ); |
|
|
|
// Is the blocked ground the push entity? |
|
info.m_bPusherIsGround = false; |
|
if ( pBlocker->GetGroundEntity() && pBlocker->GetGroundEntity()->GetRootMoveParent() == m_rgPusher[0].m_pEntity ) |
|
{ |
|
info.m_bPusherIsGround = true; |
|
} |
|
|
|
// Check to see if the player is in a good spot and attempt a move again if not - but only if it isn't being ridden on. |
|
if ( IsPlayerAABBIntersetingPusherOBB( pBlocker, pRootEntity ) ) |
|
{ |
|
// Try again is the player is still blocked. |
|
// DevMsg( 1, "Pushing rotation hard!\n" ); |
|
UnlinkPusherList( pPusherHandles ); |
|
MovePlayer( pBlocker, info, 1.0f, pRootEntity->IsBaseTrain() ); |
|
RelinkPusherList( pPusherHandles ); |
|
} |
|
|
|
// The player will never stop a train from moving in TF. |
|
info.m_bBlocked = false; |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::LinearPushTFPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, bool bRotationalPush ) |
|
{ |
|
// Clear out the collision entity so that if we early out we don't send bogus collision data to the physics system. |
|
info.m_Trace.m_pEnt = NULL; |
|
|
|
// Get the player. |
|
CTFPlayer *pPlayer = ToTFPlayer( info.m_pEntity ); |
|
if ( !pPlayer ) |
|
return false; |
|
|
|
info.m_vecStartAbsOrigin = pPlayer->GetAbsOrigin(); |
|
|
|
// Get the player collision data. |
|
CCollisionProperty *pCollisionPlayer = info.m_pEntity->CollisionProp(); |
|
if ( !pCollisionPlayer ) |
|
return false; |
|
|
|
// Find the root object if in hierarchy. |
|
CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent(); |
|
if ( !pRootEntity ) |
|
return false; |
|
|
|
// Get the pusher collision data. |
|
CCollisionProperty *pCollisionPusher = pRootEntity->CollisionProp(); |
|
if ( !pCollisionPusher ) |
|
return false; |
|
|
|
// Do we have a collision. |
|
if ( !IsOBBIntersectingOBB( pCollisionPlayer->GetCollisionOrigin(), pCollisionPlayer->GetCollisionAngles(), pCollisionPlayer->OBBMins(), pCollisionPlayer->OBBMaxs(), |
|
pCollisionPusher->GetCollisionOrigin(), pCollisionPusher->GetCollisionAngles(), pCollisionPusher->OBBMins(), pCollisionPusher->OBBMaxs(), |
|
0.0f ) ) |
|
return false; |
|
|
|
if ( pPlayer->GetGroundEntity() == pRootEntity ) |
|
{ |
|
m_vecPushVector = vecAbsPush; |
|
m_flPushDist = VectorNormalize( m_vecPushVector ); |
|
} |
|
else |
|
{ |
|
m_vecPushVector = vecAbsPush; |
|
m_flPushDist = VectorNormalize( m_vecPushVector ); |
|
m_vecPushVector.z = 0.0f; |
|
VectorNormalize( m_vecPushVector ); |
|
} |
|
|
|
return LinearCheckPush( info ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::LinearCheckPush( PhysicsPushedInfo_t &info ) |
|
{ |
|
// Get the blocking and pushing entities. |
|
CBaseEntity *pBlocker = info.m_pEntity; |
|
CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent(); |
|
if ( !pBlocker || !pRootEntity ) |
|
return true; |
|
|
|
// Unlink the pusher from the spatial partition and attempt a player move. |
|
int *pPusherHandles = ( int* )stackalloc( m_rgPusher.Count() * sizeof( int ) ); |
|
UnlinkPusherList( pPusherHandles ); |
|
MovePlayer( pBlocker, info, 1.0f, pRootEntity->IsBaseTrain() ); |
|
RelinkPusherList( pPusherHandles ); |
|
|
|
// Is the pusher the ground entity the blocker is standing on? |
|
info.m_bPusherIsGround = false; |
|
if ( pBlocker->GetGroundEntity() && pBlocker->GetGroundEntity()->GetRootMoveParent() == m_rgPusher[0].m_pEntity ) |
|
{ |
|
info.m_bPusherIsGround = true; |
|
} |
|
|
|
// Check to see if the player is in a good spot and attempt a move again if not - but only if it isn't being ridden on. |
|
if ( !info.m_bPusherIsGround && !IsPushedPositionValid( pBlocker ) ) |
|
{ |
|
// Try again is the player is still blocked. |
|
// DevMsg( 1, "Pushing linear hard!\n" ); |
|
UnlinkPusherList( pPusherHandles ); |
|
MovePlayer( pBlocker, info, 1.0f, pRootEntity->IsBaseTrain() ); |
|
RelinkPusherList( pPusherHandles ); |
|
} |
|
|
|
// The player will never stop a train from moving in TF. |
|
info.m_bBlocked = false; |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFPhysicsPushEntities::IsPlayerAABBIntersetingPusherOBB( CBaseEntity *pEntity, CBaseEntity *pRootEntity ) |
|
{ |
|
// Get the player. |
|
CTFPlayer *pPlayer = ToTFPlayer( pEntity ); |
|
if ( !pPlayer ) |
|
return false; |
|
|
|
// Get the player collision data. |
|
CCollisionProperty *pCollisionPlayer = pEntity->CollisionProp(); |
|
if ( !pCollisionPlayer ) |
|
return false; |
|
|
|
// Get the pusher collision data. |
|
CCollisionProperty *pCollisionPusher = pRootEntity->CollisionProp(); |
|
if ( !pCollisionPusher ) |
|
return false; |
|
|
|
// Do we have a collision. |
|
return IsOBBIntersectingOBB( pCollisionPlayer->GetCollisionOrigin(), pCollisionPlayer->GetCollisionAngles(), pCollisionPlayer->OBBMins(), pCollisionPlayer->OBBMaxs(), |
|
pCollisionPusher->GetCollisionOrigin(), pCollisionPusher->GetCollisionAngles(), pCollisionPusher->OBBMins(), pCollisionPusher->OBBMaxs(), |
|
0.0f ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFPhysicsPushEntities::FindNewPushDirection( Vector &vecCurrent, Vector &vecNormal, Vector &vecOutput ) |
|
{ |
|
// Determine how far along plane to slide based on incoming direction. |
|
float flBackOff = DotProduct( vecCurrent, vecNormal ); |
|
|
|
for ( int iAxis = 0; iAxis < 3; ++iAxis ) |
|
{ |
|
float flDelta = vecNormal[iAxis] * flBackOff; |
|
vecOutput[iAxis] = vecCurrent[iAxis] - flDelta; |
|
} |
|
|
|
// iterate once to make sure we aren't still moving through the plane |
|
float flAdjust = DotProduct( vecOutput, vecNormal ); |
|
if( flAdjust < 0.0f ) |
|
{ |
|
vecOutput -= ( vecNormal * flAdjust ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFPhysicsPushEntities::MovePlayer( CBaseEntity *pBlocker, PhysicsPushedInfo_t &info, float flMoveScale, bool bPusherIsTrain ) |
|
{ |
|
// Find out how far we still need to move. |
|
float flFractionLeft = 1.0f; |
|
float flNewDist = m_flPushDist *flMoveScale; |
|
Vector vecPush = m_vecPushVector; |
|
|
|
// Find a new push vector. |
|
Vector vecStart = pBlocker->GetAbsOrigin(); |
|
vecStart.z += 4.0f; |
|
for ( int iTest = 0; iTest < 4; ++iTest ) |
|
{ |
|
// Clear the trace entity. |
|
Vector vecEnd = pBlocker->GetAbsOrigin() + ( flNewDist * vecPush ); |
|
UTIL_TraceEntity( pBlocker, vecStart, vecEnd, MASK_PLAYERSOLID, NULL, COLLISION_GROUP_PLAYER_MOVEMENT, &info.m_Trace ); |
|
|
|
// we don't want trains pushing enemy players through a respawn room visualizer |
|
if ( bPusherIsTrain && pBlocker->IsPlayer() ) |
|
{ |
|
if ( PointsCrossRespawnRoomVisualizer( vecStart, info.m_Trace.endpos, pBlocker->GetTeamNumber() ) ) |
|
{ |
|
CTFPlayer *pTFPlayer = ToTFPlayer( pBlocker ); |
|
if ( pTFPlayer ) |
|
{ |
|
pTFPlayer->CommitSuicide( true, true ); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
if ( info.m_Trace.fraction > 0.0f ) |
|
{ |
|
pBlocker->SetAbsOrigin( info.m_Trace.endpos ); |
|
} |
|
|
|
if ( info.m_Trace.fraction == 1.0f || !info.m_Trace.m_pEnt ) |
|
break; |
|
|
|
// New test distance and position. |
|
flFractionLeft = 1.0f - info.m_Trace.fraction; |
|
flNewDist = flFractionLeft * flNewDist; |
|
flNewDist = flNewDist * ( 1.0f + ( 1.0f - fabs( info.m_Trace.plane.normal.Dot( vecPush ) ) ) ); |
|
|
|
// Find the new push direction. |
|
Vector vecTmp; |
|
FindNewPushDirection( vecPush, info.m_Trace.plane.normal, vecTmp ); |
|
VectorCopy( vecTmp, vecPush ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Causes all entities in the list to touch triggers from their prev position |
|
//----------------------------------------------------------------------------- |
|
void CTFPhysicsPushEntities::FinishRotPushedEntity( CBaseEntity *pPushedEntity, const RotatingPushMove_t &rotPushMove ) |
|
{ |
|
// Only do this for "payload" or "escort" maps. |
|
if ( !( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT ) ) |
|
return BaseClass::FinishRotPushedEntity( pPushedEntity, rotPushMove ); |
|
|
|
if ( !pPushedEntity->IsPlayer() ) |
|
{ |
|
QAngle angles = pPushedEntity->GetAbsAngles(); |
|
|
|
// only rotate YAW with pushing. Freely rotateable entities should either use VPHYSICS |
|
// or be set up as children |
|
angles.y += rotPushMove.amove.y; |
|
pPushedEntity->SetAbsAngles( angles ); |
|
} |
|
}
|
|
|