You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
762 lines
23 KiB
762 lines
23 KiB
//========= 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; |
|
}
|
|
|