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.
741 lines
26 KiB
741 lines
26 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: Scoreboard for MvM
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================
|
||
|
#include "cbase.h"
|
||
|
#include "tf_hud_mann_vs_machine_scoreboard.h"
|
||
|
#include <filesystem.h>
|
||
|
#include <time.h>
|
||
|
#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<CCreditDisplayPanel*>( m_pCreditStatsPanel->FindChildByName("PreviousWaveCreditInfoPanel") );
|
||
|
m_pTotalCreditsInfo = dynamic_cast<CCreditDisplayPanel*>( m_pCreditStatsPanel->FindChildByName("TotalGameCreditInfoPanel") );
|
||
|
m_pPreviousWaveCreditsSpend = dynamic_cast<CCreditSpendPanel*>( m_pCreditStatsPanel->FindChildByName("PreviousWaveCreditSpendPanel") );
|
||
|
m_pTotalCreditsSpend = dynamic_cast<CCreditSpendPanel*>( 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<vgui::ScalableImagePanel*>( 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
|