//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Draws achievement progress bars on the HUD // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "hudelement.h" #include "hud_macros.h" #include #include #include #include #include #include "vgui/ILocalize.h" #include "hud_baseachievement_tracker.h" #include "iachievementmgr.h" #include "baseachievement.h" #include "iclientmode.h" #include "cdll_int.h" #include #include #include "fmtstr.h" #include "engine/IEngineSound.h" #include "cdll_client_int.h" #include "steam/isteamuserstats.h" #include "steam/steam_api.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace vgui; #define PROGRESS_BAR_NEEDS_UPDATE -1 #define UNKNOWN_ACHIEVEMENT_ID -1 void TrackerDescriptionChanged( IConVar *var, const char *pOldString, float flOldValue ) { static int s_iTimesChanged = 0; if ( s_iTimesChanged > 0 ) { engine->ClientCmd_Unrestricted( "hud_reloadscheme" ); } s_iTimesChanged++; } ConVar hud_achievement_description("hud_achievement_description", "1", FCVAR_ARCHIVE, "Show full descriptions of achievements on the HUD", TrackerDescriptionChanged ); #ifdef CSTRIKE_DLL ConVar hud_achievement_count("hud_achievement_count", "5", FCVAR_ARCHIVE, "Max number of achievements that can be shown on the HUD" ); #else ConVar hud_achievement_count("hud_achievement_count", "8", FCVAR_ARCHIVE, "Max number of achievements that can be shown on the HUD" ); #endif ConVar hud_achievement_glowtime("hud_achievement_glowtime", "2.5", FCVAR_NONE, "Duration of glow effect around incremented achievements" ); ConVar hud_achievement_tracker("hud_achievement_tracker", "1", FCVAR_NONE, "Show or hide the achievement tracker" ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CHudBaseAchievementTracker::CHudBaseAchievementTracker( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudAchievementTracker" ) { vgui::Panel *pParent = GetClientMode()->GetViewport(); SetParent( pParent ); SetHiddenBits( HIDEHUD_HEALTH | HIDEHUD_PLAYERDEAD ); for ( int i=0; i < GetMaxAchievementsShown(); i++ ) { CAchievementTrackerItem *pNewItem = CreateAchievementPanel(); pNewItem->SetAchievement( NULL ); m_AchievementItem.AddToTail( pNewItem ); } m_flNextThink = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudBaseAchievementTracker::Reset() { m_flNextThink = gpGlobals->curtime + 0.05f; } void CHudBaseAchievementTracker::LevelInit() { // clear out tracker items and floating numbers on level change for ( int i = 0; i < GetChildCount(); i++ ) { GetChild( i )->SetVisible( false ); GetChild( i )->MarkForDeletion(); } m_AchievementItem.Purge(); for ( int i=0; i < GetMaxAchievementsShown(); i++ ) { CAchievementTrackerItem *pNewItem = CreateAchievementPanel(); pNewItem->SetAchievement( NULL ); m_AchievementItem.AddToTail( pNewItem ); } CHudElement::LevelInit(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudBaseAchievementTracker::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHudBaseAchievementTracker::ShouldDraw() { return CHudElement::ShouldDraw(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudBaseAchievementTracker::OnThink() { if ( m_flNextThink < gpGlobals->curtime ) { UpdateAchievementItems(); m_flNextThink = gpGlobals->curtime + 0.1f; } } //----------------------------------------------------------------------------- // Purpose: Max number of achievements shown on the HUD //----------------------------------------------------------------------------- int CHudBaseAchievementTracker::GetMaxAchievementsShown() { return hud_achievement_count.GetInt(); } bool CHudBaseAchievementTracker::ShouldShowAchievement( IAchievement *pAchievement ) { return ( hud_achievement_tracker.GetBool() && pAchievement && pAchievement->ShouldShowOnHUD() && !pAchievement->IsAchieved() ); } CAchievementTrackerItem* CHudBaseAchievementTracker::CreateAchievementPanel() { return new CAchievementTrackerItem( this, "HudAchievementTrackerItem" ); } //----------------------------------------------------------------------------- // Purpose: create panels for each achievement the player wants shown on the HUD and assign achievements to each one //----------------------------------------------------------------------------- void CHudBaseAchievementTracker::UpdateAchievementItems() { IAchievementMgr *pAchievementMgr = engine->GetAchievementMgr(); if ( !pAchievementMgr ) return; // $FIXME(hpe): needs split screen support; use playerSlot 0 for now; should we be using m_AchievementItem slot? int playerSlot = 0; int iCount = pAchievementMgr->GetAchievementCount(); int iShown = 0; for ( int i = 0; i < iCount; ++i ) { IAchievement* pCur = pAchievementMgr->GetAchievementByIndex( i, playerSlot ); if ( !ShouldShowAchievement( pCur) ) { // don't remove achievements that are still glowing (typically a just completed achievement) if ( pCur && m_AchievementItem.Count() > iShown && m_AchievementItem[iShown]->GetAchievementID() == pCur->GetAchievementID() && m_AchievementItem[iShown]->GetGlow() > 0 ) { iShown++; } continue; } if ( m_AchievementItem.Count() < iShown+1 ) { CAchievementTrackerItem *pNewItem = CreateAchievementPanel(); SETUP_PANEL( pNewItem ); m_AchievementItem.AddToTail( pNewItem ); } m_AchievementItem[iShown]->SetAchievement( pCur ); m_AchievementItem[iShown]->SetSlot( iShown ); m_AchievementItem[iShown]->SetVisible( true ); iShown++; if ( iShown >= GetMaxAchievementsShown() ) break; } // hide any extra panels we may have created from when the list was longer if ( iShown < m_AchievementItem.Count() ) { for ( int i = m_AchievementItem.Count() - 1; i >= iShown ; i-- ) { m_AchievementItem[i]->SetVisible( false ); m_AchievementItem[i]->SetAchievement( NULL ); } } } //----------------------------------------------------------------------------- // Purpose: Layout all child panels vertically //----------------------------------------------------------------------------- void CHudBaseAchievementTracker::PerformLayout() { // make sure all children are laid out first for ( int i=0; i < m_AchievementItem.Count(); i++ ) { m_AchievementItem[i]->InvalidateLayout( true ); } int iCurrentY = 0; int x, y; for ( int i=0; i< m_AchievementItem.Count(); i++ ) { m_AchievementItem[i]->GetPos( x, y ); m_AchievementItem[i]->SetPos( x, iCurrentY ); iCurrentY += m_AchievementItem[i]->GetTall() + m_iItemPadding; } } CAchievementTrackerItem* CHudBaseAchievementTracker::GetAchievementPanel( int i ) { if ( i < 0 || i >= m_AchievementItem.Count() ) return NULL; return m_AchievementItem[i]; } //----------------------------------------------------------------------------- // Purpose: The child panels //----------------------------------------------------------------------------- CAchievementTrackerItem::CAchievementTrackerItem( vgui::Panel* pParent, const char *pElementName ) : BaseClass( pParent, pElementName ) { m_pAchievementNameGlow = new vgui::Label( this, "AchievementNameGlow", "" ); m_pAchievementName = new vgui::Label( this, "AchievementName", "" ); m_pAchievementDesc = new vgui::Label( this, "AchievementDesc", "" ); m_pProgressBarBackground = SETUP_PANEL( new ImagePanel( this, "ProgressBarBG" ) ); m_pProgressBar = SETUP_PANEL( new ImagePanel( this, "ProgressBar" ) ); m_iAchievementID = UNKNOWN_ACHIEVEMENT_ID; m_iLastPaintedAchievementID = UNKNOWN_ACHIEVEMENT_ID; m_iAccumulatedIncrement = 0; m_flShowIncrementsTime = 0; m_iLastCount = 0; m_iPadding = 1; } CAchievementTrackerItem::~CAchievementTrackerItem() { delete m_pAchievementName; delete m_pAchievementNameGlow; delete m_pAchievementDesc; delete m_pProgressBarBackground; delete m_pProgressBar; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAchievementTrackerItem::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/UI/HudAchievementTrackerItem.res" ); m_pAchievementDesc->SetVisible( hud_achievement_description.GetBool() ); m_iLastPaintedAchievementID = UNKNOWN_ACHIEVEMENT_ID; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAchievementTrackerItem::PerformLayout() { BaseClass::PerformLayout(); int x, y, w, t; m_pAchievementName->GetContentSize( w, t ); // needed in order to load up font for the name m_pAchievementNameGlow->GetContentSize( w, t ); // needed in order to load up font for the glow if ( hud_achievement_description.GetBool() ) { m_pAchievementDesc->GetContentSize( w, t ); m_pAchievementDesc->SetTall( t ); m_pAchievementDesc->GetBounds( x, y, w, t ); } else { m_pAchievementName->GetBounds( x, y, w, t ); } if ( m_pProgressBarBackground->IsVisible() ) { // put progress bar after description int bx, by; m_pProgressBarBackground->GetPos( bx, by ); m_pProgressBarBackground->SetPos( bx, y + t + m_iPadding ); SetTall( y + t + m_pProgressBarBackground->GetTall() + m_iPadding * 2 ); } else { SetTall( y + t + m_iPadding ); } m_iLastProgressBarCount = m_iLastProgressBarGoal = PROGRESS_BAR_NEEDS_UPDATE; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAchievementTrackerItem::SetAchievement( IAchievement* pAchievement ) { if ( !pAchievement ) { m_iAchievementID = UNKNOWN_ACHIEVEMENT_ID; m_iLastCount = 0; return; } if ( m_iAchievementID != pAchievement->GetAchievementID() ) { m_iAchievementID = pAchievement->GetAchievementID(); m_iLastCount = pAchievement->GetCount(); UpdateAchievementDisplay(); } } void CAchievementTrackerItem::OnThink() { UpdateAchievementDisplay(); } //----------------------------------------------------------------------------- // Purpose: Make sure our labels and progress bar are up to date //----------------------------------------------------------------------------- void CAchievementTrackerItem::UpdateAchievementDisplay() { IAchievementMgr *pAchievementMgr = engine->GetAchievementMgr(); if ( !pAchievementMgr ) return; // $FIXME(hpe): needs split screen support; use playerslot 0 for now int playerSlot = 0; CBaseAchievement* pAchievement = pAchievementMgr->GetAchievementByID( m_iAchievementID, playerSlot ); if ( !pAchievement ) return; if ( m_iAchievementID != m_iLastPaintedAchievementID ) { // need to update labels m_pAchievementName->SetText( ACHIEVEMENT_LOCALIZED_NAME( pAchievement ) ); m_pAchievementNameGlow->SetText( ACHIEVEMENT_LOCALIZED_NAME( pAchievement ) ); m_pAchievementDesc->SetText( ACHIEVEMENT_LOCALIZED_DESC( pAchievement ) ); m_pProgressBarBackground->SetVisible( pAchievement->GetGoal() > 1 ); m_pProgressBar->SetVisible( pAchievement->GetGoal() > 1 ); m_iLastPaintedAchievementID = m_iAchievementID; m_flGlow = 0.0f; m_flGlowTime = 0.0f; m_iAccumulatedIncrement = 0; //InvalidateLayout( true ); GetParent()->InvalidateLayout( true ); } if ( m_iAccumulatedIncrement > 0 && gpGlobals->curtime > m_flShowIncrementsTime ) { ShowAccumulatedIncrements(); } if ( pAchievement->GetCount() != m_iLastProgressBarCount || pAchievement->GetGoal() != m_iLastProgressBarGoal ) { // need to update progress bar float flProgress = float ( pAchievement->GetCount() ) / float( pAchievement->GetGoal() ); int x, y, w, t; m_pProgressBarBackground->GetBounds( x, y, w, t ); m_pProgressBar->SetBounds( x, y, w * flProgress, t ); m_iLastProgressBarCount = pAchievement->GetCount(); m_iLastProgressBarGoal = pAchievement->GetGoal(); AchievementIncremented( pAchievement->GetCount() ); } if ( gpGlobals->curtime < m_flGlowTime ) { m_flGlow = MIN( 1.0f, m_flGlow + gpGlobals->frametime * 5.0f ); } else { m_flGlow = MAX( 0.0f, m_flGlow - gpGlobals->frametime * 5.0f ); } m_pAchievementNameGlow->SetAlpha( m_flGlow * 255.0f ); } //----------------------------------------------------------------------------- // Purpose: Achievement count has gone up, make it flash //----------------------------------------------------------------------------- void CAchievementTrackerItem::AchievementIncremented( int iNewCount ) { int iIncrement = iNewCount - m_iLastCount; m_iLastCount = iNewCount; if ( iIncrement <= 0 ) return; if ( m_iLastProgressBarGoal > 1500 ) { // for achievements with very high counts, accumulate increments so we don't have too many +1s on screen // also don't play sounds as these achievements tend to increment constantly if ( m_flShowIncrementsTime < gpGlobals->curtime ) { m_flShowIncrementsTime = gpGlobals->curtime + 2.0f; } m_iAccumulatedIncrement += iIncrement; } else { m_flGlowTime = gpGlobals->curtime + hud_achievement_glowtime.GetFloat(); // create a floating +X to scroll up alongside this achievement if ( m_pProgressBarBackground->IsVisible() ) { int px, py; GetPos( px, py ); int x, y, w, t; m_pProgressBarBackground->GetBounds( x, y, w, t ); x += w; new CFloatingAchievementNumber( iIncrement, px + x, py + y + ( t * 0.5f ), FN_DIR_RIGHT, GetParent() ); } CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Hud.AchievementIncremented" ); } } //----------------------------------------------------------------------------- // Purpose: Show all the increments we've accumulated //----------------------------------------------------------------------------- void CAchievementTrackerItem::ShowAccumulatedIncrements() { int px, py; GetPos( px, py ); int x, y, w, t; m_pProgressBarBackground->GetBounds( x, y, w, t ); x += w; new CFloatingAchievementNumber( m_iAccumulatedIncrement, px + x, py + y + ( t * 0.5f ), FN_DIR_RIGHT, GetParent() ); m_iAccumulatedIncrement = 0; m_flGlowTime = gpGlobals->curtime + hud_achievement_glowtime.GetFloat(); } //----------------------------------------------------------------------------- // Purpose: Floating numbers showing how much achievement progress bars have gone up //----------------------------------------------------------------------------- CFloatingAchievementNumber::CFloatingAchievementNumber( int iProgress, int x, int y, floating_number_directions iDir, vgui::Panel* pParent ) : BaseClass( pParent, "FloatingAchievementNumber" ) { m_iStartX = x; m_iStartY = y; m_iProgress = iProgress; m_fStartTime = gpGlobals->curtime; m_iDirection = iDir; char szLabel[64]; Q_snprintf( szLabel, sizeof( szLabel ), "+%d", iProgress ); m_pNumberLabel = new vgui::Label( this, "FloatingNumberLabel", szLabel ); } CFloatingAchievementNumber::~CFloatingAchievementNumber() { delete m_pNumberLabel; } void CFloatingAchievementNumber::ApplySchemeSettings( vgui::IScheme *scheme ) { BaseClass::ApplySchemeSettings( scheme ); LoadControlSettings( "resource/UI/HudAchievementFloatingNumber.res" ); int fontHeight = surface()->GetFontTall( m_pNumberLabel->GetFont() ); SetPos( m_iStartX, m_iStartY - ( fontHeight * 0.5f ) ); m_pNumberLabel->SetAlpha( 0 ); GetAnimationController()->RunAnimationCommand( m_pNumberLabel, "alpha", 255, 0, 0.3, vgui::AnimationController::INTERPOLATOR_LINEAR ); switch ( m_iDirection ) { default: case FN_DIR_UP: vgui::GetAnimationController()->RunAnimationCommand( this, "ypos", m_iStartY - m_iScrollDistance, 0, 2.0f, vgui::AnimationController::INTERPOLATOR_LINEAR ); break; case FN_DIR_DOWN: vgui::GetAnimationController()->RunAnimationCommand( this, "ypos", m_iStartY + m_iScrollDistance, 0, 2.0f, vgui::AnimationController::INTERPOLATOR_LINEAR ); break; case FN_DIR_LEFT: vgui::GetAnimationController()->RunAnimationCommand( this, "xpos", m_iStartX - m_iScrollDistance, 0, 2.0f, vgui::AnimationController::INTERPOLATOR_LINEAR ); break; case FN_DIR_RIGHT: vgui::GetAnimationController()->RunAnimationCommand( this, "xpos", m_iStartX + m_iScrollDistance, 0, 2.0f, vgui::AnimationController::INTERPOLATOR_LINEAR ); break; } } //----------------------------------------------------------------------------- // Purpose: Delete panel when floating number has faded out //----------------------------------------------------------------------------- void CFloatingAchievementNumber::OnThink() { if ( gpGlobals->curtime > m_fStartTime + 1.0f ) { if ( m_pNumberLabel->GetAlpha() >= 255 ) { m_pNumberLabel->SetAlpha( 254 ); GetAnimationController()->RunAnimationCommand( m_pNumberLabel, "alpha", 0, 0.0, 1.0f, vgui::AnimationController::INTERPOLATOR_LINEAR ); } else if ( m_pNumberLabel->GetAlpha() <= 0 ) { MarkForDeletion(); SetVisible( false ); } } } //----------------------------------------------------------------------------- // Purpose: Debug command to make one of the achievement panels flash as though it just went up //----------------------------------------------------------------------------- class CHudAchievementTracker; void cc_TrackerAnim_f( const CCommand &args ) { CHudBaseAchievementTracker *pTracker = ( CHudBaseAchievementTracker * )GET_HUDELEMENT( CHudAchievementTracker ); if ( !pTracker ) return; CAchievementTrackerItem *pItem = pTracker->GetAchievementPanel( atoi(args[1]) ); if ( !pItem ) return; pItem->AchievementIncremented( pItem->GetLastCount() + RandomInt(1, 1) ); } ConCommand cc_TrackerAnim( "TrackerAnim", cc_TrackerAnim_f, "Test animation of the achievement tracker. Parameter is achievement number on HUD to flash", FCVAR_NONE );