summaryrefslogtreecommitdiff
path: root/game/server/portal/func_liquidportal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/portal/func_liquidportal.cpp')
-rw-r--r--game/server/portal/func_liquidportal.cpp344
1 files changed, 344 insertions, 0 deletions
diff --git a/game/server/portal/func_liquidportal.cpp b/game/server/portal/func_liquidportal.cpp
new file mode 100644
index 0000000..e99043d
--- /dev/null
+++ b/game/server/portal/func_liquidportal.cpp
@@ -0,0 +1,344 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rising liquid that acts as a one-way portal
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "cbase.h"
+#include "func_liquidportal.h"
+#include "portal_player.h"
+#include "isaverestore.h"
+#include "saverestore_utlvector.h"
+
+LINK_ENTITY_TO_CLASS( func_liquidportal, CFunc_LiquidPortal );
+
+BEGIN_DATADESC( CFunc_LiquidPortal )
+ DEFINE_FIELD( m_hLinkedPortal, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bFillInProgress, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_fFillStartTime, FIELD_TIME ),
+ DEFINE_FIELD( m_fFillEndTime, FIELD_TIME ),
+ DEFINE_FIELD( m_matrixThisToLinked, FIELD_VMATRIX ),
+ DEFINE_UTLVECTOR( m_hTeleportList, FIELD_EHANDLE ),
+ DEFINE_UTLVECTOR( m_hLeftToTeleportThisFill, FIELD_EHANDLE ),
+
+ DEFINE_KEYFIELD( m_strInitialLinkedPortal, FIELD_STRING, "InitialLinkedPortal" ),
+ DEFINE_KEYFIELD( m_fFillTime, FIELD_FLOAT, "FillTime" ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetLinkedLiquidPortal", InputSetLinkedLiquidPortal ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFillTime", InputSetFillTime ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "StartFilling", InputStartFilling ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "AddActivatorToTeleportList", InputAddActivatorToTeleportList ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "RemoveActivatorFromTeleportList", InputRemoveActivatorFromTeleportList ),
+
+ DEFINE_FUNCTION( CBaseEntity::Think ),
+END_DATADESC()
+
+
+IMPLEMENT_SERVERCLASS_ST( CFunc_LiquidPortal, DT_Func_LiquidPortal )
+ SendPropEHandle( SENDINFO(m_hLinkedPortal) ),
+ SendPropFloat( SENDINFO(m_fFillStartTime) ),
+ SendPropFloat( SENDINFO(m_fFillEndTime) ),
+END_SEND_TABLE()
+
+
+CFunc_LiquidPortal::CFunc_LiquidPortal( void )
+: m_bFillInProgress( false )
+{
+ m_matrixThisToLinked.Identity(); //Zero space is a bad place. No heroes to face, but we need 1's in this case.
+
+}
+
+void CFunc_LiquidPortal::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ SetSolid( SOLID_VPHYSICS );
+ SetSolidFlags( FSOLID_NOT_SOLID );
+ SetMoveType( MOVETYPE_NONE );
+ SetModel( STRING( GetModelName() ) );
+
+ CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, STRING(m_strInitialLinkedPortal) );
+ Assert( (pBaseEnt == NULL) || (dynamic_cast<CFunc_LiquidPortal *>(pBaseEnt) != NULL) );
+ SetLinkedLiquidPortal( (CFunc_LiquidPortal *)pBaseEnt );
+ SetThink( &CFunc_LiquidPortal::Think );
+}
+
+void CFunc_LiquidPortal::Activate( void )
+{
+ BaseClass::Activate();
+
+ SetSolid( SOLID_VPHYSICS );
+ SetSolidFlags( FSOLID_NOT_SOLID );
+ SetMoveType( MOVETYPE_NONE );
+ SetModel( STRING( GetModelName() ) );
+
+ ComputeLinkMatrix(); //collision origin may have changed during activation
+
+ SetThink( &CFunc_LiquidPortal::Think );
+
+ for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
+ {
+ CBaseEntity *pEnt = m_hLeftToTeleportThisFill[i].Get();
+
+ if( pEnt && pEnt->IsPlayer() )
+ {
+ ((CPortal_Player *)pEnt)->m_hSurroundingLiquidPortal = this;
+ }
+ }
+}
+
+int CFunc_LiquidPortal::Save( ISave &save )
+{
+ if( !BaseClass::Save( save ) )
+ return 0;
+
+ save.StartBlock( "LiquidPortal" );
+
+ short iTeleportListCount = m_hTeleportList.Count();
+ save.WriteShort( &iTeleportListCount );
+
+ if( iTeleportListCount != 0 )
+ save.WriteEHandle( m_hTeleportList.Base(), iTeleportListCount );
+
+ short iLeftToTeleportThisFillCount = m_hLeftToTeleportThisFill.Count();
+ save.WriteShort( &iLeftToTeleportThisFillCount );
+
+ if( iLeftToTeleportThisFillCount != 0 )
+ save.WriteEHandle( m_hLeftToTeleportThisFill.Base(), iLeftToTeleportThisFillCount );
+
+ save.EndBlock();
+
+ return 1;
+}
+
+int CFunc_LiquidPortal::Restore( IRestore &restore )
+{
+ m_hTeleportList.RemoveAll();
+ m_hLeftToTeleportThisFill.RemoveAll();
+
+ if( !BaseClass::Restore( restore ) )
+ return 0;
+
+ char szBlockName[SIZE_BLOCK_NAME_BUF];
+ restore.StartBlock( szBlockName );
+
+ if( !FStrEq( szBlockName, "LiquidPortal" ) ) //loading a save without liquid portal save data
+ return 1;
+
+ short iTeleportListCount;
+ restore.ReadShort( &iTeleportListCount );
+
+ if( iTeleportListCount != 0 )
+ {
+ m_hTeleportList.SetCount( iTeleportListCount );
+ restore.ReadEHandle( m_hTeleportList.Base(), iTeleportListCount );
+ }
+
+ short iLeftToTeleportThisFillCount;
+ restore.ReadShort( &iLeftToTeleportThisFillCount );
+
+ if( iLeftToTeleportThisFillCount != 0 )
+ {
+ m_hLeftToTeleportThisFill.SetCount( iLeftToTeleportThisFillCount );
+ restore.ReadEHandle( m_hLeftToTeleportThisFill.Base(), iLeftToTeleportThisFillCount );
+ }
+
+ restore.EndBlock();
+
+ return 1;
+}
+
+
+void CFunc_LiquidPortal::InputSetLinkedLiquidPortal( inputdata_t &inputdata )
+{
+ CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, inputdata.value.String() );
+ Assert( (pBaseEnt == NULL) || (dynamic_cast<CFunc_LiquidPortal *>(pBaseEnt) != NULL) );
+ SetLinkedLiquidPortal( (CFunc_LiquidPortal *)pBaseEnt );
+}
+
+void CFunc_LiquidPortal::InputSetFillTime( inputdata_t &inputdata )
+{
+ m_fFillTime = inputdata.value.Float();
+}
+
+void CFunc_LiquidPortal::InputStartFilling( inputdata_t &inputdata )
+{
+ AssertMsg( m_fFillEndTime <= gpGlobals->curtime, "Fill already in progress." );
+ m_fFillStartTime = gpGlobals->curtime;
+ m_fFillEndTime = gpGlobals->curtime + m_fFillTime;
+ m_bFillInProgress = true;
+
+ //reset the teleport list for this fill
+ m_hLeftToTeleportThisFill.RemoveAll();
+ m_hLeftToTeleportThisFill.AddVectorToTail( m_hTeleportList );
+
+ SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
+}
+
+void CFunc_LiquidPortal::InputAddActivatorToTeleportList( inputdata_t &inputdata )
+{
+ if( inputdata.pActivator == NULL )
+ return;
+
+ for( int i = m_hTeleportList.Count(); --i >= 0; )
+ {
+ if( m_hTeleportList[i].Get() == inputdata.pActivator )
+ return; //only have 1 reference of each entity
+ }
+
+ m_hTeleportList.AddToTail( inputdata.pActivator );
+ if( m_bFillInProgress )
+ m_hLeftToTeleportThisFill.AddToTail( inputdata.pActivator );
+
+ if( inputdata.pActivator->IsPlayer() )
+ ((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal = this;
+}
+
+void CFunc_LiquidPortal::InputRemoveActivatorFromTeleportList( inputdata_t &inputdata )
+{
+ if( inputdata.pActivator == NULL )
+ return;
+
+ for( int i = m_hTeleportList.Count(); --i >= 0; )
+ {
+ if( m_hTeleportList[i].Get() == inputdata.pActivator )
+ {
+ m_hTeleportList.FastRemove( i );
+
+ if( inputdata.pActivator->IsPlayer() && (((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal.Get() == this) )
+ ((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal = NULL;
+
+ if( m_bFillInProgress )
+ {
+ //remove from the list for this fill as well
+ for( int j = m_hLeftToTeleportThisFill.Count(); --j >= 0; )
+ {
+ if( m_hLeftToTeleportThisFill[j].Get() == inputdata.pActivator )
+ {
+ m_hLeftToTeleportThisFill.FastRemove( j );
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+}
+
+void CFunc_LiquidPortal::SetLinkedLiquidPortal( CFunc_LiquidPortal *pLinked )
+{
+ CFunc_LiquidPortal *pCurrentLinkedPortal = m_hLinkedPortal.Get();
+ if( pCurrentLinkedPortal == pLinked )
+ return;
+
+ if( pCurrentLinkedPortal != NULL )
+ {
+ m_hLinkedPortal = NULL;
+ pCurrentLinkedPortal->SetLinkedLiquidPortal( NULL );
+ }
+
+ m_hLinkedPortal = pLinked;
+ if( pLinked != NULL )
+ pLinked->SetLinkedLiquidPortal( this );
+
+ ComputeLinkMatrix();
+}
+
+void CFunc_LiquidPortal::ComputeLinkMatrix( void )
+{
+ CFunc_LiquidPortal *pLinkedPortal = m_hLinkedPortal.Get();
+ if( pLinkedPortal )
+ {
+ VMatrix matLocalToWorld, matLocalToWorldInv, matRemoteToWorld;
+
+ matLocalToWorld = EntityToWorldTransform();
+ matRemoteToWorld = pLinkedPortal->EntityToWorldTransform();
+
+ MatrixInverseTR( matLocalToWorld, matLocalToWorldInv );
+ m_matrixThisToLinked = matRemoteToWorld * matLocalToWorldInv;
+
+ MatrixInverseTR( m_matrixThisToLinked, pLinkedPortal->m_matrixThisToLinked );
+ }
+ else
+ {
+ m_matrixThisToLinked.Identity();
+ }
+}
+
+void CFunc_LiquidPortal::TeleportImmersedEntity( CBaseEntity *pEntity )
+{
+ if( pEntity == NULL )
+ return;
+
+ if( pEntity->IsPlayer() )
+ {
+ CPortal_Player *pEntityAsPlayer = (CPortal_Player *)pEntity;
+
+ Vector vNewOrigin = m_matrixThisToLinked * pEntity->GetAbsOrigin();
+ QAngle qNewAngles = TransformAnglesToWorldSpace( pEntityAsPlayer->EyeAngles(), m_matrixThisToLinked.As3x4() );
+ Vector vNewVelocity = m_matrixThisToLinked.ApplyRotation( pEntity->GetAbsVelocity() );
+
+ pEntity->Teleport( &vNewOrigin, &qNewAngles, &vNewVelocity );
+
+ pEntityAsPlayer->m_hSurroundingLiquidPortal = m_hLinkedPortal;
+ }
+ else
+ {
+ Vector vNewOrigin = m_matrixThisToLinked * pEntity->GetAbsOrigin();
+ QAngle qNewAngles = TransformAnglesToWorldSpace( pEntity->GetAbsAngles(), m_matrixThisToLinked.As3x4() );
+ Vector vNewVelocity = m_matrixThisToLinked.ApplyRotation( pEntity->GetAbsVelocity() );
+
+ pEntity->Teleport( &vNewOrigin, &qNewAngles, &vNewVelocity );
+ }
+}
+
+void CFunc_LiquidPortal::Think( void )
+{
+ if( m_bFillInProgress )
+ {
+ if( gpGlobals->curtime < m_fFillEndTime )
+ {
+ float fInterp = ((gpGlobals->curtime - m_fFillStartTime) / (m_fFillEndTime - m_fFillStartTime));
+ Vector vMins, vMaxs;
+ GetCollideable()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
+ vMaxs.z = vMins.z + ((vMaxs.z - vMins.z) * fInterp);
+
+ for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
+ {
+ CBaseEntity *pEntity = m_hLeftToTeleportThisFill[i].Get();
+ if( pEntity == NULL )
+ continue;
+
+ Vector vEntMins, vEntMaxs;
+ pEntity->GetCollideable()->WorldSpaceSurroundingBounds( &vEntMins, &vEntMaxs );
+
+ if( vEntMaxs.z <= vMaxs.z )
+ {
+ TeleportImmersedEntity( pEntity );
+ m_hLeftToTeleportThisFill.FastRemove( i );
+ }
+ }
+
+ SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
+ }
+ else
+ {
+ //teleport everything that's left in the list
+ for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
+ {
+ TeleportImmersedEntity( m_hLeftToTeleportThisFill[i].Get() );
+ }
+
+ m_hLeftToTeleportThisFill.RemoveAll();
+ m_bFillInProgress = false;
+ }
+ }
+}
+
+
+
+