Skip to content

[TF2] Add a ConVar to be always first in the MvM scoreboard #1373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 153 additions & 132 deletions src/game/client/tf/tf_hud_mann_vs_machine_scoreboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

using namespace vgui;

ConVar cl_mvm_scoreboard_player_always_first( "cl_mvm_scoreboard_player_always_first", "0", FCVAR_ARCHIVE, "Force the client to always be first on the MvM scoreboard." );

extern ConVar tf_mvm_respec_credit_goal;
extern ConVar tf_mvm_respec_limit;
Expand Down Expand Up @@ -269,175 +270,195 @@ char *ConvertScoreboardValueToString( int iValue )
}

//-----------------------------------------------------------------------------
void CTFHudMannVsMachineScoreboard::UpdatePlayerList ()
void CTFHudMannVsMachineScoreboard::PopulatePlayerListEntry(int playerIndex)
{
m_pPlayerList->ClearSelection();
m_pPlayerList->RemoveAll();

if ( !g_TF_PR )
if( !g_PR->IsConnected( playerIndex ) )
{
return;
}

for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ )
if ( g_PR->GetTeam( playerIndex ) != TF_TEAM_PVE_DEFENDERS )
{
if( !g_PR->IsConnected( playerIndex ) )
{
continue;
}

if ( g_PR->GetTeam( playerIndex ) != TF_TEAM_PVE_DEFENDERS )
{
continue;
}
return;
}

const char *szName = g_TF_PR->GetPlayerName( playerIndex );
KeyValues *pKeyValues = new KeyValues( "data" );
const char *szName = g_TF_PR->GetPlayerName( playerIndex );
KeyValues *pKeyValues = new KeyValues( "data" );

pKeyValues->SetInt( "playerIndex", playerIndex );
pKeyValues->SetString( "name", szName );
pKeyValues->SetInt( "playerIndex", playerIndex );
pKeyValues->SetString( "name", szName );

bool bAlive = g_TF_PR->IsAlive( playerIndex );
bool bAlive = g_TF_PR->IsAlive( playerIndex );

if( g_PR->IsConnected( playerIndex ) )
if( g_PR->IsConnected( playerIndex ) )
{
int iClass = g_TF_PR->GetPlayerClass( playerIndex );
if ( GetLocalPlayerIndex() == playerIndex && !bAlive )
{
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 )
{
// 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 );
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 ( iClass >= TF_FIRST_NORMAL_CLASS && iClass <= TF_LAST_NORMAL_CLASS )
{
if ( bAlive )
{
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
}
pKeyValues->SetInt( "class", tf_scoreboard_alt_class_icons.GetBool() ? m_iImageClassAlt[iClass] : m_iImageClass[iClass] );
}
else
{
pKeyValues->SetInt( "class", 0 );
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
}
}

// 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 ) ) );
}
pKeyValues->SetInt( "class", 0 );
}
}

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 )
// 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 )
{
UpdatePlayerAvatar( playerIndex, pKeyValues );
fgClr = g_PR->GetTeamColor( TF_TEAM_RED );
bgClr = Color( 0, 0, 0, 0 );
pKeyValues->SetString( "ping", "" );
}
else
{
pKeyValues->SetInt( "avatar", m_iImageDead );
fgClr = Color( 117, 107, 94, 255 );
bgClr = Color( 78, 78, 78, 150 );
pKeyValues->SetString( "ping", ConvertScoreboardValueToString( g_PR->GetPing( playerIndex ) ) );
}
}

// the medal column is just a place holder for the images that are displayed later
pKeyValues->SetInt( "medal", 0 );
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() )
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
{
CTFGSLobby *pLobby = GTFGCClientSystem()->GetLobby();
if ( pLobby )
{
CTFGSLobby *pLobby = GTFGCClientSystem()->GetLobby();
if ( pLobby )
int nTourNo = 0;
int bSurplusEnabled = false;
int idxMember = pLobby->GetMemberIndexBySteamID( GetSteamIDForPlayerIndex( playerIndex ) );
if ( idxMember >= 0 )
{
int nTourNo = 0;
int bSurplusEnabled = false;
int idxMember = pLobby->GetMemberIndexBySteamID( GetSteamIDForPlayerIndex( playerIndex ) );
if ( idxMember >= 0 )
{
ConstTFLobbyPlayer member = pLobby->GetMemberDetails( idxMember );
// I guess they could be on standby to join the match while being themselves ad-hoc or something
// bizarre?
bSurplusEnabled = member.BMatchPlayer() && member.GetSquadSurplus();
nTourNo = member.GetBadgeLevel();
}
ConstTFLobbyPlayer member = pLobby->GetMemberDetails( idxMember );
// I guess they could be on standby to join the match while being themselves ad-hoc or something
// bizarre?
bSurplusEnabled = member.BMatchPlayer() && member.GetSquadSurplus();
nTourNo = member.GetBadgeLevel();
}

if ( bSurplusEnabled )
{
pKeyValues->SetInt( "squad_surplus", m_iSquadSurplusTexture );
}
else
{
pKeyValues->SetInt( "squad_surplus", 0 );
}
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", "" );
}
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 );
// 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 );
m_pPlayerList->SetItemFgColor( itemID, fgClr );
m_pPlayerList->SetItemBgColor( itemID, bgClr );
m_pPlayerList->SetItemFont( itemID, m_hScoreFont );

pKeyValues->deleteThis();
pKeyValues->deleteThis();
}

//-----------------------------------------------------------------------------
void CTFHudMannVsMachineScoreboard::UpdatePlayerList ()
{
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();

m_pPlayerList->ClearSelection();
m_pPlayerList->RemoveAll();

if ( !g_TF_PR )
return;

if (cl_mvm_scoreboard_player_always_first.GetBool())
{
// Populate ourselves first.
PopulatePlayerListEntry(pPlayer->entindex());
}

for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ )
{
// Don't populate something that we've already potentially done.
if (cl_mvm_scoreboard_player_always_first.GetBool() && (pPlayer->entindex() == playerIndex))
{
continue;
}

PopulatePlayerListEntry(playerIndex);
}

// force the list to PerformLayout() now so we can update our medal images after we return
Expand Down
1 change: 1 addition & 0 deletions src/game/client/tf/tf_hud_mann_vs_machine_scoreboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class CTFHudMannVsMachineScoreboard : public vgui::EditablePanel, public CGameEv
private:
void InitPlayerList( vgui::IScheme *pScheme );
void UpdatePlayerList();
void PopulatePlayerListEntry(int playerIndex);
void UpdatePlayerAvatar( int playerIndex, KeyValues *kv );
void UpdateCreditStats();
void UpdateCreditSpend();
Expand Down