summaryrefslogtreecommitdiff
path: root/game/server/tf/tf_pushentity.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/tf_pushentity.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/tf/tf_pushentity.cpp')
-rw-r--r--game/server/tf/tf_pushentity.cpp481
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 );
+ }
+}