//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Scoreboard for MvM // // $NoKeywords: $ //============================================================================= #include "cbase.h" #include "tf_hud_mann_vs_machine_scoreboard.h" #include #include #include "tf_lobby_server.h" using namespace vgui; #ifdef STAGING_ONLY ConVar cl_mvm_log_playerstats( "cl_mvm_log_playerstats", "0", FCVAR_ARCHIVE, "Internal dump of verbose player stats from mvm missions." ); #endif // STAGING_ONLY extern ConVar tf_mvm_respec_credit_goal; extern ConVar tf_mvm_respec_limit; extern ConVar tf_scoreboard_alt_class_icons; DECLARE_BUILD_FACTORY( CMvMScoreboardEnemyInfo ); //----------------------------------------------------------------------------- CMvMScoreboardEnemyInfo::CMvMScoreboardEnemyInfo( Panel *parent, const char *pName ): vgui::EditablePanel( parent, pName ) { } //----------------------------------------------------------------------------- void CMvMScoreboardEnemyInfo::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); // load control settings... LoadControlSettings( "resource/UI/MvMScoreboardEnemyInfo.res" ); } //----------------------------------------------------------------------------- void CMvMScoreboardEnemyInfo::FireGameEvent( IGameEvent *event ) { } //----------------------------------------------------------------------------- void CMvMScoreboardEnemyInfo::UpdateEntry( char* icon, bool bIsGiant ) { const wchar_t *pwchHint = g_pVGuiLocalize->Find( VarArgs( "#TF_PVE_ROBOT_%s", icon ) ); if ( pwchHint && pwchHint[ 0 ] != L'\0' ) { SetDialogVariable( "description", pwchHint ); } else { SetDialogVariable( "description", VarArgs( "NYI: #TF_PVE_ROBOT_%s", icon ) ); } CTFImagePanel *pImage = dynamic_cast< CTFImagePanel* >( FindChildByName( "EnemyIcon" ) ); if ( pImage ) { pImage->SetImage( VarArgs( "../hud/leaderboard_class_%s", icon ) ); pImage->SetVisible( true ); } Panel *pBGPanel = dynamic_cast< Panel* >( FindChildByName( "Background" ) ); if ( pBGPanel ) { if ( bIsGiant ) { pBGPanel->SetBgColor( m_clrMiniBoss ); } else { pBGPanel->SetBgColor( m_clrNormal ); } } } //----------------------------------------------------------------------------- // CTFHudMannVsMachineScoreboard //----------------------------------------------------------------------------- DECLARE_BUILD_FACTORY( CTFHudMannVsMachineScoreboard ); CTFHudMannVsMachineScoreboard::CTFHudMannVsMachineScoreboard( Panel *parent, const char *pName ): vgui::EditablePanel( parent, pName ) { m_pPlayerList = new SectionedListPanel( this, "MvMPlayerList" ); m_pImageList = NULL; m_mapAvatarsToImageList.SetLessFunc( DefLessFunc( CSteamID ) ); m_mapAvatarsToImageList.RemoveAll(); m_pLocalPlayerStatsPanel = new vgui::EditablePanel( this, "LocalPlayerStatsPanel" ); m_pCreditStatsPanel = new vgui::EditablePanel( this, "CreditStatsContainer"); m_pDifficultyContainer = new vgui::EditablePanel( this, "DifficultyContainer"); m_iDisplayedWave = -1; m_bInitialized = false; m_iSquadSurplusTexture = 0; Q_memset( m_iImageClass, NULL, sizeof( m_iImageClass ) ); Q_memset( m_iImageClassAlt, NULL, sizeof( m_iImageClassAlt ) ); ListenForGameEvent( "mvm_wave_complete" ); ListenForGameEvent( "mvm_reset_stats" ); ListenForGameEvent( "player_connect_client" ); } //----------------------------------------------------------------------------- CTFHudMannVsMachineScoreboard::~CTFHudMannVsMachineScoreboard() { if ( NULL != m_pImageList ) { delete m_pImageList; m_pImageList = NULL; } } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); // load control settings... LoadControlSettings( "resource/UI/MvMScoreboard.res" ); InitPlayerList( pScheme ); m_iDisplayedWave = -1; if ( m_pCreditStatsPanel ) { m_pPreviousWaveCreditsInfo = dynamic_cast( m_pCreditStatsPanel->FindChildByName("PreviousWaveCreditInfoPanel") ); m_pTotalCreditsInfo = dynamic_cast( m_pCreditStatsPanel->FindChildByName("TotalGameCreditInfoPanel") ); m_pPreviousWaveCreditsSpend = dynamic_cast( m_pCreditStatsPanel->FindChildByName("PreviousWaveCreditSpendPanel") ); m_pTotalCreditsSpend = dynamic_cast( m_pCreditStatsPanel->FindChildByName("TotalGameCreditSpendPanel") ); m_pRespecStatusLabel = dynamic_cast< CExLabel* >( m_pCreditStatsPanel->FindChildByName( "RespecStatusLabel" ) ); } // Last m_bInitialized = true; m_popfile[0] = '\0'; // clear the popfile name so everything will be reset again in the UpdatePopFile() call in OnTick() OnTick(); } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::FireGameEvent( IGameEvent * event ) { const char *pszEvent = event->GetName(); if ( !Q_strcmp( pszEvent, "mvm_wave_complete" ) ) { if ( !g_TF_PR ) return; #ifdef STAGING_ONLY // Write our current state to a log file if we're configured to if ( cl_mvm_log_playerstats.GetBool() ) { WritePlayerScoreStats(); } #endif // STAGING_ONLY // Adds current stats to "prev" containers (i.e. current wave's stats added to total of all previous wave stats) g_TF_PR->UpdatePlayerScoreStats(); } else if ( !Q_strcmp( pszEvent, "mvm_reset_stats" ) ) { if ( !g_TF_PR ) return; g_TF_PR->ResetPlayerScoreStats(); } else if ( !Q_strcmp( pszEvent, "player_connect_client" ) ) { if ( !g_TF_PR ) return; g_TF_PR->ResetPlayerScoreStats( event->GetInt( "index" ) + 1 ); } } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::OnTick () { // Ensure the panels and everything else have been initialized if (!m_bInitialized || !TFGameRules() || !TFGameRules()->IsMannVsMachineMode() || !TFObjectiveResource() ) { return; } UpdatePlayerList(); UpdateCreditStats(); UpdatePopFile(); } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::InitPlayerList ( IScheme *pScheme ) { // Scoreboard if ( m_pImageList ) { delete m_pImageList; } m_pImageList = new ImageList( false ); m_mapAvatarsToImageList.RemoveAll(); m_pPlayerListBackground = dynamic_cast( FindChildByName("PlayerListBackground") ); m_iImageDead = m_pImageList->AddImage( scheme()->GetImage( "../hud/leaderboard_dead", true ) ); m_iSquadSurplusTexture = m_pImageList->AddImage( scheme()->GetImage( "pve/mvm_squad_surplus_small", true ) ); for(int i = 1 ; i < SCOREBOARD_CLASS_ICONS ; i++) { m_iImageClass[i] = m_pImageList->AddImage( scheme()->GetImage( g_pszClassIcons[i], true ) ); m_iImageClassAlt[i] = m_pImageList->AddImage( scheme()->GetImage( g_pszClassIconsAlt[i], true ) ); } // resize the images to our resolution for (int i = 1 ; i < m_pImageList->GetImageCount(); i++ ) { int wide = 14, tall = 14; m_pImageList->GetImage(i)->SetSize(scheme()->GetProportionalScaledValueEx( GetScheme(), wide ), scheme()->GetProportionalScaledValueEx( GetScheme(),tall ) ); } m_pPlayerList->SetVerticalScrollbar( false ); m_pPlayerList->RemoveAll(); m_pPlayerList->RemoveAllSections(); m_pPlayerList->AddSection( 0, "Players" ); m_pPlayerList->SetSectionAlwaysVisible( 0, true ); m_pPlayerList->SetSectionFgColor( 0, Color( 255, 255, 255, 255 ) ); m_pPlayerList->SetBgColor( Color( 0, 0, 0, 0 ) ); m_pPlayerList->SetBorder( NULL ); m_hScoreFont = pScheme->GetFont( "ScoreboardVerySmall", true ); m_pPlayerList->AddColumnToSection( 0, "medal", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, m_iMedalWidth ); m_pPlayerList->AddColumnToSection( 0, "spacer1", "", 0, m_iMedalSpacerWidth ); m_pPlayerList->AddColumnToSection( 0, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth ); m_pPlayerList->AddColumnToSection( 0, "spacer0", "", 0, m_iSpacerWidth ); m_pPlayerList->AddColumnToSection( 0, "name", "#TF_Scoreboard_Name", 0, m_iNameWidth ); m_pPlayerList->AddColumnToSection( 0, "class", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, m_iClassWidth ); m_pPlayerList->AddColumnToSection( 0, "tour_no", "#TF_MvMScoreboard_Tour", SectionedListPanel::COLUMN_RIGHT, m_iClassWidth ); m_pPlayerList->AddColumnToSection( 0, "score", "#TF_Scoreboard_Score", SectionedListPanel::COLUMN_RIGHT, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "damage", "#TF_MvMScoreboard_Damage", SectionedListPanel::COLUMN_RIGHT, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "tank", "#TF_MvMScoreboard_Tank", SectionedListPanel::COLUMN_RIGHT, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "healing", "#TF_MvMScoreboard_Healing", SectionedListPanel::COLUMN_RIGHT, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "support", "#TF_MvMScoreboard_Support", SectionedListPanel::COLUMN_RIGHT, m_iStatWidth ); // m_pPlayerList->AddColumnToSection( 0, "blocked", "Blocked", 0, m_iStatWidth ); // m_pPlayerList->AddColumnToSection( 0, "bonus", "Bonus", 0, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "credits", "#TF_MvMScoreboard_Money", SectionedListPanel::COLUMN_RIGHT, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "squad_surplus", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, m_iStatWidth ); m_pPlayerList->AddColumnToSection( 0, "ping", "#TF_Scoreboard_Ping", SectionedListPanel::COLUMN_RIGHT, m_iPingWidth ); m_pPlayerList->SetImageList( m_pImageList, false ); m_pPlayerList->SetVisible( true ); } //----------------------------------------------------------------------------- char *ConvertScoreboardValueToString( int iValue ) { static char szConversion[32]; szConversion[0] = '\0'; if ( iValue >= 1000000 ) { V_sprintf_safe( szConversion, "%d%s%d%d%d%s%d%d%d", iValue / 1000000, ",", ( iValue % 1000000 ) / 100000, ( iValue % 100000 ) / 10000, ( iValue % 10000 ) / 1000, ",", ( iValue % 1000 ) / 100, ( iValue % 100 ) / 10, iValue % 10 ); } else if ( iValue >= 1000 ) { V_sprintf_safe( szConversion, "%d%s%d%d%d", iValue / 1000, ",", ( iValue % 1000 ) / 100, ( iValue % 100 ) / 10, iValue % 10 ); } else if ( iValue >= 0 ) { V_sprintf_safe( szConversion, "%d", iValue ); } return szConversion; } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::UpdatePlayerList () { m_pPlayerList->ClearSelection(); m_pPlayerList->RemoveAll(); if ( !g_TF_PR ) return; for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ ) { if( !g_PR->IsConnected( playerIndex ) ) { continue; } if ( g_PR->GetTeam( playerIndex ) != TF_TEAM_PVE_DEFENDERS ) { continue; } const char *szName = g_TF_PR->GetPlayerName( playerIndex ); KeyValues *pKeyValues = new KeyValues( "data" ); pKeyValues->SetInt( "playerIndex", playerIndex ); pKeyValues->SetString( "name", szName ); bool bAlive = g_TF_PR->IsAlive( playerIndex ); if( g_PR->IsConnected( playerIndex ) ) { int iClass = g_TF_PR->GetPlayerClass( playerIndex ); if ( GetLocalPlayerIndex() == playerIndex && !bAlive ) { // If this is local player and he is dead, show desired class (which he will spawn as) rather than current class. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); int iDesiredClass = pPlayer->m_Shared.GetDesiredPlayerClassIndex(); // use desired class unless it's random -- if random, his future class is not decided until moment of spawn if ( TF_CLASS_RANDOM != iDesiredClass ) { iClass = iDesiredClass; } } else { // for non-local players, show the current class iClass = g_TF_PR->GetPlayerClass( playerIndex ); } if ( iClass >= TF_FIRST_NORMAL_CLASS && iClass <= TF_LAST_NORMAL_CLASS ) { if ( bAlive ) { pKeyValues->SetInt( "class", tf_scoreboard_alt_class_icons.GetBool() ? m_iImageClassAlt[iClass] : m_iImageClass[iClass] ); } else { pKeyValues->SetInt( "class", tf_scoreboard_alt_class_icons.GetBool() ? m_iImageClassAlt[iClass + 9] : m_iImageClass[iClass + 9] ); // +9 is to jump ahead to the darker dead icons } } else { pKeyValues->SetInt( "class", 0 ); } } // check for bots first, so malicious server operators can't fake a ping and stuff their server with bots that look like players if ( g_PR->IsFakePlayer( playerIndex ) ) { pKeyValues->SetString( "ping", "#TF_Scoreboard_Bot" ); } else { if ( g_PR->GetPing( playerIndex ) < 1 ) { pKeyValues->SetString( "ping", "" ); } else { pKeyValues->SetString( "ping", ConvertScoreboardValueToString( g_PR->GetPing( playerIndex ) ) ); } } Color fgClr; Color bgClr; // change color based off alive or dead status. Also slightly different if its local player since the name is highlighted. if ( bAlive ) { UpdatePlayerAvatar( playerIndex, pKeyValues ); fgClr = g_PR->GetTeamColor( TF_TEAM_RED ); bgClr = Color( 0, 0, 0, 0 ); } else { pKeyValues->SetInt( "avatar", m_iImageDead ); fgClr = Color( 117, 107, 94, 255 ); bgClr = Color( 78, 78, 78, 150 ); } // the medal column is just a place holder for the images that are displayed later pKeyValues->SetInt( "medal", 0 ); if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) { CTFGSLobby *pLobby = GTFGCClientSystem()->GetLobby(); if ( pLobby ) { int nTourNo = 0; int bSurplusEnabled = false; const CTFLobbyMember *pMember = pLobby->GetMemberDetails( GetSteamIDForPlayerIndex( playerIndex ) ); if ( pMember ) { bSurplusEnabled = pMember->squad_surplus(); nTourNo = pMember->badge_level(); } if ( bSurplusEnabled ) { pKeyValues->SetInt( "squad_surplus", m_iSquadSurplusTexture ); } else { pKeyValues->SetInt( "squad_surplus", 0 ); } if ( nTourNo > 0 ) { pKeyValues->SetString( "tour_no", ConvertScoreboardValueToString( nTourNo ) ); } else { pKeyValues->SetString( "tour_no", "" ); } } // int nTeam = g_PR->GetTeam( playerIndex ); // int nTeamDamage = g_TF_PR->GetTeamDamage( nTeam ); // int nTeamTankDamage = g_TF_PR->GetTeamDamageBoss( nTeam ); // int nTeamAssist = g_TF_PR->GetTeamDamageAssist( nTeam ) + g_TF_PR->GetTeamHealingAssist( nTeam ); // int nTeamHealing = g_TF_PR->GetTeamHealingAssist( nTeam ); // Note: Inflate bonus points when bucketing with healing and damage assist. // We do this because each have different weights. Example: Every 250 points // of healing or damage assist results in 1 point of Score. Bonus requires // only 10. This makes it easier for players to evaluate their "support" value. int nSupport = g_TF_PR->GetDamageAssist( playerIndex ) + g_TF_PR->GetHealingAssist( playerIndex ) + g_TF_PR->GetDamageBlocked( playerIndex ) + ( g_TF_PR->GetBonusPoints( playerIndex ) * 25 ); pKeyValues->SetString( "score", ConvertScoreboardValueToString( g_TF_PR->GetTotalScore( playerIndex ) ) ); pKeyValues->SetString( "damage", ConvertScoreboardValueToString( g_TF_PR->GetDamage( playerIndex ) ) ); pKeyValues->SetString( "tank", ConvertScoreboardValueToString( g_TF_PR->GetDamageBoss( playerIndex ) ) ); pKeyValues->SetString( "healing", ConvertScoreboardValueToString( g_TF_PR->GetHealing( playerIndex ) ) ); pKeyValues->SetString( "support", ConvertScoreboardValueToString( nSupport ) ); //pKeyValues->SetString( "blocked", ConvertScoreboardValueToString( g_TF_PR->GetDamageBlocked( playerIndex ) ) ); //pKeyValues->SetString( "bonus", ConvertScoreboardValueToString( g_TF_PR->GetBonusPoints( playerIndex ) ) ); pKeyValues->SetString( "credits", ConvertScoreboardValueToString( g_TF_PR->GetCurrencyCollected( playerIndex ) ) ); } int itemID = m_pPlayerList->AddItem( 0, pKeyValues ); m_pPlayerList->SetItemFgColor( itemID, fgClr ); m_pPlayerList->SetItemBgColor( itemID, bgClr ); m_pPlayerList->SetItemFont( itemID, m_hScoreFont ); pKeyValues->deleteThis(); } // force the list to PerformLayout() now so we can update our medal images after we return m_pPlayerList->InvalidateLayout( true ); } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::UpdatePlayerAvatar( int playerIndex, KeyValues *kv ) { // Update their avatar if ( kv && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() ) { player_info_t pi; if ( engine->GetPlayerInfo( playerIndex, &pi ) ) { if ( pi.friendsID ) { CSteamID steamIDForPlayer( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual ); // See if we already have that avatar in our list int iMapIndex = m_mapAvatarsToImageList.Find( steamIDForPlayer ); int iImageIndex; if ( iMapIndex == m_mapAvatarsToImageList.InvalidIndex() ) { CAvatarImage *pImage = new CAvatarImage(); pImage->SetAvatarSteamID( steamIDForPlayer ); pImage->SetAvatarSize( 32, 32 ); // Deliberately non scaling pImage->SetDrawFriend( false ); iImageIndex = m_pImageList->AddImage( pImage ); m_mapAvatarsToImageList.Insert( steamIDForPlayer, iImageIndex ); } else { iImageIndex = m_mapAvatarsToImageList[ iMapIndex ]; } kv->SetInt( "avatar", iImageIndex ); CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( iImageIndex ); pAvIm->UpdateFriendStatus(); } } } } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::UpdateCreditStats() { int iWaveNumber = TFObjectiveResource()->GetMannVsMachineWaveCount() - 1; if ( ( TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() == true ) && ( iWaveNumber > 0 ) ) { iWaveNumber--; // Examine Previous wave credits } if ( !m_pPreviousWaveCreditsInfo || !m_pTotalCreditsInfo ) { return; } CMannVsMachineStats *pMVMStats = MannVsMachineStats_GetInstance(); if ( pMVMStats == NULL ) { return; } // Previous or current int nAcquired = pMVMStats->GetAcquiredCredits( iWaveNumber, false ); int nMissed = pMVMStats->GetMissedCredits( iWaveNumber ); int nBonus = pMVMStats->GetBonusCredits( iWaveNumber ); wchar_t wszWaveNumber[16]; _snwprintf( wszWaveNumber, ARRAYSIZE( wszWaveNumber ), L"%d", MAX( 1, iWaveNumber + 1 ) ); wchar_t wszLocalizedWave[512]; g_pVGuiLocalize->ConstructString_safe( wszLocalizedWave, g_pVGuiLocalize->Find( "#TF_PVE_WaveCount" ), 1, wszWaveNumber ); m_pPreviousWaveCreditsInfo->SetDialogVariable( "header", wszLocalizedWave ); UpdateCreditPanel( m_pPreviousWaveCreditsInfo, nAcquired, nMissed, nBonus ); // Total nAcquired = pMVMStats->GetAcquiredCredits( -1, false ); nMissed = pMVMStats->GetMissedCredits( -1 ); nBonus = pMVMStats->GetBonusCredits( -1 ); m_pTotalCreditsInfo->SetDialogVariable( "header", g_pVGuiLocalize->Find( "#TF_PVE_GameTotal" ) ); UpdateCreditPanel( m_pTotalCreditsInfo, nAcquired, nMissed, nBonus ); // Credit Spend if ( m_pPreviousWaveCreditsSpend ) { m_pPreviousWaveCreditsSpend->SetDialogVariable( "header", "" ); } CPlayerWaveSpendingStats *pWaveStats = pMVMStats->GetLocalSpending( iWaveNumber ); int nUpgradeSpending = pWaveStats ? pWaveStats->nCreditsSpentOnUpgrades : 0; int nBuyBackSpending = pWaveStats ? pWaveStats->nCreditsSpentOnBuyBacks : 0; int nBottleSpending = pWaveStats ? pWaveStats->nCreditsSpentOnBottles : 0; UpdateCreditSpend( m_pPreviousWaveCreditsSpend, nUpgradeSpending, nBuyBackSpending, nBottleSpending ); if ( m_pTotalCreditsSpend ) { m_pTotalCreditsSpend->SetDialogVariable( "header", "" ); } // Get the current WaveStats, have to re-add 1 from above if ( ( TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() == true ) && ( TFObjectiveResource()->GetMannVsMachineWaveCount() > 1 ) ) { pWaveStats = pMVMStats->GetLocalSpending( iWaveNumber + 1 ); } else { pWaveStats = pMVMStats->GetLocalSpending( iWaveNumber ); } nUpgradeSpending = pWaveStats ? pWaveStats->nCreditsSpentOnUpgrades : 0; nBuyBackSpending = pWaveStats ? pWaveStats->nCreditsSpentOnBuyBacks : 0; nBottleSpending = pWaveStats ? pWaveStats->nCreditsSpentOnBottles : 0; pWaveStats = pMVMStats->GetLocalSpending( -1 ); nUpgradeSpending += pWaveStats ? pWaveStats->nCreditsSpentOnUpgrades : 0; nBuyBackSpending += pWaveStats ? pWaveStats->nCreditsSpentOnBuyBacks : 0; nBottleSpending += pWaveStats ? pWaveStats->nCreditsSpentOnBottles : 0; UpdateCreditSpend( m_pTotalCreditsSpend, nUpgradeSpending, nBuyBackSpending, nBottleSpending ); if ( m_pRespecStatusLabel ) { // Respec progress. Don't show if we aren't limiting respecs. bool bShowRespec = TFGameRules() && TFGameRules()->IsMannVsMachineRespecEnabled() && tf_mvm_respec_limit.GetInt(); if ( m_pRespecStatusLabel->IsVisible() != bShowRespec ) { m_pRespecStatusLabel->SetVisible( bShowRespec ); } if ( bShowRespec ) { int nCredits = pMVMStats->GetAcquiredCreditsForRespec(); int nCreditGoal = tf_mvm_respec_credit_goal.GetInt(); wchar_t wzRespecProg[256]; wchar_t wzCredits[32]; wchar_t wzGoal[32]; g_pVGuiLocalize->ConvertANSIToUnicode( CFmtStr( "%d", nCredits ), wzCredits, sizeof( wzCredits ) ); g_pVGuiLocalize->ConvertANSIToUnicode( CFmtStr( "%d", nCreditGoal ), wzGoal, sizeof( wzGoal ) ); g_pVGuiLocalize->ConstructString_safe( wzRespecProg, g_pVGuiLocalize->Find( "#TF_PVE_RespecsProgress" ), 2, wzCredits, wzGoal ); m_pRespecStatusLabel->SetText( wzRespecProg ); } } } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::UpdateCreditPanel( CCreditDisplayPanel *panel, int nAcquired, int nMissed, int nBonus ) { panel->SetDialogVariable( "creditscollected", nAcquired ); panel->SetDialogVariable( "creditsmissed", nMissed ); panel->SetDialogVariable( "creditbonus", nBonus ); int nDropped = nAcquired + nMissed; if ( ( TFGameRules() && TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS && TFObjectiveResource()->GetMannVsMachineWaveCount() == 1 ) || nDropped == 0 ) { panel->SetDialogVariable( "rating", "" ); panel->SetDialogVariable( "ratingshadow", "" ); return; } //calc a score const char* pletterScore = "F"; float fPercent = (float)nAcquired / (float)nDropped; if ( fPercent >= 1.0 ) { pletterScore = "A+"; } else if ( fPercent >= 0.9 ) { pletterScore = "A"; } else if ( fPercent >= 0.8 ) { pletterScore = "B"; } else if ( fPercent >= 0.7 ) { pletterScore = "C"; } else if ( fPercent >= 0.6 ) { pletterScore = "D"; } panel->SetDialogVariable( "rating", pletterScore ); panel->SetDialogVariable( "ratingshadow", pletterScore ); } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::UpdateCreditSpend ( CCreditSpendPanel *panel, int nUpgrades, int nBuybacks, int nBottles ) { if ( panel ) { panel->SetDialogVariable( "upgrades", nUpgrades ); panel->SetDialogVariable( "buybacks", nBuybacks ); panel->SetDialogVariable( "bottles", nBottles ); } } //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::UpdatePopFile( void ) { if ( TFObjectiveResource() ) { if ( Q_strcmp( m_popfile, TFObjectiveResource()->GetMvMPopFileName() ) != 0 ) { V_strcpy_safe( m_popfile, TFObjectiveResource()->GetMvMPopFileName() ); char szTempName[MAX_PATH]; V_FileBase( m_popfile, szTempName, sizeof( szTempName ) ); int iChallengeIndex = GetItemSchema()->FindMvmMissionByName( szTempName ); if ( GetItemSchema()->GetMvmMissions().IsValidIndex( iChallengeIndex ) ) { const MvMMission_t &mission = GetItemSchema()->GetMvmMissions()[ iChallengeIndex ]; wchar_t wszChallengeName[ 256 ]; g_pVGuiLocalize->ConstructString_safe( wszChallengeName, L"%s1 (%s2)", 2, g_pVGuiLocalize->Find( mission.m_sDisplayName.Get() ), g_pVGuiLocalize->Find( mission.m_sMode.Get() ) ); SetDialogVariable( "popfile", wszChallengeName ); m_pDifficultyContainer->SetVisible( true ); m_pDifficultyContainer->SetDialogVariable( "difficultyvalue", g_pVGuiLocalize->Find( GetMvMChallengeDifficultyLocName( mission.m_eDifficulty ) ) ); } else { SetDialogVariable( "popfile", GetMapDisplayName(szTempName) ); // Hide Difficulty Panel since we dont know what it is m_pDifficultyContainer->SetVisible( false ); } } } } #ifdef STAGING_ONLY //----------------------------------------------------------------------------- void CTFHudMannVsMachineScoreboard::WritePlayerScoreStats( void ) { if ( g_TF_PR ) { FileHandle_t hFile = g_pFullFileSystem->Open( "mvm_mission_stats.txt", "a" ); if ( hFile != FILESYSTEM_INVALID_HANDLE ) { CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); tm newtime; VCRHook_LocalTime( &newtime ); buf.Printf( "%s - Wave %d - %s", TFObjectiveResource()->GetMvMPopFileName(), TFObjectiveResource()->GetMannVsMachineWaveCount(), asctime( &newtime ) ); buf.Printf( "----\n" ); // Player stats for ( int i = 1; i <= MAX_PLAYERS; i++ ) { C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); if ( !pPlayer ) continue; if ( pPlayer->GetTeamNumber() != TF_TEAM_PVE_DEFENDERS ) continue; const char *pszClassName = GetPlayerClassName( g_TF_PR->GetPlayerClass( i ) ); buf.Printf( "%10.10s %8.8s\tScore: %-6d Damage: %-6d DmgAssist: %-6d Tank: %-6d Healing: %-6d HealAssist: %-6d Blocked: %-6d Money: %-6d Bonus: %-6d\n", pPlayer->GetPlayerName(), pszClassName ? pszClassName : "Unknown", g_TF_PR->GetTotalScore( i ), g_TF_PR->GetDamage( i ), g_TF_PR->GetDamageAssist( i ), g_TF_PR->GetDamageBoss( i ), g_TF_PR->GetHealing( i ), g_TF_PR->GetHealingAssist( i ), g_TF_PR->GetDamageBlocked( i ), g_TF_PR->GetCurrencyCollected( i ), // Bonus points come from special events and helping teammates, such as: // Resetting the bomb, killing a fully charged medic, shooting down projectiles // extinguishing a teammate, pushing enemy giants away with airblast, // removing a sapper from someone else's building, dispensers giving ammo to the team, // someone gaining health from your sandvich, bow headshots, stunning enemies with baseballs, // scouts killing medics that were actively healing g_TF_PR->GetBonusPoints( i ) ); } buf.Printf( "----\n\n" ); g_pFullFileSystem->Write( buf.String(), buf.TellPut(), hFile ); buf.Clear(); g_pFullFileSystem->Close( hFile ); } } } #endif // STAGING_ONLY