diff options
Diffstat (limited to 'game/server/tf/tf_pushentity.cpp')
| -rw-r--r-- | game/server/tf/tf_pushentity.cpp | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/game/server/tf/tf_pushentity.cpp b/game/server/tf/tf_pushentity.cpp new file mode 100644 index 0000000..a4d0db2 --- /dev/null +++ b/game/server/tf/tf_pushentity.cpp @@ -0,0 +1,481 @@ +//========= 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 ); + } +} |