diff options
Diffstat (limited to 'game/client/tf/tf_hud_item_progress_tracker.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_item_progress_tracker.cpp | 1122 |
1 files changed, 1122 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_item_progress_tracker.cpp b/game/client/tf/tf_hud_item_progress_tracker.cpp new file mode 100644 index 0000000..70ad3a5 --- /dev/null +++ b/game/client/tf/tf_hud_item_progress_tracker.cpp @@ -0,0 +1,1122 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "cbase.h" +#include "tf_hud_item_progress_tracker.h" +#include "iclientmode.h" +#include <vgui_controls/Label.h> +#include <vgui_controls/ImagePanel.h> +#include "gc_clientsystem.h" +#include "engine/IEngineSound.h" +#include "quest_log_panel.h" +#include "econ_controls.h" +#include "tf_item_inventory.h" +#include "c_tf_player.h" +#include "quest_objective_manager.h" +#include "tf_spectatorgui.h" +#include "econ_quests.h" +#include "inputsystem/iinputsystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +const float ATTRIB_TRACK_GLOW_HOLD_TIME = 2.f; +const float ATTRIB_TRACK_BAR_GROW_RATE = 0.3f; +const float ATTRIB_TRACK_COMPLETE_PULSE_RATE = 2.f; +const float ATTRIB_TRACK_COMPLETE_PULSE_DIM_HOLD = 0.3f; +const float ATTRIB_TRACK_COMPLETE_PULSE_GLOW_HOLD = 0.9f; + +enum EContractHUDVisibility +{ + CONTRACT_HUD_SHOW_NONE = 0, + CONTRACT_HUD_SHOW_EVERYTHING, + CONTRACT_HUD_SHOW_ACTIVE, +}; + +void cc_contract_progress_show_update( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + CHudItemAttributeTracker *pTrackerPanel = (CHudItemAttributeTracker *)GET_HUDELEMENT( CHudItemAttributeTracker ); + if ( pTrackerPanel ) + { + pTrackerPanel->InvalidateLayout(); + } +} +ConVar tf_contract_progress_show( "tf_contract_progress_show", "1", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Settings for the contract HUD element: 0 show nothing, 1 show everything, 2 show only active contracts.", cc_contract_progress_show_update ); +ConVar tf_contract_competitive_show( "tf_contract_competitive_show", "2", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Settings for the contract HUD element during competitive matches: 0 show nothing, 1 show everything, 2 show only active contracts.", cc_contract_progress_show_update ); + +EContractHUDVisibility GetContractHUDVisibility() +{ + if ( TFGameRules() && TFGameRules()->IsMatchTypeCompetitive() ) + return (EContractHUDVisibility)tf_contract_competitive_show.GetInt(); + + return (EContractHUDVisibility)tf_contract_progress_show.GetInt(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CItemAttributeProgressPanel::CItemAttributeProgressPanel( Panel* pParent, const char *pElementName, const CQuestObjectiveDefinition *pObjectiveDef, const char* pszResFileName ) + : EditablePanel( pParent, pElementName ) + , m_nDefIndex( pObjectiveDef->GetDefinitionIndex() ) + , m_flUpdateTime( 0.f ) + , m_flLastThink( 0.f ) + , m_bAdvanced( pObjectiveDef->IsAdvanced() ) + , m_strResFileName( pszResFileName ) +{ + Assert( !m_strResFileName.IsEmpty() ); + + m_pAttribBlur = new Label( this, "AttribBlur", "" ); + m_pAttribGlow = new Label( this, "AttribGlow", "" ); + m_pAttribDesc = new Label( this, "AttribDesc", "" ); + + REGISTER_COLOR_AS_OVERRIDABLE( m_enabledTextColor, "enabled_text_color_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( m_disabledTextColor, "disabled_text_color_override" ); + + // We're being set for the first time, instantly be progressed + m_pAttribBlur->SetAlpha( 0 ); + m_pAttribGlow->SetAlpha( 0 ); + m_flUpdateTime = Plat_FloatTime() - ATTRIB_TRACK_GLOW_HOLD_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemAttributeProgressPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings( m_strResFileName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemAttributeProgressPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_strNormalPointLocToken = inResourceData->GetString( "normal_token" ); + m_strAdvancedLocToken = inResourceData->GetString( "advanced_token" ); + + const CQuestObjectiveDefinition *pObjectiveDef = GEconItemSchema().GetQuestObjectiveByDefIndex( m_nDefIndex ); + if ( pObjectiveDef ) + { + const char *pszDescriptionToken = pObjectiveDef->GetDescriptionToken(); + + locchar_t loc_ItemDescription[MAX_ITEM_NAME_LENGTH]; + const locchar_t *pLocalizedObjectiveName = GLocalizationProvider()->Find( pszDescriptionToken ); + if ( !pLocalizedObjectiveName || !pLocalizedObjectiveName[0] ) + { + // Couldn't localize it, just use it raw + GLocalizationProvider()->ConvertUTF8ToLocchar( pszDescriptionToken, loc_ItemDescription, ARRAYSIZE( loc_ItemDescription ) ); + } + else + { + locchar_t loc_IntermediateName[ MAX_ITEM_NAME_LENGTH ]; + locchar_t locValue[ MAX_ITEM_NAME_LENGTH ]; + loc_sprintf_safe( locValue, LOCCHAR( "%d" ), pObjectiveDef->GetPoints() ); + + loc_scpy_safe( loc_IntermediateName, CConstructLocalizedString( pLocalizedObjectiveName, locValue ) ); + + locchar_t *pszLocString = GLocalizationProvider()->Find( pObjectiveDef->IsAdvanced() ? m_strAdvancedLocToken : m_strNormalPointLocToken ); + + loc_scpy_safe( loc_ItemDescription, CConstructLocalizedString( pszLocString, loc_IntermediateName ) ); + } + + SetDialogVariable( "attr_desc", loc_ItemDescription ); + } + + //SetTall( GetContentTall() ); + //m_pAttribDesc->SetTall( GetTall() ); + + InvalidateLayout(); +} + +void CItemAttributeProgressPanel::SetIsValid( bool bIsValid ) +{ + if ( bIsValid ) + { + m_pAttribDesc->SetFgColor( m_enabledTextColor ); + } + else + { + m_pAttribDesc->SetFgColor( m_disabledTextColor ); + } +} + +int CItemAttributeProgressPanel::GetContentTall() const +{ + // Find the bottom of the text + int nTextWide = 0, nTextTall = 0; + m_pAttribDesc->GetContentSize( nTextWide, nTextTall ); + int nTextYpos = m_pAttribDesc->GetYPos(); + return nTextYpos + nTextTall; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemAttributeProgressPanel::SetProgress( Color glowColor ) +{ + m_flUpdateTime = Plat_FloatTime(); + m_pAttribBlur->SetAlpha( 255 ); + m_pAttribGlow->SetAlpha( 255 ); + m_pAttribDesc->SetAlpha( 0 ); + m_pAttribBlur->SetFgColor( glowColor ); + m_pAttribGlow->SetFgColor( glowColor ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemAttributeProgressPanel::OnThink() +{ + float flGlowTime = Plat_FloatTime() - m_flUpdateTime; + if ( flGlowTime > ATTRIB_TRACK_GLOW_HOLD_TIME ) + { + float flGlowAlpha = RemapValClamped( flGlowTime, ATTRIB_TRACK_GLOW_HOLD_TIME, ATTRIB_TRACK_GLOW_HOLD_TIME + 0.25f, 1.f, 0.f ); + m_pAttribBlur->SetAlpha( 255 * flGlowAlpha ); + m_pAttribGlow->SetAlpha( 255 * flGlowAlpha ); + m_pAttribDesc->SetAlpha( 255 * ( 1.f - flGlowAlpha ) ); + } + + m_flLastThink = Plat_FloatTime(); +} + +float CItemTrackerPanel::m_sflEventRecievedTime = 0.f; +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CItemTrackerPanel::CItemTrackerPanel( Panel* pParent, const char *pElementName, const CEconItem* pItem, const char* pszItemTrackerResFile ) + : EditablePanel( pParent, pElementName ) + , m_pItem( NULL ) + , m_pCompletedContainer( NULL ) + , m_pCompletedDescGlow( NULL ) + , m_pCompletedNameGlow( NULL ) + , m_flStandardTargetProgress( 0.f ) + , m_flStandardCurrentProgress( 0.f ) + , m_flBonusCurrentProgress( 0.f ) + , m_flBonusTargetProgress( 0.f ) + , m_flUpdateTime( 0.f ) + , m_flLastThink( 0.f ) + , m_nMaxStandardPoints( 0 ) + , m_nMaxBonusPoints( 0 ) + , m_eSoundToPlay( SOUND_NONE ) + , m_nContentTall( 0 ) + , m_bNoEffects( false ) + , m_strItemTrackerResFile( pszItemTrackerResFile ) +{ + Assert( pszItemTrackerResFile ); + + SetItem( pItem ); + ListenForGameEvent( "quest_objective_completed" ); + ListenForGameEvent( "player_spawn" ); + ListenForGameEvent( "inventory_updated" ); + ListenForGameEvent( "localplayer_changeclass" ); + ListenForGameEvent( "schema_updated" ); + + m_pItemName = new Label( this, "ItemName", "" ); + m_pCompletedContainer = new EditablePanel( this, "CompletedContainer" ); + + m_pProgressBarBackground = new EditablePanel( this, "ProgressBarBG" ); + m_pProgressBarStandard = new EditablePanel( m_pProgressBarBackground, "ProgressBarStandard" ); + m_pProgressBarBonus = new EditablePanel( m_pProgressBarBackground, "ProgressBarBonus" ); + m_pProgressBarStandardHighlight = new EditablePanel( m_pProgressBarBackground, "ProgressBarStandardHighlight" ); + m_pProgressBarBonusHighlight = new EditablePanel( m_pProgressBarBackground, "ProgressBarBonusHighlight" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CItemTrackerPanel::~CItemTrackerPanel() +{} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_strItemAttributeResFile = inResourceData->GetString( "item_attribute_res_file" ); + m_strProgressBarStandardLocToken = inResourceData->GetString( "progress_bar_standard_loc_token" ); + m_strProgressBarAdvancedLocToken = inResourceData->GetString( "progress_bar_advanced_loc_token" ); + + Assert( !m_strItemAttributeResFile.IsEmpty() ); + Assert( !m_strProgressBarStandardLocToken.IsEmpty() ); + Assert( !m_strProgressBarAdvancedLocToken.IsEmpty() ); + + m_strStandardObjectiveTick = inResourceData->GetString( "standard_objective_tick_sound", NULL ); + m_strStandardPointsComplete = inResourceData->GetString( "standard_points_complete_sound", NULL ); + m_strAdvancedObjectiveComplete = inResourceData->GetString( "advanced_objective_sound_complete", NULL ); + m_strAdvancedPointsComplete = inResourceData->GetString( "advanced_points_complete_sound", NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings( m_strItemTrackerResFile ); + + m_pCompletedDescGlow = FindControl<Label>( "CompleteGlowText", true ); + m_pCompletedNameGlow = FindControl<Label>( "CompleteItemNameGlow", true ); + + QuestObjectiveDefVec_t vecChosenObjectives; + if ( m_pItem ) + { + m_pItem->GetItemDefinition()->GetQuestDef()->GetRolledObjectivesForItem( vecChosenObjectives, m_pItem->GetSOCData() ); + } + FOR_EACH_VEC( vecChosenObjectives, i ) + { + auto pObjective = vecChosenObjectives[ i ]; + + // Find or create the individual attribute panel + CItemAttributeProgressPanel *pPanel = GetPanelForObjective( pObjective ); + pPanel->InvalidateLayout(); + } + + CaptureProgress(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int QuestSort_PointsAscending( CItemAttributeProgressPanel* const* p1, CItemAttributeProgressPanel* const* p2 ) +{ + const CQuestObjectiveDefinition* pObj1 = GEconItemSchema().GetQuestObjectiveByDefIndex( (*p1)->m_nDefIndex ); + const CQuestObjectiveDefinition* pObj2 = GEconItemSchema().GetQuestObjectiveByDefIndex( (*p2)->m_nDefIndex ); + + if ( pObj1->GetPoints() != pObj2->GetPoints() ) + { + // Smallest point value on the bottom + return pObj1->GetPoints() - pObj2->GetPoints(); + } + + return pObj1->GetDefinitionIndex() - pObj2->GetDefinitionIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( !m_pItem ) + return; + + // Set the name into dialog variables + const wchar_t *pszLocToken = g_pVGuiLocalize->Find( m_pItem->GetItemDefinition()->GetQuestDef()->GetRolledNameForItem( m_pItem->GetSOCData() ) ); + SetDialogVariable( "itemname", pszLocToken ); + if ( m_pCompletedContainer ) + { + m_pCompletedContainer->SetDialogVariable( "itemname", pszLocToken ); + } + + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_pItem->GetItemID() ); + + if ( pItemTracker ) + { + bool bStandardPointsCompleteAndBonusPossible = m_flStandardCurrentProgress == m_flStandardTargetProgress && m_flStandardTargetProgress == 1.f && m_nMaxBonusPoints > 0; + uint32 nCurrentPoints = bStandardPointsCompleteAndBonusPossible ? pItemTracker->GetEarnedBonusPoints() : pItemTracker->GetEarnedStandardPoints(); + uint32 nTargetPoints = bStandardPointsCompleteAndBonusPossible ? m_nMaxBonusPoints : m_nMaxStandardPoints; + + locchar_t locValue[ MAX_ITEM_NAME_LENGTH ]; + const locchar_t *pPointsToken = GLocalizationProvider()->Find( bStandardPointsCompleteAndBonusPossible ? m_strProgressBarAdvancedLocToken : m_strProgressBarStandardLocToken ); + loc_scpy_safe( locValue, CConstructLocalizedString( pPointsToken, nCurrentPoints, nTargetPoints ) ); + + m_pProgressBarBackground->SetDialogVariable( "points", locValue ); + m_pProgressBarStandard->SetDialogVariable( "points" , locValue ); + m_pProgressBarStandardHighlight->SetDialogVariable( "points" , locValue ); + m_pProgressBarBonus->SetDialogVariable( "points", locValue ); + m_pProgressBarBonusHighlight->SetDialogVariable( "points" , locValue ); + } + else + { + m_pProgressBarBackground->SetDialogVariable( "points", "" ); + m_pProgressBarStandard->SetDialogVariable( "points" , "" ); + m_pProgressBarStandardHighlight->SetDialogVariable( "points" , "" ); + m_pProgressBarBonus->SetDialogVariable( "points", "" ); + m_pProgressBarBonusHighlight->SetDialogVariable( "points" , "" ); + } + + m_pProgressBarStandardHighlight->SetVisible( !m_bNoEffects ); + m_pProgressBarBonusHighlight->SetVisible( !m_bNoEffects ); + + int nWide = 0, nTall = 0; + if ( m_pItemName->IsVisible() && !m_bNoEffects ) + { + m_pItemName->GetContentSize( nWide, nTall ); + } + + int nX = m_nAttribXOffset; + int nY = nTall + m_nAttribYStartOffset; + + bool bCompleted = true; + + m_vecAttribPanels.Sort( &QuestSort_PointsAscending ); + + if ( !IsStandardCompleted() ) + { + bCompleted = false; + } + + m_pCompletedContainer->SetVisible( bCompleted ); + + if ( bCompleted && !m_bNoEffects ) + { + m_pCompletedContainer->SetVisible( true ); + CExLabel* pCompleted = m_pCompletedContainer->FindControl< CExLabel >( "CompleteDesc", true ); + CExLabel* pCompletedGlow = m_pCompletedContainer->FindControl< CExLabel > ( "CompleteGlowText", true ); + if ( pCompleted && pCompletedGlow ) + { + const wchar_t *pszText = NULL; + const char *pszTextKey = "#QuestTracker_Complete"; + if ( pszTextKey ) + { + pszText = g_pVGuiLocalize->Find( pszTextKey ); + } + if ( pszText ) + { + wchar_t wzFinal[512] = L""; + UTIL_ReplaceKeyBindings( pszText, 0, wzFinal, sizeof( wzFinal ), GAME_ACTION_SET_FPSCONTROLS ); + pCompleted->SetText( wzFinal ); + pCompletedGlow->SetText( wzFinal ); + } + } + + nY = m_pCompletedContainer->GetTall(); + } + else + { + m_pCompletedContainer->SetVisible( false ); + } + + // Place the bars at the bottom of the text, not the bottom of the text panel + m_pProgressBarBackground->SetPos( m_pProgressBarBackground->GetXPos(), nY ); + nY += m_pProgressBarBackground->GetTall() + m_nBarGap; + + UpdateBars(); + + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + FOR_EACH_VEC( m_vecAttribPanels, i ) + { + CItemAttributeProgressPanel* pPanel = m_vecAttribPanels[i]; + + // Hide all objectives if everything is completed, or just hide standard objectives is standard points re completed + if ( !m_bNoEffects && ( IsEverythingCompleted() || ( IsStandardCompleted() && !pPanel->IsAdvanced() ) ) ) + { + pPanel->SetVisible( false ); + } + else + { + pPanel->SetPos( nX, nY ); + nY += pPanel->GetContentTall(); + pPanel->SetVisible( true ); + + InvalidReasonsContainer_t invalidReasons; + if ( pItemTracker && pLocalPlayer ) + { + // Fixup validity, which changes the color of the + const CBaseQuestObjectiveTracker* pObjectiveTracker = pItemTracker->FindTrackerForDefIndex( pPanel->m_nDefIndex ); + if ( pObjectiveTracker ) + { + pObjectiveTracker->IsValidForPlayer( pLocalPlayer, invalidReasons ); + } + } + + pPanel->SetIsValid( invalidReasons.IsValid() ); + + nY += m_nAttribYStep; + } + } + + m_nContentTall = nY; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CItemTrackerPanel::GetContentTall() const +{ + return m_nContentTall; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::OnThink() +{ + BaseClass::OnThink(); + + if ( !m_pItem || m_bNoEffects ) + return; + + static double s_flLastSoundTime = 0; + // Give a little time in case other messaes are in flight + if ( ( Plat_FloatTime() - m_sflEventRecievedTime ) > 0.1f ) + { + // Dont play sounds very frequently + if ( m_eSoundToPlay != SOUND_NONE && ( ( Plat_FloatTime() - s_flLastSoundTime ) > 0.1f ) ) + { + const char *pszSoundName = NULL; + + // Figure out which sound to play + switch( m_eSoundToPlay ) + { + case SOUND_STANDARD_OBJECTIVE_TICK: + pszSoundName = m_strStandardObjectiveTick; + break; + case SOUND_ADVANCED_OBJECTIVE_TICK: + pszSoundName = m_strAdvancedObjectiveComplete; + break; + case SOUND_QUEST_STANDARD_COMPLETE: + pszSoundName = m_strStandardPointsComplete; + break; + case SOUND_QUEST_ADVANCED_COMPLETE: + pszSoundName = m_strAdvancedPointsComplete; + break; + }; + + s_flLastSoundTime = Plat_FloatTime(); + CLocalPlayerFilter filter; + + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName ); + } + + m_eSoundToPlay = SOUND_NONE; + } + + // If the highlight is done, and the quest is ready to turn in, pulse a glow behind + // the title and the completion instruction + if ( IsDoneProgressing() && IsStandardCompleted() ) + { + if ( m_pCompletedDescGlow && m_pCompletedNameGlow ) + { + float flPeriod = ( sin( gpGlobals->curtime * ATTRIB_TRACK_COMPLETE_PULSE_RATE ) * 0.5f ) + 0.5f; + float flGlowAlpha = RemapValClamped( flPeriod, ATTRIB_TRACK_COMPLETE_PULSE_DIM_HOLD, ATTRIB_TRACK_COMPLETE_PULSE_GLOW_HOLD, 0.f, 1.f ); + m_pCompletedDescGlow->SetAlpha( 255 * flGlowAlpha ); + m_pCompletedNameGlow->SetAlpha( 255 * flGlowAlpha ); + } + } + + float flGlowTime = Plat_FloatTime() - m_flUpdateTime; + if ( flGlowTime > ATTRIB_TRACK_GLOW_HOLD_TIME && ( m_flStandardCurrentProgress != m_flStandardTargetProgress || m_flBonusCurrentProgress != m_flBonusTargetProgress ) ) + { + float flDelta = Plat_FloatTime() - m_flLastThink; + m_flStandardCurrentProgress = Approach( m_flStandardTargetProgress, m_flStandardCurrentProgress, flDelta * ATTRIB_TRACK_BAR_GROW_RATE ); + m_flBonusCurrentProgress = Approach( m_flBonusTargetProgress, m_flBonusCurrentProgress, flDelta * ATTRIB_TRACK_BAR_GROW_RATE ); + + // Resize/position the bars + UpdateBars(); + + // This happens when all the bars are all caught up + if ( IsDoneProgressing() ) + { + InvalidateLayout(); + } + } + + if ( IsDoneProgressing() ) + { + m_pProgressBarStandardHighlight->SetVisible( false ); + m_pProgressBarBonusHighlight->SetVisible( false ); + } + + m_flLastThink = Plat_FloatTime(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::FireGameEvent( IGameEvent *pEvent ) +{ + if ( FStrEq( pEvent->GetName(), "quest_objective_completed" ) ) + { + itemid_t nIDLow = 0x00000000FFFFFFFF & (itemid_t)pEvent->GetInt( "quest_item_id_low" ); + itemid_t nIDHi = 0xFFFFFFFF00000000 & (itemid_t)pEvent->GetInt( "quest_item_id_hi" ) << 32; + itemid_t nID = nIDLow | nIDHi; + if ( !m_pItem || m_pItem->GetItemID() != nID ) + return; + + // Capture whatever progress has happened + CaptureProgress(); + + uint32 nObjectiveDefIndex = pEvent->GetInt( "quest_objective_id" ); + + // Don't do sounds if there's no objective that made this progress + if ( nObjectiveDefIndex == (uint32)-1 ) + return; + + bool bAdvanced = false; + const CQuestObjectiveDefinition* pObjective = GEconItemSchema().GetQuestObjectiveByDefIndex( nObjectiveDefIndex ); + if ( !pObjective ) + return; + + bAdvanced = pObjective->IsAdvanced(); + + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_pItem->GetItemID() ); + + if ( pItemTracker ) + { + const Color& glowColor = pItemTracker->GetEarnedStandardPoints() < m_nMaxStandardPoints ? m_clrStandardHighlight + : m_clrBonusHighlight; + FOR_EACH_VEC( m_vecAttribPanels, i ) + { + if ( m_vecAttribPanels[i]->m_nDefIndex == nObjectiveDefIndex ) + { + m_vecAttribPanels[i]->SetProgress( glowColor ); + break; + } + } + + // Check if it's time to play a sound + ESoundToPlay eNewSound = SOUND_NONE; + + if ( m_flStandardCurrentProgress < m_flStandardTargetProgress && pItemTracker->GetEarnedStandardPoints() == m_nMaxStandardPoints ) + { + eNewSound = SOUND_QUEST_STANDARD_COMPLETE; + } + else if ( m_flBonusCurrentProgress < m_flBonusTargetProgress && pItemTracker->GetEarnedBonusPoints() == m_nMaxBonusPoints ) + { + eNewSound = SOUND_QUEST_ADVANCED_COMPLETE; + } + else if ( bAdvanced ) + { + eNewSound = SOUND_ADVANCED_OBJECTIVE_TICK; + } + else + { + eNewSound = SOUND_STANDARD_OBJECTIVE_TICK; + } + + // Priority will handle this for us + if ( eNewSound > m_eSoundToPlay ) + { + m_eSoundToPlay = eNewSound; + if ( m_sflEventRecievedTime < Plat_FloatTime() ) + { + m_sflEventRecievedTime = Plat_FloatTime(); + } + } + } + } + else if ( FStrEq( pEvent->GetName(), "player_spawn" ) + || FStrEq( pEvent->GetName(), "inventory_updated" ) + || FStrEq( pEvent->GetName(), "localplayer_changeclass" ) ) + { + InvalidateLayout(); + } + else if ( FStrEq( pEvent->GetName(), "schema_updated" ) ) + { + FOR_EACH_VEC( m_vecAttribPanels, i ) + { + m_vecAttribPanels[ i ]->InvalidateLayout(); + } + + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::UpdateBars() +{ + // Everything is relative to the background bar + int nWide = m_pProgressBarBackground->GetWide(); + + // Resize standard bar + m_pProgressBarStandard->SetWide( floor(m_flStandardCurrentProgress * nWide ) ); + // Resize bonus bar + m_pProgressBarBonus->SetWide( floor(m_flBonusCurrentProgress * nWide ) ); + + // Highlight bars snap to the target width + m_pProgressBarStandardHighlight->SetWide( floor(m_flStandardTargetProgress * nWide) ); + m_pProgressBarBonusHighlight->SetWide( floor( m_flBonusTargetProgress * nWide ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::CaptureProgress() +{ + if ( !m_pItem ) + return; + + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_pItem->GetItemID() ); + + // Fixup bar bounds + float flStandardProgress = 0.f; + float flBonusProgress = 0.f; + + if ( pItemTracker ) + { + // We use the item trackers for quest progress since they're the most up-to-date + flStandardProgress = (float)pItemTracker->GetEarnedStandardPoints() / (float)( m_nMaxStandardPoints); + flBonusProgress = (float)pItemTracker->GetEarnedBonusPoints() / (float)( m_nMaxBonusPoints ); + } + + // Standard progress + bool bChange = flStandardProgress != m_flStandardTargetProgress; + m_flStandardTargetProgress = flStandardProgress; + + // Bonus progress + bChange |= flBonusProgress != m_flBonusTargetProgress; + m_flBonusTargetProgress = flBonusProgress; + + // We're being set for the first time, instantly be progressed + if ( m_flUpdateTime == 0.f || m_bNoEffects ) + { + m_flStandardCurrentProgress = m_flStandardTargetProgress; + m_flBonusCurrentProgress = m_flBonusTargetProgress; + + m_flUpdateTime = Plat_FloatTime() - ATTRIB_TRACK_GLOW_HOLD_TIME; + InvalidateLayout(); + } + else if ( bChange ) // If this is a change, play effects + { + m_flUpdateTime = Plat_FloatTime(); + InvalidateLayout(); + } + + m_pProgressBarStandardHighlight->SetVisible( !m_bNoEffects ); + m_pProgressBarBonusHighlight->SetVisible( !m_bNoEffects ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CItemTrackerPanel::IsStandardCompleted() const +{ + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_pItem->GetItemID() ); + if ( pItemTracker ) + { + return pItemTracker->GetEarnedStandardPoints() >= m_nMaxStandardPoints; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CItemTrackerPanel::IsEverythingCompleted() const +{ + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_pItem->GetItemID() ); + if ( pItemTracker ) + { + return ( pItemTracker->GetEarnedBonusPoints() + pItemTracker->GetEarnedStandardPoints() ) >= ( m_nMaxBonusPoints + m_nMaxStandardPoints ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CItemTrackerPanel::SetItem( const CEconItem* pItem ) +{ + m_pItem.SetItem( TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( pItem->GetItemID() ) ); + + m_nMaxStandardPoints = pItem->GetItemDefinition()->GetQuestDef()->GetMaxStandardPoints(); + m_nMaxBonusPoints = pItem->GetItemDefinition()->GetQuestDef()->GetMaxBonusPoints(); + + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CItemAttributeProgressPanel* CItemTrackerPanel::GetPanelForObjective( const CQuestObjectiveDefinition* pObjective ) +{ + FOR_EACH_VEC( m_vecAttribPanels, i ) + { + if ( m_vecAttribPanels[ i ]->m_nDefIndex == pObjective->GetDefinitionIndex() ) + return m_vecAttribPanels[ i ]; + } + + CItemAttributeProgressPanel *pPanel = new CItemAttributeProgressPanel( this, "ItemAttributeProgressPanel", pObjective, m_strItemAttributeResFile ); + SETUP_PANEL( pPanel ); + + m_vecAttribPanels.AddToTail( pPanel ); + + return pPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CItemTrackerPanel::IsValidForLocalPlayer() const +{ + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + CSteamID steamID; + if ( !pLocalPlayer || !pLocalPlayer->GetSteamID( &steamID ) ) + return false; + + Assert( m_pItem ); + + // Safeguard. There's a crash in public from this. + if ( !m_pItem ) + return false; + + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_pItem->GetItemID() ); + InvalidReasonsContainer_t invalidReasons; + if ( pItemTracker ) + { + int nNumInvalid = pItemTracker->IsValidForPlayer( pLocalPlayer, invalidReasons ); + if ( nNumInvalid < pItemTracker->GetTrackers().Count() ) + { + return true; + } + } + + // Check for completed quests. They are always visible. + CEconItemView *pItem = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_pItem->GetItemID() ); + if ( pItem && IsQuestItemReadyToTurnIn( pItem ) && GetContractHUDVisibility() == CONTRACT_HUD_SHOW_EVERYTHING ) + { + return true; + } + + return false; +} + + +DECLARE_HUDELEMENT( CHudItemAttributeTracker ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudItemAttributeTracker::CHudItemAttributeTracker( const char *pElementName ) + : CHudElement( pElementName ) + , EditablePanel( NULL, "ItemAttributeTracker" ) + , m_mapTrackers( DefLessFunc( itemid_t ) ) +{ + Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + ListenForGameEvent( "player_spawn" ); + ListenForGameEvent( "inventory_updated" ); + ListenForGameEvent( "client_disconnect" ); + ListenForGameEvent( "localplayer_changeclass" ); + ListenForGameEvent( "quest_objective_completed" ); + + RegisterForRenderGroup( "weapon_selection" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "resource/UI/HudItemAttributeTracker.res" ); + + m_pStatusContainer = FindControl< EditablePanel >( "QuestsStatusContainer" ); + m_pStatusHeaderLabel = m_pStatusContainer->FindControl< Label >( "Header" ); + m_pCallToActionLabel = m_pStatusContainer->FindControl< Label >( "CallToAction" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::PerformLayout() +{ + BaseClass::PerformLayout(); + + CUtlVector<CEconItemView*> vecQuestItems; + TFInventoryManager()->GetAllQuestItems( &vecQuestItems ); + int nNumCompleted = 0; + int nNumUnidentified = 0; + int nNumInactive = 0; + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + FOR_EACH_VEC( vecQuestItems, i ) + { + const CEconItemView* pItem = vecQuestItems[i]; + if ( IsQuestItemReadyToTurnIn( pItem ) ) + { + ++nNumCompleted; + } + else if ( IsQuestItemUnidentified( pItem->GetSOCData() ) ) + { + ++nNumUnidentified; + } + else if ( pLocalPlayer ) + { + const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( pItem->GetItemID() ); + InvalidReasonsContainer_t invalidReasons; + if ( pItemTracker ) + { + if ( pItemTracker->IsValidForPlayer( pLocalPlayer, invalidReasons ) == pItemTracker->GetTrackers().Count() ) + { + ++nNumInactive; + } + } + } + } + + const char* pszHeaderString = NULL; + const char* pszCallToActionString = NULL; + int nNumToShow = 0; + if ( nNumCompleted > 0 ) + { + nNumToShow = nNumCompleted; + pszHeaderString = nNumToShow == 1 ? "#QuestTracker_Complete_Single" : "#QuestTracker_Complete_Multiple"; + pszCallToActionString = "QuestTracker_Complete"; + } + else if ( nNumUnidentified > 0 ) + { + nNumToShow = nNumUnidentified; + pszHeaderString = nNumToShow == 1 ? "#QuestTracker_New_Single" : "#QuestTracker_New_Multiple"; + pszCallToActionString = "QuestTracker_New_CallToAction"; + } + else if ( nNumInactive > 0 ) + { + nNumToShow = nNumInactive; + pszHeaderString = nNumToShow == 1 ? "#QuestTracker_Inactive_Single" : "#QuestTracker_Inactive_Multiple"; + pszCallToActionString = "QuestTracker_New_CallToAction"; + } + + bool bAnyStatusToShow = pszHeaderString != NULL; + + bool bShowExtras = bAnyStatusToShow && ( GetContractHUDVisibility() != CONTRACT_HUD_SHOW_ACTIVE ); + if ( bShowExtras ) + { + // Build the "X New Contracts" string + locchar_t locNumNewQuests[ 256 ]; + const locchar_t *pLocalizedString = GLocalizationProvider()->Find( pszHeaderString ); + if ( pLocalizedString && pLocalizedString[0] ) + { + wchar_t wszCounter[ 256 ]; + loc_sprintf_safe( wszCounter, LOCCHAR( "%d" ), nNumToShow); + loc_scpy_safe( locNumNewQuests, + CConstructLocalizedString( pLocalizedString, wszCounter ) ); + } + + m_pStatusContainer->SetDialogVariable( "header", locNumNewQuests ); + + // Build the "Press [ F2 ] to view" string. + const wchar_t *pszText = NULL; + if ( pszCallToActionString ) + { + pszText = g_pVGuiLocalize->Find( pszCallToActionString ); + } + if ( pszText ) + { + wchar_t wzFinal[512] = L""; + UTIL_ReplaceKeyBindings( pszText, 0, wzFinal, sizeof( wzFinal ), GAME_ACTION_SET_FPSCONTROLS ); + m_pStatusContainer->SetDialogVariable( "call_to_action", wzFinal ); + } + } + + int nY = 0; + + if ( m_pStatusContainer ) + { + nY = m_pStatusContainer->GetYPos(); + + m_pStatusContainer->SetVisible( bShowExtras ); + if ( m_pStatusContainer->IsVisible() ) + { + nY += m_pStatusContainer->GetTall(); + int nLabelWide, nLabelTall; + m_pStatusHeaderLabel->GetContentSize( nLabelWide, nLabelTall ); + + m_pStatusContainer->SetWide( m_nStatusBufferWidth + nLabelWide ); + + m_pStatusHeaderLabel->SetPos( m_pStatusContainer->GetWide() - m_pStatusHeaderLabel->GetWide(), m_pStatusHeaderLabel->GetYPos() ); + m_pCallToActionLabel->SetPos( m_pStatusContainer->GetWide() - m_pCallToActionLabel->GetWide(), m_pCallToActionLabel->GetYPos() ); + m_pStatusContainer->SetPos( m_pStatusContainer->GetParent()->GetWide() - m_pStatusContainer->GetWide(), m_pStatusContainer->GetYPos() ); + } + } + + FOR_EACH_MAP( m_mapTrackers, i ) + { + CItemTrackerPanel* pPanel = m_mapTrackers[ i ]; + if ( pPanel ) + { + if ( pPanel->IsValidForLocalPlayer() ) + { + int nTall = pPanel->GetContentTall(); + pPanel->SetPos( GetWide() - pPanel->GetWide(), nY ); + nY += nTall; + pPanel->SetVisible( true ); + } + else + { + pPanel->SetVisible( false ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::OnThink() +{ + BaseClass::OnThink(); + + // If the specator GUI is up, we need to drop down a bit so that we're + // not on top of the death notices + int nY = g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ? g_pSpectatorGUI->GetTopBarHeight() : 0; + int nCurrentX, nCurrentY; + GetPos( nCurrentX, nCurrentY ); + if ( nCurrentY != nY ) + { + SetPos( nCurrentX, nY ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHudItemAttributeTracker::ShouldDraw( void ) +{ + if ( engine->IsPlayingDemo() ) + return false; + + if ( GetContractHUDVisibility() == CONTRACT_HUD_SHOW_NONE ) + return false; + + if ( GetLocalPlayerTeam() < FIRST_GAME_TEAM ) + return false; + + if ( TFGameRules() ) + { + if ( TFGameRules()->ShowMatchSummary() ) + return false; + + if ( TFGameRules()->GetRoundRestartTime() > -1.f ) + { + float flTime = TFGameRules()->GetRoundRestartTime() - gpGlobals->curtime; + if ( flTime <= 10.f ) + return false; + } + } + + return CHudElement::ShouldDraw(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::FireGameEvent( IGameEvent *pEvent ) +{ + if ( FStrEq( pEvent->GetName(), "player_spawn" ) + || FStrEq( pEvent->GetName(), "inventory_updated" ) + || FStrEq( pEvent->GetName(), "client_disconnect" ) + || FStrEq( pEvent->GetName(), "localplayer_changeclass" ) + || FStrEq( pEvent->GetName(), "quest_objective_completed" ) ) + { + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::HandleSOEvent( const CSteamID & steamIDOwner, const CSharedObject *pObject, ETrackerHandling_t eHandling ) +{ + // We only care about items! + if( pObject->GetTypeID() != CEconItem::k_nTypeID ) + return; + + const CEconItem *pItem = (CEconItem *)pObject; + + // We only care about quests + if ( pItem->GetItemDefinition()->GetQuestDef() == NULL ) + return; + + // We dont do anything with trackers for unidentified quests + if ( IsQuestItemUnidentified( pItem ) ) + return; + + CItemTrackerPanel* pTracker = NULL; + switch ( eHandling ) + { + case TRACKER_CREATE: + case TRACKER_UPDATE: + FindTrackerForItem( pItem, &pTracker, true ); + if ( pTracker ) + { + pTracker->SetItem( pItem ); + } + break; + case TRACKER_REMOVE: + FindTrackerForItem( pItem, &pTracker, false ); + if ( pTracker ) + { + m_mapTrackers.Remove( pItem->GetItemID() ); + pTracker->MarkForDeletion(); + } + + break; + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHudItemAttributeTracker::FindTrackerForItem( const CEconItem* pItem, CItemTrackerPanel** ppTracker, bool bCreateIfNotFound ) +{ + (*ppTracker) = NULL; + bool bCreatedNew = false; + + auto idx = m_mapTrackers.Find( pItem->GetItemID() ); + if ( idx == m_mapTrackers.InvalidIndex() && bCreateIfNotFound ) + { + (*ppTracker) = new CItemTrackerPanel( this + , "ItemTrackerPanel" + , pItem + , pItem->GetItemDefinition()->GetQuestDef()->GetQuestTheme()->GetInGameTrackerResFile() ); + (*ppTracker)->InvalidateLayout( true, true ); + m_mapTrackers.Insert( pItem->GetItemID(), (*ppTracker) ); + bCreatedNew = true; + } + else if ( idx != m_mapTrackers.InvalidIndex() ) + { + (*ppTracker) = m_mapTrackers[ idx ]; + } + + return bCreatedNew; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::LevelInit( void ) +{ + // We want to listen for our player's SO cache updates + if ( steamapicontext && steamapicontext->SteamUser() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + GCClientSystem()->GetGCClient()->AddSOCacheListener( steamID, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudItemAttributeTracker::LevelShutdown( void ) +{ + if ( steamapicontext && steamapicontext->SteamUser() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + GCClientSystem()->GetGCClient()->RemoveSOCacheListener( steamID, this ); + } +}
\ No newline at end of file |