diff options
Diffstat (limited to 'game/client/tf/vgui/tf_pvp_rank_panel.cpp')
| -rw-r--r-- | game/client/tf/vgui/tf_pvp_rank_panel.cpp | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_pvp_rank_panel.cpp b/game/client/tf/vgui/tf_pvp_rank_panel.cpp new file mode 100644 index 0000000..70b6249 --- /dev/null +++ b/game/client/tf/vgui/tf_pvp_rank_panel.cpp @@ -0,0 +1,762 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" + +#include "tf_pvp_rank_panel.h" +#include "tf_matchmaking_shared.h" +#include "basemodel_panel.h" +#include "vgui_controls/ProgressBar.h" +#include "tf_ladder_data.h" +#include "iclientmode.h" +#include <vgui_controls/AnimationController.h> +#include "tf_controls.h" +#include "vgui/ISurface.h" +#include "animation.h" +#include "clientmode_tf.h" +#include "vgui/IInput.h" +#include "vgui_controls/MenuItem.h" +#include "tf_gc_client.h" +#include "tf_xp_source.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#ifdef STAGING_ONLY + extern ConVar tf_test_pvp_rank_xp_change; +#endif + +ConVar tf_xp_breakdown_interval( "tf_xp_breakdown_interval", "1.85", FCVAR_DEVELOPMENTONLY ); +ConVar tf_xp_breakdown_lifetime( "tf_xp_breakdown_lifetime", "3.5", FCVAR_DEVELOPMENTONLY ); + +extern const char *s_pszMatchGroups[]; + +CPvPRankPanel::XPState_t::XPState_t( EMatchGroup eMatchGroup ) + : m_nStartXP( 0u ) + , m_nTargetXP( 0u ) + , m_nActualXP( 0u ) + , m_bCurrentDeltaViewed( true ) + , m_eMatchGroup( eMatchGroup ) +{ + Assert( m_eMatchGroup != k_nMatchGroup_Invalid ); + + // Default to level 1's starting XP value + const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( m_eMatchGroup ); + if ( pMatchDesc && pMatchDesc->m_pProgressionDesc ) + { + auto level = pMatchDesc->m_pProgressionDesc->GetLevelByNumber( 1 ); + m_nStartXP = level.m_nStartXP; + m_nActualXP = m_nStartXP; + m_nTargetXP = m_nStartXP; + } + + ListenForGameEvent( "experience_changed" ); + ListenForGameEvent( "server_spawn" ); +} + +void CPvPRankPanel::XPState_t::FireGameEvent( IGameEvent *pEvent ) +{ + if ( FStrEq( pEvent->GetName(), "experience_changed" ) ) // For changing tf_progression_set_xp_to_level + { + UpdateXP( false ); + } + else if ( FStrEq( pEvent->GetName(), "server_spawn" ) && GTFGCClientSystem()->BHaveLiveMatch() + && GTFGCClientSystem()->GetLiveMatchGroup() == m_eMatchGroup ) + { + UpdateXP( false ); + // Acknowledge any outstanding XP sources when we start a match. It looks really weird when + // you see old match xp sources at the end of a match. + GTFGCClientSystem()->AcknowledgePendingXPSources( m_eMatchGroup ); + } +} + +void CPvPRankPanel::XPState_t::UpdateXP( bool bInitial ) +{ + uint32 nStartXP = 0u; + uint32 nNewXP = 0u; + + const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( m_eMatchGroup ); + // Should only be creating this object for matches that have progressions + Assert( pMatchDesc && pMatchDesc->m_pProgressionDesc ); + if ( pMatchDesc && pMatchDesc->m_pProgressionDesc ) + { + // Default to level 1's lowest XP value + auto level = pMatchDesc->m_pProgressionDesc->GetLevelByNumber( 1 ); + nNewXP = level.m_nStartXP; + nStartXP = level.m_nStartXP; + + if ( steamapicontext && steamapicontext->SteamUser() ) + { + nStartXP = pMatchDesc->m_pProgressionDesc->GetLocalPlayerLastAckdExperience(); + // Get the actual user's XP + nNewXP = pMatchDesc->m_pProgressionDesc->GetPlayerExperienceBySteamID( steamapicontext->SteamUser()->GetSteamID() ); + } + } + + // If there's no change, don't do anything + if ( nNewXP != m_nActualXP || bInitial ) + { + m_nActualXP = nNewXP; + m_nStartXP = nStartXP; + m_bCurrentDeltaViewed = false; + } + + if ( bInitial ) + { + m_progressTimer.Start( 1.f ); + m_bCurrentDeltaViewed = true; + m_nTargetXP = m_nStartXP; + } +} + +uint32 CPvPRankPanel::XPState_t::GetCurrentXP() const +{ + float flTimeProgress = 0.f; + if ( m_progressTimer.HasStarted() ) + { + flTimeProgress = Clamp( m_progressTimer.GetElapsedTime(), 0.f, m_progressTimer.GetCountdownDuration() ); + flTimeProgress = Gain( flTimeProgress / m_progressTimer.GetCountdownDuration(), 0.9f ); + } + + return RemapValClamped( flTimeProgress, 0.f, 1.f, m_nStartXP, m_nTargetXP ); +} + +bool CPvPRankPanel::XPState_t::BeginXPDeltaLerp() +{ + if ( !m_bCurrentDeltaViewed ) + { + m_bCurrentDeltaViewed = true; + // Change our target + m_nTargetXP = m_nActualXP; + m_progressTimer.Start( 5.f ); + + return true; + } + + return false; +} + +void CPvPRankPanel::XPState_t::SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) +{ + if ( pObject->GetTypeID() == CSOTFLadderData::k_nTypeID ) + { + CSOTFLadderData* pLadderObject = (CSOTFLadderData*)pObject; + if( (EMatchGroup)pLadderObject->Obj().match_group() != m_eMatchGroup ) + return; + + // We'll get a eSOCacheEvent_Incremental when we're actually creating the + // first CSOTFLadderData object. We dont want that to come through as + // "initializing" because it'll skip the leveling effects + UpdateXP( eEvent == eSOCacheEvent_ListenerAdded ); + } + + if ( pObject->GetTypeID() == CXPSource::k_nTypeID ) + { + CXPSource *pXPSource = (CXPSource*)pObject; + if ( pXPSource->Obj().match_group() == m_eMatchGroup ) + { + UpdateXP( false ); + } + } +} + +void CPvPRankPanel::XPState_t::SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) +{ + if ( pObject->GetTypeID() == CSOTFLadderData::k_nTypeID ) + { + CSOTFLadderData* pLadderObject = (CSOTFLadderData*)pObject; + if( (EMatchGroup)pLadderObject->Obj().match_group() != m_eMatchGroup ) + return; + + // If the GC comes on after we've already subscribed to the cache, + // eSOCacheEvent_Subscribed when the object is created. This one + // we want to skip the effects. + UpdateXP( eEvent == eSOCacheEvent_Subscribed ); + } + + if ( pObject->GetTypeID() == CXPSource::k_nTypeID ) + { + CXPSource *pXPSource = (CXPSource*)pObject; + if ( pXPSource->Obj().match_group() == m_eMatchGroup ) + { + UpdateXP( false ); + } + } +} + +class CMiniPvPRankPanel : public CPvPRankPanel +{ +public: + CMiniPvPRankPanel( Panel* pParent, const char* pszPanelName ) : CPvPRankPanel( pParent, pszPanelName ) + {} + +private: + virtual KeyValues* GetConditions() const OVERRIDE + { + KeyValues* pConditions = new KeyValues( "conditions" ); + pConditions->AddSubKey( new KeyValues( "if_mini" ) ); + + return pConditions; + } +}; + +class CXPSourcePanel : public vgui::EditablePanel +{ +public: + DECLARE_CLASS_SIMPLE( CXPSourcePanel , vgui::EditablePanel); + CXPSourcePanel( Panel *pParent, const char* panelName, const CMsgTFXPSource& source ) + : BaseClass( pParent, panelName ) + , m_source( source ) + {} + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) OVERRIDE + { + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "resource/ui/XPSourcePanel.res" ); + } + + virtual void PerformLayout() OVERRIDE + { + BaseClass::PerformLayout(); + + static wchar_t wszOutString[ 128 ]; + wchar_t wszCount[ 16 ]; + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", m_source.amount() ); + const wchar_t *wpszFormat = g_pVGuiLocalize->Find( g_XPSourceDefs[ m_source.type() ].m_pszFormattingLocToken ); + g_pVGuiLocalize->ConstructString_safe( wszOutString, wpszFormat, 2, g_pVGuiLocalize->Find( g_XPSourceDefs[ m_source.type() ].m_pszTypeLocToken ), wszCount ); + + SetDialogVariable( "source", wszOutString ); + } + +protected: + CMsgTFXPSource m_source; +}; + +class CScrollingXPSourcePanel : public CXPSourcePanel +{ +public: + DECLARE_CLASS_SIMPLE( CScrollingXPSourcePanel , CXPSourcePanel ); + CScrollingXPSourcePanel( Panel *pParent, const char* panelName, const CMsgTFXPSource& source, float flStartTime ) + : BaseClass( pParent, panelName, source ) + , m_flStartTime( flStartTime ) + , m_bStarted( false ) + { + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + SetAutoDelete( false ); + } + + virtual ~CScrollingXPSourcePanel() + { + } + + virtual void OnTick() OVERRIDE + { + BaseClass::OnTick(); + + if ( Plat_FloatTime() > m_flStartTime && !m_bStarted ) + { + m_bStarted = true; + SetVisible( true ); + + // Do starting stuff + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_source.amount() >= 0 ? "XPSourceShow_Positive" : "XPSourceShow_Negative", false ); + + if ( g_XPSourceDefs[ m_source.type() ].m_pszSoundName ) + { + PlaySoundEntry( g_XPSourceDefs[ m_source.type() ].m_pszSoundName ); + } + } + + SetVisible( m_bStarted ); + + if ( Plat_FloatTime() > ( m_flStartTime + tf_xp_breakdown_lifetime.GetFloat() ) ) + { + // We're done! Delete ourselves + MarkForDeletion(); + } + } + +private: + + float m_flStartTime; + bool m_bStarted; +}; + +DECLARE_BUILD_FACTORY( CMiniPvPRankPanel ); +DECLARE_BUILD_FACTORY( CPvPRankPanel ); + +CPvPRankPanel::CPvPRankPanel( Panel *parent, const char *panelName ) + : BaseClass( parent, panelName ) + , m_eMatchGroup( k_nMatchGroup_Invalid ) + , m_pProgressionDesc( NULL ) + , m_pContinuousProgressBar( NULL ) + , m_pModelPanel( NULL ) + , m_pXPBar( NULL ) + , m_pBGPanel( NULL ) + , m_nLastLerpXP( 0 ) + , m_nLastSeenLevel( 0 ) + , m_bClicked( false ) +{ + ListenForGameEvent( "begin_xp_lerp" ); +} + +void CPvPRankPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + KeyValues* pConditions = GetConditions(); + + LoadControlSettings( GetResFile(), NULL, NULL, pConditions ); + + if ( pConditions ) + { + pConditions->deleteThis(); + pConditions = NULL; + } + + m_pBGPanel = FindControl< EditablePanel >( "BGPanel", true ); + m_pContinuousProgressBar = FindControl< ContinuousProgressBar >( "ContinuousProgressBar", true ); + m_pXPBar = FindControl< EditablePanel >( "XPBar", true ); + m_pModelPanel = FindControl< CBaseModelPanel >( "RankModel", true ); + m_pModelPanel->AddActionSignalTarget( this ); + + m_pModelButton = FindChildByName( "MedalButton", true ); + +#ifdef STAGING_ONLY + if ( m_pBGPanel ) + { + Panel* pDebugButton = m_pBGPanel->FindChildByName( "TestLevelDownButton", true ); + if ( pDebugButton ) { pDebugButton->SetVisible( true ); } + pDebugButton = m_pBGPanel->FindChildByName( "LevelDebugButton", true ); + if ( pDebugButton ) { pDebugButton->SetVisible( true ); } + pDebugButton = m_pBGPanel->FindChildByName( "TestLevelUpButton", true ); + if ( pDebugButton ) { pDebugButton->SetVisible( true ); } + } +#endif +} + +void CPvPRankPanel::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings( inResourceData ); + + SetMatchGroup( (EMatchGroup)StringFieldToInt( inResourceData->GetString( "matchgroup" ), s_pszMatchGroups, (int)k_nMatchGroup_Count, false ) ); +} + +void CPvPRankPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( !m_pProgressionDesc ) + return; + + EditablePanel* pStatsContainer = FindControl< EditablePanel >( "Stats", true ); + if ( !pStatsContainer ) + return; + + if ( !m_pModelPanel ) + return; + + if ( !m_pXPBar ) + return; + + ProgressBar* pProgressBar = FindControl< ProgressBar > ( "ProgressBar", true ); + if ( !pProgressBar ) + return; + + if ( !m_pContinuousProgressBar ) + return; + + // Chop up the progress bar into 10th's and use it as a mask overtop + pProgressBar->SetSegmentInfo( ( pProgressBar->GetWide() / 10 ) - ( ( 9 * 4 ) / 10 ) , 4 ); + + const XPState_t& xpstate = GetXPState(); + uint32 nCurrentXP = xpstate.GetCurrentXP(); + const LevelInfo_t& levelCur = m_pProgressionDesc->GetLevelForExperience( nCurrentXP ); + m_pProgressionDesc->SetupBadgePanel( m_pModelPanel, levelCur ); + + // Update labels + UpdateControls( xpstate.GetStartXP(), nCurrentXP, levelCur ); + + SetMatchStats(); +} + +void CPvPRankPanel::OnThink() +{ + BaseClass::OnThink(); + + if ( m_pContinuousProgressBar && m_pXPBar && m_pModelPanel && m_pProgressionDesc && m_pBGPanel ) + { + + // SUPER HACKS. I dont have time to figure out popups + if ( m_pModelButton && vgui::input()->IsMouseDown( MOUSE_LEFT ) ) + { + int nMouseX, nMouseY; + input()->GetCursorPos( nMouseX, nMouseY ); + if ( !m_bClicked && m_pModelButton->IsWithin( nMouseX, nMouseY ) ) + { + OnCommand( "medal_clicked" ); + } + + m_bClicked = true; + } + else + { + m_bClicked = false; + } + + const XPState_t& xpstate = GetXPState(); + uint32 nCurrentXP = xpstate.GetCurrentXP(); + + // Check if the last XP we lerp'd to isn't the current XP. If it's not, then we want to animate over to it. + if ( m_nLastLerpXP != nCurrentXP ) + { + uint32 nPrevXP = xpstate.GetStartXP(); + const LevelInfo_t& levelCur = m_pProgressionDesc->GetLevelForExperience( nCurrentXP ); + + UpdateControls( nPrevXP, nCurrentXP, levelCur ); + + // We only want to do level up effects if we are thinking (visible) when the current XP passes the level boundary + if ( m_nLastLerpXP != xpstate.GetStartXP() ) + { + const LevelInfo_t& levelPrevThink = m_pProgressionDesc->GetLevelForExperience( m_nLastLerpXP ); + if ( levelCur.m_nLevelNum > levelPrevThink.m_nLevelNum ) // Level up :) + { + PlayLevelUpEffects( levelCur ); + } + else if ( levelCur.m_nLevelNum < levelPrevThink.m_nLevelNum ) // Level down :( + { + PlayLevelDownEffects( levelCur ); + } + } + + m_nLastLerpXP = nCurrentXP; + } + else + { + // Our last lerp XP is caught up with current XP, but our last seen level is not up to date with what + // our current level actually is. This can happen if we haven't been thinking, but we did level up somewhere + // else. In this case, we need to update our badge. + const LevelInfo_t& levelCur = m_pProgressionDesc->GetLevelForExperience( nCurrentXP ); + + if ( m_nLastSeenLevel != levelCur.m_nLevelNum ) + { + m_nLastSeenLevel = levelCur.m_nLevelNum; + m_pProgressionDesc->SetupBadgePanel( m_pModelPanel, levelCur ); + } + } + } +} + + +void CPvPRankPanel::UpdateControls( uint32 nPreviousXP, uint32 nCurrentXP, const LevelInfo_t& levelCurrent ) +{ + if ( m_pContinuousProgressBar ) + { + // Calculate progress bar percentages. PrevBarProgress is the bar that shows where you started. + // CurrentBarProgress is the bar that lerps from the start to your actual, current XP + float flPrevBarProgress = RemapValClamped( (float)nPreviousXP, (float)levelCurrent.m_nStartXP, (float)levelCurrent.m_nEndXP, 0.f, 1.f ); + float flCurrentBarProgress = RemapValClamped( (float)nCurrentXP, (float)levelCurrent.m_nStartXP, (float)levelCurrent.m_nEndXP, 0.f, 1.f ); + + m_pContinuousProgressBar->SetPrevProgress( flPrevBarProgress ); + m_pContinuousProgressBar->SetProgress( flCurrentBarProgress ); + } + + if ( m_pXPBar ) + { + m_pXPBar->SetDialogVariable( "current_xp", LocalizeNumberWithToken( "TF_Competitive_XP_Current", nCurrentXP ) ); + + if ( levelCurrent.m_nLevelNum < m_pProgressionDesc->GetNumLevels() ) + { + m_pXPBar->SetDialogVariable( "next_level_xp", LocalizeNumberWithToken( "TF_Competitive_XP", levelCurrent.m_nEndXP ) ); + } + else // Hide the next level XP value at max level + { + m_pXPBar->SetDialogVariable( "next_level_xp", "" ); + } + + static wchar_t wszOutString[ 128 ]; + wchar_t wszCount[ 16 ]; + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", levelCurrent.m_nLevelNum ); + const wchar_t *wpszFormat = g_pVGuiLocalize->Find( m_pProgressionDesc->m_pszLevelToken ); + g_pVGuiLocalize->ConstructString_safe( wszOutString, wpszFormat, 2, wszCount, g_pVGuiLocalize->Find( levelCurrent.m_pszLevelTitle ) ); + + m_pXPBar->SetDialogVariable( "level", wszOutString ); + } +} + +void CPvPRankPanel::OnCommand( const char *command ) +{ + if ( FStrEq( "medal_clicked", command ) ) + { + // Default effects + const char *pszSeqName = "click_A"; + const char *pszSoundName = "ui/mm_medal_click.wav"; + + // Roll for a crit + int nRandomRoll = RandomInt( 0, 9 ); + if ( nRandomRoll == 0 ) + { + // CRIT! + pszSeqName = "click_B"; + pszSoundName = "MatchMaking.MedalClickRare"; + } + + m_pModelPanel->PlaySequence( pszSeqName ); + PlaySoundEntry( pszSoundName ); + + EditablePanel* pModelContainer = FindControl< EditablePanel >( "ModelContainer" ); + if ( pModelContainer ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( pModelContainer, "PvPRankModelClicked", false); + } + + return; + } + else if ( FStrEq( "begin_xp_lerp", command ) ) + { + BeginXPLerp(); + return; + } + else if ( FStrEq( "update_base_state", command ) ) + { + UpdateBaseState(); + return; + } + BaseClass::OnCommand( command ); +} + +void CPvPRankPanel::FireGameEvent( IGameEvent *pEvent ) +{ + // This is really only for tf_test_pvp_rank_xp_change + if ( FStrEq( pEvent->GetName(), "begin_xp_lerp" ) ) + { + BeginXPLerp(); + } +} + +void CPvPRankPanel::SetVisible( bool bVisible ) +{ + if ( IsVisible() != bVisible ) + { + UpdateBaseState(); + } + + BaseClass::SetVisible( bVisible ); +} + +int SortXPSources( CXPSource* const* pLeft, CXPSource* const* pRight ) +{ + return (*pLeft)->Obj().type() - (*pRight)->Obj().type(); +} + +void CPvPRankPanel::BeginXPLerp() +{ + XPState_t& xpstate = GetXPState(); + if ( xpstate.BeginXPDeltaLerp() ) + { + // Play sounds if this is a change + if ( xpstate.GetTargetXP() > xpstate.GetStartXP() ) + { + PlaySoundEntry( "MatchMaking.RankProgressTickUp" ); + } + else if ( xpstate.GetTargetXP() < xpstate.GetStartXP() ) + { + PlaySoundEntry( "MatchMaking.RankProgressTickDown" ); + } + } + + if ( steamapicontext && steamapicontext->SteamUser() ) + { + GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamapicontext->SteamUser()->GetSteamID() ); + + if ( pSOCache ) + { + GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( CXPSource::k_nTypeID ); + + if ( pTypeCache ) + { + CUtlVector< CXPSource* > vecSources; + + // Grab all the XP sources we want to show + for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i ) + { + CXPSource *pXPSource = (CXPSource*)pTypeCache->GetObject( i ); + + if ( pXPSource->Obj().match_group() == m_eMatchGroup ) + { + vecSources.AddToTail( pXPSource ); + } + } + + // Sort them so users get a consistent experience + vecSources.Sort( &SortXPSources ); + + // Show the sources + FOR_EACH_VEC( vecSources, i ) + { + CXPSource *pXPSource = vecSources[ i ]; + CScrollingXPSourcePanel* pSourcePanel = new CScrollingXPSourcePanel( this, "XPSourcePanel", pXPSource->Obj(), Plat_FloatTime() + ( i * tf_xp_breakdown_interval.GetFloat() ) ); + pSourcePanel->MakeReadyForUse(); + + // Offset from the BGPanel. + pSourcePanel->SetPos( m_pBGPanel->GetXPos() + m_iXPSourceNotificationCenterX - ( pSourcePanel->GetWide() * 0.5f ), m_pBGPanel->GetYPos() + m_pXPBar->GetYPos() + YRES( 22 ) - pSourcePanel->GetTall() ); + } + } + } + } + + GTFGCClientSystem()->AcknowledgePendingXPSources( m_eMatchGroup ); +} + +void CPvPRankPanel::UpdateBaseState() +{ + if ( !m_pProgressionDesc || m_pModelPanel == NULL ) + return; + + // Set our "last seen" variables so things dont try to interpolate + m_nLastLerpXP = GetXPState().GetCurrentXP(); + LevelInfo_t levelCur = m_pProgressionDesc->GetLevelForExperience( m_nLastLerpXP ); + m_nLastSeenLevel = levelCur.m_nLevelNum; + + m_pProgressionDesc->SetupBadgePanel( m_pModelPanel, levelCur ); + + // Update progress bars and labels + UpdateControls( GetXPState().GetStartXP(), m_nLastLerpXP, levelCur ); +} + +void CPvPRankPanel::OnAnimEvent( KeyValues *pParams ) +{ + // This gets fired by the model panel that has the badge model in it when we want + // to do our bodygroup changes. This is so we can time it to when the model is doing + // a flashy maneuver to mask the bodygroup change pop + if ( FStrEq( pParams->GetString( "name" ), "AE_CL_BODYGROUP_SET_VALUE" ) && m_pProgressionDesc ) + { + const LevelInfo_t& levelCur = m_pProgressionDesc->GetLevelForExperience( m_nLastLerpXP ); + m_pProgressionDesc->SetupBadgePanel( m_pModelPanel, levelCur ); + } +} + +void CPvPRankPanel::PlayLevelUpEffects( const LevelInfo_t& level ) const +{ + m_pModelPanel->PlaySequence( "level_up" ); + PlaySoundEntry( level.m_pszLevelUpSound ); + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pXPBar, "PvPRankLevelUpXPBar", false); + + EditablePanel* pModelContainer = const_cast< CPvPRankPanel* >( this )->FindControl< EditablePanel >( "ModelContainer" ); + if ( pModelContainer ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( pModelContainer, "PvPRankLevelUpModel", false ); + } +} + +void CPvPRankPanel::PlayLevelDownEffects( const LevelInfo_t& level ) const +{ + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pXPBar, "PvPRankLevelDownXPBar", false); + m_pModelPanel->PlaySequence( "level_down" ); + + EditablePanel* pModelContainer = const_cast< CPvPRankPanel* >( this )->FindControl< EditablePanel >( "ModelContainer" ); + if ( pModelContainer ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( pModelContainer, "PvPRankLevelDownModel", false ); + } +} + +void CPvPRankPanel::SetMatchGroup( EMatchGroup eMatchGroup ) +{ + if ( m_eMatchGroup != eMatchGroup ) + { + m_eMatchGroup = eMatchGroup; + m_pProgressionDesc = NULL; + + const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( m_eMatchGroup ); + if ( pMatchDesc && pMatchDesc->m_pProgressionDesc ) + { + // Snag the progression desc. We use it often + m_pProgressionDesc = pMatchDesc->m_pProgressionDesc; + UpdateBaseState(); + } + + Assert( m_pProgressionDesc ); + + // Many things needs to change + InvalidateLayout( true, true ); + UpdateBaseState(); + } +} + +void CPvPRankPanel::SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) +{ + if ( pObject->GetTypeID() != CSOTFLadderData::k_nTypeID ) + return; + + SetMatchStats(); +} + +void CPvPRankPanel::SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) +{ + if ( pObject->GetTypeID() != CSOTFLadderData::k_nTypeID ) + return; + + SetMatchStats(); +} + +void CPvPRankPanel::SetMatchStats( void ) +{ + EditablePanel* pStatsContainer = FindControl< EditablePanel >( "Stats", true ); + if ( !pStatsContainer ) + return; + + CSOTFLadderData *pData = GetLocalPlayerLadderData( m_eMatchGroup ); + + // Update all stats. Default to 0 incase we dont have ladder data + int nGames = 0, nKills = 0, nDeaths = 0, nDamage = 0, nHealing = 0, nSupport = 0, nScore = 0; + + if ( pData ) + { + nGames = pData->Obj().games(); + nKills = pData->Obj().kills(); + nDeaths = pData->Obj().deaths(); + nDamage = pData->Obj().damage(); + nHealing = pData->Obj().healing(); + nSupport = pData->Obj().support(); + nScore = pData->Obj().score(); + } + + pStatsContainer->SetDialogVariable( "stat_games", LocalizeNumberWithToken( "TF_Competitive_Games", nGames ) ); + pStatsContainer->SetDialogVariable( "stat_kills", LocalizeNumberWithToken( "TF_Competitive_Kills", nKills ) ); + pStatsContainer->SetDialogVariable( "stat_deaths", LocalizeNumberWithToken( "TF_Competitive_Deaths", nDeaths ) ); + pStatsContainer->SetDialogVariable( "stat_damage", LocalizeNumberWithToken( "TF_Competitive_Damage", nDamage ) ); + pStatsContainer->SetDialogVariable( "stat_healing", LocalizeNumberWithToken( "TF_Competitive_Healing", nHealing ) ); + pStatsContainer->SetDialogVariable( "stat_support", LocalizeNumberWithToken( "TF_Competitive_Support", nSupport ) ); + pStatsContainer->SetDialogVariable( "stat_score", LocalizeNumberWithToken( "TF_Competitive_Score", nScore ) ); +} + +CPvPRankPanel::XPState_t& CPvPRankPanel::GetXPState() const +{ + // Singletons for each XPState_t + static CUtlMap< EMatchGroup, CPvPRankPanel::XPState_t* > s_mapXPStates( DefLessFunc( EMatchGroup ) ); + + auto idx = s_mapXPStates.Find( m_eMatchGroup ); + if ( idx == s_mapXPStates.InvalidIndex() ) + { + idx = s_mapXPStates.Insert( m_eMatchGroup, new CPvPRankPanel::XPState_t( m_eMatchGroup ) ); + } + + return *s_mapXPStates[ idx ]; +} + +const char* CPvPRankPanel::GetResFile() const +{ + return m_pProgressionDesc ? m_pProgressionDesc->m_pszProgressionResFile : "resource/ui/PvPCompRankPanel.res"; +} + +KeyValues* CPvPRankPanel::GetConditions() const +{ + return NULL; +} |