diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/trigger_portal.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/trigger_portal.cpp')
| -rw-r--r-- | mp/src/game/server/trigger_portal.cpp | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/mp/src/game/server/trigger_portal.cpp b/mp/src/game/server/trigger_portal.cpp new file mode 100644 index 00000000..5e7eb911 --- /dev/null +++ b/mp/src/game/server/trigger_portal.cpp @@ -0,0 +1,343 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Entity which teleports touched entities and reorients their physics
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "baseentity.h"
+#include "triggers.h"
+#include "modelentities.h"
+#include "saverestore_utlvector.h"
+#include "player_pickup.h"
+#include "vphysics/friction.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define TRIGGER_DISABLED_THINK "PortalDisabledThink"
+
+ConVar portal_debug( "portal_debug", "0", FCVAR_CHEAT, "Turn on debugging for portal connections." );
+
+//////////////////////////////////////////////////////////////////////////
+// CTriggerPortal
+// Moves touched entity to a target location, changing the model's orientation
+// to match the exit target. It differs from CTriggerTeleport in that it
+// reorients physics and has inputs to enable/disable its function.
+//////////////////////////////////////////////////////////////////////////
+class CTriggerPortal : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerPortal, CBaseTrigger );
+ DECLARE_SERVERCLASS();
+
+ virtual void Spawn( void );
+ virtual void Activate();
+
+ void Touch( CBaseEntity *pOther );
+ void EndTouch(CBaseEntity *pOther);
+ void DisableForIncomingEntity( CBaseEntity *pEntity );
+ bool IsTouchingPortal( CBaseEntity *pEntity );
+
+ void DisabledThink( void );
+
+ // TEMP: Since brushes have no directionality, give this wall a forward face specified in hammer
+ QAngle m_qFaceAngles;
+
+private:
+ string_t m_strRemotePortal;
+ CNetworkHandle( CTriggerPortal, m_hRemotePortal );
+ CUtlVector<EHANDLE> m_hDisabledForEntities;
+
+ // Input for setting remote portal entity (for teleporting to it)
+ void SetRemotePortal ( const char* strRemotePortalName );
+ void InputSetRemotePortal ( inputdata_t &inputdata );
+
+};
+
+LINK_ENTITY_TO_CLASS( trigger_portal, CTriggerPortal );
+
+BEGIN_DATADESC( CTriggerPortal )
+ DEFINE_KEYFIELD( m_strRemotePortal, FIELD_STRING, "RemotePortal" ),
+
+ DEFINE_FIELD( m_hRemotePortal, FIELD_EHANDLE ),
+ DEFINE_UTLVECTOR( m_hDisabledForEntities, FIELD_EHANDLE ),
+
+ // TEMP: Only keep this field while portals are still brushes
+ DEFINE_FIELD( m_qFaceAngles, FIELD_VECTOR ),
+
+ DEFINE_THINKFUNC( DisabledThink ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetRemotePortal", InputSetRemotePortal ),
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CTriggerPortal, DT_TriggerPortal )
+ SendPropEHandle(SENDINFO(m_hRemotePortal)),
+END_SEND_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTriggerPortal::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ InitTrigger();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void CTriggerPortal::Activate()
+{
+ BaseClass::Activate();
+
+ m_qFaceAngles = this->GetAbsAngles();
+
+ // keep the remote portal's pointer at activate time to avoid redundant FindEntity calls
+ if ( m_strRemotePortal != NULL_STRING )
+ {
+ SetRemotePortal( STRING(m_strRemotePortal) );
+ m_strRemotePortal = NULL_STRING;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void CTriggerPortal::InputSetRemotePortal(inputdata_t &inputdata )
+{
+ SetRemotePortal( inputdata.value.String() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : strRemotePortalName -
+//-----------------------------------------------------------------------------
+void CTriggerPortal::SetRemotePortal(const char *strRemotePortalName )
+{
+ m_hRemotePortal = dynamic_cast<CTriggerPortal*> (gEntList.FindEntityByName( NULL, strRemotePortalName, NULL, NULL, NULL ));
+ if ( m_hRemotePortal == NULL )
+ {
+ Warning ( "trigger_portal: Cannot find remote portal entity named %s\n", strRemotePortalName );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CTriggerPortal::EndTouch(CBaseEntity *pOther)
+{
+ BaseClass::EndTouch(pOther);
+
+ if ( portal_debug.GetBool() )
+ {
+ Msg("%s ENDTOUCH: for %s\n", GetDebugName(), pOther->GetDebugName() );
+ }
+
+ EHANDLE hHandle;
+ hHandle = pOther;
+ m_hDisabledForEntities.FindAndRemove( hHandle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Upon touching a non-filtered entity, CTriggerPortal teleports them to it's
+// remote portal location.
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CTriggerPortal::Touch( CBaseEntity *pOther )
+{
+ // If we are enabled, and allowed to react to the touched entity
+ if ( PassesTriggerFilters(pOther) )
+ {
+ // If we somehow lost our pointer to the remote portal, get a new one
+ if ( m_hRemotePortal == NULL )
+ {
+ Disable();
+ return;
+ }
+
+ bool bDebug = portal_debug.GetBool();
+ if ( bDebug )
+ {
+ Msg("%s TOUCH: for %s\n", GetDebugName(), pOther->GetDebugName() );
+ }
+
+ // Don't touch entities that came through us and haven't left us yet.
+ EHANDLE hHandle;
+ hHandle = pOther;
+ if ( m_hDisabledForEntities.Find(hHandle) != m_hDisabledForEntities.InvalidIndex() )
+ {
+ Msg(" IGNORED\n", GetDebugName(), pOther->GetDebugName() );
+ return;
+ }
+
+ Pickup_ForcePlayerToDropThisObject( pOther );
+
+ // de-ground this entity
+ pOther->SetGroundEntity( NULL );
+
+ // Build a this --> remote transformation
+ VMatrix matMyModelToWorld, matMyInverse;
+ matMyModelToWorld = this->EntityToWorldTransform();
+ MatrixInverseGeneral ( matMyModelToWorld, matMyInverse );
+
+ // Teleport our object
+ VMatrix matRemotePortalTransform = m_hRemotePortal->EntityToWorldTransform();
+ Vector ptNewOrigin, vLook, vRight, vUp, vNewLook;
+ pOther->GetVectors( &vLook, &vRight, &vUp );
+
+ // Move origin
+ ptNewOrigin = matMyInverse * pOther->GetAbsOrigin();
+ ptNewOrigin = matRemotePortalTransform * Vector( ptNewOrigin.x, -ptNewOrigin.y, ptNewOrigin.z );
+
+ // Re-aim camera
+ vNewLook = matMyInverse.ApplyRotation( vLook );
+ vNewLook = matRemotePortalTransform.ApplyRotation( Vector( -vNewLook.x, -vNewLook.y, vNewLook.z ) );
+
+ // Reorient the physics
+ Vector vVelocity, vOldVelocity;
+ pOther->GetVelocity( &vOldVelocity );
+ vVelocity = matMyInverse.ApplyRotation( vOldVelocity );
+ vVelocity = matRemotePortalTransform.ApplyRotation( Vector( -vVelocity.x, -vVelocity.y, vVelocity.z ) );
+
+ QAngle qNewAngles;
+ VectorAngles( vNewLook, qNewAngles );
+
+ if ( pOther->IsPlayer() )
+ {
+ ((CBasePlayer*)pOther)->SnapEyeAngles(qNewAngles);
+ }
+
+ Vector vecOldPos = pOther->WorldSpaceCenter();
+ if ( bDebug )
+ {
+ NDebugOverlay::Box( pOther->GetAbsOrigin(), pOther->WorldAlignMins(), pOther->WorldAlignMaxs(), 255,0,0, 8, 20 );
+ NDebugOverlay::Axis( pOther->GetAbsOrigin(), pOther->GetAbsAngles(), 10.0f, true, 50 );
+ }
+
+ // place player at the new destination
+ CTriggerPortal *pPortal = m_hRemotePortal.Get();
+ pPortal->DisableForIncomingEntity( pOther );
+ pOther->Teleport( &ptNewOrigin, &qNewAngles, &vVelocity );
+
+ if ( bDebug )
+ {
+ NDebugOverlay::Box( pOther->GetAbsOrigin(), pOther->WorldAlignMins(), pOther->WorldAlignMaxs(), 0,255,0, 8, 20 );
+ NDebugOverlay::Line( vecOldPos, pOther->WorldSpaceCenter(), 0,255,0, true, 20 );
+ NDebugOverlay::Axis( pOther->GetAbsOrigin(), pOther->GetAbsAngles(), 10.0f, true, 50 );
+
+ Msg("%s TELEPORTED: %s\n", GetDebugName(), pOther->GetDebugName() );
+ }
+
+ // test collision on the new teleport location
+ Vector vMin, vMax, vCenter;
+ pOther->CollisionProp()->WorldSpaceAABB( &vMin, &vMax );
+ vCenter = (vMin + vMax) * 0.5f;
+ vMin -= vCenter;
+ vMax -= vCenter;
+
+ Vector vStart, vEnd;
+ vStart = ptNewOrigin;
+ vEnd = ptNewOrigin;
+
+ Ray_t ray;
+ ray.Init( vStart, vEnd, vMin, vMax );
+ trace_t tr;
+ pPortal->TestCollision( ray, pOther->PhysicsSolidMaskForEntity(), tr );
+
+ // Teleportation caused us to hit something, deal with it.
+ if ( tr.DidHit() )
+ {
+
+ }
+
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTriggerPortal::DisableForIncomingEntity( CBaseEntity *pEntity )
+{
+ EHANDLE hHandle;
+ hHandle = pEntity;
+ Assert( m_hDisabledForEntities.Find(hHandle) == m_hDisabledForEntities.InvalidIndex() );
+ m_hDisabledForEntities.AddToTail( hHandle );
+
+ // Start thinking, and remove the other as soon as it's not touching me.
+ // Needs to be done in addition to EndTouch, because entities may move fast
+ // enough through the portal to come out not touching the other portal.
+ SetContextThink( DisabledThink, gpGlobals->curtime + 0.1, TRIGGER_DISABLED_THINK );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTriggerPortal::DisabledThink( void )
+{
+ // If we've got no disabled entities left, we're done
+ if ( !m_hDisabledForEntities.Count() )
+ {
+ SetContextThink( NULL, gpGlobals->curtime, TRIGGER_DISABLED_THINK );
+ return;
+ }
+
+ for ( int i = m_hDisabledForEntities.Count()-1; i >= 0; i-- )
+ {
+ CBaseEntity *pEntity = m_hDisabledForEntities[i];
+ if ( !pEntity || !IsTouchingPortal(pEntity) )
+ {
+ m_hDisabledForEntities.Remove(i);
+ }
+ }
+
+ SetContextThink( DisabledThink, gpGlobals->curtime + 0.1, TRIGGER_DISABLED_THINK );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTriggerPortal::IsTouchingPortal( CBaseEntity *pEntity )
+{
+ // First, check the touchlinks. This will find non-vphysics entities touching us
+ touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+ for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
+ {
+ CBaseEntity *pTouch = link->entityTouched;
+ if ( pTouch == pEntity )
+ return true;
+ }
+ }
+
+ // Then check the friction snapshot. This will find vphysics objects touching us.
+ IPhysicsObject *pPhysics = VPhysicsGetObject();
+ if ( !pPhysics )
+ return false;
+
+ IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
+ bool bFound = false;
+ while ( pSnapshot->IsValid() )
+ {
+ IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
+ if ( ((CBaseEntity *)pOther->GetGameData()) == pEntity )
+ {
+ bFound = true;
+ break;
+ }
+
+ pSnapshot->NextFrictionData();
+ }
+ pPhysics->DestroyFrictionSnapshot( pSnapshot );
+
+ return bFound;
+}
\ No newline at end of file |