summaryrefslogtreecommitdiff
path: root/game/client/tf/c_obj_teleporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/c_obj_teleporter.cpp')
-rw-r--r--game/client/tf/c_obj_teleporter.cpp465
1 files changed, 465 insertions, 0 deletions
diff --git a/game/client/tf/c_obj_teleporter.cpp b/game/client/tf/c_obj_teleporter.cpp
new file mode 100644
index 0000000..629ebcb
--- /dev/null
+++ b/game/client/tf/c_obj_teleporter.cpp
@@ -0,0 +1,465 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client's CObjectTeleporter
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "c_baseobject.h"
+#include "c_tf_player.h"
+#include "vgui/ILocalize.h"
+#include "c_obj_teleporter.h"
+#include "soundenvelope.h"
+#include "vgui/ILocalize.h"
+#include "tf_gamerules.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+#define TELEPORTER_MINS Vector( -24, -24, 0)
+#define TELEPORTER_MAXS Vector( 24, 24, 12)
+
+//-----------------------------------------------------------------------------
+// Purpose: Teleporter object
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CLIENTCLASS_DT(C_ObjectTeleporter, DT_ObjectTeleporter, CObjectTeleporter)
+ RecvPropInt( RECVINFO(m_iState) ),
+ RecvPropTime( RECVINFO(m_flRechargeTime) ),
+ RecvPropTime( RECVINFO(m_flCurrentRechargeDuration) ),
+ RecvPropInt( RECVINFO(m_iTimesUsed) ),
+ RecvPropFloat( RECVINFO(m_flYawToExit) ),
+ RecvPropBool( RECVINFO(m_bMatchBuilding) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_ObjectTeleporter::C_ObjectTeleporter()
+{
+ m_hChargedEffect = NULL;
+ m_hDirectionEffect = NULL;
+ m_hChargedLeftArmEffect = NULL;
+ m_hChargedRightArmEffect = NULL;
+
+ m_iDirectionArrowPoseParam = 0;
+
+ m_pSpinSound = NULL;
+
+ m_bMatchBuilding = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::UpdateOnRemove( void )
+{
+ StopActiveEffects();
+ StopChargedEffects();
+
+ if ( m_pSpinSound )
+ {
+ CSoundEnvelopeController::GetController().SoundDestroy( m_pSpinSound );
+ }
+
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnPreDataChanged( updateType );
+
+ m_iOldState = m_iState;
+ m_bOldMatchBuilding = m_bMatchBuilding;
+}
+
+void C_ObjectTeleporter::StartBuildingEffects()
+{
+ StopBuildingEffects();
+ char szEffect[128];
+
+ // arm glow effects
+ Q_snprintf( szEffect, sizeof(szEffect), "teleporter_arms_circle_%s_blink", ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue" );
+
+ Assert( m_hBuildingLeftArmEffect.m_pObject == NULL );
+ m_hBuildingLeftArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 1 );
+
+ Assert( m_hBuildingRightArmEffect.m_pObject == NULL );
+ m_hBuildingRightArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 3 );
+}
+
+void C_ObjectTeleporter::StartChargedEffects()
+{
+ StopChargedEffects();
+ char szEffect[128];
+
+ Q_snprintf( szEffect, sizeof(szEffect), "teleporter_%s_charged_level%d",
+ ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue", GetUpgradeLevel() );
+
+ Assert( m_hChargedEffect.m_pObject == NULL );
+ m_hChargedEffect = ParticleProp()->Create( szEffect, PATTACH_ABSORIGIN );
+}
+
+void C_ObjectTeleporter::StartActiveEffects()
+{
+ StopActiveEffects();
+ char szEffect[128];
+
+ Q_snprintf( szEffect, sizeof(szEffect), "teleporter_%s_%s_level%d",
+ ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue",
+ GetObjectMode() == MODE_TELEPORTER_ENTRANCE ? "entrance" : "exit",
+ GetUpgradeLevel() );
+
+ Assert( m_hDirectionEffect.m_pObject == NULL );
+ m_hDirectionEffect = ParticleProp()->Create( szEffect, PATTACH_ABSORIGIN );
+
+ // arm glow effects
+ Q_snprintf( szEffect, sizeof(szEffect), "teleporter_arms_circle_%s",
+ ( GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue" );
+
+ Assert( m_hChargedLeftArmEffect.m_pObject == NULL );
+ m_hChargedLeftArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 1 );
+
+ Assert( m_hChargedRightArmEffect.m_pObject == NULL );
+ m_hChargedRightArmEffect = ParticleProp()->Create( szEffect, PATTACH_POINT_FOLLOW, 3 );
+
+ // always reinitializes sound since this only gets called when the sound needs to start or change
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+ if ( m_pSpinSound )
+ {
+ controller.SoundDestroy( m_pSpinSound );
+ m_pSpinSound = NULL;
+ }
+ char szSound[128];
+ Q_snprintf( szSound, sizeof(szSound), "Building_Teleporter.SpinLevel%d", GetUpgradeLevel());
+
+ CLocalPlayerFilter filter;
+ m_pSpinSound = controller.SoundCreate( filter, entindex(), szSound );
+ controller.Play( m_pSpinSound, 1.0, 100 );
+}
+
+void C_ObjectTeleporter::StopBuildingEffects()
+{
+ if ( m_hBuildingLeftArmEffect )
+ {
+ ParticleProp()->StopEmission( m_hBuildingLeftArmEffect );
+ m_hBuildingLeftArmEffect = NULL;
+ }
+
+ if ( m_hBuildingRightArmEffect )
+ {
+ ParticleProp()->StopEmission( m_hBuildingRightArmEffect );
+ m_hBuildingRightArmEffect = NULL;
+ }
+}
+
+void C_ObjectTeleporter::StopChargedEffects()
+{
+ if ( m_hChargedEffect )
+ {
+ ParticleProp()->StopEmission( m_hChargedEffect );
+ m_hChargedEffect = NULL;
+ }
+}
+
+void C_ObjectTeleporter::StopActiveEffects()
+{
+ if ( m_hDirectionEffect )
+ {
+ ParticleProp()->StopEmission( m_hDirectionEffect );
+ m_hDirectionEffect = NULL;
+ }
+
+ if ( m_hChargedLeftArmEffect )
+ {
+ ParticleProp()->StopEmission( m_hChargedLeftArmEffect );
+ m_hChargedLeftArmEffect = NULL;
+ }
+
+ if ( m_hChargedRightArmEffect )
+ {
+ ParticleProp()->StopEmission( m_hChargedRightArmEffect );
+ m_hChargedRightArmEffect = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::SetInvisibilityLevel( float flValue )
+{
+ if ( IsEnteringOrExitingFullyInvisible( flValue ) )
+ {
+ UpdateTeleporterEffects();
+ }
+
+ BaseClass::SetInvisibilityLevel( flValue );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::UpdateTeleporterEffects( void )
+{
+#ifdef STAGING_ONLY
+ C_TFPlayer *pTFOwner = GetOwner();
+ if ( ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() ) || GetInvisibilityLevel() == 1.f )
+ {
+ StopActiveEffects();
+ StopBuildingEffects();
+ StopChargedEffects();
+ return;
+ }
+#endif // STAGING_ONLY
+
+ if ( m_bMatchBuilding )
+ {
+ StartBuildingEffects();
+ }
+ else
+ {
+ StopBuildingEffects();
+ }
+
+ // In MVM, teleporter from invaders act as spawn point. Always play active effect
+ if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
+ {
+ if ( m_iState != TELEPORTER_STATE_BUILDING && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
+ {
+ StartChargedEffects();
+ StartActiveEffects();
+ return;
+ }
+ }
+
+ if ( m_iState == TELEPORTER_STATE_READY )
+ {
+ StartChargedEffects();
+ }
+ else
+ {
+ StopChargedEffects();
+ }
+
+ if ( m_iState > TELEPORTER_STATE_IDLE && m_iOldState <= TELEPORTER_STATE_IDLE )
+ {
+ StartActiveEffects();
+ }
+ else if ( ( m_iState <= TELEPORTER_STATE_IDLE || m_iState == TELEPORTER_STATE_UPGRADING ) && m_iOldState > TELEPORTER_STATE_IDLE )
+ {
+ StopActiveEffects();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( m_bOldMatchBuilding != m_bMatchBuilding )
+ {
+ m_bOldMatchBuilding = m_bMatchBuilding;
+ UpdateTeleporterEffects();
+ }
+
+ if ( m_iOldState != m_iState )
+ {
+ UpdateTeleporterEffects();
+ m_iOldState = m_iState;
+ }
+
+ // update the pitch based on our playback rate
+ if ( m_pSpinSound )
+ {
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ controller.SoundChangePitch( m_pSpinSound, GetPlaybackRate() * 100.0f, 0.1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float C_ObjectTeleporter::GetChargeTime( void )
+{
+ float flTime = m_flRechargeTime - gpGlobals->curtime;
+
+ if ( flTime < 0 )
+ return 0;
+
+ return flTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int C_ObjectTeleporter::GetTimesUsed( void )
+{
+ return m_iTimesUsed;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CStudioHdr *C_ObjectTeleporter::OnNewModel( void )
+{
+ CStudioHdr *hdr = BaseClass::OnNewModel();
+
+ m_iDirectionArrowPoseParam = LookupPoseParameter( "direction" );
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ return hdr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the direction arrow
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::ClientThink( void )
+{
+ if ( m_iState >= TELEPORTER_STATE_READY )
+ {
+ SetPoseParameter( m_iDirectionArrowPoseParam, m_flYawToExit);
+ }
+
+#ifdef STAGING_ONLY
+ C_TFPlayer *pTFOwner = GetOwner();
+ if ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() )
+ {
+ UpdateTeleporterEffects();
+ }
+#endif // STAGING_ONLY
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::GetTargetIDDataString( OUT_Z_BYTECAP(iMaxLenInBytes) wchar_t *sDataString, int iMaxLenInBytes )
+{
+ Assert( iMaxLenInBytes >= sizeof(sDataString[0]) );
+ wchar_t wzBaseString[MAX_ID_STRING];
+ BaseClass::GetTargetIDDataString( wzBaseString, sizeof( wzBaseString ) );
+
+ sDataString[0] = '\0';
+ if ( m_iState == TELEPORTER_STATE_RECHARGING && gpGlobals->curtime < m_flRechargeTime )
+ {
+ float flPercent = clamp( ( m_flRechargeTime - gpGlobals->curtime ) / m_flCurrentRechargeDuration, 0.0f, 1.0f );
+
+ wchar_t wszRecharging[ 32 ];
+ _snwprintf( wszRecharging, ARRAYSIZE(wszRecharging) - 1, L"%.0f", 100 - (flPercent * 100) );
+ wszRecharging[ ARRAYSIZE(wszRecharging)-1 ] = '\0';
+
+ const char *printFormatString = "#TF_playerid_object_recharging";
+
+ g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find(printFormatString),
+ 1,
+ wszRecharging );
+ }
+ else if ( m_iState == TELEPORTER_STATE_IDLE )
+ {
+ g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_teleporter_nomatch" ), 0 );
+ }
+
+ // Concatenate the base level string
+ V_wcsncat( sDataString, L" ", iMaxLenInBytes / sizeof( wchar_t ) );
+ V_wcsncat( sDataString, wzBaseString, iMaxLenInBytes / sizeof( wchar_t ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Damage level has changed, update our effects
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
+{
+ if ( m_hDamageEffects )
+ {
+ m_hDamageEffects->StopEmission( false, false );
+ m_hDamageEffects = NULL;
+ }
+
+ const char *pszEffect = "";
+
+ switch( damageLevel )
+ {
+ case BUILDING_DAMAGE_LEVEL_LIGHT:
+ pszEffect = "tpdamage_1";
+ break;
+ case BUILDING_DAMAGE_LEVEL_MEDIUM:
+ pszEffect = "tpdamage_2";
+ break;
+ case BUILDING_DAMAGE_LEVEL_HEAVY:
+ pszEffect = "tpdamage_3";
+ break;
+ case BUILDING_DAMAGE_LEVEL_CRITICAL:
+ pszEffect = "tpdamage_4";
+ break;
+
+ default:
+ break;
+ }
+
+ if ( Q_strlen(pszEffect) > 0 )
+ {
+ m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_ABSORIGIN );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool C_ObjectTeleporter::IsPlacementPosValid( void )
+{
+ bool bResult = BaseClass::IsPlacementPosValid();
+
+ if ( !bResult )
+ {
+ return false;
+ }
+
+ // m_vecBuildOrigin is the proposed build origin
+
+ // start above the teleporter position
+ Vector vecTestPos = m_vecBuildOrigin;
+ vecTestPos.z += TELEPORTER_MAXS.z;
+
+ // make sure we can fit a player on top in this pos
+ trace_t tr;
+ UTIL_TraceHull( vecTestPos, vecTestPos, VEC_HULL_MIN, VEC_HULL_MAX, MASK_SOLID | CONTENTS_PLAYERCLIP, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
+
+ return ( tr.fraction >= 1.0 );
+}
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::UpgradeLevelChanged( void )
+{
+ StopActiveEffects();
+ StopChargedEffects();
+
+ if ( m_iState >= TELEPORTER_STATE_READY && m_iState != TELEPORTER_STATE_UPGRADING )
+ {
+ StartActiveEffects();
+ if ( m_iState != TELEPORTER_STATE_RECHARGING )
+ {
+ StartChargedEffects();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectTeleporter::OnGoInactive( void )
+{
+ StopActiveEffects();
+ StopBuildingEffects();
+ StopChargedEffects();
+
+ BaseClass::OnGoInactive();
+}