diff options
Diffstat (limited to 'game/client/tf/c_obj_teleporter.cpp')
| -rw-r--r-- | game/client/tf/c_obj_teleporter.cpp | 465 |
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(); +} |