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.
740 lines
26 KiB
740 lines
26 KiB
//========= 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
|
|
|