diff options
Diffstat (limited to 'game/client/cstrike/VGUI')
47 files changed, 15614 insertions, 0 deletions
diff --git a/game/client/cstrike/VGUI/achievement_stats_summary.cpp b/game/client/cstrike/VGUI/achievement_stats_summary.cpp new file mode 100644 index 0000000..81ef298 --- /dev/null +++ b/game/client/cstrike/VGUI/achievement_stats_summary.cpp @@ -0,0 +1,120 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "achievement_stats_summary.h" +#include "achievements_page.h" +#include "lifetime_stats_page.h" +#include "match_stats_page.h" +#include "stats_summary.h" + +#include <stdio.h> + +using namespace vgui; + +#include <vgui/ILocalize.h> +#include "vgui/ISurface.h" + +#include "filesystem.h" +#include <KeyValues.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +const int cDialogWidth = 900; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CAchievementAndStatsSummary::CAchievementAndStatsSummary(vgui::Panel *parent) : BaseClass(parent, "AchievementAndStatsSummary") +{ + SetDeleteSelfOnClose(false); + //SetBounds(0, 0, 640, 384); + SetBounds(0, 0, 900, 780); + SetMinimumSize( 640, 780 ); + SetSizeable( false ); + + SetTitle("#GameUI_CreateAchievementsAndStats", true); + SetOKButtonText("#GameUI_Close"); + SetCancelButtonVisible(false); + + m_pStatsSummary = new CStatsSummary( this, "StatsSummary" ); + m_pAchievementsPage = new CAchievementsPage(this, "AchievementsPage"); + m_pLifetimeStatsPage = new CLifetimeStatsPage(this, "StatsPage"); + m_pMatchStatsPage = new CMatchStatsPage(this, "MatchStatsPage"); + + AddPage(m_pStatsSummary, "#GameUI_Stats_Summary"); + AddPage(m_pAchievementsPage, "#GameUI_Achievements_Tab"); + AddPage(m_pMatchStatsPage, "#GameUI_MatchStats"); + AddPage(m_pLifetimeStatsPage, "#GameUI_LifetimeStats"); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CAchievementAndStatsSummary::~CAchievementAndStatsSummary() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAchievementAndStatsSummary::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + int screenWide, screenTall; + surface()->GetScreenSize( screenWide, screenTall ); + + // [smessick] Close the achievements dialog for a low resolution screen. + if ( screenWide < cAchievementsDialogMinWidth ) + { + OnOK( true ); + Close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: runs the server when the OK button is pressed +//----------------------------------------------------------------------------- +bool CAchievementAndStatsSummary::OnOK(bool applyOnly) +{ + BaseClass::OnOK(applyOnly); + + return true; +} + +//---------------------------------------------------------- +// Purpose: Preserve our width to the one in the .res file +//---------------------------------------------------------- +void CAchievementAndStatsSummary::OnSizeChanged(int newWide, int newTall) +{ + // Lock the width, but allow height scaling + if ( newWide != cDialogWidth ) + { + SetSize( cDialogWidth, newTall ); + return; + } + + BaseClass::OnSizeChanged(newWide, newTall); +} + +//---------------------------------------------------------- +// Purpose: Processes when summary dialog is activated. +//---------------------------------------------------------- +void CAchievementAndStatsSummary::Activate() +{ + m_pStatsSummary->MakeReadyForUse(); + m_pStatsSummary->UpdateStatsData(); + m_pAchievementsPage->UpdateAchievementDialogInfo(); + m_pLifetimeStatsPage->UpdateStatsData(); + m_pMatchStatsPage->UpdateStatsData(); + + BaseClass::Activate(); +} diff --git a/game/client/cstrike/VGUI/achievement_stats_summary.h b/game/client/cstrike/VGUI/achievement_stats_summary.h new file mode 100644 index 0000000..c73b4d7 --- /dev/null +++ b/game/client/cstrike/VGUI/achievement_stats_summary.h @@ -0,0 +1,62 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ACHIEVEMENTANDSTATSSUMMARY_H +#define ACHIEVEMENTANDSTATSSUMMARY_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyDialog.h> + +class CAchievementsPage; +class CLifetimeStatsPage; +class CMatchStatsPage; +class StatCard; +class CStatsSummary; + +const int cAchievementsDialogMinWidth = 1024; // don't show this screen for lower resolutions + +//----------------------------------------------------------------------------- +// Purpose: dialog for displaying the achievements/stats summary +//----------------------------------------------------------------------------- +class CAchievementAndStatsSummary : public vgui::PropertyDialog +{ + DECLARE_CLASS_SIMPLE( CAchievementAndStatsSummary, vgui::PropertyDialog ); + +public: + CAchievementAndStatsSummary(vgui::Panel *parent); + ~CAchievementAndStatsSummary(); + + virtual void Activate(); + + void OnKeyCodePressed( vgui::KeyCode code ) + { + if ( code == KEY_XBUTTON_B ) + { + Close(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + } + +protected: + virtual bool OnOK(bool applyOnly); + virtual void OnSizeChanged( int newWide, int newTall ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + +private: + CAchievementsPage* m_pAchievementsPage; + CLifetimeStatsPage* m_pLifetimeStatsPage; + CMatchStatsPage* m_pMatchStatsPage; + CStatsSummary* m_pStatsSummary; +}; + + +#endif // ACHIEVEMENTANDSTATSSUMMARY_H diff --git a/game/client/cstrike/VGUI/achievements_page.cpp b/game/client/cstrike/VGUI/achievements_page.cpp new file mode 100644 index 0000000..bc6036e --- /dev/null +++ b/game/client/cstrike/VGUI/achievements_page.cpp @@ -0,0 +1,1061 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Display a list of achievements for the current game +// +//=============================================================================// + +#include "cbase.h" +#include "achievements_page.h" +#include "vgui_controls/Button.h" +#include "vgui/ILocalize.h" +#include "ixboxsystem.h" +#include "iachievementmgr.h" +#include "filesystem.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/CheckButton.h" +#include "fmtstr.h" +#include "c_cs_playerresource.h" +#include "stat_card.h" +#include <vgui/IInput.h> + +#include "../../../public/steam/steam_api.h" +#include "achievementmgr.h" +#include "../../../../public/vgui/IScheme.h" +#include "../vgui_controls/ScrollBar.h" +#include "achievements_cs.h" + +extern CAchievementMgr g_AchievementMgrCS; + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +KeyValues *g_pPreloadedCSAchievementPageItemLayout = NULL; +KeyValues *g_pPreloadedCSAchievementPageGroupLayout = NULL; + +// Shared helper functions so xbox and pc can share as much code as possible while coming from different bases. + +//----------------------------------------------------------------------------- +// Purpose: Sets the parameter pIconPanel to display the specified achievement's icon file. +//----------------------------------------------------------------------------- +bool CSLoadAchievementIconForPage( vgui::ImagePanel* pIconPanel, CCSBaseAchievement* pAchievement, const char *pszExt /*= NULL*/ ) +{ + char imagePath[_MAX_PATH]; + Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) ); + Q_strncat( imagePath, pAchievement->GetName(), sizeof(imagePath), COPY_ALL_CHARACTERS ); + if ( pszExt ) + { + Q_strncat( imagePath, pszExt, sizeof(imagePath), COPY_ALL_CHARACTERS ); + } + Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS ); + + char checkFile[_MAX_PATH]; + Q_snprintf( checkFile, sizeof(checkFile), "materials\\vgui\\%s", imagePath ); + if ( !g_pFullFileSystem->FileExists( checkFile ) ) + { + Q_snprintf( imagePath, sizeof(imagePath), "hud\\icon_locked.vtf" ); + } + + pIconPanel->SetShouldScaleImage( true ); + pIconPanel->SetImage( imagePath ); + pIconPanel->SetVisible( true ); + + return pIconPanel->IsVisible(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the parameter pIconPanel to display the specified achievement's icon file. +//----------------------------------------------------------------------------- +bool CSLoadIconForPage( vgui::ImagePanel* pIconPanel, const char* pFilename, const char *pszExt /*= NULL*/ ) +{ + char imagePath[_MAX_PATH]; + Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) ); + Q_strncat( imagePath, pFilename, sizeof(imagePath), COPY_ALL_CHARACTERS ); + if ( pszExt ) + { + Q_strncat( imagePath, pszExt, sizeof(imagePath), COPY_ALL_CHARACTERS ); + } + Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS ); + + char checkFile[_MAX_PATH]; + Q_snprintf( checkFile, sizeof(checkFile), "materials\\vgui\\%s", imagePath ); + if ( !g_pFullFileSystem->FileExists( checkFile ) ) + { + Q_snprintf( imagePath, sizeof(imagePath), "hud\\icon_locked.vtf" ); + } + + pIconPanel->SetShouldScaleImage( true ); + pIconPanel->SetImage( imagePath ); + pIconPanel->SetVisible( true ); + + return pIconPanel->IsVisible(); +} + +//----------------------------------------------------------------------------- +// The bias is to ensure the percentage bar gets plenty orange before it reaches the text, +// as the white-on-grey is hard to read. +//----------------------------------------------------------------------------- +Color CSLerpColorsForPage ( Color cStart, Color cEnd, float flPercent ) +{ + float r = (float)((float)(cStart.r()) + (float)(cEnd.r() - cStart.r()) * Bias( flPercent, 0.75 ) ); + float g = (float)((float)(cStart.g()) + (float)(cEnd.g() - cStart.g()) * Bias( flPercent, 0.75 ) ); + float b = (float)((float)(cStart.b()) + (float)(cEnd.b() - cStart.b()) * Bias( flPercent, 0.75 ) ); + float a = (float)((float)(cStart.a()) + (float)(cEnd.a() - cStart.a()) * Bias( flPercent, 0.75 ) ); + return Color( r, g, b, a ); +} + +//----------------------------------------------------------------------------- +// Purpose: Shares common percentage bar calculations/color settings between xbox and pc. +// Not really intended for robustness or reuse across many panels. +// Input : pFrame - assumed to have certain child panels (see below) +// *pAchievement - source achievement to poll for progress. Non progress achievements will not show a percentage bar. +//----------------------------------------------------------------------------- +void CSUpdateProgressBarForPage( vgui::EditablePanel* pPanel, CCSBaseAchievement* pAchievement, Color clrProgressBar ) +{ + ///* + if ( pAchievement->GetGoal() > 1 ) + { + bool bShowProgress = true; + + // if this achievement gets saved with game and we're not in a level and have not achieved it, then we do not have any state + // for this achievement, don't show progress + if ( ( pAchievement->GetFlags() & ACH_SAVE_WITH_GAME ) && /*!GameUI().IsInLevel() &&*/ !pAchievement->IsAchieved() ) + { + bShowProgress = false; + } + + float flCompletion = 0.0f; + + // Once achieved, we can't rely on count. If they've completed the achievement just set to 100%. + int iCount = pAchievement->GetCount(); + if ( pAchievement->IsAchieved() ) + { + flCompletion = 1.0f; + iCount = pAchievement->GetGoal(); + } + else if ( bShowProgress ) + { + flCompletion = ( ((float)pAchievement->GetCount()) / ((float)pAchievement->GetGoal()) ); + // In rare cases count can exceed goal and not be achieved (switch local storage on X360, take saved game from different user on PC). + // These will self-correct with continued play, but if we're in that state don't show more than 100% achieved. + flCompletion = MIN( flCompletion, 1.0 ); + } + + char szPercentageText[ 256 ] = ""; + if ( bShowProgress ) + { + Q_snprintf( szPercentageText, 256, "%d/%d", iCount, pAchievement->GetGoal() ); + } + + pPanel->SetControlString( "PercentageText", szPercentageText ); + pPanel->SetControlVisible( "PercentageText", true ); + pPanel->SetControlVisible( "CompletionText", true ); + + vgui::ImagePanel *pPercentageBar = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBar" ); + vgui::ImagePanel *pPercentageBarBkg = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBarBackground" ); + + if ( pPercentageBar && pPercentageBarBkg ) + { + pPercentageBar->SetFillColor( clrProgressBar ); + pPercentageBar->SetWide( pPercentageBarBkg->GetWide() * flCompletion ); + + pPanel->SetControlVisible( "PercentageBarBackground", IsX360() ? bShowProgress : true ); + pPanel->SetControlVisible( "PercentageBar", true ); + } + } + //*/ +} + +// TODO: revisit this once other games are rebuilt using the updated IAchievement interface +bool CSGameSupportsAchievementTrackerForPage() +{ + const char *pGame = Q_UnqualifiedFileName( engine->GetGameDirectory() ); + return ( !Q_stricmp( pGame, "cstrike" ) ); +} + +////////////////////////////////////////////////////////////////////////// +// PC Implementation +////////////////////////////////////////////////////////////////////////// + + + +int AchivementSortPredicate( CCSBaseAchievement* const* pLeft, CCSBaseAchievement* const* pRight ) +{ + if ( (*pLeft)->IsAchieved() && !(*pRight)->IsAchieved() ) + return -1; + + if ( !(*pLeft)->IsAchieved() && (*pRight)->IsAchieved() ) + return 1; + + if ( (*pLeft)->GetAchievementID() < (*pRight)->GetAchievementID() ) + return -1; + + if ( (*pLeft)->GetAchievementID() > (*pRight)->GetAchievementID() ) + return 1; + + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CAchievementsPage::CAchievementsPage(vgui::Panel *parent, const char *name) : BaseClass(parent, "CSAchievementsDialog") +{ + m_iFixedWidth = 900; // Give this an initial value in order to set a proper size + SetBounds(0, 0, 900, 780); + SetMinimumSize( 256, 780 ); + + m_pStatCard = new StatCard(this, "ignored"); + + m_pAchievementsList = new vgui::PanelListPanel( this, "listpanel_achievements" ); + m_pAchievementsList->SetFirstColumnWidth( 0 ); + + m_pGroupsList = new vgui::PanelListPanel( this, "listpanel_groups" ); + m_pGroupsList->SetFirstColumnWidth( 0 ); + + m_pListBG = new vgui::ImagePanel( this, "listpanel_background" ); + + m_pPercentageBarBackground = SETUP_PANEL( new ImagePanel( this, "PercentageBarBackground" ) ); + m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "PercentageBar" ) ); + + ListenForGameEvent( "player_stats_updated" ); + ListenForGameEvent( "achievement_earned_local" ); + + // int that holds the highest number achievement id we've found + int iHighestAchievementIDSeen = -1; + int iNextGroupBoundary = 1000; + + Q_memset( m_AchievementGroups, 0, sizeof(m_AchievementGroups) ); + m_iNumAchievementGroups = 0; + + // Base groups + int iCount = g_AchievementMgrCS.GetAchievementCount(); + for ( int i = 0; i < iCount; ++i ) + { + CCSBaseAchievement* pAchievement = dynamic_cast<CCSBaseAchievement*>(g_AchievementMgrCS.GetAchievementByIndex( i )); + + if ( !pAchievement ) + continue; + + int iAchievementID = pAchievement->GetAchievementID(); + + if ( iAchievementID > iHighestAchievementIDSeen ) + { + // if it's crossed the next group boundary, create a new group + if ( iAchievementID >= iNextGroupBoundary ) + { + int iNewGroupBoundary = iAchievementID; + CreateNewAchievementGroup( iNewGroupBoundary, iNewGroupBoundary+99 ); + + iNextGroupBoundary = iNewGroupBoundary + 100; + } + + iHighestAchievementIDSeen = iAchievementID; + } + } + + LoadControlSettings("resource/ui/CSAchievementsDialog.res"); + UpdateTotalProgressDisplay(); + CreateOrUpdateComboItems( true ); + + // Default display shows the first achievement group + UpdateAchievementList(1001, 1100); + + m_bStatsDirty = true; + m_bAchievementsDirty = true; +} + +CAchievementsPage::~CAchievementsPage() +{ + g_AchievementMgrCS.SaveGlobalStateIfDirty( false ); // check for saving here to store achievements we want pinned to HUD + + m_pAchievementsList->DeleteAllItems(); + delete m_pAchievementsList; + delete m_pPercentageBarBackground; + delete m_pPercentageBar; +} + +void CAchievementsPage::CreateNewAchievementGroup( int iMinRange, int iMaxRange ) +{ + m_AchievementGroups[m_iNumAchievementGroups].m_iMinRange = iMinRange; + m_AchievementGroups[m_iNumAchievementGroups].m_iMaxRange = iMaxRange; + m_iNumAchievementGroups++; +} + +//---------------------------------------------------------- +// Get the width we're going to lock at +//---------------------------------------------------------- +void CAchievementsPage::ApplySettings( KeyValues *pResourceData ) +{ + m_iFixedWidth = pResourceData->GetInt( "wide", 512 ); + + BaseClass::ApplySettings( pResourceData ); +} + +//---------------------------------------------------------- +// Preserve our width to the one in the .res file +//---------------------------------------------------------- +void CAchievementsPage::OnSizeChanged(int newWide, int newTall) +{ + // Lock the width, but allow height scaling + if ( newWide != m_iFixedWidth ) + { + SetSize( m_iFixedWidth, newTall ); + return; + } + + BaseClass::OnSizeChanged(newWide, newTall); +} + +//---------------------------------------------------------- +// Re-populate the achievement list with the selected group +//---------------------------------------------------------- +void CAchievementsPage::UpdateAchievementList(CAchievementsPageGroupPanel* groupPanel) +{ + if (!groupPanel) + return; + + UpdateAchievementList( groupPanel->GetFirstAchievementID(), groupPanel->GetLastAchievementID() ); + + vgui::IScheme *pGroupScheme = scheme()->GetIScheme( GetScheme() ); + + // Update active status for button display + for (int i = 0; i < m_pGroupsList->GetItemCount(); i++) + { + CAchievementsPageGroupPanel *pPanel = (CAchievementsPageGroupPanel*)m_pGroupsList->GetItemPanel(i); + if ( pPanel ) + { + if ( pPanel != groupPanel ) + { + pPanel->SetGroupActive( false ); + } + else + { + pPanel->SetGroupActive( true ); + } + + pPanel->UpdateAchievementInfo( pGroupScheme ); + } + } +} + +void CAchievementsPage::UpdateTotalProgressDisplay() +{ + // Set up total completion percentage bar + float flCompletion = 0.0f; + + int iCount = g_AchievementMgrCS.GetAchievementCount(); + int nUnlocked = 0; + + if ( iCount > 0 ) + { + for ( int i = 0; i < iCount; ++i ) + { + CCSBaseAchievement* pAchievement = dynamic_cast<CCSBaseAchievement*>(g_AchievementMgrCS.GetAchievementByIndex( i )); + + if ( pAchievement && pAchievement->IsAchieved() ) + ++nUnlocked; + } + + flCompletion = (((float)nUnlocked) / ((float)g_AchievementMgrCS.GetAchievementCount())); + } + + char szPercentageText[64]; + V_sprintf_safe( szPercentageText, "%d / %d", + nUnlocked, g_AchievementMgrCS.GetAchievementCount() ); + + SetControlString( "PercentageText", szPercentageText ); + SetControlVisible( "PercentageText", true ); + SetControlVisible( "CompletionText", true ); + + vgui::IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + Color clrHighlight = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + Color clrWhite(255, 255, 255, 255); + + Color cProgressBar = Color( static_cast<float>( clrHighlight.r() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.r() ) * flCompletion, + static_cast<float>( clrHighlight.g() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.g() ) * flCompletion, + static_cast<float>( clrHighlight.b() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.b() ) * flCompletion, + static_cast<float>( clrHighlight.a() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.a() ) * flCompletion ); + + m_pPercentageBar->SetFgColor( cProgressBar ); + m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion ); + + SetControlVisible( "PercentageBarBackground", true ); + SetControlVisible( "PercentageBar", true ); +} + +//---------------------------------------------------------- +// Re-populate the achievement list with the selected group +//---------------------------------------------------------- +void CAchievementsPage::UpdateAchievementList(int minID, int maxID) +{ + int iMinRange = minID; + int iMaxRange = maxID; + + int iCount = g_AchievementMgrCS.GetAchievementCount(); + + CUtlVector<CCSBaseAchievement*> sortedAchivementList; + sortedAchivementList.EnsureCapacity(iCount); + + for ( int i = 0; i < iCount; ++i ) + { + CCSBaseAchievement* pAchievement = dynamic_cast<CCSBaseAchievement*>(g_AchievementMgrCS.GetAchievementByIndex(i)); + + if ( !pAchievement ) + continue; + + int iAchievementID = pAchievement->GetAchievementID(); + + if ( iAchievementID < iMinRange || iAchievementID > iMaxRange ) + continue; + + // don't show hidden achievements if not achieved + if ( pAchievement->ShouldHideUntilAchieved() && !pAchievement->IsAchieved() ) + continue; + + sortedAchivementList.AddToTail(pAchievement); + } + + sortedAchivementList.Sort(AchivementSortPredicate); + + m_pAchievementsList->DeleteAllItems(); + + FOR_EACH_VEC(sortedAchivementList, i) + { + CCSBaseAchievement* pAchievement = sortedAchivementList[i]; + + CAchievementsPageItemPanel *pAchievementItemPanel = new CAchievementsPageItemPanel( m_pAchievementsList, "AchievementDialogItemPanel"); + pAchievementItemPanel->SetAchievementInfo(pAchievement); + + // force all our new panel to have the correct internal layout and size so that our parent container can layout properly + pAchievementItemPanel->InvalidateLayout(true, true); + + m_pAchievementsList->AddItem( NULL, pAchievementItemPanel ); + } + + m_pAchievementsList->MoveScrollBarToTop(); +} + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from achievementsdialog.res in hl2/resource/ui/ +// Sets up progress bar displaying total achievement unlocking progress by the user. +//----------------------------------------------------------------------------- +void CAchievementsPage::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_pGroupsList->SetBgColor(Color(86,86,86,255)); + + SetBgColor(Color(86,86,86,255)); + + // Set text color for percentage + Panel *pPanel; + pPanel = FindChildByName("PercentageText"); + if (pPanel) + { + pPanel->SetFgColor(Color(157, 194, 80, 255)); + } + + // Set text color for achievement earned label + pPanel = FindChildByName("AchievementsEarnedLabel"); + if (pPanel) + { + pPanel->SetFgColor(Color(157, 194, 80, 255)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Each sub-panel gets its data updated +//----------------------------------------------------------------------------- +void CAchievementsPage::UpdateAchievementDialogInfo( void ) +{ + // Hide the group list scrollbar + if (m_pGroupsList->GetScrollbar()) + { + m_pGroupsList->GetScrollbar()->SetWide(0); + } + + int iCount = m_pAchievementsList->GetItemCount(); + vgui::IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + int i; + for ( i = 0; i < iCount; i++ ) + { + CAchievementsPageItemPanel *pPanel = (CAchievementsPageItemPanel*)m_pAchievementsList->GetItemPanel(i); + if ( pPanel ) + { + pPanel->UpdateAchievementInfo( pScheme ); + } + } + + // Update all group panels + int iGroupCount = m_pGroupsList->GetItemCount(); + for ( i = 0; i < iGroupCount; i++ ) + { + CAchievementsPageGroupPanel *pPanel = (CAchievementsPageGroupPanel*)m_pGroupsList->GetItemPanel(i); + if ( pPanel ) + { + pPanel->UpdateAchievementInfo( pScheme ); + + if ( pPanel->IsGroupActive() ) + { + UpdateAchievementList( pPanel ); + } + } + } + + // update the groups and overall progress bar + CreateOrUpdateComboItems( false ); // update them with new achieved counts + + UpdateTotalProgressDisplay(); + + m_pStatCard->UpdateInfo(); + + m_bAchievementsDirty = false; + m_bStatsDirty = false; +} + +void CAchievementsPage::CreateOrUpdateComboItems( bool bCreate ) +{ + // Build up achievement group names + for ( int i=0;i<m_iNumAchievementGroups;i++ ) + { + char buf[128]; + + int achievementRangeStart = (m_AchievementGroups[i].m_iMinRange / 1000) * 1000; + Q_snprintf( buf, sizeof(buf), "#Achievement_Group_%d", achievementRangeStart ); + + wchar_t *wzGroupName = g_pVGuiLocalize->Find( buf ); + + if ( !wzGroupName ) + { + wzGroupName = L"Need Title ( %s1 of %s2 )"; + } + + wchar_t wzGroupTitle[128]; + + if ( wzGroupName ) + { + // Determine number of achievements in the group which have been awarded + int numAwarded = 0; + int numTested = 0; + for (int j = m_AchievementGroups[i].m_iMinRange; j < m_AchievementGroups[i].m_iMaxRange; j++) + { + IAchievement* pCur = g_AchievementMgrCS.GetAchievementByID( j ); + + if ( !pCur ) + continue; + + numTested++; + + if ( pCur->IsAchieved() ) + { + numAwarded++; + } + } + + wchar_t wzNumUnlocked[8]; + V_snwprintf( wzNumUnlocked, ARRAYSIZE( wzNumUnlocked ), L"%d", numAwarded ); + + wchar_t wzNumAchievements[8]; + V_snwprintf( wzNumAchievements, ARRAYSIZE( wzNumAchievements ), L"%d", numTested ); + + g_pVGuiLocalize->ConstructString( wzGroupTitle, sizeof( wzGroupTitle ), wzGroupName, 0 ); + } + + KeyValues *pKV = new KeyValues( "grp" ); + pKV->SetInt( "minrange", m_AchievementGroups[i].m_iMinRange ); + pKV->SetInt( "maxrange", m_AchievementGroups[i].m_iMaxRange ); + + if ( bCreate ) + { + // Create an achievement group instance + CAchievementsPageGroupPanel *achievementGroupPanel = new CAchievementsPageGroupPanel( m_pGroupsList, this, "AchievementDialogGroupPanel", i ); + achievementGroupPanel->SetGroupInfo( wzGroupTitle, m_AchievementGroups[i].m_iMinRange, m_AchievementGroups[i].m_iMaxRange ); + + if (i == 0) + { + achievementGroupPanel->SetGroupActive(true); + } + else + { + achievementGroupPanel->SetGroupActive(false); + } + + m_pGroupsList->AddItem( NULL, achievementGroupPanel ); + } + } + + m_pStatCard->UpdateInfo(); +} + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CAchievementsPageItemPanel::CAchievementsPageItemPanel( vgui::PanelListPanel *parent, const char* name) : BaseClass( parent, name ) +{ + m_pParent = parent; + m_pSchemeSettings = NULL; + + m_pAchievementIcon = SETUP_PANEL(new vgui::ImagePanel( this, "AchievementIcon" )); + m_pAchievementNameLabel = new vgui::Label( this, "AchievementName", "name" ); + m_pAchievementDescLabel = new vgui::Label( this, "AchievementDesc", "desc" ); + m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "PercentageBar" ) ); + m_pPercentageText = new vgui::Label( this, "PercentageText", "" ); + m_pAwardDate = new vgui::Label( this, "AwardDate", "date" ); + m_pShowOnHUDButton = new vgui::CheckButton( this, "ShowOnHudToggle", "" ); + m_pShowOnHUDButton->SetMouseInputEnabled( true ); + m_pShowOnHUDButton->SetEnabled( true ); + m_pShowOnHUDButton->SetCheckButtonCheckable( true ); + m_pShowOnHUDButton->AddActionSignalTarget( this ); + + m_pHiddenHUDToggleButton = new CHiddenHUDToggleButton( this, "HiddenHUDToggle", "" ); + m_pHiddenHUDToggleButton->SetPaintBorderEnabled( false ); + + + SetMouseInputEnabled( true ); + parent->SetMouseInputEnabled( true ); +} + +CAchievementsPageItemPanel::~CAchievementsPageItemPanel() +{ + delete m_pAchievementIcon; + delete m_pAchievementNameLabel; + delete m_pAchievementDescLabel; + delete m_pPercentageBar; + delete m_pPercentageText; + delete m_pAwardDate; + delete m_pShowOnHUDButton; + delete m_pHiddenHUDToggleButton; +} + +void CAchievementsPageItemPanel::ToggleShowOnHUDButton() +{ + if (m_pShowOnHUDButton) + { + m_pShowOnHUDButton->SetSelected( !m_pShowOnHUDButton->IsSelected() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates displayed achievement data. In applyschemesettings, and when gameui activates. +//----------------------------------------------------------------------------- +void CAchievementsPageItemPanel::UpdateAchievementInfo( vgui::IScheme* pScheme ) +{ + if ( m_pSourceAchievement && m_pSchemeSettings ) + { + //============================================================================= + // HPE_BEGIN: + // [dwenger] Get achievement name and description text from the localized file + //============================================================================= + + // Set name, description and unlocked state text + m_pAchievementNameLabel->SetText( ACHIEVEMENT_LOCALIZED_NAME( m_pSourceAchievement ) ); + m_pAchievementDescLabel->SetText( ACHIEVEMENT_LOCALIZED_DESC( m_pSourceAchievement ) ); + + //============================================================================= + // HPE_END + //============================================================================= + + // Setup icon + // get the vtfFilename from the path. + + // Display percentage completion for progressive achievements + // Set up total completion percentage bar. Goal > 1 means its a progress achievement. + CSUpdateProgressBarForPage( this, m_pSourceAchievement, m_clrProgressBar ); + + if ( m_pSourceAchievement->IsAchieved() ) + { + CSLoadAchievementIconForPage( m_pAchievementIcon, m_pSourceAchievement ); + + SetBgColor( pScheme->GetColor( "AchievementsLightGrey", Color(255, 0, 0, 255) ) ); + + m_pAchievementNameLabel->SetFgColor( pScheme->GetColor( "SteamLightGreen", Color(255, 255, 255, 255) ) ); + + Color fgColor = pScheme->GetColor( "Label.TextBrightColor", Color(255, 255, 255, 255) ); + m_pAchievementDescLabel->SetFgColor( fgColor ); + m_pPercentageText->SetFgColor( fgColor ); + m_pShowOnHUDButton->SetVisible( false ); + m_pShowOnHUDButton->SetSelected( false ); + m_pHiddenHUDToggleButton->SetVisible( false ); + m_pAwardDate->SetVisible( true ); + m_pAwardDate->SetFgColor( pScheme->GetColor( "SteamLightGreen", Color(255, 255, 255, 255) ) ); + + // Assign the award date text + int year, month, day, hour, minute, second; + if ( m_pSourceAchievement->GetAwardTime(year, month, day, hour, minute, second) ) + { + char dateBuffer[32] = ""; + Q_snprintf( dateBuffer, 32, "%4d-%02d-%02d", year, month, day ); + m_pAwardDate->SetText( dateBuffer ); + } + else + m_pAwardDate->SetText( "" ); + } + else + { + CSLoadAchievementIconForPage( m_pAchievementIcon, m_pSourceAchievement, "_bw" ); + + SetBgColor( pScheme->GetColor( "AchievementsDarkGrey", Color(255, 0, 0, 255) ) ); + + Color fgColor = pScheme->GetColor( "AchievementsInactiveFG", Color(255, 255, 255, 255) ); + m_pAchievementNameLabel->SetFgColor( fgColor ); + m_pAchievementDescLabel->SetFgColor( fgColor ); + m_pPercentageText->SetFgColor( fgColor ); + + if ( CSGameSupportsAchievementTrackerForPage() ) + { + m_pShowOnHUDButton->SetVisible( !m_pSourceAchievement->ShouldHideUntilAchieved() ); + m_pShowOnHUDButton->SetSelected( m_pSourceAchievement->ShouldShowOnHUD() ); + + m_pHiddenHUDToggleButton->SetVisible( !m_pSourceAchievement->ShouldHideUntilAchieved() ); + } + else + { + m_pShowOnHUDButton->SetVisible( false ); + m_pHiddenHUDToggleButton->SetVisible( false ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Makes a local copy of a pointer to the achievement entity stored on the client. +//----------------------------------------------------------------------------- +void CAchievementsPageItemPanel::SetAchievementInfo( CCSBaseAchievement* pAchievement ) +{ + if ( !pAchievement ) + { + Assert( 0 ); + return; + } + + m_pSourceAchievement = pAchievement; + m_iSourceAchievementIndex = pAchievement->GetAchievementID(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAchievementsPageItemPanel::PreloadResourceFile( void ) +{ + const char *controlResourceName = "resource/ui/AchievementItem.res"; + + g_pPreloadedCSAchievementPageItemLayout = new KeyValues(controlResourceName); + g_pPreloadedCSAchievementPageItemLayout->LoadFromFile(g_pFullFileSystem, controlResourceName); + +/* + // precache all achievement icons + int iCount = g_AchievementMgrCS.GetAchievementCount(); + for ( int i = 0; i < iCount; ++i ) + { + CCSBaseAchievement* pAchievement = dynamic_cast<CCSBaseAchievement*>(g_AchievementMgrCS.GetAchievementByIndex( i )); + char imagePath[_MAX_PATH]; + + Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) ); + Q_strncat( imagePath, pAchievement->GetName(), sizeof(imagePath), COPY_ALL_CHARACTERS ); + Q_strncat( imagePath, "_bw", sizeof(imagePath), COPY_ALL_CHARACTERS ); + Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS ); + + scheme()->GetImage(imagePath, true); + + Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) ); + Q_strncat( imagePath, pAchievement->GetName(), sizeof(imagePath), COPY_ALL_CHARACTERS ); + Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS ); + + scheme()->GetImage(imagePath, true); + } +*/ +} + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from hl2/resource/ui/achievementitem.res +// Sets display info for this achievement item. +//----------------------------------------------------------------------------- +void CAchievementsPageItemPanel::ApplySchemeSettings( vgui::IScheme* pScheme ) +{ + if ( !g_pPreloadedCSAchievementPageItemLayout ) + { + PreloadResourceFile(); + } + + LoadControlSettings( "", NULL, g_pPreloadedCSAchievementPageItemLayout ); + + m_pSchemeSettings = pScheme; + + if ( !m_pSourceAchievement ) + { + return; + } + + BaseClass::ApplySchemeSettings( pScheme ); + + // m_pSchemeSettings must be set for this. + UpdateAchievementInfo( pScheme ); +} + +void CAchievementsPageItemPanel::OnCheckButtonChecked(Panel *panel) +{ + if ( CSGameSupportsAchievementTrackerForPage() && panel == m_pShowOnHUDButton && m_pSourceAchievement ) + { + m_pSourceAchievement->SetShowOnHUD( m_pShowOnHUDButton->IsSelected() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CAchievementsPageGroupPanel::CAchievementsPageGroupPanel( vgui::PanelListPanel *parent, CAchievementsPage *owner, const char* name, int iListItemID ) : BaseClass( parent, name ) +{ + m_pParent = parent; + m_pOwner = owner; + m_pSchemeSettings = NULL; + + m_pGroupIcon = SETUP_PANEL(new vgui::ImagePanel( this, "GroupIcon" )); + m_pAchievementGroupLabel = new vgui::Label( this, "GroupName", "name" ); + m_pPercentageText = new vgui::Label( this, "GroupPercentageText", "1/1" ); + m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "GroupPercentageBar" ) ); + m_pGroupButton = new CGroupButton( this, "GroupButton", "" ); + m_pGroupButton->SetPos( 0, 0 ); + m_pGroupButton->SetZPos( 20 ); + m_pGroupButton->SetWide( 256 ); + m_pGroupButton->SetTall( 64 ); + SetMouseInputEnabled( true ); + parent->SetMouseInputEnabled( true ); + + m_bActiveButton = false; +} + +CAchievementsPageGroupPanel::~CAchievementsPageGroupPanel() +{ + delete m_pAchievementGroupLabel; + delete m_pPercentageBar; + delete m_pPercentageText; + delete m_pGroupIcon; +} + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from hl2/resource/ui/achievementitem.res +// Sets display info for this achievement item. +//----------------------------------------------------------------------------- +void CAchievementsPageGroupPanel::ApplySchemeSettings( vgui::IScheme* pScheme ) +{ + if ( !g_pPreloadedCSAchievementPageGroupLayout ) + { + PreloadResourceFile(); + } + + LoadControlSettings( "", NULL, g_pPreloadedCSAchievementPageGroupLayout ); + + m_pSchemeSettings = pScheme; + + BaseClass::ApplySchemeSettings( pScheme ); + + // m_pSchemeSettings must be set for this. + UpdateAchievementInfo( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates displayed achievement data. In ApplySchemeSettings(), and +// when gameui activates. +//----------------------------------------------------------------------------- +void CAchievementsPageGroupPanel::UpdateAchievementInfo( vgui::IScheme* pScheme ) +{ + if ( m_pSchemeSettings ) + { + int numAwarded = 0; + int numTested = 0; + + char buf[128]; + int achievementRangeStart = (m_iFirstAchievementID / 1000) * 1000; + Q_snprintf( buf, sizeof(buf), "#Achievement_Group_%d", achievementRangeStart ); + + wchar_t *wzGroupName = g_pVGuiLocalize->Find( buf ); + + if ( !wzGroupName ) + { + wzGroupName = L"Need Title ( %s1 of %s2 )"; + } + + wchar_t wzGroupTitle[128]; + + if ( wzGroupName ) + { + // Determine number of achievements in the group which have been awarded + for (int i = m_iFirstAchievementID; i < m_iLastAchievementID; i++) + { + IAchievement* pCur = g_AchievementMgrCS.GetAchievementByID( i ); + + if ( !pCur ) + continue; + + numTested++; + + if ( pCur->IsAchieved() ) + { + numAwarded++; + } + } + + wchar_t wzNumUnlocked[8]; + V_snwprintf( wzNumUnlocked, ARRAYSIZE( wzNumUnlocked ), L"%d", numAwarded ); + + wchar_t wzNumAchievements[8]; + V_snwprintf( wzNumAchievements,ARRAYSIZE( wzNumAchievements ), L"%d", numTested ); + + g_pVGuiLocalize->ConstructString( wzGroupTitle, sizeof( wzGroupTitle ), wzGroupName, 2, wzNumUnlocked, wzNumAchievements ); + } + + // Set group name text + m_pAchievementGroupLabel->SetText( wzGroupTitle );//m_pGroupName ); + m_pAchievementGroupLabel->SetFgColor(Color(157, 194, 80, 255)); + + char* buff[32]; + Q_snprintf( (char*)buff, 32, "%d / %d", numAwarded, numTested ); + m_pPercentageText->SetText( (const char*)buff ); + m_pPercentageText->SetFgColor(Color(157, 194, 80, 255)); + + if ( !m_bActiveButton ) + { + CSLoadIconForPage( m_pGroupIcon, "achievement-btn-up" ); + } + else + { + CSLoadIconForPage( m_pGroupIcon, "achievement-btn-select" ); + } + + // Update the percentage complete bar + vgui::ImagePanel *pPercentageBar = (vgui::ImagePanel*)FindChildByName( "GroupPercentageBar" ); + vgui::ImagePanel *pPercentageBarBkg = (vgui::ImagePanel*)FindChildByName( "GroupPercentageBarBackground" ); + + if ( pPercentageBar && pPercentageBarBkg ) + { + float flCompletion = (float)numAwarded / (float)numTested; + pPercentageBar->SetFillColor( Color(157, 194, 80, 255) ); + pPercentageBar->SetWide( pPercentageBarBkg->GetWide() * flCompletion ); + + SetControlVisible( "GroupPercentageBarBackground", true ); + SetControlVisible( "GroupPercentageBar", true ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAchievementsPageGroupPanel::PreloadResourceFile( void ) +{ + const char *controlResourceName = "resource/ui/AchievementGroup.res"; + + g_pPreloadedCSAchievementPageGroupLayout = new KeyValues(controlResourceName); + g_pPreloadedCSAchievementPageGroupLayout->LoadFromFile(g_pFullFileSystem, controlResourceName); + + +} + +//----------------------------------------------------------------------------- +// Purpose: Assigns a name and achievement id bounds for an achievement group. +//----------------------------------------------------------------------------- +void CAchievementsPageGroupPanel::SetGroupInfo( const wchar_t* name, int firstAchievementID, int lastAchievementID ) +{ + // Store away the group name + short _textLen = (short)wcslen(name) + 1; + m_pGroupName = new wchar_t[_textLen]; + Q_memcpy( m_pGroupName, name, _textLen * sizeof(wchar_t) ); + + // Store off the start & end achievement IDs + m_iFirstAchievementID = firstAchievementID; + m_iLastAchievementID = lastAchievementID; +} + +CGroupButton::CGroupButton( vgui::Panel *pParent, const char *pName, const char *pText ) : +BaseClass( pParent, pName, pText ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the case where the user presses an achievement group button. +//----------------------------------------------------------------------------- +void CGroupButton::DoClick( void ) +{ + // Process when a group button is hit + CAchievementsPageGroupPanel* pParent = static_cast<CAchievementsPageGroupPanel*>(GetParent()); + + if (pParent) + { + CAchievementsPage* pAchievementsPage = static_cast<CAchievementsPage*>(pParent->GetOwner()); + + if (pAchievementsPage) + { + // Update the list of group achievements + pAchievementsPage->UpdateAchievementList( pParent ); + } + } +} + +void CAchievementsPage::OnPageShow() +{ + m_pGroupsList->GetScrollbar()->SetWide(0); +} + +void CAchievementsPage::FireGameEvent( IGameEvent *event ) +{ + const char *type = event->GetName(); + + if ( 0 == Q_strcmp( type, "achievement_earned_local" ) ) + m_bAchievementsDirty = true; + + if ( 0 == Q_strcmp( type, "player_stats_updated" ) ) + m_bStatsDirty = true; +} + +void CAchievementsPage::OnThink() +{ + vgui::IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + if ( m_bAchievementsDirty ) + { + UpdateAchievementDialogInfo(); + } + else if ( m_bStatsDirty ) + { + // Update progress for currently displayed achievements + int itemId = m_pAchievementsList->FirstItem(); + + while (itemId != m_pAchievementsList->InvalidItemID() ) + { + CAchievementsPageItemPanel *pAchievementItem = dynamic_cast<CAchievementsPageItemPanel*>(m_pAchievementsList->GetItemPanel(itemId)); + pAchievementItem->UpdateAchievementInfo(pScheme); + + itemId = m_pAchievementsList->NextItem(itemId); + } + m_bStatsDirty = false; + } +} + +CHiddenHUDToggleButton::CHiddenHUDToggleButton( vgui::Panel *pParent, const char *pName, const char *pText ) : +BaseClass( pParent, pName, pText ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the case where the user shift-clicks on an un-awarded achievement. +//----------------------------------------------------------------------------- +void CHiddenHUDToggleButton::DoClick( void ) +{ + if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) + { + // Process when a group button is hit + CAchievementsPageItemPanel* pParent = static_cast<CAchievementsPageItemPanel*>(GetParent()); + + if (pParent) + { + pParent->ToggleShowOnHUDButton(); + } + } +} diff --git a/game/client/cstrike/VGUI/achievements_page.h b/game/client/cstrike/VGUI/achievements_page.h new file mode 100644 index 0000000..2066b1d --- /dev/null +++ b/game/client/cstrike/VGUI/achievements_page.h @@ -0,0 +1,218 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSACHIEVEMENTSPAGE_H +#define CSACHIEVEMENTSPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/Label.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Button.h" +#include "c_cs_player.h" +#include "vgui_avatarimage.h" +#include "GameEventListener.h" + +class CCSBaseAchievement; +class IScheme; +class CAchievementsPageGroupPanel; +class StatCard; + +#define ACHIEVED_ICON_PATH "hud/icon_check.vtf" +#define LOCK_ICON_PATH "hud/icon_locked.vtf" + +// Loads an achievement's icon into a specified image panel, or turns the panel off if no achievement icon was found. +bool CSLoadAchievementIconForPage( vgui::ImagePanel* pIconPanel, CCSBaseAchievement *pAchievement, const char *pszExt = NULL ); + +// Loads an achievement's icon into a specified image panel, or turns the panel off if no achievement icon was found. +bool CSLoadIconForPage( vgui::ImagePanel* pIconPanel, const char* pFilename, const char *pszExt = NULL ); + +// Updates a listed achievement item's progress bar. +void CSUpdateProgressBarForPage( vgui::EditablePanel* pPanel, CCSBaseAchievement *pAchievement, Color clrProgressBar ); + +//////////////////////////////////////////////////////////////////////////// +// PC version +////////////////////////////////////////////////////////////////////////// +class CAchievementsPage : public vgui::PropertyPage, public CGameEventListener +{ + DECLARE_CLASS_SIMPLE ( CAchievementsPage, vgui::PropertyPage ); + +public: + CAchievementsPage( vgui::Panel *parent, const char *name ); + ~CAchievementsPage(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + void UpdateTotalProgressDisplay(); + virtual void UpdateAchievementDialogInfo( void ); + + virtual void OnPageShow(); + virtual void OnThink(); + + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void OnSizeChanged( int newWide, int newTall ); + + virtual void FireGameEvent( IGameEvent *event ); + + void CreateNewAchievementGroup( int iMinRange, int iMaxRange ); + void CreateOrUpdateComboItems( bool bCreate ); + void UpdateAchievementList(CAchievementsPageGroupPanel* groupPanel); + void UpdateAchievementList(int minID, int maxID); + + vgui::PanelListPanel *m_pAchievementsList; + vgui::ImagePanel *m_pListBG; + + vgui::PanelListPanel *m_pGroupsList; + vgui::ImagePanel *m_pGroupListBG; + + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; + + StatCard* m_pStatCard; + + int m_iFixedWidth; + + bool m_bStatsDirty; + bool m_bAchievementsDirty; + + typedef struct + { + int m_iMinRange; + int m_iMaxRange; + } achievement_group_t; + + int m_iNumAchievementGroups; + + achievement_group_t m_AchievementGroups[15]; +}; + +class CHiddenHUDToggleButton : public vgui::Button +{ + DECLARE_CLASS_SIMPLE( CHiddenHUDToggleButton, vgui::Button ); + +public: + + CHiddenHUDToggleButton( vgui::Panel *pParent, const char *pName, const char *pText ); + + virtual void DoClick( void ); +}; + +////////////////////////////////////////////////////////////////////////// +// Individual item panel, displaying stats for one achievement +class CAchievementsPageItemPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CAchievementsPageItemPanel, vgui::EditablePanel ); + +public: + CAchievementsPageItemPanel( vgui::PanelListPanel *parent, const char* name); + ~CAchievementsPageItemPanel(); + + void SetAchievementInfo ( CCSBaseAchievement* pAchievement ); + CCSBaseAchievement* GetAchievementInfo( void ) { return m_pSourceAchievement; } + void UpdateAchievementInfo( vgui::IScheme *pScheme ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + void ToggleShowOnHUDButton(); + + MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel ); + +private: + static void PreloadResourceFile(); + + CCSBaseAchievement* m_pSourceAchievement; + int m_iSourceAchievementIndex; + + vgui::PanelListPanel *m_pParent; + + vgui::Label *m_pAchievementNameLabel; + vgui::Label *m_pAchievementDescLabel; + vgui::Label *m_pPercentageText; + vgui::Label *m_pAwardDate; + + vgui::ImagePanel *m_pLockedIcon; + vgui::ImagePanel *m_pAchievementIcon; + + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; + + vgui::CheckButton *m_pShowOnHUDButton; + + vgui::IScheme *m_pSchemeSettings; + + CHiddenHUDToggleButton *m_pHiddenHUDToggleButton; + + CPanelAnimationVar( Color, m_clrProgressBar, "ProgressBarColor", "140 140 140 255" ); +}; + +class CGroupButton : public vgui::Button +{ + DECLARE_CLASS_SIMPLE( CGroupButton, vgui::Button ); + +public: + + CGroupButton( vgui::Panel *pParent, const char *pName, const char *pText ); + + virtual void DoClick( void ); +}; + +////////////////////////////////////////////////////////////////////////// +// Individual achievement group panel, displaying info for one achievement group +class CAchievementsPageGroupPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CAchievementsPageGroupPanel, vgui::EditablePanel ); + +public: + CAchievementsPageGroupPanel( vgui::PanelListPanel *parent, CAchievementsPage *owner, const char* name, int iListItemID ); + ~CAchievementsPageGroupPanel(); + + void SetGroupInfo ( const wchar_t* name, int firstAchievementID, int lastAchievementID ); + void UpdateAchievementInfo( vgui::IScheme *pScheme ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + int GetFirstAchievementID() { return m_iFirstAchievementID; } + int GetLastAchievementID() { return m_iLastAchievementID; } + + vgui::PanelListPanel* GetParent() { return m_pParent; } + CAchievementsPage* GetOwner() { return m_pOwner; } + + void SetGroupActive(bool active) { m_bActiveButton = active; } + bool IsGroupActive() { return m_bActiveButton; } + +private: + void PreloadResourceFile( void ); + + vgui::PanelListPanel *m_pParent; + CAchievementsPage *m_pOwner; + + vgui::Label *m_pAchievementGroupLabel; + vgui::Label *m_pPercentageText; + + CGroupButton *m_pGroupButton; + + vgui::ImagePanel *m_pGroupIcon; + + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; + + vgui::IScheme *m_pSchemeSettings; + + bool m_bActiveButton; + + CPanelAnimationVar( Color, m_clrProgressBar, "ProgressBarColor", "140 140 140 255" ); + + int m_iFirstAchievementID; + int m_iLastAchievementID; + + wchar_t *m_pGroupName; +}; + + + +#endif // CSACHIEVEMENTSPAGE_H diff --git a/game/client/cstrike/VGUI/backgroundpanel.cpp b/game/client/cstrike/VGUI/backgroundpanel.cpp new file mode 100644 index 0000000..c875217 --- /dev/null +++ b/game/client/cstrike/VGUI/backgroundpanel.cpp @@ -0,0 +1,676 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "backgroundpanel.h" + +#include <vgui/IVGui.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include "vgui_controls/BuildGroup.h" +#include "vgui_controls/BitmapImagePanel.h" + +using namespace vgui; + +#define DEBUG_WINDOW_RESIZING 0 +#define DEBUG_WINDOW_REPOSITIONING 0 + +//----------------------------------------------------------------------------- +const int NumSegments = 7; +static int coord[NumSegments+1] = { + 0, + 1, + 2, + 3, + 4, + 6, + 9, + 10 +}; + +//----------------------------------------------------------------------------- +void DrawRoundedBackground( Color bgColor, int wide, int tall ) +{ + int x1, x2, y1, y2; + surface()->DrawSetColor(bgColor); + surface()->DrawSetTextColor(bgColor); + + int i; + + // top-left corner -------------------------------------------------------- + int xDir = 1; + int yDir = -1; + int xIndex = 0; + int yIndex = NumSegments - 1; + int xMult = 1; + int yMult = 1; + int x = 0; + int y = 0; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = MAX( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + y2 = y + coord[NumSegments]; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + xIndex += xDir; + yIndex += yDir; + } + + // top-right corner ------------------------------------------------------- + xDir = 1; + yDir = -1; + xIndex = 0; + yIndex = NumSegments - 1; + x = wide; + y = 0; + xMult = -1; + yMult = 1; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = MAX( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + y2 = y + coord[NumSegments]; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + xIndex += xDir; + yIndex += yDir; + } + + // bottom-right corner ---------------------------------------------------- + xDir = 1; + yDir = -1; + xIndex = 0; + yIndex = NumSegments - 1; + x = wide; + y = tall; + xMult = -1; + yMult = -1; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = y - coord[NumSegments]; + y2 = MIN( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + surface()->DrawFilledRect( x1, y1, x2, y2 ); + xIndex += xDir; + yIndex += yDir; + } + + // bottom-left corner ----------------------------------------------------- + xDir = 1; + yDir = -1; + xIndex = 0; + yIndex = NumSegments - 1; + x = 0; + y = tall; + xMult = 1; + yMult = -1; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = y - coord[NumSegments]; + y2 = MIN( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + surface()->DrawFilledRect( x1, y1, x2, y2 ); + xIndex += xDir; + yIndex += yDir; + } + + // paint between top left and bottom left --------------------------------- + x1 = 0; + x2 = coord[NumSegments]; + y1 = coord[NumSegments]; + y2 = tall - coord[NumSegments]; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + // paint between left and right ------------------------------------------- + x1 = coord[NumSegments]; + x2 = wide - coord[NumSegments]; + y1 = 0; + y2 = tall; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + // paint between top right and bottom right ------------------------------- + x1 = wide - coord[NumSegments]; + x2 = wide; + y1 = coord[NumSegments]; + y2 = tall - coord[NumSegments]; + surface()->DrawFilledRect( x1, y1, x2, y2 ); +} + +//----------------------------------------------------------------------------- +void DrawRoundedBorder( Color borderColor, int wide, int tall ) +{ + int x1, x2, y1, y2; + surface()->DrawSetColor(borderColor); + surface()->DrawSetTextColor(borderColor); + + int i; + + // top-left corner -------------------------------------------------------- + int xDir = 1; + int yDir = -1; + int xIndex = 0; + int yIndex = NumSegments - 1; + int xMult = 1; + int yMult = 1; + int x = 0; + int y = 0; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = MIN( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + y2 = MAX( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + xIndex += xDir; + yIndex += yDir; + } + + // top-right corner ------------------------------------------------------- + xDir = 1; + yDir = -1; + xIndex = 0; + yIndex = NumSegments - 1; + x = wide; + y = 0; + xMult = -1; + yMult = 1; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = MIN( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + y2 = MAX( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + surface()->DrawFilledRect( x1, y1, x2, y2 ); + xIndex += xDir; + yIndex += yDir; + } + + // bottom-right corner ---------------------------------------------------- + xDir = 1; + yDir = -1; + xIndex = 0; + yIndex = NumSegments - 1; + x = wide; + y = tall; + xMult = -1; + yMult = -1; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = MIN( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + y2 = MAX( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + surface()->DrawFilledRect( x1, y1, x2, y2 ); + xIndex += xDir; + yIndex += yDir; + } + + // bottom-left corner ----------------------------------------------------- + xDir = 1; + yDir = -1; + xIndex = 0; + yIndex = NumSegments - 1; + x = 0; + y = tall; + xMult = 1; + yMult = -1; + for ( i=0; i<NumSegments; ++i ) + { + x1 = MIN( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + x2 = MAX( x + coord[xIndex]*xMult, x + coord[xIndex+1]*xMult ); + y1 = MIN( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + y2 = MAX( y + coord[yIndex]*yMult, y + coord[yIndex+1]*yMult ); + surface()->DrawFilledRect( x1, y1, x2, y2 ); + xIndex += xDir; + yIndex += yDir; + } + + // top -------------------------------------------------------------------- + x1 = coord[NumSegments]; + x2 = wide - coord[NumSegments]; + y1 = 0; + y2 = 1; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + // bottom ----------------------------------------------------------------- + x1 = coord[NumSegments]; + x2 = wide - coord[NumSegments]; + y1 = tall - 1; + y2 = tall; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + // left ------------------------------------------------------------------- + x1 = 0; + x2 = 1; + y1 = coord[NumSegments]; + y2 = tall - coord[NumSegments]; + surface()->DrawFilledRect( x1, y1, x2, y2 ); + + // right ------------------------------------------------------------------ + x1 = wide - 1; + x2 = wide; + y1 = coord[NumSegments]; + y2 = tall - coord[NumSegments]; + surface()->DrawFilledRect( x1, y1, x2, y2 ); +} + +//----------------------------------------------------------------------------- +class CaptionLabel : public Label +{ +public: + CaptionLabel(Panel *parent, const char *panelName, const char *text) : Label(parent, panelName, text) + { + } + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + Label::ApplySchemeSettings( pScheme ); + SetFont( pScheme->GetFont( "MenuTitle", IsProportional() ) ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: transform a normalized value into one that is scaled based the minimum +// of the horizontal and vertical ratios +//----------------------------------------------------------------------------- +static int GetAlternateProportionalValueFromNormal(int normalizedValue) +{ + int wide, tall; + GetHudSize( wide, tall ); + int proH, proW; + surface()->GetProportionalBase( proW, proH ); + double scaleH = (double)tall / (double)proH; + double scaleW = (double)wide / (double)proW; + double scale = (scaleW < scaleH) ? scaleW : scaleH; + + return (int)( normalizedValue * scale ); +} + +//----------------------------------------------------------------------------- +// Purpose: transform a standard scaled value into one that is scaled based the minimum +// of the horizontal and vertical ratios +//----------------------------------------------------------------------------- +int GetAlternateProportionalValueFromScaled( HScheme hScheme, int scaledValue) +{ + return GetAlternateProportionalValueFromNormal( scheme()->GetProportionalNormalizedValueEx( hScheme, scaledValue ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: moves and resizes a single control +//----------------------------------------------------------------------------- +static void RepositionControl( Panel *pPanel ) +{ + int x, y, w, h; + pPanel->GetBounds(x, y, w, h); + +#if DEBUG_WINDOW_RESIZING + int x1, y1, w1, h1; + pPanel->GetBounds(x1, y1, w1, h1); + int x2, y2, w2, h2; + x2 = scheme()->GetProportionalNormalizedValueEx( pPanel->GetScheme(), x1 ); + y2 = scheme()->GetProportionalNormalizedValueEx( pPanel->GetScheme(), y1 ); + w2 = scheme()->GetProportionalNormalizedValueEx( pPanel->GetScheme(), w1 ); + h2 = scheme()->GetProportionalNormalizedValueEx( pPanel->GetScheme(), h1 ); +#endif + + x = GetAlternateProportionalValueFromScaled( pPanel->GetScheme(), x ); + y = GetAlternateProportionalValueFromScaled( pPanel->GetScheme(), y ); + w = GetAlternateProportionalValueFromScaled( pPanel->GetScheme(), w ); + h = GetAlternateProportionalValueFromScaled( pPanel->GetScheme(), h ); + + pPanel->SetBounds(x, y, w, h); + +#if DEBUG_WINDOW_RESIZING + DevMsg( "Resizing '%s' from (%d,%d) %dx%d to (%d,%d) %dx%d -- initially was (%d,%d) %dx%d\n", + pPanel->GetName(), x1, y1, w1, h1, x, y, w, h, x2, y2, w2, h2 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Sets colors etc for background image panels +//----------------------------------------------------------------------------- +void ApplyBackgroundSchemeSettings( EditablePanel *pWindow, vgui::IScheme *pScheme ) +{ + Color bgColor = Color( 255, 255, 255, pScheme->GetColor( "BgColor", Color( 0, 0, 0, 0 ) )[3] ); + Color fgColor = pScheme->GetColor( "FgColor", Color( 0, 0, 0, 0 ) ); + + if ( !pWindow ) + return; + + CBitmapImagePanel *pBitmapPanel; + + // corners -------------------------------------------- + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "TopLeftPanel" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "TopRightPanel" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "BottomLeftPanel" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "BottomRightPanel" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + + // background ----------------------------------------- + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "TopSolid" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "UpperMiddleSolid" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "LowerMiddleSolid" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "BottomSolid" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( bgColor ); + } + + // Logo ----------------------------------------------- + pBitmapPanel = dynamic_cast< CBitmapImagePanel * >(pWindow->FindChildByName( "ExclamationPanel" )); + if ( pBitmapPanel ) + { + pBitmapPanel->setImageColor( fgColor ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Re-aligns background image panels so they are touching. +//----------------------------------------------------------------------------- +static void FixupBackgroundPanels( EditablePanel *pWindow, int offsetX, int offsetY ) +{ + if ( !pWindow ) + return; + + int screenWide, screenTall; + pWindow->GetSize( screenWide, screenTall ); + + int inset = GetAlternateProportionalValueFromNormal( 20 ); + int cornerSize = GetAlternateProportionalValueFromNormal( 10 ); + + int titleHeight = GetAlternateProportionalValueFromNormal( 42 ); + int mainHeight = GetAlternateProportionalValueFromNormal( 376 ); + + int logoSize = titleHeight; + + int captionInset = GetAlternateProportionalValueFromNormal( 76 ); + + Panel *pPanel; + + // corners -------------------------------------------- + pPanel = pWindow->FindChildByName( "TopLeftPanel" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( offsetX + inset, offsetY + inset, cornerSize, cornerSize ); + } + + pPanel = pWindow->FindChildByName( "TopRightPanel" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( screenWide - offsetX - inset - cornerSize, offsetY + inset, cornerSize, cornerSize ); + } + + pPanel = pWindow->FindChildByName( "BottomLeftPanel" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( offsetX + inset, screenTall - offsetY - inset - cornerSize, cornerSize, cornerSize ); + } + + pPanel = pWindow->FindChildByName( "BottomRightPanel" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( screenWide - offsetX - inset - cornerSize, screenTall - offsetY - inset - cornerSize, cornerSize, cornerSize ); + } + + // background ----------------------------------------- + pPanel = pWindow->FindChildByName( "TopSolid" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( offsetX + inset + cornerSize, offsetY + inset, screenWide - 2*offsetX - 2*inset - 2*cornerSize, cornerSize ); + } + + pPanel = pWindow->FindChildByName( "UpperMiddleSolid" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( offsetX + inset, offsetY + inset + cornerSize, screenWide - 2*offsetX - 2*inset, titleHeight ); + } + + pPanel = pWindow->FindChildByName( "LowerMiddleSolid" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( offsetX + inset + cornerSize, screenTall - offsetY - inset - cornerSize, screenWide - 2*offsetX - 2*inset - 2*cornerSize, cornerSize ); + } + + pPanel = pWindow->FindChildByName( "BottomSolid" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( offsetX + inset, screenTall - offsetY - inset - cornerSize - mainHeight, screenWide - 2*offsetX - 2*inset, mainHeight ); + } + + // transparent border --------------------------------- + pPanel = pWindow->FindChildByName( "TopClear" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( 0, 0, screenWide, offsetY + inset ); + } + + pPanel = pWindow->FindChildByName( "BottomClear" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( 0, screenTall - offsetY - inset, screenWide, offsetY + inset ); + } + + pPanel = pWindow->FindChildByName( "LeftClear" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( 0, offsetY + inset, offsetX + inset, screenTall - 2*offsetY - 2*inset ); + } + + pPanel = pWindow->FindChildByName( "RightClear" ); + if ( pPanel ) + { + pPanel->SetZPos( -20 ); + pPanel->SetBounds( screenWide - offsetX - inset, offsetY + inset, offsetX + inset, screenTall - 2*offsetY - 2*inset ); + } + + // Logo ----------------------------------------------- + int logoInset = (cornerSize + titleHeight - logoSize)/2; + pPanel = pWindow->FindChildByName( "ExclamationPanel" ); + if ( pPanel ) + { + pPanel->SetZPos( -19 ); // higher than the background + pPanel->SetBounds( offsetX + inset + logoInset, offsetY + inset + logoInset, logoSize, logoSize ); + } + + // Title caption -------------------------------------- + pPanel = dynamic_cast< Label * >(pWindow->FindChildByName( "CaptionLabel" )); + if ( pPanel ) + { + pPanel->SetZPos( -19 ); // higher than the background + pPanel->SetBounds( offsetX + captionInset/*inset + 2*logoInset + logoSize*/, offsetY + inset + logoInset, screenWide, logoSize ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Creates background image panels +//----------------------------------------------------------------------------- +void CreateBackground( EditablePanel *pWindow ) +{ + // corners -------------------------------------------- + new CBitmapImagePanel( pWindow, "TopLeftPanel", "gfx/vgui/round_corner_nw" ); + new CBitmapImagePanel( pWindow, "TopRightPanel", "gfx/vgui/round_corner_ne" ); + new CBitmapImagePanel( pWindow, "BottomLeftPanel", "gfx/vgui/round_corner_sw" ); + new CBitmapImagePanel( pWindow, "BottomRightPanel", "gfx/vgui/round_corner_se" ); + + // background ----------------------------------------- + new CBitmapImagePanel( pWindow, "TopSolid", "gfx/vgui/solid_background" ); + new CBitmapImagePanel( pWindow, "UpperMiddleSolid", "gfx/vgui/solid_background" ); + new CBitmapImagePanel( pWindow, "LowerMiddleSolid", "gfx/vgui/solid_background" ); + new CBitmapImagePanel( pWindow, "BottomSolid", "gfx/vgui/solid_background" ); + + // transparent border --------------------------------- + new CBitmapImagePanel( pWindow, "TopClear", "gfx/vgui/trans_background" ); + new CBitmapImagePanel( pWindow, "BottomClear", "gfx/vgui/trans_background" ); + new CBitmapImagePanel( pWindow, "LeftClear", "gfx/vgui/trans_background" ); + new CBitmapImagePanel( pWindow, "RightClear", "gfx/vgui/trans_background" ); + + // Logo ----------------------------------------------- + new CBitmapImagePanel( pWindow, "ExclamationPanel", "gfx/vgui/CS_logo" ); + + // Title caption -------------------------------------- + Panel *pPanel = dynamic_cast< Label * >(pWindow->FindChildByName( "CaptionLabel" )); + if ( !pPanel ) + new CaptionLabel( pWindow, "CaptionLabel", "" ); +} + +void ResizeWindowControls( EditablePanel *pWindow, int tall, int wide, int offsetX, int offsetY ) +{ + if (!pWindow || !pWindow->GetBuildGroup() || !pWindow->GetBuildGroup()->GetPanelList()) + return; + + CUtlVector<PHandle> *panelList = pWindow->GetBuildGroup()->GetPanelList(); + CUtlVector<Panel *> resizedPanels; + CUtlVector<Panel *> movedPanels; + + // Resize to account for 1.25 aspect ratio (1280x1024) screens + { + for ( int i = 0; i < panelList->Size(); ++i ) + { + PHandle handle = (*panelList)[i]; + + Panel *panel = handle.Get(); + + bool found = false; + for ( int j = 0; j < resizedPanels.Size(); ++j ) + { + if (panel == resizedPanels[j]) + found = true; + } + + if (!panel || found) + { + continue; + } + + resizedPanels.AddToTail( panel ); // don't move a panel more than once + + if ( panel != pWindow ) + { + RepositionControl( panel ); + } + } + } + + // and now re-center them. Woohoo! + for ( int i = 0; i < panelList->Size(); ++i ) + { + PHandle handle = (*panelList)[i]; + + Panel *panel = handle.Get(); + + bool found = false; + for ( int j = 0; j < movedPanels.Size(); ++j ) + { + if (panel == movedPanels[j]) + found = true; + } + + if (!panel || found) + { + continue; + } + + movedPanels.AddToTail( panel ); // don't move a panel more than once + + if ( panel != pWindow ) + { + int x, y; + + panel->GetPos( x, y ); + panel->SetPos( x + offsetX, y + offsetY ); + +#if DEBUG_WINDOW_REPOSITIONING + DevMsg( "Repositioning '%s' from (%d,%d) to (%d,%d) -- a distance of (%d,%d)\n", + panel->GetName(), x, y, x + offsetX, y + offsetY, offsetX, offsetY ); +#endif + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Resizes windows to fit completely on-screen (for 1280x1024), and +// centers them on the screen. Sub-controls are also resized and moved. +//----------------------------------------------------------------------------- +void LayoutBackgroundPanel( EditablePanel *pWindow ) +{ + if ( !pWindow ) + return; + + int screenW, screenH; + GetHudSize( screenW, screenH ); + + int wide, tall; + pWindow->GetSize( wide, tall ); + + int offsetX = 0; + int offsetY = 0; + + // Slide everything over to the center + pWindow->SetBounds( 0, 0, screenW, screenH ); + + if ( wide != screenW || tall != screenH ) + { + wide = GetAlternateProportionalValueFromScaled( pWindow->GetScheme(), wide); + tall = GetAlternateProportionalValueFromScaled( pWindow->GetScheme(), tall); + + offsetX = (screenW - wide)/2; + offsetY = (screenH - tall)/2; + + ResizeWindowControls( pWindow, tall, wide, offsetX, offsetY ); + } + + // now that the panels are moved/resized, look for some bg panels, and re-align them + FixupBackgroundPanels( pWindow, offsetX, offsetY ); +} + +//----------------------------------------------------------------------------- + diff --git a/game/client/cstrike/VGUI/backgroundpanel.h b/game/client/cstrike/VGUI/backgroundpanel.h new file mode 100644 index 0000000..5edf22e --- /dev/null +++ b/game/client/cstrike/VGUI/backgroundpanel.h @@ -0,0 +1,53 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSBACKGROUND_H +#define CSBACKGROUND_H + +#include <vgui_controls/Frame.h> +#include <vgui_controls/EditablePanel.h> + +//----------------------------------------------------------------------------- +// Purpose: Creates background image panels +//----------------------------------------------------------------------------- +void CreateBackground( vgui::EditablePanel *pWindow ); + +//----------------------------------------------------------------------------- +// Purpose: Resizes windows to fit completely on-screen (for 1280x1024), and +// centers them on the screen. Sub-controls are also resized and moved. +//----------------------------------------------------------------------------- +void LayoutBackgroundPanel( vgui::EditablePanel *pWindow ); + +//----------------------------------------------------------------------------- +// Purpose: Sets colors etc for background image panels +//----------------------------------------------------------------------------- +void ApplyBackgroundSchemeSettings( vgui::EditablePanel *pWindow, vgui::IScheme *pScheme ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ResizeWindowControls( vgui::EditablePanel *pWindow, int tall, int wide, int offsetX, int offsetY ); + +//----------------------------------------------------------------------------- +// Purpose: transform a standard scaled value into one that is scaled based the minimum +// of the horizontal and vertical ratios +//----------------------------------------------------------------------------- +int GetAlternateProportionalValueFromScaled( vgui::HScheme scheme, int scaledValue ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void DrawRoundedBackground( Color bgColor, int wide, int tall ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void DrawRoundedBorder( Color borderColor, int wide, int tall ); + +//----------------------------------------------------------------------------- + +#endif // CSBACKGROUND_H diff --git a/game/client/cstrike/VGUI/base_stats_page.cpp b/game/client/cstrike/VGUI/base_stats_page.cpp new file mode 100644 index 0000000..ab27865 --- /dev/null +++ b/game/client/cstrike/VGUI/base_stats_page.cpp @@ -0,0 +1,359 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// +//=============================================================================// + +#include "cbase.h" +#include "tier3/tier3.h" +#include "vgui/ILocalize.h" +#include "lifetime_stats_page.h" +#include <vgui_controls/SectionedListPanel.h> +#include "cs_client_gamestats.h" +#include "filesystem.h" +#include "cs_weapon_parse.h" +#include "buy_presets/buy_presets.h" +#include "../vgui_controls/ScrollBar.h" +#include "stat_card.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +KeyValues *g_pPreloadedCSBaseStatGroupLayout = NULL; + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CBaseStatsPage::CBaseStatsPage(vgui::Panel *parent, const char *name) : BaseClass(parent, "CSBaseStatsDialog") +{ + vgui::IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + m_listItemFont = pScheme->GetFont( "StatsPageText", IsProportional() ); + + m_statsList = new SectionedListPanel( this, "StatsList" ); + m_statsList->SetClickable(false); + m_statsList->SetDrawHeaders(false); + + m_bottomBar = new ImagePanel(this, "BottomBar"); + + m_pGroupsList = new vgui::PanelListPanel( this, "listpanel_groups" ); + m_pGroupsList->SetFirstColumnWidth( 0 ); + + SetBounds(0, 0, 900, 780); + SetMinimumSize( 256, 780 ); + + SetBgColor(GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme)); + + m_pStatCard = new StatCard(this, "ignored"); + + ListenForGameEvent( "player_stats_updated" ); + + m_bStatsDirty = true; +} + +CBaseStatsPage::~CBaseStatsPage() +{ + delete m_statsList; +} + + +void CBaseStatsPage::MoveToFront() +{ + UpdateStatsData(); + m_pStatCard->UpdateInfo(); +} + +void CBaseStatsPage::UpdateStatsData() +{ + // Hide the group list scrollbar + if (m_pGroupsList->GetScrollbar()) + { + m_pGroupsList->GetScrollbar()->SetWide(0); + } + + UpdateGroupPanels(); + RepopulateStats(); + + m_bStatsDirty = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from statsdialog.res in hl2/resource/ui/ +//----------------------------------------------------------------------------- +void CBaseStatsPage::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings("resource/ui/CSBaseStatsDialog.res"); + + m_statsList->SetClickable(false); + m_statsList->SetDrawHeaders(false); + + m_statsList->SetVerticalScrollbar(true); + + SetBgColor(Color(86,86,86,255)); + + //Remove any pre-existing sections and add then fresh (this can happen on a resolution change) + m_statsList->RemoveAllSections(); + + m_statsList->AddSection( 0, "Players"); + + m_statsList->SetFontSection(0, m_listItemFont); + + m_pGroupsList->SetBgColor(Color(86,86,86,255)); + m_statsList->SetBgColor(Color(52,52,52,255)); +} + +void CBaseStatsPage::SetActiveStatGroup (CBaseStatGroupPanel* groupPanel) +{ + for (int i = 0; i < m_pGroupsList->GetItemCount(); i++) + { + CBaseStatGroupPanel *pPanel = (CBaseStatGroupPanel*)m_pGroupsList->GetItemPanel(i); + if ( pPanel ) + { + if ( pPanel != groupPanel ) + { + pPanel->SetGroupActive( false ); + } + else + { + pPanel->SetGroupActive( true ); + } + } + } +} + +void CBaseStatsPage::UpdateGroupPanels() +{ + int iGroupCount = m_pGroupsList->GetItemCount(); + vgui::IScheme *pGroupScheme = scheme()->GetIScheme( GetScheme() ); + + for ( int i = 0; i < iGroupCount; i++ ) + { + CBaseStatGroupPanel *pPanel = (CBaseStatGroupPanel*)m_pGroupsList->GetItemPanel(i); + if ( pPanel ) + { + pPanel->Update( pGroupScheme ); + } + } +} + + + +void CBaseStatsPage::OnSizeChanged(int newWide, int newTall) +{ + BaseClass::OnSizeChanged(newWide, newTall); + + if (m_statsList) + { + int labelX, labelY, listX, listY, listWide, listTall; + m_statsList->GetBounds(listX, listY, listWide, listTall); + + if (m_bottomBar) + { + m_bottomBar->GetPos(labelX, labelY); + m_bottomBar->SetPos(labelX, listY + listTall); + } + } +} + +const wchar_t* CBaseStatsPage::TranslateWeaponKillIDToAlias( int statKillID ) +{ + CSWeaponID weaponIDIndex = WEAPON_MAX; + for ( int i = 0; WeaponName_StatId_Table[i].killStatId != CSSTAT_UNDEFINED; ++i ) + { + if( WeaponName_StatId_Table[i].killStatId == statKillID ) + { + weaponIDIndex = WeaponName_StatId_Table[i].weaponId; + break; + } + } + + if (weaponIDIndex == WEAPON_MAX) + { + return NULL; + } + else + { + return WeaponIDToDisplayName(weaponIDIndex); + } +} + +const wchar_t* CBaseStatsPage::LocalizeTagOrUseDefault( const char* tag, const wchar_t* def ) +{ + const wchar_t* result = g_pVGuiLocalize->Find( tag ); + + if ( !result ) + result = def ? def : L"\0"; + + return result; +} + +CBaseStatGroupPanel* CBaseStatsPage::AddGroup( const wchar_t* name, const char* title_tag, const wchar_t* def ) +{ + CBaseStatGroupPanel* newGroup = new CBaseStatGroupPanel( m_pGroupsList, this, "StatGroupPanel", 0 ); + newGroup->SetGroupInfo( name, LocalizeTagOrUseDefault( title_tag, def ) ); + newGroup->SetGroupActive( false ); + + m_pGroupsList->AddItem( NULL, newGroup ); + + return newGroup; +} + +void CBaseStatsPage::FireGameEvent( IGameEvent * event ) +{ + const char *type = event->GetName(); + + if ( 0 == Q_strcmp( type, "player_stats_updated" ) ) + m_bStatsDirty = true; +} + +void CBaseStatsPage::OnThink() +{ + if ( m_bStatsDirty ) + UpdateStatsData(); +} + +CBaseStatGroupPanel::CBaseStatGroupPanel( vgui::PanelListPanel *parent, CBaseStatsPage *owner, const char* name, int iListItemID ) : BaseClass( parent, name ) +{ + m_pParent = parent; + m_pOwner = owner; + m_pSchemeSettings = NULL; + + m_pGroupIcon = SETUP_PANEL(new vgui::ImagePanel( this, "GroupIcon" )); + m_pBaseStatGroupLabel = new vgui::Label( this, "GroupName", "name" ); + m_pGroupButton = new CBaseStatGroupButton(this, "GroupButton", "" ); + m_pGroupButton->SetPos( 0, 0 ); + m_pGroupButton->SetZPos( 20 ); + m_pGroupButton->SetWide( 256 ); + m_pGroupButton->SetTall( 64 ); + SetMouseInputEnabled( true ); + parent->SetMouseInputEnabled( true ); + + m_bActiveButton = false; +} + +CBaseStatGroupPanel::~CBaseStatGroupPanel() +{ + delete m_pBaseStatGroupLabel; + delete m_pGroupIcon; +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the parameter pIconPanel to display the specified achievement's icon file. +//----------------------------------------------------------------------------- +bool CBaseStatGroupPanel::LoadIcon( const char* pFilename) +{ + char imagePath[_MAX_PATH]; + Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) ); + Q_strncat( imagePath, pFilename, sizeof(imagePath), COPY_ALL_CHARACTERS ); + Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS ); + + char checkFile[_MAX_PATH]; + Q_snprintf( checkFile, sizeof(checkFile), "materials\\vgui\\%s", imagePath ); + if ( !g_pFullFileSystem->FileExists( checkFile ) ) + { + Q_snprintf( imagePath, sizeof(imagePath), "hud\\icon_locked.vtf" ); + } + + m_pGroupIcon->SetShouldScaleImage( true ); + m_pGroupIcon->SetImage( imagePath ); + m_pGroupIcon->SetVisible( true ); + + return m_pGroupIcon->IsVisible(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from hl2/resource/ui/achievementitem.res +// Sets display info for this achievement item. +//----------------------------------------------------------------------------- +void CBaseStatGroupPanel::ApplySchemeSettings( vgui::IScheme* pScheme ) +{ + if ( !g_pPreloadedCSBaseStatGroupLayout ) + { + PreloadResourceFile(); + } + + LoadControlSettings( "", NULL, g_pPreloadedCSBaseStatGroupLayout ); + + m_pSchemeSettings = pScheme; + + BaseClass::ApplySchemeSettings( pScheme ); +} + +void CBaseStatGroupPanel::Update( vgui::IScheme* pScheme ) +{ + if ( m_pSchemeSettings ) + { + + // Set group name text + m_pBaseStatGroupLabel->SetText( m_pGroupTitle ); + m_pBaseStatGroupLabel->SetFgColor(Color(157, 194, 80, 255)); + + if ( !m_bActiveButton ) + { + LoadIcon( "achievement-btn-up" ); + } + else + { + LoadIcon( "achievement-btn-select" ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseStatGroupPanel::PreloadResourceFile( void ) +{ + const char *controlResourceName = "resource/ui/StatGroup.res"; + + g_pPreloadedCSBaseStatGroupLayout = new KeyValues(controlResourceName); + g_pPreloadedCSBaseStatGroupLayout->LoadFromFile(g_pFullFileSystem, controlResourceName); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Assigns a name and achievement id bounds for an achievement group. +//----------------------------------------------------------------------------- +void CBaseStatGroupPanel::SetGroupInfo ( const wchar_t* name, const wchar_t* title) +{ + // Store away the group name + short _textLen = (short)wcslen(name) + 1; + m_pGroupName = new wchar_t[_textLen]; + Q_memcpy( m_pGroupName, name, _textLen * sizeof(wchar_t) ); + + _textLen = (short)wcslen(title) + 1; + m_pGroupTitle = new wchar_t[_textLen]; + Q_memcpy( m_pGroupTitle, title, _textLen * sizeof(wchar_t) ); +} + + +CBaseStatGroupButton::CBaseStatGroupButton( vgui::Panel *pParent, const char *pName, const char *pText ) : +BaseClass( pParent, pName, pText ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the case where the user presses an achievement group button. +//----------------------------------------------------------------------------- +void CBaseStatGroupButton::DoClick( void ) +{ + // Process when a group button is hit + CBaseStatGroupPanel* pParent = static_cast<CBaseStatGroupPanel*>(GetParent()); + + if (pParent) + { + CBaseStatsPage* pBaseStatsPage = static_cast<CBaseStatsPage*>(pParent->GetOwner()); + + if (pBaseStatsPage) + { + pBaseStatsPage->SetActiveStatGroup( pParent ); + pBaseStatsPage->UpdateStatsData(); + } + } +} diff --git a/game/client/cstrike/VGUI/base_stats_page.h b/game/client/cstrike/VGUI/base_stats_page.h new file mode 100644 index 0000000..e22ec32 --- /dev/null +++ b/game/client/cstrike/VGUI/base_stats_page.h @@ -0,0 +1,135 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSBASESTATSPAGE_H +#define CSBASESTATSPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/Label.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/ImagePanel.h" +#include "GameEventListener.h" + +struct PlayerStatData_t; +class IScheme; +class CBaseStatGroupPanel; +class StatCard; +struct StatsCollection_t; +struct RoundStatsDirectAverage_t; + +class CBaseStatsPage : public vgui::PropertyPage, public CGameEventListener +{ + DECLARE_CLASS_SIMPLE ( CBaseStatsPage, vgui::PropertyPage ); + +public: + CBaseStatsPage( vgui::Panel *parent, const char *name ); + + ~CBaseStatsPage(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void MoveToFront(); + virtual void OnSizeChanged(int wide, int tall); + virtual void OnThink(); + + void UpdateStatsData(); + void SetActiveStatGroup (CBaseStatGroupPanel* groupPanel); + + virtual void FireGameEvent( IGameEvent * event ); + +protected: + + void UpdateGroupPanels(); + CBaseStatGroupPanel* AddGroup( const wchar_t* name, const char* title_tag, const wchar_t* def = NULL ); + const wchar_t* TranslateWeaponKillIDToAlias( int statKillID ); + const wchar_t* LocalizeTagOrUseDefault( const char* tag, const wchar_t* def = NULL ); + + virtual void RepopulateStats() = 0; + + vgui::SectionedListPanel *m_statsList; + vgui::HFont m_listItemFont; + +private: + + vgui::PanelListPanel *m_pGroupsList; + vgui::ImagePanel* m_bottomBar; + StatCard* m_pStatCard; + bool m_bStatsDirty; +}; + + + + +class CBaseStatGroupButton : public vgui::Button +{ + DECLARE_CLASS_SIMPLE( CBaseStatGroupButton, vgui::Button ); + +public: + + CBaseStatGroupButton( vgui::Panel *pParent, const char *pName, const char *pText ); + + virtual void DoClick( void ); +}; + + + + + +class CBaseStatGroupPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CBaseStatGroupPanel, vgui::EditablePanel ); + +public: + CBaseStatGroupPanel( vgui::PanelListPanel *parent, CBaseStatsPage *owner, const char* name, int iListItemID ); + ~CBaseStatGroupPanel(); + + void SetGroupInfo ( const wchar_t* name, const wchar_t* title); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + void Update( vgui::IScheme* pScheme ); + + vgui::PanelListPanel* GetParent() { return m_pParent; } + CBaseStatsPage* GetOwner() { return m_pOwner; } + + void SetGroupActive(bool active) { m_bActiveButton = active; } + bool IsGroupActive() { return m_bActiveButton; } + +protected: + + // Loads an icon into a specified image panel, or turns the panel off if no icon was found. + bool LoadIcon( const char* pFilename); + +private: + void PreloadResourceFile( void ); + + vgui::PanelListPanel *m_pParent; + CBaseStatsPage *m_pOwner; + + vgui::Label *m_pBaseStatGroupLabel; + + CBaseStatGroupButton *m_pGroupButton; + + vgui::ImagePanel *m_pGroupIcon; + + vgui::IScheme *m_pSchemeSettings; + + bool m_bActiveButton; + + wchar_t *m_pGroupName; + wchar_t *m_pGroupTitle; +}; + + + + + +#endif // CSBASESTATSPAGE_H diff --git a/game/client/cstrike/VGUI/bordered_panel.cpp b/game/client/cstrike/VGUI/bordered_panel.cpp new file mode 100644 index 0000000..cec1d70 --- /dev/null +++ b/game/client/cstrike/VGUI/bordered_panel.cpp @@ -0,0 +1,27 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +//------------------------------------------------------------- +// File: BorderedPanel.cpp +// Desc: +// Author: Peter Freese <[email protected]> +// Date: 2009/05/20 +// Copyright: � 2009 Hidden Path Entertainment +//------------------------------------------------------------- + +#include "cbase.h" +#include "bordered_panel.h" +#include "backgroundpanel.h" // rounded border support + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void BorderedPanel::PaintBackground() +{ + int wide, tall; + GetSize( wide, tall ); + + DrawRoundedBackground( GetBgColor(), wide, tall ); + DrawRoundedBorder( GetFgColor(), wide, tall ); +} + +DECLARE_BUILD_FACTORY( BorderedPanel ); + diff --git a/game/client/cstrike/VGUI/bordered_panel.h b/game/client/cstrike/VGUI/bordered_panel.h new file mode 100644 index 0000000..7279f10 --- /dev/null +++ b/game/client/cstrike/VGUI/bordered_panel.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +//------------------------------------------------------------- +// File: bordered_panel.h +// Desc: +// Author: Peter Freese <[email protected]> +// Date: 2009/05/20 +// Copyright: � 2009 Hidden Path Entertainment +//------------------------------------------------------------- + +#ifndef INCLUDED_BorderedPanel +#define INCLUDED_BorderedPanel +#pragma once + +#include <vgui_controls/EditablePanel.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Editable panel with a forced rounded/outlined border +//----------------------------------------------------------------------------- +class BorderedPanel : public EditablePanel +{ +public: + DECLARE_CLASS_SIMPLE( BorderedPanel, EditablePanel ); + + BorderedPanel( Panel *parent, const char *name ) : + EditablePanel( parent, name ) + { + } + + void PaintBackground(); +}; + + +#endif // INCLUDED_BorderedPanel diff --git a/game/client/cstrike/VGUI/buymouseoverpanelbutton.h b/game/client/cstrike/VGUI/buymouseoverpanelbutton.h new file mode 100644 index 0000000..9e5abbf --- /dev/null +++ b/game/client/cstrike/VGUI/buymouseoverpanelbutton.h @@ -0,0 +1,397 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUYMOUSEOVERPANELBUTTON_H +#define BUYMOUSEOVERPANELBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <KeyValues.h> +#include <filesystem.h> +#include "mouseoverpanelbutton.h" +#include "hud.h" +#include "c_cs_player.h" +#include "cs_gamerules.h" +#include "cstrike/bot/shared_util.h" +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include <vgui_controls/ImagePanel.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Triggers a new panel when the mouse goes over the button +//----------------------------------------------------------------------------- +class BuyMouseOverPanelButton : public MouseOverPanelButton +{ +private: + typedef MouseOverPanelButton BaseClass; +public: + BuyMouseOverPanelButton(vgui::Panel *parent, const char *panelName, vgui::EditablePanel *panel) : + MouseOverPanelButton( parent, panelName, panel) + { + m_iPrice = 0; + m_iPreviousPrice = 0; + m_iASRestrict = 0; + m_iDEUseOnly = 0; + m_command = NULL; + m_bIsBargain = false; + + m_pBlackMarketPrice = NULL;//new EditablePanel( parent, "BlackMarket_Labels" ); + if ( m_pBlackMarketPrice ) + { + m_pBlackMarketPrice->LoadControlSettings( "Resource/UI/BlackMarket_Labels.res" ); + + int x,y,wide,tall; + GetClassPanel()->GetBounds( x, y, wide, tall ); + m_pBlackMarketPrice->SetBounds( x, y, wide, tall ); + int px, py; + GetClassPanel()->GetPinOffset( px, py ); + int rx, ry; + GetClassPanel()->GetResizeOffset( rx, ry ); + // Apply pin settings from template, too + m_pBlackMarketPrice->SetAutoResize( GetClassPanel()->GetPinCorner(), GetClassPanel()->GetAutoResize(), px, py, rx, ry ); + } + } + + virtual void ApplySettings( KeyValues *resourceData ) + { + BaseClass::ApplySettings( resourceData ); + + KeyValues *kv = resourceData->FindKey( "cost", false ); + if( kv ) // if this button has a cost defined for it + { + m_iPrice = kv->GetInt(); // save the price away + } + + kv = resourceData->FindKey( "as_restrict", false ); + if( kv ) // if this button has a map limitation for it + { + m_iASRestrict = kv->GetInt(); // save the as_restrict away + } + + kv = resourceData->FindKey( "de_useonly", false ); + if( kv ) // if this button has a map limitation for it + { + m_iDEUseOnly = kv->GetInt(); // save the de_useonly away + } + + if ( m_command ) + { + delete[] m_command; + m_command = NULL; + } + kv = resourceData->FindKey( "command", false ); + if ( kv ) + { + m_command = CloneString( kv->GetString() ); + } + + SetPriceState(); + SetMapTypeState(); + } + + int GetASRestrict() { return m_iASRestrict; } + + int GetDEUseOnly() { return m_iDEUseOnly; } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + SetPriceState(); + SetMapTypeState(); + +#ifndef CS_SHIELD_ENABLED + if ( !Q_stricmp( GetName(), "shield" ) ) + { + SetVisible( false ); + SetEnabled( false ); + } +#endif + } + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + + m_avaliableColor = pScheme->GetColor( "Label.TextColor", Color( 0, 0, 0, 0 ) ); + m_unavailableColor = pScheme->GetColor( "Label.DisabledFgColor2", Color( 0, 0, 0, 0 ) ); + m_bargainColor = Color( 0, 255, 0, 192 ); + + SetPriceState(); + SetMapTypeState(); + } + + void SetPriceState() + { + if ( CSGameRules() && CSGameRules()->IsBlackMarket() ) + { + SetMarketState(); + } + else + { + if ( GetParent() ) + { + Panel *pPanel = dynamic_cast< Panel * >(GetParent()->FindChildByName( "MarketSticker" ) ); + + if ( pPanel ) + { + pPanel->SetVisible( false ); + } + } + + m_bIsBargain = false; + } + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( m_iPrice && ( pPlayer && m_iPrice > pPlayer->GetAccount() ) ) + { + SetFgColor( m_unavailableColor ); + SetCommand( "buy_unavailable" ); + } + else + { + if ( m_bIsBargain == false ) + { + SetFgColor( m_avaliableColor ); + } + else + { + SetFgColor( m_bargainColor ); + } + + SetCommand( m_command ); + } + } + + void SetMarketState( void ) + { + Panel *pClassPanel = GetClassPanel(); + if ( pClassPanel ) + { + pClassPanel->SetVisible( false ); + } + + if ( m_pBlackMarketPrice ) + { + Label *pLabel = dynamic_cast< Label * >(m_pBlackMarketPrice->FindChildByName( "pricelabel" ) ); + + if ( pLabel ) + { + const int BufLen = 2048; + wchar_t wbuf[BufLen] = L""; + const wchar_t *formatStr = g_pVGuiLocalize->Find("#Cstrike_MarketPreviousPrice"); + + if ( !formatStr ) + formatStr = L"%s1"; + + char strPrice[16]; + wchar_t szPrice[64]; + Q_snprintf( strPrice, sizeof( strPrice ), "%d", m_iPreviousPrice ); + + g_pVGuiLocalize->ConvertANSIToUnicode( strPrice, szPrice, sizeof(szPrice)); + + g_pVGuiLocalize->ConstructString( wbuf, sizeof(wbuf), formatStr, 1, szPrice ); + pLabel->SetText( wbuf ); + pLabel->SetVisible( true ); + } + + pLabel = dynamic_cast< Label * >(m_pBlackMarketPrice->FindChildByName( "price" ) ); + + if ( pLabel ) + { + const int BufLen = 2048; + wchar_t wbuf[BufLen] = L""; + const wchar_t *formatStr = g_pVGuiLocalize->Find("#Cstrike_MarketCurrentPrice"); + + if ( !formatStr ) + formatStr = L"%s1"; + + char strPrice[16]; + wchar_t szPrice[64]; + Q_snprintf( strPrice, sizeof( strPrice ), "%d", m_iPrice ); + + g_pVGuiLocalize->ConvertANSIToUnicode( strPrice, szPrice, sizeof(szPrice)); + + g_pVGuiLocalize->ConstructString( wbuf, sizeof(wbuf), formatStr, 1, szPrice ); + pLabel->SetText( wbuf ); + pLabel->SetVisible( true ); + } + + pLabel = dynamic_cast< Label * >(m_pBlackMarketPrice->FindChildByName( "difference" ) ); + + if ( pLabel ) + { + const int BufLen = 2048; + wchar_t wbuf[BufLen] = L""; + const wchar_t *formatStr = g_pVGuiLocalize->Find("#Cstrike_MarketDeltaPrice"); + + if ( !formatStr ) + formatStr = L"%s1"; + + char strPrice[16]; + wchar_t szPrice[64]; + + int iDifference = m_iPreviousPrice - m_iPrice; + + if ( iDifference >= 0 ) + { + pLabel->SetFgColor( m_bargainColor ); + } + else + { + pLabel->SetFgColor( Color( 192, 28, 0, 255 ) ); + } + + Q_snprintf( strPrice, sizeof( strPrice ), "%d", abs( iDifference ) ); + + g_pVGuiLocalize->ConvertANSIToUnicode( strPrice, szPrice, sizeof(szPrice)); + + g_pVGuiLocalize->ConstructString( wbuf, sizeof(wbuf), formatStr, 1, szPrice ); + pLabel->SetText( wbuf ); + pLabel->SetVisible( true ); + } + + ImagePanel *pImage = dynamic_cast< ImagePanel * >(m_pBlackMarketPrice->FindChildByName( "classimage" ) ); + + if ( pImage ) + { + ImagePanel *pClassImage = dynamic_cast< ImagePanel * >(GetClassPanel()->FindChildByName( "classimage" ) ); + + if ( pClassImage ) + { + pImage->SetSize( pClassImage->GetWide(), pClassImage->GetTall() ); + pImage->SetImage( pClassImage->GetImage() ); + } + } + + if ( GetParent() ) + { + Panel *pPanel = dynamic_cast< Panel * >(GetParent()->FindChildByName( "MarketSticker" ) ); + + if ( pPanel ) + { + if ( m_bIsBargain ) + { + pPanel->SetVisible( true ); + } + else + { + pPanel->SetVisible( false ); + } + } + } + } + } + + void SetMapTypeState() + { + CCSGameRules *pRules = CSGameRules(); + + if ( pRules ) + { + if( pRules->IsVIPMap() ) + { + if ( m_iASRestrict ) + { + SetFgColor( m_unavailableColor ); + SetCommand( "buy_unavailable" ); + } + } + + if ( !pRules->IsBombDefuseMap() ) + { + if ( m_iDEUseOnly ) + { + SetFgColor( m_unavailableColor ); + SetCommand( "buy_unavailable" ); + } + } + } + } + + void SetBargainButton( bool state ) + { + m_bIsBargain = state; + } + + void SetCurrentPrice( int iPrice ) + { + m_iPrice = iPrice; + } + + void SetPreviousPrice( int iPrice ) + { + m_iPreviousPrice = iPrice; + } + + const char *GetBuyCommand( void ) + { + return m_command; + } + + virtual void ShowPage() + { + if ( g_lastPanel ) + { + for( int i = 0; i< g_lastPanel->GetParent()->GetChildCount(); i++ ) + { + MouseOverPanelButton *buyButton = dynamic_cast<MouseOverPanelButton *>(g_lastPanel->GetParent()->GetChild(i)); + + if ( buyButton ) + { + buyButton->HidePage(); + } + } + } + + BaseClass::ShowPage(); + + if ( !Q_stricmp( m_command, "vguicancel" ) ) + return; + + if ( CSGameRules() && CSGameRules()->IsBlackMarket() ) + { + if ( m_pBlackMarketPrice && !m_pBlackMarketPrice->IsVisible() ) + { + m_pBlackMarketPrice->SetVisible( true ); + } + } + } + + virtual void HidePage() + { + BaseClass::HidePage(); + + if ( m_pBlackMarketPrice && m_pBlackMarketPrice->IsVisible() ) + { + m_pBlackMarketPrice->SetVisible( false ); + } + } + +private: + + int m_iPrice; + int m_iPreviousPrice; + int m_iASRestrict; + int m_iDEUseOnly; + bool m_bIsBargain; + + Color m_avaliableColor; + Color m_unavailableColor; + Color m_bargainColor; + + char *m_command; + +public: + vgui::EditablePanel *m_pBlackMarketPrice; +}; + + +#endif // BUYMOUSEOVERPANELBUTTON_H diff --git a/game/client/cstrike/VGUI/buypreset_imageinfo.cpp b/game/client/cstrike/VGUI/buypreset_imageinfo.cpp new file mode 100644 index 0000000..69f93cc --- /dev/null +++ b/game/client/cstrike/VGUI/buypreset_imageinfo.cpp @@ -0,0 +1,577 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#include "weapon_csbase.h" +#include "cs_ammodef.h" + +#include <vgui/IVGui.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include "vgui_controls/BuildGroup.h" +#include "vgui_controls/BitmapImagePanel.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/RichText.h" +#include "vgui_controls/QueryBox.h" +#include "career_box.h" +#include "buypreset_listbox.h" +#include "buypreset_weaponsetlabel.h" + +#include "cstrike/bot/shared_util.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +WeaponImageInfo::WeaponImageInfo() +{ + m_needLayout = m_isCentered = false; + m_left = m_top = m_wide = m_tall = 0; + m_isPrimary = false; + memset( &m_weapon, 0, sizeof(ImageInfo) ); + memset( &m_ammo, 0, sizeof(ImageInfo) ); + m_weaponScale = m_ammoScale = 0; + m_pAmmoText = new TextImage( "" ); +} + +//-------------------------------------------------------------------------------------------------------------- +WeaponImageInfo::~WeaponImageInfo() +{ + delete m_pAmmoText; +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::ApplyTextSettings( vgui::IScheme *pScheme, bool isProportional ) +{ + Color color = pScheme->GetColor( "FgColor", Color( 0, 0, 0, 0 ) ); + + m_pAmmoText->SetColor( color ); + m_pAmmoText->SetFont( pScheme->GetFont( "Default", isProportional ) ); + m_pAmmoText->SetWrap( false ); +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::SetBounds( int left, int top, int wide, int tall ) +{ + m_left = left; + m_top = top; + m_wide = wide; + m_tall = tall; + m_needLayout = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::SetCentered( bool isCentered ) +{ + m_isCentered = isCentered; + m_needLayout = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::SetScaleAt1024( int weaponScale, int ammoScale ) +{ + m_weaponScale = weaponScale; + m_ammoScale = ammoScale; + m_needLayout = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::SetWeapon( const BuyPresetWeapon *pWeapon, bool isPrimary, bool useCurrentAmmoType ) +{ + m_pAmmoText->SetText( L"" ); + m_weapon.image = NULL; + m_ammo.image = NULL; + m_isPrimary = isPrimary; + + if ( !pWeapon ) + return; + + wchar_t *multiplierString = g_pVGuiLocalize->Find("#Cstrike_BuyMenuPresetMultiplier"); + if ( !multiplierString ) + multiplierString = L""; + const int BufLen = 32; + wchar_t buf[BufLen]; + + if ( pWeapon->GetAmmoType() == AMMO_CLIPS ) + { + CSWeaponID weaponID = pWeapon->GetWeaponID(); + const CCSWeaponInfo *info = GetWeaponInfo( weaponID ); + int numClips = pWeapon->GetAmmoAmount(); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int buyClipSize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + + int maxClips = (buyClipSize > 0) ? ceil(maxRounds/(float)buyClipSize) : 0; + numClips = MIN( numClips, maxClips ); + m_weapon.image = scheme()->GetImage( ImageFnameFromWeaponID( weaponID, m_isPrimary ), true ); + if ( numClips == 0 ) + { + m_ammo.image = NULL; + } + else if ( info->m_WeaponType == WEAPONTYPE_SHOTGUN ) + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/shell", true ); + } + else if ( isPrimary ) + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/bullet", true ); + } + else + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/cartridge", true ); + } + + if ( numClips > 1 ) + { + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), multiplierString, 1, NumAsWString( numClips ) ); + m_pAmmoText->SetText( buf ); + } + else + { + m_pAmmoText->SetText( L"" ); + } + } + else if ( numClips > 0 || !useCurrentAmmoType ) + { + if ( useCurrentAmmoType ) + { + CSWeaponID currentID = GetClientWeaponID( isPrimary ); + m_weapon.image = scheme()->GetImage( ImageFnameFromWeaponID( currentID, m_isPrimary ), true ); + info = GetWeaponInfo( currentID ); + if ( !info ) + { + m_weapon.image = NULL; + numClips = 0; + } + else if ( info->m_WeaponType == WEAPONTYPE_SHOTGUN ) + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/shell", true ); + } + else if ( isPrimary ) + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/bullet", true ); + } + else + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/cartridge", true ); + } + } + else + { + m_weapon.image = scheme()->GetImage( ImageFnameFromWeaponID( weaponID, m_isPrimary ), true ); + if ( numClips == 0 ) + { + m_ammo.image = NULL; + } + else if ( isPrimary ) + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/bullet", true ); + } + else + { + m_ammo.image = scheme()->GetImage( "gfx/vgui/cartridge", true ); + } + } + if ( numClips > 1 ) + { + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), multiplierString, 1, NumAsWString( numClips ) ); + m_pAmmoText->SetText( buf ); + } + else + { + m_pAmmoText->SetText( L"" ); + } + } + } + m_needLayout = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::Paint() +{ + if ( m_needLayout ) + PerformLayout(); + + m_weapon.Paint(); + m_ammo.Paint(); +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::PaintText() +{ + if ( m_needLayout ) + PerformLayout(); + + m_pAmmoText->Paint(); +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponImageInfo::PerformLayout() +{ + m_needLayout = false; + + m_weapon.FitInBounds( m_left, m_top, m_wide*0.8, m_tall, m_isCentered, m_weaponScale ); + int ammoX = MIN( m_wide*5/6, m_weapon.w ); + int ammoSize = m_tall * 9 / 16; + if ( !m_isPrimary ) + { + ammoSize = ammoSize * 25 / 40; + ammoX = MIN( m_wide*5/6, m_weapon.w*3/4 ); + } + if ( ammoX + ammoSize > m_wide ) + { + ammoX = m_wide - ammoSize; + } + m_ammo.FitInBounds( m_left + ammoX, m_top + m_tall - ammoSize, ammoSize, ammoSize, false, m_ammoScale ); + + int w, h; + m_pAmmoText->ResizeImageToContent(); + m_pAmmoText->GetSize( w, h ); + if ( m_isPrimary ) + { + if ( m_ammoScale < 75 ) + { + m_pAmmoText->SetPos( m_left + ammoX + ammoSize*1.25f - w, m_top + m_tall - h ); + } + else + { + m_pAmmoText->SetPos( m_left + ammoX + ammoSize - w, m_top + m_tall - h ); + } + } + else + { + m_pAmmoText->SetPos( m_left + ammoX + ammoSize, m_top + m_tall - h ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +WeaponLabel::WeaponLabel(Panel *parent, const char *panelName) : BaseClass( parent, panelName ) +{ + SetSize( 10, 10 ); + SetMouseInputEnabled( false ); +} + +//-------------------------------------------------------------------------------------------------------------- +WeaponLabel::~WeaponLabel() +{ +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponLabel::SetWeapon( const BuyPresetWeapon *pWeapon, bool isPrimary, bool showAmmo ) +{ + BuyPresetWeapon weapon(WEAPON_NONE); + if ( pWeapon ) + weapon = *pWeapon; + if ( !showAmmo ) + weapon.SetAmmoAmount( 0 ); + m_weapon.SetWeapon( &weapon, isPrimary, false ); +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponLabel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_weapon.ApplyTextSettings( pScheme, IsProportional() ); +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponLabel::PerformLayout() +{ + BaseClass::PerformLayout(); + + int wide, tall; + GetSize( wide, tall ); + m_weapon.SetBounds( 0, 0, wide, tall ); +} + +//-------------------------------------------------------------------------------------------------------------- +void WeaponLabel::Paint() +{ + BaseClass::Paint(); + + m_weapon.Paint(); + m_weapon.PaintText(); +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +ItemImageInfo::ItemImageInfo() +{ + m_needLayout = false; + m_left = m_top = m_wide = m_tall = 0; + m_count = 0; + memset( &m_image, 0, sizeof(ImageInfo) ); + m_pText = new TextImage( "" ); + + SetBounds( 0, 0, 100, 100 ); +} + +//-------------------------------------------------------------------------------------------------------------- +ItemImageInfo::~ItemImageInfo() +{ + delete m_pText; +} + +//-------------------------------------------------------------------------------------------------------------- +void ItemImageInfo::ApplyTextSettings( vgui::IScheme *pScheme, bool isProportional ) +{ + Color color = pScheme->GetColor( "Label.TextColor", Color( 0, 0, 0, 0 ) ); + + m_pText->SetColor( color ); + m_pText->SetFont( pScheme->GetFont( "Default", isProportional ) ); + m_pText->SetWrap( false ); +} + +//-------------------------------------------------------------------------------------------------------------- +void ItemImageInfo::SetBounds( int left, int top, int wide, int tall ) +{ + m_left = left; + m_top = top; + m_wide = wide; + m_tall = tall; + m_needLayout = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void ItemImageInfo::SetItem( const char *imageFname, int count ) +{ + m_pText->SetText( L"" ); + m_count = count; + + if ( imageFname ) + m_image.image = scheme()->GetImage( imageFname, true ); + else + m_image.image = NULL; + + if ( count > 1 ) + { + wchar_t *multiplierString = g_pVGuiLocalize->Find("#Cstrike_BuyMenuPresetMultiplier"); + if ( !multiplierString ) + multiplierString = L""; + const int BufLen = 32; + wchar_t buf[BufLen]; + + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), multiplierString, 1, NumAsWString( count ) ); + m_pText->SetText( buf ); + } + m_needLayout = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void ItemImageInfo::Paint() +{ + if ( m_needLayout ) + PerformLayout(); + + if ( m_count ) + m_image.Paint(); +} + +//-------------------------------------------------------------------------------------------------------------- +void ItemImageInfo::PaintText() +{ + if ( m_needLayout ) + PerformLayout(); + + m_pText->Paint(); +} + +//-------------------------------------------------------------------------------------------------------------- +void ItemImageInfo::PerformLayout() +{ + m_needLayout = false; + + m_image.FitInBounds( m_left, m_top, m_wide, m_tall, false, 0 ); + + int w, h; + m_pText->ResizeImageToContent(); + m_pText->GetSize( w, h ); + m_pText->SetPos( m_left + m_image.w - w, m_top + m_tall - h ); +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +EquipmentLabel::EquipmentLabel(Panel *parent, const char *panelName, const char *imageFname) : BaseClass( parent, panelName ) +{ + SetSize( 10, 10 ); + m_item.SetItem( imageFname, 0 ); + SetMouseInputEnabled( false ); +} + +//-------------------------------------------------------------------------------------------------------------- +EquipmentLabel::~EquipmentLabel() +{ +} + +//-------------------------------------------------------------------------------------------------------------- +void EquipmentLabel::SetItem( const char *imageFname, int count ) +{ + m_item.SetItem( imageFname, count ); +} + +//-------------------------------------------------------------------------------------------------------------- +void EquipmentLabel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_item.ApplyTextSettings( pScheme, IsProportional() ); +} + +//-------------------------------------------------------------------------------------------------------------- +void EquipmentLabel::PerformLayout() +{ + BaseClass::PerformLayout(); + + int wide, tall; + GetSize( wide, tall ); + m_item.SetBounds( 0, 0, wide, tall ); +} + +//-------------------------------------------------------------------------------------------------------------- +void EquipmentLabel::Paint() +{ + BaseClass::Paint(); + + m_item.Paint(); + m_item.PaintText(); +} + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +/// Helper function: draws a simple dashed line +void DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen) +{ + // work out which way the line goes + if ((x1 - x0) > (y1 - y0)) + { + // x direction line + while (1) + { + if (x0 + dashLen > x1) + { + // draw partial + surface()->DrawFilledRect(x0, y0, x1, y1+1); + } + else + { + surface()->DrawFilledRect(x0, y0, x0 + dashLen, y1+1); + } + + x0 += dashLen; + + if (x0 + gapLen > x1) + break; + + x0 += gapLen; + } + } + else + { + // y direction + while (1) + { + if (y0 + dashLen > y1) + { + // draw partial + surface()->DrawFilledRect(x0, y0, x1+1, y1); + } + else + { + surface()->DrawFilledRect(x0, y0, x1+1, y0 + dashLen); + } + + y0 += dashLen; + + if (y0 + gapLen > y1) + break; + + y0 += gapLen; + } + } +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +void ImageInfo::Paint() +{ + if ( !image ) + return; + + image->SetSize( w, h ); + image->SetPos( x, y ); + image->Paint(); + image->SetSize( 0, 0 ); // restore image size to content size to not mess up other places that use the same image +} + +//-------------------------------------------------------------------------------------------------------------- +void ImageInfo::FitInBounds( int baseX, int baseY, int width, int height, bool center, int scaleAt1024, bool halfHeight ) +{ + if ( !image ) + { + x = y = w = h = 0; + return; + } + + image->GetContentSize(fullW, fullH); + + if ( scaleAt1024 ) + { + int screenW, screenH; + GetHudSize( screenW, screenH ); + + w = fullW * screenW / 1024 * scaleAt1024 / 100; + h = fullH * screenW / 1024 * scaleAt1024 / 100; + + if ( fullH > 64 && scaleAt1024 == 100 ) + { + w = w * 64 / fullH; + h = h * 64 / fullH; + } + + if ( h > height * 1.2 ) + scaleAt1024 = 0; + } + if ( !scaleAt1024 ) + { + w = fullW; + h = fullH; + + if ( h != height ) + { + w = (int) w * 1.0f * height / h; + h = height; + } + + if ( w > width ) + { + h = (int) h * 1.0f * width / w; + w = width; + } + } + + if ( center ) + { + x = baseX + (width - w)/2; + } + else + { + x = baseX; + } + y = baseY + (height - h)/2; +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/VGUI/buypreset_listbox.cpp b/game/client/cstrike/VGUI/buypreset_listbox.cpp new file mode 100644 index 0000000..add8dd7 --- /dev/null +++ b/game/client/cstrike/VGUI/buypreset_listbox.cpp @@ -0,0 +1,406 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#include <KeyValues.h> +#include <vgui/MouseCode.h> +#include <vgui/IInput.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> + +#include <vgui_controls/EditablePanel.h> +#include <vgui_controls/ScrollBar.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/Controls.h> +#include "buypreset_listbox.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +BuyPresetListBox::BuyPresetListBox( vgui::Panel *parent, char const *panelName ) : Panel( parent, panelName ) +{ + m_visibleIndex = 0; + m_lastSize = 0; + + SetBounds( 0, 0, 100, 100 ); + + m_vbar = new ScrollBar(this, "PanelListPanelVScroll", true); + m_vbar->SetBounds( 0, 0, 20, 20 ); + m_vbar->SetVisible(true); + m_vbar->AddActionSignalTarget( this ); + + m_pPanelEmbedded = new EditablePanel(this, "PanelListEmbedded"); + m_pPanelEmbedded->SetBounds(0, 0, 20, 20); + m_pPanelEmbedded->SetPaintBackgroundEnabled( false ); + m_pPanelEmbedded->SetPaintBorderEnabled(false); + + if( IsProportional() ) + { + int width, height; + int sw,sh; + surface()->GetProportionalBase( width, height ); + GetHudSize(sw, sh); + + // resize scrollbar, etc + m_iScrollbarSize = static_cast<int>( static_cast<float>( SCROLLBAR_SIZE )*( static_cast<float>( sw )/ static_cast<float>( width ))); + m_iDefaultHeight = static_cast<int>( static_cast<float>( DEFAULT_HEIGHT )*( static_cast<float>( sw )/ static_cast<float>( width ))); + m_iPanelBuffer = static_cast<int>( static_cast<float>( PANELBUFFER )*( static_cast<float>( sw )/ static_cast<float>( width ))); + } + else + { + m_iScrollbarSize = SCROLLBAR_SIZE; + m_iDefaultHeight = DEFAULT_HEIGHT; + m_iPanelBuffer = PANELBUFFER; + } +} + +//-------------------------------------------------------------------------------------------------------------- +BuyPresetListBox::~BuyPresetListBox() +{ + // free data from table + DeleteAllItems(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Passes commands up to the parent + */ +void BuyPresetListBox::OnCommand( const char *command ) +{ + GetParent()->OnCommand( command ); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Scrolls the list according to the mouse wheel movement + */ +void BuyPresetListBox::OnMouseWheeled(int delta) +{ + int scale = 3; + if ( m_items.Count() ) + { + Panel *panel = m_items[0].panel; + if ( panel ) + { + scale = panel->GetTall() + m_iPanelBuffer; + } + } + int val = m_vbar->GetValue(); + val -= (delta * scale); + m_vbar->SetValue(val); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Computes vertical pixels needed by listbox contents + */ +int BuyPresetListBox::computeVPixelsNeeded( void ) +{ + int pixels = 0; + + int i; + for ( i = 0; i < m_items.Count(); i++ ) + { + Panel *panel = m_items[i].panel; + if ( !panel ) + continue; + + int w, h; + panel->GetSize( w, h ); + + pixels += m_iPanelBuffer; // add in buffer. between items. + pixels += h; + } + + pixels += m_iPanelBuffer; // add in buffer below last item + + return pixels; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Adds an item to the end of the listbox. UserData is assumed to be a pointer that can be freed by the listbox if non-NULL. + */ +int BuyPresetListBox::AddItem( vgui::Panel *panel, void * userData ) +{ + assert(panel); + + DataItem item = { panel, userData }; + + panel->SetParent( m_pPanelEmbedded ); + + m_items.AddToTail( item ); + + InvalidateLayout(); + return m_items.Count(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Exchanges two items in the listbox + */ +void BuyPresetListBox::SwapItems( int index1, int index2 ) +{ + if ( index1 < 0 || index2 < 0 || index1 >= m_items.Count() || index2 >= m_items.Count() ) + { + return; + } + + DataItem temp = m_items[index1]; + m_items[index1] = m_items[index2]; + m_items[index2] = temp; + + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the number of items in the listbox + */ +int BuyPresetListBox::GetItemCount( void ) const +{ + return m_items.Count(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the panel in the given index, or NULL + */ +Panel * BuyPresetListBox::GetItemPanel(int index) const +{ + if ( index < 0 || index >= m_items.Count() ) + return NULL; + + return m_items[index].panel; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the userData in the given index, or NULL + */ +void * BuyPresetListBox::GetItemUserData(int index) +{ + if ( index < 0 || index >= m_items.Count() ) + { + return NULL; + } + + return m_items[index].userData; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Sets the userData in the given index + */ +void BuyPresetListBox::SetItemUserData( int index, void * userData ) +{ + if ( index < 0 || index >= m_items.Count() ) + return; + + m_items[index].userData = userData; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Removes an item from the table (changing the indices of all following items), deleting the panel and userData + */ +void BuyPresetListBox::RemoveItem(int index) +{ + if ( index < 0 || index >= m_items.Count() ) + return; + + DataItem item = m_items[index]; + if ( item.panel ) + { + item.panel->MarkForDeletion(); + } + if ( item.userData ) + { + delete item.userData; + } + + m_items.Remove( index ); + + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * clears the listbox, deleting all panels and userData + */ +void BuyPresetListBox::DeleteAllItems() +{ + while ( m_items.Count() ) + { + RemoveItem( 0 ); + } + + // move the scrollbar to the top of the list + m_vbar->SetValue(0); + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Handles Count changes + */ +void BuyPresetListBox::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Positions listbox items, etc after internal changes + */ +void BuyPresetListBox::PerformLayout() +{ + int wide, tall; + GetSize( wide, tall ); + + int vpixels = computeVPixelsNeeded(); + + int visibleIndex = m_visibleIndex; + + //!! need to make it recalculate scroll positions + m_vbar->SetVisible(true); + m_vbar->SetEnabled(false); + m_vbar->SetRange( 0, (MAX( 0, vpixels - tall + m_iDefaultHeight )) ); + m_vbar->SetRangeWindow( m_iDefaultHeight ); + m_vbar->SetButtonPressedScrollValue( m_iDefaultHeight ); // standard height of labels/buttons etc. + m_vbar->SetPos(wide - m_iScrollbarSize, 1); + m_vbar->SetSize(m_iScrollbarSize, tall - 2); + + m_visibleIndex = visibleIndex; + + int top = MAX( 0, m_vbar->GetValue() ); + + m_pPanelEmbedded->SetPos( 1, -top ); + m_pPanelEmbedded->SetSize( wide-m_iScrollbarSize -2, vpixels ); + + // Now lay out the controls on the embedded panel + int y = 0; + int h = 0; + int totalh = 0; + + int i; + for ( i = 0; i < m_items.Count(); i++, y += h ) + { + // add in a little buffer between panels + y += m_iPanelBuffer; + DataItem item = m_items[i]; + + h = item.panel->GetTall(); + + totalh += h; + item.panel->SetBounds( 8, y, wide - m_iScrollbarSize - 8 - 8, h ); + item.panel->InvalidateLayout(); + } + + if ( m_visibleIndex >= 0 && m_visibleIndex < m_items.Count() ) + { + + int vpos = 0; + + int tempWide, tempTall; + GetSize( tempWide, tempTall ); + + int vtop, vbottom; + m_vbar->GetRange( vtop, vbottom ); + + int tempTop = MAX( 0, m_vbar->GetValue() ); // top pixel in the embedded panel + int bottom = tempTop + tempTall - 2; + + int itemTop, itemLeft, itemBottom, itemRight; + m_items[m_visibleIndex].panel->GetBounds( itemLeft, itemTop, itemRight, itemBottom ); + itemBottom += itemTop; + itemRight += itemLeft; + + if ( itemTop < tempTop ) + { + // item's top is too high + vpos -= ( tempTop - itemTop ); + + m_vbar->SetValue(vpos); + OnSliderMoved(vpos); + } + else if ( itemBottom > bottom ) + { + // item's bottom is too low + vpos += ( itemBottom - bottom ); + + m_vbar->SetValue(vpos); + OnSliderMoved(vpos); + } + } + + if ( m_lastSize == vpixels ) + { + m_visibleIndex = -1; + } + m_lastSize = vpixels; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Try to ensure that the given index is visible + */ +void BuyPresetListBox::MakeItemVisible( int index ) +{ + m_visibleIndex = index; + m_lastSize = 0; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Loads colors, fonts, etc + */ +void BuyPresetListBox::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBgColor(GetSchemeColor("BuyPresetListBox.BgColor", GetBgColor(), pScheme)); + + SetBorder(pScheme->GetBorder("BrowserBorder")); + m_vbar->SetBorder(pScheme->GetBorder("BrowserBorder")); + + PerformLayout(); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Handles slider being dragged + */ +void BuyPresetListBox::OnSliderMoved( int position ) +{ + InvalidateLayout(); + Repaint(); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Moves slider to the top + */ +void BuyPresetListBox::MoveScrollBarToTop() +{ + m_vbar->SetValue(0); + OnSliderMoved(0); +} + diff --git a/game/client/cstrike/VGUI/buypreset_listbox.h b/game/client/cstrike/VGUI/buypreset_listbox.h new file mode 100644 index 0000000..68d6de6 --- /dev/null +++ b/game/client/cstrike/VGUI/buypreset_listbox.h @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef BUYPRESET_LISTBOX_H +#define BUYPRESET_LISTBOX_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui_controls/Panel.h> + +#include <utlvector.h> + +//-------------------------------------------------------------------------------------------------------------- +/** + * ListBox-style control with behavior needed by weapon lists for BuyPreset editing + */ +class BuyPresetListBox : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( BuyPresetListBox, vgui::Panel ); + +public: + BuyPresetListBox( vgui::Panel *parent, char const *panelName ); + ~BuyPresetListBox(); + + virtual int AddItem( vgui::Panel *panel, void * userData ); ///< Adds an item to the end of the listbox. UserData is assumed to be a pointer that can be freed by the listbox if non-NULL. + virtual int GetItemCount( void ) const; ///< Returns the number of items in the listbox + void SwapItems( int index1, int index2 ); ///< Exchanges two items in the listbox + void MakeItemVisible( int index ); ///< Try to ensure that the given index is visible + + vgui::Panel * GetItemPanel( int index ) const; ///< Returns the panel in the given index, or NULL + void * GetItemUserData( int index ); ///< Returns the userData in the given index, or NULL + void SetItemUserData( int index, void * userData ); ///< Sets the userData in the given index + + virtual void RemoveItem( int index ); ///< Removes an item from the table (changing the indices of all following items), deleting the panel and userData + virtual void DeleteAllItems(); ///< clears the listbox, deleting all panels and userData + + // overrides + virtual void OnSizeChanged(int wide, int tall); ////< Handles size changes + MESSAGE_FUNC_INT( OnSliderMoved, "ScrollBarSliderMoved", position ); ///< Handles slider being dragged + virtual void OnMouseWheeled(int delta); ///< Scrolls the list according to the mouse wheel movement + virtual void MoveScrollBarToTop(); ///< Moves slider to the top + +protected: + + virtual int computeVPixelsNeeded( void ); ///< Computes vertical pixels needed by listbox contents + + virtual void PerformLayout(); ///< Positions listbox items, etc after internal changes + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); ///< Loads colors, fonts, etc + + virtual void OnCommand( const char *command ); ///< Passes commands up to the parent + +private: + enum { SCROLLBAR_SIZE = 18, DEFAULT_HEIGHT = 24, PANELBUFFER = 5 }; + + typedef struct dataitem_s + { + vgui::Panel *panel; + void * userData; + } DataItem; + CUtlVector< DataItem > m_items; + + vgui::ScrollBar *m_vbar; + vgui::Panel *m_pPanelEmbedded; + + int m_iScrollbarSize; + int m_iDefaultHeight; + int m_iPanelBuffer; + + int m_visibleIndex; + int m_lastSize; +}; + +#endif // BUYPRESET_LISTBOX_H diff --git a/game/client/cstrike/VGUI/buypreset_panel.cpp b/game/client/cstrike/VGUI/buypreset_panel.cpp new file mode 100644 index 0000000..321984b --- /dev/null +++ b/game/client/cstrike/VGUI/buypreset_panel.cpp @@ -0,0 +1,447 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#include "weapon_csbase.h" +#include "cs_ammodef.h" + +#include <vgui/IVGui.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include "vgui_controls/BuildGroup.h" +#include "vgui_controls/BitmapImagePanel.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/RichText.h" +#include "vgui_controls/QueryBox.h" +#include "career_box.h" +#include "buypreset_listbox.h" +#include "buypreset_weaponsetlabel.h" +#include "backgroundpanel.h" + +#include "cstrike/bot/shared_util.h" + +using namespace vgui; + +const float horizTitleRatio = 18.0f/68.0f; + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +/* +class PresetNameTextEntry : public TextEntry +{ +public: + PresetNameTextEntry(Panel *parent, CBuyPresetEditMainMenu *menu, const char *name ) : TextEntry( parent, name ) + { + m_pMenu = menu; + } + + virtual void FireActionSignal() + { + TextEntry::FireActionSignal(); + if ( m_pMenu ) + { + m_pMenu->SetDirty(); + } + } + +private: + CBuyPresetEditMainMenu *m_pMenu; +}; +*/ + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +int GetScaledValue( HScheme hScheme, int unscaled ) +{ + int val = scheme()->GetProportionalScaledValueEx( hScheme, unscaled ); + return GetAlternateProportionalValueFromScaled( hScheme, val ); +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +class PresetBackgroundPanel : public vgui::Panel +{ + typedef vgui::Panel BaseClass; + +public: + PresetBackgroundPanel( vgui::Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) + { + }; + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + SetBorder( pScheme->GetBorder("ButtonBorder") ); + m_lineColor = pScheme->GetColor( "Border.Bright", Color( 0, 0, 0, 0 ) ); + } + + virtual void ApplySettings( KeyValues *inResourceData ) + { + BaseClass::ApplySettings( inResourceData ); + + m_lines.RemoveAll(); + KeyValues *lines = inResourceData->FindKey( "lines", false ); + if ( lines ) + { + KeyValues *line = lines->GetFirstValue(); + while ( line ) + { + const char *str = line->GetString( NULL, "" ); + Vector4D p; + int numPoints = sscanf( str, "%f %f %f %f", &p[0], &p[1], &p[2], &p[3] ); + if ( numPoints == 4 ) + { + m_lines.AddToTail( p ); + } + line = line->GetNextValue(); + } + } + } + + virtual void PaintBackground( void ) + { + BaseClass::PaintBackground(); + + vgui::surface()->DrawSetColor( m_lineColor ); + vgui::surface()->DrawSetTextColor( m_lineColor ); + for ( int i=0; i<m_scaledLines.Count(); ++i ) + { + int x1, x2, y1, y2; + + x1 = m_scaledLines[i][0]; + y1 = m_scaledLines[i][1]; + x2 = m_scaledLines[i][2]; + y2 = m_scaledLines[i][3]; + + vgui::surface()->DrawFilledRect( x1, y1, x2, y2 ); + } + } + + virtual void PerformLayout( void ) + { + m_scaledLines.RemoveAll(); + for ( int i=0; i<m_lines.Count(); ++i ) + { + int x1, x2, y1, y2; + + x1 = GetScaledValue( GetScheme(), m_lines[i][0] ); + y1 = GetScaledValue( GetScheme(), m_lines[i][1] ); + x2 = GetScaledValue( GetScheme(), m_lines[i][2] ); + y2 = GetScaledValue( GetScheme(), m_lines[i][3] ); + + if ( x1 == x2 ) + { + ++x2; + } + + if ( y1 == y2 ) + { + ++y2; + } + + m_scaledLines.AddToTail( Vector4D( x1, y1, x2, y2 ) ); + } + } + +private: + Color m_lineColor; + CUtlVector< Vector4D > m_lines; + CUtlVector< Vector4D > m_scaledLines; +}; + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +BuyPresetEditPanel::BuyPresetEditPanel( Panel *parent, const char *panelName, const char *resourceFilename, int fallbackIndex, bool editableName ) : BaseClass( parent, panelName ) +{ + SetProportional( parent->IsProportional() ); + if ( IsProportional() ) + { + m_baseWide = m_baseTall = scheme()->GetProportionalScaledValueEx( GetScheme(), 100 ); + } + else + { + m_baseWide = m_baseTall = 100; + } + SetSize( m_baseWide, m_baseTall ); + + m_fallbackIndex = fallbackIndex; + + m_pBgPanel = new PresetBackgroundPanel( this, "mainBackground" ); + + m_pTitleEntry = NULL; + m_pTitleLabel = NULL; + m_pCostLabel = NULL; + /* + m_pTitleEntry = new PresetNameTextEntry( this, dynamic_cast<CBuyPresetEditMainMenu *>(parent), "titleEntry" ); + m_pTitleLabel = new Label( this, "title", "" ); + m_pCostLabel = new Label( this, "cost", "" ); + */ + + m_pPrimaryWeapon = new WeaponLabel( this, "primary" ); + m_pSecondaryWeapon = new WeaponLabel( this, "secondary" ); + + m_pHEGrenade = new EquipmentLabel( this, "hegrenade" ); + m_pSmokeGrenade = new EquipmentLabel( this, "smokegrenade" ); + m_pFlashbangs = new EquipmentLabel( this, "flashbang" ); + + m_pDefuser = new EquipmentLabel( this, "defuser" ); + m_pNightvision = new EquipmentLabel( this, "nightvision" ); + + m_pArmor = new EquipmentLabel( this, "armor" ); + + if ( resourceFilename ) + { + LoadControlSettings( resourceFilename ); + } + + int x, y, w, h; + m_pBgPanel->GetBounds( x, y, w, h ); + + m_baseWide = x + w; + m_baseTall = y + h; + SetSize( m_baseWide, m_baseTall ); +} + +//-------------------------------------------------------------------------------------------------------------- +BuyPresetEditPanel::~BuyPresetEditPanel() +{ +} + +//-------------------------------------------------------------------------------------------------------------- +void BuyPresetEditPanel::SetWeaponSet( const WeaponSet *pWeaponSet, bool current ) +{ + // set to empty state + Reset(); + + // now fill in items + if ( pWeaponSet ) + { + if ( m_pTitleLabel ) + { + m_pTitleLabel->SetText( SharedVarArgs( "#Cstrike_BuyPresetChoice%d", m_fallbackIndex ) ); + } + if ( m_pTitleEntry ) + { + m_pTitleEntry->SetText( SharedVarArgs( "#Cstrike_BuyPresetChoice%d", m_fallbackIndex ) ); + } + + if ( m_pCostLabel ) + { + const int BufLen = 256; + wchar_t wbuf[BufLen]; + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( pWeaponSet->FullCost() ) ); + m_pCostLabel->SetText( wbuf ); + } + + m_pPrimaryWeapon->SetWeapon( &pWeaponSet->m_primaryWeapon, true, true ); + m_pSecondaryWeapon->SetWeapon( &pWeaponSet->m_secondaryWeapon, false, true ); + + if ( pWeaponSet->m_HEGrenade ) + m_pHEGrenade->SetItem( "gfx/vgui/hegrenade_square", 1 ); + if ( pWeaponSet->m_smokeGrenade ) + m_pSmokeGrenade->SetItem( "gfx/vgui/smokegrenade_square", 1 ); + if ( pWeaponSet->m_flashbangs ) + m_pFlashbangs->SetItem( "gfx/vgui/flashbang_square", pWeaponSet->m_flashbangs ); + + if ( pWeaponSet->m_defuser ) + m_pDefuser->SetItem( "gfx/vgui/defuser", 1 ); + if ( pWeaponSet->m_nightvision ) + m_pNightvision->SetItem( "gfx/vgui/nightvision", 1 ); + + if ( pWeaponSet->m_armor ) + { + if ( pWeaponSet->m_helmet ) + m_pArmor->SetItem( "gfx/vgui/kevlar_helmet", 1 ); + else + m_pArmor->SetItem( "gfx/vgui/kevlar", 1 ); + } + } +} + +//-------------------------------------------------------------------------------------------------------------- +void BuyPresetEditPanel::SetText( const wchar_t *text ) +{ + if ( !text ) + text = L""; + if ( m_pTitleLabel ) + { + m_pTitleLabel->SetText( text ); + } + if ( m_pTitleEntry ) + { + m_pTitleEntry->SetText( text ); + } + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Handle command callbacks + */ +void BuyPresetEditPanel::OnCommand( const char *command ) +{ + if (stricmp(command, "close")) + { + PostActionSignal( new KeyValues("Command", "command", SharedVarArgs( "%s %d", command, m_fallbackIndex )) ); + } + + BaseClass::OnCommand(command); +} + +//-------------------------------------------------------------------------------------------------------------- +void BuyPresetEditPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + SetBgColor( Color( 0, 0, 0, 0 ) ); + + IBorder *pBorder = NULL; + + int i; + + for (i = 0; i < GetChildCount(); i++) + { + // perform auto-layout on the child panel + Panel *child = GetChild(i); + if (!child) + continue; + + if ( !stricmp( "button", child->GetClassName() ) ) + { + Button *pButton = dynamic_cast<Button *>(child); + if ( pButton ) + { + pButton->SetDefaultBorder( pBorder ); + pButton->SetDepressedBorder( pBorder ); + pButton->SetKeyFocusBorder( pBorder ); + } + } + } + + pBorder = pScheme->GetBorder("BuyPresetButtonBorder"); + + const int NumButtons = 4; + const char * buttonNames[4] = { "editPrimary", "editSecondary", "editGrenades", "editEquipment" }; + for ( i=0; i<NumButtons; ++i ) + { + Panel *pPanel = FindChildByName( buttonNames[i] ); + if ( pPanel ) + { + pPanel->SetBorder( pBorder ); + if ( !stricmp( "button", pPanel->GetClassName() ) ) + { + Button *pButton = dynamic_cast<Button *>(pPanel); + if ( pButton ) + { + pButton->SetDefaultBorder( pBorder ); + pButton->SetDepressedBorder( pBorder ); + pButton->SetKeyFocusBorder( pBorder ); + + Color fgColor, bgColor; + fgColor = GetSchemeColor("Label.TextDullColor", GetFgColor(), pScheme); + bgColor = Color( 0, 0, 0, 0 ); + pButton->SetDefaultColor( fgColor, bgColor ); + } + } + } + } +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Overrides EditablePanel's resizing of children to scale them proportionally to the main panel's change. + */ +void BuyPresetEditPanel::OnSizeChanged( int wide, int tall ) +{ + if ( !m_baseWide ) + m_baseWide = 1; + if ( !m_baseTall ) + m_baseTall = 1; + + Panel::OnSizeChanged(wide, tall); + InvalidateLayout(); + + if ( wide == m_baseWide && tall == m_baseTall ) + { + Repaint(); + return; + } + + float xScale = wide / (float) m_baseWide; + float yScale = tall / (float) m_baseTall; + + for (int i = 0; i < GetChildCount(); i++) + { + // perform auto-layout on the child panel + Panel *child = GetChild(i); + if (!child) + continue; + + int x, y, w, t; + child->GetBounds(x, y, w, t); + + int newX = (int) x * xScale; + int newY = (int) y * yScale; + int newW = (int) (x+w) * xScale - newX; + int newT = (int) t * yScale; + + // make sure the child isn't too big... + if(newX+newW>wide) + { + continue; + } + + if(newY+newT>tall) + { + continue; + } + + child->SetBounds(newX, newY, newW, newT); + child->InvalidateLayout(); + } + Repaint(); + + // update the baselines + m_baseWide = wide; + m_baseTall = tall; +} + +//-------------------------------------------------------------------------------------------------------------- +void BuyPresetEditPanel::Reset() +{ + if ( m_pTitleLabel ) + { + m_pTitleLabel->SetText( "#Cstrike_BuyPresetNewChoice" ); + } + if ( m_pTitleEntry ) + { + m_pTitleEntry->SetText( "#Cstrike_BuyPresetNewChoice" ); + } + if ( m_pCostLabel ) + { + m_pCostLabel->SetText( "" ); + } + + BuyPresetWeapon weapon; + m_pPrimaryWeapon->SetWeapon( &weapon, true, false ); + m_pSecondaryWeapon->SetWeapon( &weapon, false, false ); + + m_pHEGrenade->SetItem( NULL, 1 ); + m_pSmokeGrenade->SetItem( NULL, 1 ); + m_pFlashbangs->SetItem( NULL, 1 ); + + m_pDefuser->SetItem( NULL, 1 ); + m_pNightvision->SetItem( NULL, 1 ); + + m_pArmor->SetItem( NULL, 1 ); +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/VGUI/buypreset_weaponsetlabel.h b/game/client/cstrike/VGUI/buypreset_weaponsetlabel.h new file mode 100644 index 0000000..9c205ad --- /dev/null +++ b/game/client/cstrike/VGUI/buypreset_weaponsetlabel.h @@ -0,0 +1,302 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef BUYPRESET_WEAPONSETLABEL_H +#define BUYPRESET_WEAPONSETLABEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui_controls/Panel.h> +#include <vgui/IImage.h> + +namespace vgui +{ + class TextImage; + class TextEntry; +}; + +//-------------------------------------------------------------------------------------------------------------- +/// Helper function: draws a simple dashed line +void DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen); + +//-------------------------------------------------------------------------------------------------------------- +// Purpose: Wraps an IImage to perform resizes properly +class BuyPresetImage : public vgui::IImage +{ +public: + BuyPresetImage( vgui::IImage *realImage ) + { + m_image = realImage; + if ( m_image ) + { + m_image->GetSize( m_wide, m_tall ); + } + else + { + m_wide = m_tall = 0; + } + } + + // Call to Paint the image + // Image will draw within the current panel context at the specified position + virtual void Paint() + { + if ( !m_image ) + return; + + m_image->Paint(); + } + + // Set the position of the image + virtual void SetPos(int x, int y) + { + if ( !m_image ) + return; + + m_image->SetPos( x, y ); + } + + // Gets the size of the content + virtual void GetContentSize(int &wide, int &tall) + { + if ( !m_image ) + return; + + m_image->GetSize( wide, tall ); + } + + // Get the size the image will actually draw in (usually defaults to the content size) + virtual void GetSize(int &wide, int &tall) + { + if ( !m_image ) + { + wide = tall = 0; + return; + } + + wide = m_wide; + tall = m_tall; + } + + // Sets the size of the image + virtual void SetSize(int wide, int tall) + { + m_wide = wide; + m_tall = tall; + if ( !m_image ) + return; + + m_image->SetSize( wide, tall ); + } + + // Set the draw color + virtual void SetColor(Color col) + { + if ( !m_image ) + return; + + m_image->SetColor( col ); + } + + virtual bool Evict() + { + return false; + } + + virtual int GetNumFrames() + { + return 0; + } + + virtual void SetFrame( int nFrame ) + { + } + + virtual vgui::HTexture GetID() + { + return 0; + } + + virtual void SetRotation( int iRotation ) + { + return; + } + +private: + vgui::IImage *m_image; + int m_wide, m_tall; +}; + +//-------------------------------------------------------------------------------------------------------------- +struct ImageInfo { + vgui::IImage *image; + int w; + int h; + int x; + int y; + int fullW; + int fullH; + + void FitInBounds( int baseX, int baseY, int width, int height, bool center, int scaleAt1024, bool halfHeight = false ); + void Paint(); +}; + +//-------------------------------------------------------------------------------------------------------------- +class WeaponImageInfo +{ +public: + WeaponImageInfo(); + ~WeaponImageInfo(); + + void SetBounds( int left, int top, int wide, int tall ); + void SetCentered( bool isCentered ); + void SetScaleAt1024( int weaponScale, int ammoScale ); + void SetWeapon( const BuyPresetWeapon *pWeapon, bool isPrimary, bool useCurrentAmmoType ); + + void ApplyTextSettings( vgui::IScheme *pScheme, bool isProportional ); + + void Paint(); + void PaintText(); + +private: + void PerformLayout(); + + int m_left; + int m_top; + int m_wide; + int m_tall; + + bool m_isPrimary; + + int m_weaponScale; + int m_ammoScale; + + bool m_needLayout; + bool m_isCentered; + ImageInfo m_weapon; + ImageInfo m_ammo; + + vgui::TextImage *m_pAmmoText; +}; + +//-------------------------------------------------------------------------------------------------------------- +class ItemImageInfo +{ +public: + ItemImageInfo(); + ~ItemImageInfo(); + + void SetBounds( int left, int top, int wide, int tall ); + void SetItem( const char *imageFname, int count ); + void ApplyTextSettings( vgui::IScheme *pScheme, bool isProportional ); + + void Paint(); + void PaintText(); + +private: + void PerformLayout(); + + int m_left; + int m_top; + int m_wide; + int m_tall; + + int m_count; + + bool m_needLayout; + ImageInfo m_image; + + vgui::TextImage *m_pText; +}; + +//-------------------------------------------------------------------------------------------------------------- +class WeaponLabel : public vgui::Panel +{ + typedef vgui::Panel BaseClass; +public: + WeaponLabel(vgui::Panel *parent, const char *panelName); + ~WeaponLabel(); + + void SetWeapon( const BuyPresetWeapon *pWeapon, bool isPrimary, bool showAmmo = false ); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void PerformLayout(); + virtual void Paint(); + +protected: + WeaponImageInfo m_weapon; +}; + +//-------------------------------------------------------------------------------------------------------------- +class EquipmentLabel : public vgui::Panel +{ + typedef vgui::Panel BaseClass; +public: + EquipmentLabel(vgui::Panel *parent, const char *panelName, const char *imageFname = NULL); + ~EquipmentLabel(); + + void SetItem( const char *imageFname, int count ); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void PerformLayout(); + virtual void Paint(); + +protected: + ItemImageInfo m_item; +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * BuyPresetEditPanel is a panel displaying a graphical representation of a buy preset. + */ +class BuyPresetEditPanel : public vgui::EditablePanel +{ + typedef vgui::EditablePanel BaseClass; +public: + BuyPresetEditPanel( vgui::Panel *parent, const char *panelName, const char *resourceFilename, int fallbackIndex, bool editableName ); + virtual ~BuyPresetEditPanel(); + + void SetWeaponSet( const WeaponSet *pWeaponSet, bool current ); + virtual void SetText( const wchar_t *text ); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + void OnCommand( const char *command); ///< Handle command callbacks + + virtual void OnSizeChanged( int wide, int tall ); + + void SetPanelBgColor( Color color ) { if (m_pBgPanel) m_pBgPanel->SetBgColor( color ); } + +protected: + void Reset(); + + vgui::Panel *m_pBgPanel; + + vgui::TextEntry *m_pTitleEntry; + vgui::Label *m_pTitleLabel; + vgui::Label *m_pCostLabel; + + WeaponLabel *m_pPrimaryWeapon; + WeaponLabel *m_pSecondaryWeapon; + + EquipmentLabel *m_pHEGrenade; + EquipmentLabel *m_pSmokeGrenade; + EquipmentLabel *m_pFlashbangs; + + EquipmentLabel *m_pDefuser; + EquipmentLabel *m_pNightvision; + + EquipmentLabel *m_pArmor; + + int m_baseWide; + int m_baseTall; + + int m_fallbackIndex; +}; + + +#endif // BUYPRESET_WEAPONSETLABEL_H diff --git a/game/client/cstrike/VGUI/career_box.cpp b/game/client/cstrike/VGUI/career_box.cpp new file mode 100644 index 0000000..308a44b --- /dev/null +++ b/game/client/cstrike/VGUI/career_box.cpp @@ -0,0 +1,1296 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +// Author: Matthew D. Campbell ([email protected]), 2003 + +#include "cbase.h" + +#include <vgui/KeyCode.h> +#include "career_box.h" +#include "career_button.h" +#include "buypreset_listbox.h" +#include <vgui_controls/TextImage.h> +#include <vgui_controls/CheckButton.h> +#include <vgui_controls/ComboBox.h> +#include "cs_ammodef.h" +#include "weapon_csbase.h" +#include "backgroundpanel.h" +#include "cs_gamerules.h" + +#include <vgui/IInput.h> +#include "bot/shared_util.h" +#include <vgui_controls/BitmapImagePanel.h> +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +using namespace vgui; + +//-------------------------------------------------------------------------------------------------------------- +class ConVarToggleCheckButton : public vgui::CheckButton +{ + DECLARE_CLASS_SIMPLE( ConVarToggleCheckButton, vgui::CheckButton ); + +public: + ConVarToggleCheckButton( vgui::Panel *parent, const char *panelName, const char *text ); + ~ConVarToggleCheckButton(); + + virtual void SetSelected( bool state ); + + virtual void Paint(); + + void Reset(); + void ApplyChanges(); + bool HasBeenModified(); + void SetConVar( const char *name ); + + virtual void ApplySettings( KeyValues *inResourceData ) + { + BaseClass::ApplySettings( inResourceData ); + + const char *name = inResourceData->GetString( "convar", NULL ); + if ( name ) + { + SetConVar( name ); + } + } + +private: + MESSAGE_FUNC( OnButtonChecked, "CheckButtonChecked" ); + + char *m_pszCvarName; + bool m_bStartValue; +}; + + +//-------------------------------------------------------------------------------------------------------------- +ConVarToggleCheckButton::ConVarToggleCheckButton( Panel *parent, const char *panelName, const char *text ) + : CheckButton( parent, panelName, text ) +{ + m_pszCvarName = NULL; + AddActionSignalTarget( this ); +} + + +//-------------------------------------------------------------------------------------------------------------- +ConVarToggleCheckButton::~ConVarToggleCheckButton() +{ + if ( m_pszCvarName ) + delete[] m_pszCvarName; +} + + +//-------------------------------------------------------------------------------------------------------------- +void ConVarToggleCheckButton::SetConVar( const char *name ) +{ + if ( m_pszCvarName ) + delete[] m_pszCvarName; + + m_pszCvarName = CloneString( name ); + + if (m_pszCvarName && *m_pszCvarName) + { + Reset(); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void ConVarToggleCheckButton::Paint() +{ + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + { + BaseClass::Paint(); + return; + } + + // Look up current value + ConVar const *var = cvar->FindVar( m_pszCvarName ); + if ( !var ) + return; + bool value = var->GetBool(); + + if (value != m_bStartValue) + { + SetSelected( value ); + m_bStartValue = value; + } + BaseClass::Paint(); +} + + +//-------------------------------------------------------------------------------------------------------------- +void ConVarToggleCheckButton::ApplyChanges() +{ + m_bStartValue = IsSelected(); + ConVar *var = (ConVar *)cvar->FindVar( m_pszCvarName ); + if ( !var ) + return; + var->SetValue(m_bStartValue); + +} + + +//-------------------------------------------------------------------------------------------------------------- +void ConVarToggleCheckButton::Reset() +{ + ConVar const *var = cvar->FindVar( m_pszCvarName ); + if ( !var ) + return; + m_bStartValue = var->GetBool(); + SetSelected(m_bStartValue); +} + + +//-------------------------------------------------------------------------------------------------------------- +bool ConVarToggleCheckButton::HasBeenModified() +{ + return IsSelected() != m_bStartValue; +} + + +//-------------------------------------------------------------------------------------------------------------- +void ConVarToggleCheckButton::SetSelected( bool state ) +{ + BaseClass::SetSelected( state ); + + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + return; +} + + +//-------------------------------------------------------------------------------------------------------------- +void ConVarToggleCheckButton::OnButtonChecked() +{ + if (HasBeenModified()) + { + PostActionSignal(new KeyValues("ControlModified")); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +CCareerBaseBox::CCareerBaseBox(Panel *parent, const char *panelName, bool loadResources, bool useCareerButtons) : Frame(parent, panelName, false) +{ +// @TODO: SetScheme("CareerBoxScheme"); + SetScheme("ClientScheme"); + SetProportional( true ); + SetMoveable(false); + SetSizeable(false); + + m_bgColor = Color( 0, 0, 0, 0 ); + m_borderColor = Color( 0, 0, 0, 0 ); + + m_pTextLabel = new Label( this, "TextLabel", "" ); + + if ( useCareerButtons ) + { + m_pOkButton = new CCareerButton( this, "OkButton", "", "", false ); + m_pCancelButton = new CCareerButton( this, "CancelButton", "", "", false ); + } + else + { + m_pOkButton = new Button( this, "OkButton", "" ); + m_pCancelButton = new Button( this, "CancelButton", "" ); + } + m_pOkButton->SetVisible(false); + if ( useCareerButtons ) + { + m_buttons.PutElement( m_pOkButton ); + m_buttons.PutElement( m_pCancelButton ); + } + m_cancelFocus = false; + + if (loadResources) + { + const int BufLen = strlen(panelName) + 32; + char *buf = new char[BufLen]; + Q_snprintf( buf, BufLen, "Resource/Career/%s.res", panelName ); + LoadControlSettings( buf ); + delete[] buf; + } +} + +//-------------------------------------------------------------------------------------------------------------- +vgui::Panel * CCareerBaseBox::CreateControlByName(const char *controlName) +{ + if ( Q_stricmp( controlName, "ConVarCheckButton" ) == 0 ) + { + ConVarToggleCheckButton *button = new ConVarToggleCheckButton( NULL, controlName, "" ); + m_conVarCheckButtons.PutElement( button ); + return button; + } + + return BaseClass::CreateControlByName( controlName ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::SetCancelButtonAsDefault() +{ + m_cancelFocus = true; +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::SetLabelText( const wchar_t *text ) +{ + m_pTextLabel->SetText(text); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::SetLabelText( const char *text ) +{ + m_pTextLabel->SetText(text); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + for (int i=0; i<m_buttons.GetCount(); ++i) + { + m_buttons[i]->SetArmedSound("UI/buttonrollover.wav"); + m_buttons[i]->SetDepressedSound("UI/buttonclick.wav"); + m_buttons[i]->SetReleasedSound("UI/buttonclickrelease.wav"); + } + + m_bgColor = GetSchemeColor("Popup.BgColor", Color( 64, 64, 64, 255 ), pScheme); + m_borderColor = GetSchemeColor("FgColor", Color( 64, 64, 64, 255 ), pScheme); + + SetBorder( pScheme->GetBorder( "BaseBorder" ) ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::PerformLayout( ) +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + GetBounds(x, y, w, h); + + int screenWide, screenTall; + GetHudSize( screenWide, screenTall ); + if ( x + w/2 != screenWide/2 ) + { + SetPos( screenWide/2 - w/2, screenTall/2 - h/2 ); + GetBounds(x, y, w, h); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::PaintBackground( ) +{ + int wide, tall; + GetSize( wide, tall ); + + DrawRoundedBackground( m_bgColor, wide, tall ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::PaintBorder( ) +{ + int wide, tall; + GetSize( wide, tall ); + + DrawRoundedBorder( m_borderColor, wide, tall ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::ShowWindow() +{ + SetVisible( true ); + SetEnabled( true ); + MoveToFront(); + + if ( m_pCancelButton->IsVisible() && m_cancelFocus ) + { + m_pCancelButton->RequestFocus(); + } + else if ( m_pOkButton->IsVisible() ) + { + m_pOkButton->RequestFocus(); + } + else // handle message boxes with no button + { + RequestFocus(); + } + + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::DoModal() +{ + ShowWindow(); + input()->SetAppModalSurface(GetVPanel()); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::OnKeyCodeTyped(KeyCode code) +{ + if (code == KEY_ESCAPE) + { + OnCommand("Cancel"); + } + else if (code == KEY_ENTER) + { + BaseClass::OnKeyCodeTyped( KEY_SPACE ); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::OnCommand(const char *command) +{ + KeyValues *okSettings = new KeyValues( "GetCommand" ); + if ( m_pOkButton->RequestInfo( okSettings ) ) + { + const char *okCommand = okSettings->GetString( "command", "Ok" ); + if ( stricmp(command, okCommand) == 0 ) + { + for (int i=0; i<m_conVarCheckButtons.GetCount(); ++i) + { + m_conVarCheckButtons[i]->ApplyChanges(); + } + } + } + + if (stricmp(command, "close")) + { + PostActionSignal( new KeyValues("Command", "command", command) ); + } + + BaseClass::OnCommand(command); + + Close(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerBaseBox::AddButton( vgui::Button *pButton ) +{ + m_buttons.PutElement( pButton ); +} + +//-------------------------------------------------------------------------------------------------------------- +CCareerQueryBox::CCareerQueryBox(vgui::Panel *parent, const char *panelName, const char *resourceName) : CCareerBaseBox(parent, panelName, (resourceName == NULL)) +{ + if ( resourceName ) + { + LoadControlSettings( resourceName ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +CCareerQueryBox::CCareerQueryBox(const char *title, const char *labelText, const char *panelName, vgui::Panel *parent) : CCareerBaseBox(parent, panelName) +{ +} + +//-------------------------------------------------------------------------------------------------------------- +CCareerQueryBox::CCareerQueryBox(const wchar_t *title, const wchar_t *labelText, const char *panelName, vgui::Panel *parent) : CCareerBaseBox(parent, panelName) +{ +} + +//-------------------------------------------------------------------------------------------------------------- +CCareerQueryBox::~CCareerQueryBox() +{ +} + +// sorted order weapon list ----------------------------------------------------------------------- +const int NUM_SECONDARY_WEAPONS = 7; +static CSWeaponID s_secondaryWeapons[NUM_SECONDARY_WEAPONS] = +{ + WEAPON_NONE, + WEAPON_USP, + WEAPON_GLOCK, + WEAPON_DEAGLE, + WEAPON_ELITE, + WEAPON_P228, + WEAPON_FIVESEVEN, +}; + +const int NUM_PRIMARY_WEAPONS = 23; +static CSWeaponID s_primaryWeapons[NUM_PRIMARY_WEAPONS] = +{ + WEAPON_NONE, + + // Assault Rifles + CSWeaponID(-WEAPONTYPE_RIFLE), + WEAPON_SG552, + WEAPON_AUG, + WEAPON_AK47, + WEAPON_M4A1, + WEAPON_GALIL, + WEAPON_FAMAS, + + // Snipers + CSWeaponID(-WEAPONTYPE_SNIPER_RIFLE), + WEAPON_AWP, + WEAPON_SG550, + WEAPON_G3SG1, + WEAPON_SCOUT, + + // SMG + CSWeaponID(-WEAPONTYPE_SUBMACHINEGUN), + WEAPON_P90, + WEAPON_UMP45, + WEAPON_MP5NAVY, + WEAPON_MAC10, + WEAPON_TMP, + + // Heavy + CSWeaponID(-WEAPONTYPE_SHOTGUN), + WEAPON_M249, + WEAPON_XM1014, + WEAPON_M3, +// WEAPON_SHIELDGUN, +}; + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +class CWeaponButton : public vgui::Button +{ + typedef vgui::Button BaseClass; + +public: + CWeaponButton( BuyPresetListBox *pParent, CSWeaponID weaponID, bool isPrimary ) : BaseClass( pParent->GetParent(), SharedVarArgs( "WeaponButton%d", weaponID ), "" ) + { + m_isPrimary = isPrimary; + m_pImage = NULL; + m_pTitleImage = new TextImage( WeaponIDToDisplayName( weaponID ) ); + m_pCostImage = new TextImage( L"" ); + + m_pListBox = pParent; + BuyPresetWeapon weapon( weaponID ); + SetWeapon( weapon ); + } + + virtual ~CWeaponButton() { delete m_pTitleImage; delete m_pCostImage; } + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + virtual Color GetBgColor() { return (IsCurrent()) ? m_selectedBgColor : Button::GetBgColor(); } + virtual Color GetFgColor() { return (IsCurrent()) ? m_selectedFgColor : Button::GetFgColor(); } + + void SetCurrent() { s_current = this; } + bool IsCurrent() const { return s_current == this; } + + void SetWeapon( const BuyPresetWeapon& weapon ); + const BuyPresetWeapon& GetWeapon() { return m_weapon; } + + virtual void Paint(); + virtual void PerformLayout(); + virtual void PaintBorder() {} + + virtual void OnMousePressed( MouseCode code ) + { + if ( code == MOUSE_LEFT ) + { + SetCurrent(); + m_pListBox->GetParent()->OnCommand( "select_weapon" ); + } + Button::OnMousePressed( code ); + } + + virtual void OnMouseDoublePressed( MouseCode code ) + { + if ( code == MOUSE_LEFT ) + { + SetCurrent(); + m_pListBox->GetParent()->OnCommand( "select_weapon" ); + m_pListBox->GetParent()->OnCommand( "popup_ok" ); + } + Button::OnMouseDoublePressed( code ); + } + +protected: + static CWeaponButton * s_current; + BuyPresetListBox * m_pListBox; + BuyPresetWeapon m_weapon; + + IImage *m_pImage; + TextImage *m_pTitleImage; + TextImage *m_pCostImage; + int m_imageWide; + int m_imageTall; + int m_imageX; + int m_imageY; + + Color m_selectedBgColor; + Color m_selectedFgColor; + + bool m_isPrimary; +}; + +//-------------------------------------------------------------------------------------------------------------- +CWeaponButton * CWeaponButton::s_current = NULL; + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponButton::ApplySchemeSettings( IScheme *pScheme ) +{ + CWeaponButton * current = s_current; // save off current so Button::ApplySchemeSettings() can get the right colors + s_current = NULL; + Button::ApplySchemeSettings( pScheme ); + m_selectedBgColor = GetSchemeColor( "Button.ArmedBgColor", Button::GetBgColor(), pScheme ); + m_selectedFgColor = GetSchemeColor( "Button.ArmedTextColor", Button::GetFgColor(), pScheme ); + s_current = current; + m_pTitleImage->SetColor( pScheme->GetColor( "Button.TextColor", Color() ) ); + m_pTitleImage->SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + m_pTitleImage->SetWrap( true ); + m_pCostImage->SetColor( pScheme->GetColor( "Button.TextColor", Color() ) ); + m_pCostImage->SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + m_pCostImage->SetWrap( true ); + SetWeapon( m_weapon ); + SetBorder( NULL ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponButton::SetWeapon( const BuyPresetWeapon& weapon ) +{ + m_weapon = weapon; + if ( m_weapon.GetName() ) + { + // weapon string + const wchar_t * name = m_weapon.GetName(); + m_pTitleImage->SetText( name ); + + // cost string + CCSWeaponInfo *info = GetWeaponInfo( m_weapon.GetWeaponID() ); + if ( info ) + { + const int BufLen = 256; + wchar_t wbuf[BufLen]; + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( info->GetWeaponPrice() ) ); + m_pCostImage->SetText( wbuf ); + } + else + { + m_pCostImage->SetText( L"" ); + } + } + else + { + m_pTitleImage->SetText( L"" ); + m_pCostImage->SetText( L"" ); + } + + m_pTitleImage->ResizeImageToContent(); + m_pCostImage->ResizeImageToContent(); + + InvalidateLayout( true, false ); + + m_pImage = scheme()->GetImage( ImageFnameFromWeaponID( m_weapon.GetWeaponID(), m_isPrimary ), true ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponButton::PerformLayout() +{ + BaseClass::PerformLayout(); + + // Calculate some sizes + int oldWide, oldTall; + GetSize( oldWide, oldTall ); + + const float IMAGE_PERCENT = 0.42f; + const float TITLE_PERCENT = 0.45f; + const float COST_PERCENT = 1.0f - TITLE_PERCENT - IMAGE_PERCENT; + + int maxTitleWide = (int) oldWide * TITLE_PERCENT; + int maxImageWide = (int) oldWide * IMAGE_PERCENT; + int maxCostWide = (int) oldWide * COST_PERCENT; + int textTall = surface()->GetFontTall( m_pTitleImage->GetFont() ) * 2; + if ( oldTall != textTall ) + { + oldTall = textTall; + SetSize( oldWide, oldTall ); + m_pListBox->InvalidateLayout(); + } + + // Position the weapon name + { + m_pTitleImage->SetSize( maxTitleWide, oldTall ); + m_pTitleImage->RecalculateNewLinePositions(); + m_pTitleImage->ResizeImageToContent(); + + int textContentWide, textContentTall; + m_pTitleImage->GetSize( textContentWide, textContentTall ); + + if ( textContentTall < textTall ) + { + m_pTitleImage->SetSize( maxTitleWide, textContentTall ); + m_pTitleImage->SetPos( maxImageWide, (textTall - textContentTall) / 2 ); + } + else + { + m_pTitleImage->SetPos( maxImageWide, 0 ); + } + } + + // Position the weapon cost + { + m_pCostImage->SetSize( maxCostWide, oldTall ); + m_pCostImage->RecalculateNewLinePositions(); + m_pCostImage->ResizeImageToContent(); + + int textContentWide, textContentTall; + m_pCostImage->GetSize( textContentWide, textContentTall ); + + if ( textContentTall < textTall ) + { + m_pCostImage->SetSize( maxCostWide, textContentTall ); + m_pCostImage->SetPos( oldWide - maxCostWide, (textTall - textContentTall) / 2 ); + } + else + { + m_pCostImage->SetPos( oldWide - maxCostWide, 0 ); + } + } + + + // Position the weapon image + m_pImage = scheme()->GetImage( ImageFnameFromWeaponID( m_weapon.GetWeaponID(), m_isPrimary ), true ); + + int maxImageTall = textTall; + + m_pImage->GetContentSize( m_imageWide, m_imageTall ); + if ( m_imageTall > maxImageTall ) + { + m_imageWide = (int) m_imageWide * 1.0f * maxImageTall / m_imageTall; + m_imageTall = maxImageTall; + } + if ( m_imageWide > maxImageWide ) + { + m_imageTall = (int) m_imageTall * 1.0f * maxImageWide / m_imageWide; + m_imageWide = maxImageWide; + } + m_imageY = (textTall - m_imageTall) / 2; + m_imageX = (((int) oldWide * IMAGE_PERCENT) - m_imageWide) / 2; +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponButton::Paint() +{ + if ( m_pImage ) + { + m_pImage->SetSize( m_imageWide, m_imageTall ); + m_pImage->SetPos( m_imageX, m_imageY ); + m_pImage->Paint(); + m_pImage->SetSize( 0, 0 ); + } + m_pTitleImage->Paint(); + m_pCostImage->Paint(); + + BaseClass::Paint(); +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +class WeaponComboBox : public vgui::ComboBox +{ +public: + WeaponComboBox( CWeaponSelectBox *parent, const char *name, int numEntries, bool editable ) + : ComboBox( parent, name, numEntries, editable ) + { + m_pBox = parent; + } + + virtual void OnSetText( const wchar_t *newText ) + { + ComboBox::OnSetText( newText ); + if ( m_pBox ) + m_pBox->UpdateClips(); + } + +private: + CWeaponSelectBox *m_pBox; +}; + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +CWeaponSelectBox::CWeaponSelectBox(vgui::Panel *parent, WeaponSet *pWeaponSet, bool isSecondary ) : CCareerBaseBox(parent, "BuyBoxSelectWeapon", false) +{ + m_pWeaponSet = pWeaponSet; + m_isSecondary = isSecondary; + + m_numWeapons = NUM_PRIMARY_WEAPONS; + m_weaponIDs = s_primaryWeapons; + + BuyPresetWeapon *weapon; + if ( !m_isSecondary ) + { + weapon = &m_pWeaponSet->m_primaryWeapon; + if ( !IsPrimaryWeapon( weapon->GetWeaponID() ) && weapon->GetWeaponID() != WEAPON_NONE ) + { + BuyPresetWeapon tmp( WEAPON_NONE ); + m_pWeaponSet->m_primaryWeapon = tmp; + } + } + else + { + m_numWeapons = NUM_SECONDARY_WEAPONS; + m_weaponIDs = s_secondaryWeapons; + weapon = &m_pWeaponSet->m_secondaryWeapon; + if ( !IsSecondaryWeapon( weapon->GetWeaponID() ) && weapon->GetWeaponID() != WEAPON_NONE ) + { + BuyPresetWeapon tmp( WEAPON_NONE ); + *weapon = tmp; + } + } + + int maxClips = 0; + const CCSWeaponInfo *info = GetWeaponInfo( weapon->GetWeaponID() ); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int buyClipSize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + maxClips = (buyClipSize > 0) ? ceil(maxRounds/(float)buyClipSize) : 0; + } + else + { + maxClips = NUM_CLIPS_FOR_CURRENT; // so we can buy ammo for our current gun + } + + m_pClips = new WeaponComboBox( this, "Clips", 2*maxClips+1, false ); + m_pClips->SetOpenDirection( Menu::UP ); + m_pListBox = new BuyPresetListBox( this, "WeaponListBox" ); + m_pBullets = new Label( this, "bullets", "" ); + + int selectedWeaponIndex = -1; + int i; + for ( i=0; i<m_numWeapons; ++i ) + { + if ( m_weaponIDs[i] < 0 ) + { + const char *text = ""; + switch ( (int)m_weaponIDs[i] ) + { + case -WEAPONTYPE_RIFLE: + text = "#Cstrike_BuyPresetCategoryRifle"; + break; + case -WEAPONTYPE_SNIPER_RIFLE: + text = "#Cstrike_BuyPresetCategorySniper"; + break; + case -WEAPONTYPE_SUBMACHINEGUN: + text = "#Cstrike_BuyPresetCategorySMG"; + break; + case -WEAPONTYPE_SHOTGUN: + text = "#Cstrike_BuyPresetCategoryHeavy"; + break; + } + Label *pLabel = new Label( m_pListBox, SharedVarArgs("weaponlabel%d", i), text ); + m_pListBox->AddItem( pLabel, NULL ); + } + else + { + CWeaponButton *pWeaponButton = new CWeaponButton( m_pListBox, m_weaponIDs[i], !m_isSecondary ); + m_pListBox->AddItem( pWeaponButton, NULL ); + if ( m_weaponIDs[i] == weapon->GetWeaponID() ) + { + pWeaponButton->SetCurrent(); + selectedWeaponIndex = i; + } + } + } + m_pListBox->MakeItemVisible( selectedWeaponIndex ); + + LoadControlSettings( "resource/UI/BuyPreset/BoxSelectWeapon.res" ); + PopulateControls(); +} + +//-------------------------------------------------------------------------------------------------------------- +CWeaponSelectBox::~CWeaponSelectBox() +{ +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponSelectBox::ActivateBuildMode() +{ + SetClipsVisible( true ); + m_pListBox->DeleteAllItems(); + BaseClass::ActivateBuildMode(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponSelectBox::SetClipsVisible( bool visible ) +{ + SetLabelVisible( visible ); + m_pClips->SetVisible( visible ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponSelectBox::PopulateControls() +{ + BuyPresetWeapon *weapon; + if (!m_isSecondary) + { + weapon = &m_pWeaponSet->m_primaryWeapon; + } + else + { + weapon = &m_pWeaponSet->m_secondaryWeapon; + } + + int i; + int maxClips = 0; + const CCSWeaponInfo *info = GetWeaponInfo( weapon->GetWeaponID() ); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int buyClipSize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + maxClips = (buyClipSize > 0) ? ceil(maxRounds/(float)buyClipSize) : 0; + } + else + { + maxClips = NUM_CLIPS_FOR_CURRENT; + } + m_pClips->SetNumberOfEditLines( 2*maxClips+1 ); + + SetClipsVisible( maxClips != 0 ); + + m_pClips->DeleteAllItems(); + + // populate clips combo box + m_pClips->AddItem( "#Cstrike_BuyPresetEditWeaponFullClips", NULL ); + + const int BufLen = 64; + wchar_t buf[BufLen]; + for ( i=maxClips-1; i>=0; --i ) + { + const char* clipsOrMore = "#Cstrike_BuyPresetEditClipsOrMore"; + const char* clips = "#Cstrike_BuyPresetEditClips"; + if ( i == 1 ) + { + clipsOrMore = "#Cstrike_BuyPresetEditClipOrMore"; + clips = "#Cstrike_BuyPresetEditClip"; + } + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), + g_pVGuiLocalize->Find( clipsOrMore ), + 1, NumAsWString( i )); + m_pClips->AddItem( buf, NULL ); + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), + g_pVGuiLocalize->Find( clips ), + 1, NumAsWString( i )); + m_pClips->AddItem( buf, NULL ); + } + + // now select the proper entry + int clipIndexToSelect = weapon->GetFillAmmo(); + clipIndexToSelect += 2 * weapon->GetAmmoAmount(); + clipIndexToSelect = maxClips*2 - clipIndexToSelect; + m_pClips->ActivateItemByRow( clipIndexToSelect ); + + if ( m_isSecondary ) + { + Panel *pPanel = FindChildByName( "TitleLabel" ); + if ( pPanel ) + { + const wchar_t *title = g_pVGuiLocalize->Find( "#Cstrike_BuyPresetWizardSecondary" ); + if ( title ) + PostMessage(pPanel, new KeyValues("SetText", "text", title)); + } + } + + UpdateClips(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponSelectBox::UpdateClips() +{ + if ( !m_pClips ) + return; + + int numEntries = m_pClips->GetItemCount(); + int activeID = m_pClips->GetActiveItem(); + int combined = numEntries - activeID + 1; + int numClips = combined/2 - 1; + //bool isFill = (combined%2) != 0; + + BuyPresetWeapon *weapon; + if (!m_isSecondary) + { + weapon = &m_pWeaponSet->m_primaryWeapon; + } + else + { + weapon = &m_pWeaponSet->m_secondaryWeapon; + } + + const CCSWeaponInfo *info = GetWeaponInfo( weapon->GetWeaponID() ); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int buyClipSize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + m_pBullets->SetVisible( true ); + + const int BufLen = 64; + wchar_t buf[BufLen]; + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetsBullets" ), + 2, NumAsWString( MIN( maxRounds, numClips * buyClipSize ) ), NumAsWString( maxRounds ) ); + m_pBullets->SetText( buf ); + } + else + { + m_pBullets->SetVisible( false ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponSelectBox::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings(pScheme); + + HFont font = pScheme->GetFont("Default", IsProportional()); + int tall = surface()->GetFontTall(font) + 2; + if ( font != INVALID_FONT ) + { + Menu *pMenu = m_pClips->GetMenu(); + pMenu->SetMenuItemHeight( tall ); + } + + for ( int i=0; i<m_pListBox->GetItemCount(); ++i ) + { + if ( m_weaponIDs[i] >= 0 ) + { + CWeaponButton *pButton = static_cast< CWeaponButton * >(m_pListBox->GetItemPanel(i)); + if ( pButton && pButton->IsCurrent() ) + { + m_pListBox->MakeItemVisible( i ); + } + } + else + { + // it's a caption label + Label *pLabel = static_cast< Label * >(m_pListBox->GetItemPanel(i)); + if ( pLabel ) + { + pLabel->SetContentAlignment( Label::a_center ); + pLabel->SetBorder( pScheme->GetBorder( "ButtonBorder" ) ); + pLabel->SetBgColor( pScheme->GetColor( "Frame.BgColor", Color( 0, 0, 0, 255 ) ) ); + } + } + } +} + +//-------------------------------------------------------------------------------------------------------------- +CSWeaponID CWeaponSelectBox::GetSelectedWeaponID() +{ + int selectedIndex = -1; + for ( int i=0; i<m_pListBox->GetItemCount(); ++i ) + { + if ( m_weaponIDs[selectedIndex] >= 0 ) + { + CWeaponButton *pButton = static_cast< CWeaponButton * >(m_pListBox->GetItemPanel(i)); + if ( pButton && pButton->IsCurrent() ) + { + selectedIndex = i; + } + } + } + if ( selectedIndex >= 0 ) + { + return m_weaponIDs[selectedIndex]; + } + + return WEAPON_NONE; +} + +//-------------------------------------------------------------------------------------------------------------- +void CWeaponSelectBox::OnCommand(const char *command) +{ + if (!stricmp(command, "select_weapon")) + { + CSWeaponID weaponID = GetSelectedWeaponID(); + BuyPresetWeapon weapon( weaponID ); + if ( m_isSecondary ) + { + m_pWeaponSet->m_secondaryWeapon = weapon; + } + else + { + m_pWeaponSet->m_primaryWeapon = weapon; + } + + PopulateControls(); + return; + } + + if (!stricmp(command, "popup_ok")) + { + if ( !m_pClips ) + return; + + int numEntries = m_pClips->GetItemCount(); + int activeID = m_pClips->GetActiveItem(); + int combined = numEntries - activeID + 1; + int numClips = combined/2 - 1; + bool isFill = (combined%2) != 0; + + BuyPresetWeapon weapon( GetSelectedWeaponID() ); + weapon.SetAmmoType( AMMO_CLIPS ); + weapon.SetAmmoAmount( numClips ); + weapon.SetFillAmmo( isFill ); + + if ( m_isSecondary ) + { + m_pWeaponSet->m_secondaryWeapon = weapon; + } + else + { + m_pWeaponSet->m_primaryWeapon = weapon; + } + BaseClass::OnCommand( command ); + } + else + { + BaseClass::OnCommand(command); + } +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +class EquipmentComboBox : public ComboBox +{ +public: + EquipmentComboBox( CBaseSelectBox *parent, const char *name, int numEntries, bool editable ) + : ComboBox( parent, name, numEntries, editable ) + { + m_pBox = parent; + } + + virtual void OnSetText( const wchar_t *newText ) + { + ComboBox::OnSetText( newText ); + m_pBox->OnControlChanged(); + } + +private: + CBaseSelectBox *m_pBox; +}; + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +CGrenadeSelectBox::CGrenadeSelectBox( vgui::Panel *parent, WeaponSet *pWeaponSet ) : BaseClass( parent, "BuyBoxSelectGrenades", false ) +{ + m_pWeaponSet = pWeaponSet; + + // Equipment controls + m_pHEGrenade = new EquipmentComboBox( this, "hegrenade", 2, false ); + m_pSmokeGrenade = new EquipmentComboBox( this, "smokegrenade", 2, false ); + m_pFlashbangs = new EquipmentComboBox( this, "flashbangs", 3, false ); + + // Equipment images + m_pHEGrenadeImage = new EquipmentLabel( this, "HEGrenadeImage" ); + m_pSmokeGrenadeImage = new EquipmentLabel( this, "SmokeGrenadeImage" ); + m_pFlashbangImage = new EquipmentLabel( this, "FlashbangImage" ); + + m_pHELabel = new Label( this, "HECost", "" ); + m_pSmokeLabel = new Label( this, "SmokeCost", "" ); + m_pFlashLabel = new Label( this, "FlashCost", "" ); + + LoadControlSettings( "Resource/UI/BuyPreset/BoxSelectGrenades.res" ); + + // Add entries to the combo boxes + m_pHEGrenade->AddItem( "0", NULL ); + m_pHEGrenade->AddItem( "1", NULL ); + + m_pSmokeGrenade->AddItem( "0", NULL ); + m_pSmokeGrenade->AddItem( "1", NULL ); + + m_pFlashbangs->AddItem( "0", NULL ); + m_pFlashbangs->AddItem( "1", NULL ); + m_pFlashbangs->AddItem( "2", NULL ); + + // populate the data + m_pHEGrenade->ActivateItemByRow( m_pWeaponSet->m_HEGrenade ); + m_pSmokeGrenade->ActivateItemByRow( m_pWeaponSet->m_smokeGrenade ); + m_pFlashbangs->ActivateItemByRow( m_pWeaponSet->m_flashbangs ); + + m_pHEGrenadeImage->SetItem( "gfx/vgui/hegrenade_square", 1 ); + m_pSmokeGrenadeImage->SetItem( "gfx/vgui/smokegrenade_square", 1 ); + m_pFlashbangImage->SetItem( "gfx/vgui/flashbang_square", 1 ); + + OnControlChanged(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CGrenadeSelectBox::OnControlChanged() +{ + const int BufLen = 256; + wchar_t wbuf[BufLen]; + + CCSWeaponInfo *info; + + int numGrenades; + info = GetWeaponInfo( WEAPON_HEGRENADE ); + if ( info ) + { + numGrenades = m_pHEGrenade->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( info->GetWeaponPrice() * numGrenades ) ); + m_pHELabel->SetText( wbuf ); + } + + info = GetWeaponInfo( WEAPON_SMOKEGRENADE ); + if ( info ) + { + numGrenades = m_pSmokeGrenade->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( info->GetWeaponPrice() * numGrenades ) ); + m_pSmokeLabel->SetText( wbuf ); + } + + info = GetWeaponInfo( WEAPON_FLASHBANG ); + if ( info ) + { + numGrenades = m_pFlashbangs->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( info->GetWeaponPrice() * numGrenades ) ); + m_pFlashLabel->SetText( wbuf ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CGrenadeSelectBox::OnCommand( const char *command ) +{ + if (!stricmp(command, "popup_ok")) + { + // stuff values back in m_pWeaponSet + m_pWeaponSet->m_HEGrenade = m_pHEGrenade->GetActiveItem(); + m_pWeaponSet->m_smokeGrenade = m_pSmokeGrenade->GetActiveItem(); + m_pWeaponSet->m_flashbangs = m_pFlashbangs->GetActiveItem(); + } + BaseClass::OnCommand( command ); +} + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +CEquipmentSelectBox::CEquipmentSelectBox( vgui::Panel *parent, WeaponSet *pWeaponSet ) : BaseClass( parent, "BuyBoxSelectEquipment", false ) +{ + m_pWeaponSet = pWeaponSet; + + // Equipment controls + m_pKevlar = new EquipmentComboBox( this, "kevlar", 2, false ); + m_pHelmet = new EquipmentComboBox( this, "helmet", 2, false ); + m_pDefuser = new EquipmentComboBox( this, "defuser", 2, false ); + m_pNightvision = new EquipmentComboBox( this, "nightvision", 2, false ); + + // Equipment labels + m_pKevlarLabel = new Label( this, "kevlarCost", "" ); + m_pHelmetLabel = new Label( this, "helmetCost", "" ); + m_pDefuserLabel = new Label( this, "defuserCost", "" ); + m_pNightvisionLabel = new Label( this, "nightvisionCost", "" ); + + // Equipment images + m_pKevlarImage = new EquipmentLabel( this, "KevlarImage" ); + m_pHelmetImage = new EquipmentLabel( this, "HelmetImage" ); + m_pDefuserImage = new EquipmentLabel( this, "DefuserImage" ); + m_pNightvisionImage = new EquipmentLabel( this, "NightvisionImage" ); + + LoadControlSettings( "Resource/UI/BuyPreset/BoxSelectEquipment.res" ); + + // Add entries to the combo boxes + m_pKevlar->AddItem( "0", NULL ); + m_pKevlar->AddItem( "1", NULL ); + + m_pHelmet->AddItem( "0", NULL ); + m_pHelmet->AddItem( "1", NULL ); + + m_pDefuser->AddItem( "0", NULL ); + m_pDefuser->AddItem( "1", NULL ); + + m_pNightvision->AddItem( "0", NULL ); + m_pNightvision->AddItem( "1", NULL ); + + // populate the data + m_pKevlar->ActivateItemByRow( ( m_pWeaponSet->m_armor > 0 ) ); + m_pHelmet->ActivateItemByRow( ( m_pWeaponSet->m_armor && m_pWeaponSet->m_helmet ) ); + m_pDefuser->ActivateItemByRow( m_pWeaponSet->m_defuser ); + m_pNightvision->ActivateItemByRow( m_pWeaponSet->m_nightvision ); + + m_pKevlarImage->SetItem( "gfx/vgui/kevlar", 1 ); + m_pHelmetImage->SetItem( "gfx/vgui/helmet", 1 ); + m_pDefuserImage->SetItem( "gfx/vgui/defuser", 1 ); + m_pNightvisionImage->SetItem( "gfx/vgui/nightvision", 1 ); + + OnControlChanged(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CEquipmentSelectBox::OnControlChanged() +{ + const int BufLen = 256; + wchar_t wbuf[BufLen]; + + int iHelmetPrice = HELMET_PRICE; + int iKevlarPrice = KEVLAR_PRICE; + int iNVGPrice = NVG_PRICE; + + if ( CSGameRules()->IsBlackMarket() ) + { + iHelmetPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT ) - CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR ); + iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR ); + iNVGPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG ); + } + + int count = m_pKevlar->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( iKevlarPrice * count ) ); + m_pKevlarLabel->SetText( wbuf ); + + m_pHelmet->SetEnabled( count ); + if ( !count && m_pHelmet->GetActiveItem() ) + m_pHelmet->ActivateItemByRow( 0 ); + + count = m_pHelmet->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( iHelmetPrice * count ) ); + m_pHelmetLabel->SetText( wbuf ); + + count = m_pDefuser->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( DEFUSEKIT_PRICE * count ) ); + m_pDefuserLabel->SetText( wbuf ); + + count = m_pNightvision->GetActiveItem(); + g_pVGuiLocalize->ConstructString( wbuf, sizeof( wbuf ), + g_pVGuiLocalize->Find( "#Cstrike_BuyPresetPlainCost" ), + 1, NumAsWString( NVG_PRICE * count ) ); + m_pNightvisionLabel->SetText( wbuf ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CEquipmentSelectBox::OnCommand( const char *command ) +{ + if (!stricmp(command, "popup_ok")) + { + // stuff values back in m_pWeaponSet + m_pWeaponSet->m_armor = m_pKevlar->GetActiveItem() ? 100 : 0; + m_pWeaponSet->m_helmet = m_pWeaponSet->m_armor && m_pHelmet->GetActiveItem(); + m_pWeaponSet->m_defuser = m_pDefuser->GetActiveItem(); + m_pWeaponSet->m_nightvision = m_pNightvision->GetActiveItem(); + } + BaseClass::OnCommand( command ); +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/VGUI/career_box.h b/game/client/cstrike/VGUI/career_box.h new file mode 100644 index 0000000..7506308 --- /dev/null +++ b/game/client/cstrike/VGUI/career_box.h @@ -0,0 +1,202 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +// Author: Matthew D. Campbell ([email protected]), 2003 + +#ifndef CAREER_BOX_H +#define CAREER_BOX_H +#ifdef _WIN32 +#pragma once +#endif + +#include <KeyValues.h> +#include <vgui_controls/Frame.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/TextEntry.h> +#include "weapon_csbase.h" +#include "buy_presets/buy_presets.h" +#include "buypreset_listbox.h" +#include "buypreset_weaponsetlabel.h" + +class ConVarToggleCheckButton; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Base class for career popup dialogs (handles custom backgrounds, etc) + */ +class CCareerBaseBox : public vgui::Frame +{ +public: + CCareerBaseBox(vgui::Panel *parent, const char *panelName, bool loadResources = true, bool useCareerButtons = false ); + virtual void ShowWindow(); + void DoModal(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PaintBackground(); + virtual void PaintBorder(); + virtual void PerformLayout(); + void SetLabelText( const char *text ); + void SetLabelText( const wchar_t *text ); + void SetCancelButtonAsDefault(); + vgui::Button * GetOkButton() { return m_pOkButton; } + virtual vgui::Panel *CreateControlByName(const char *controlName); + +private: + typedef vgui::Frame BaseClass; + vgui::Button *m_pOkButton; + vgui::Button *m_pCancelButton; + vgui::Dar<vgui::Button *> m_buttons; + vgui::Dar<ConVarToggleCheckButton *> m_conVarCheckButtons; + + Color m_bgColor; + Color m_borderColor; + vgui::Label *m_pTextLabel; + bool m_cancelFocus; + +protected: + void SetLabelVisible( bool visible ) { m_pTextLabel->SetVisible( visible ); } + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + virtual void OnCommand( const char *command ); ///< Handle button presses + void AddButton( vgui::Button *pButton ); ///< Add a button to our list of buttons for rollover sounds +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Popup dialog with functionality similar to QueryBox, bot with different layout + */ +class CCareerQueryBox : public CCareerBaseBox +{ +public: + CCareerQueryBox(vgui::Panel *parent, const char *panelName, const char *resourceName = NULL); + CCareerQueryBox(const char *title, const char *labelText, const char *panelName, vgui::Panel *parent = NULL); + CCareerQueryBox(const wchar_t *title, const wchar_t *labelText, const char *panelName, vgui::Panel *parent = NULL); + + virtual ~CCareerQueryBox(); + +private: + typedef CCareerBaseBox BaseClass; +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Popup dialog for selecting and editing a primary weapon (ammo to buy, side-availability) + */ +class CWeaponSelectBox : public CCareerBaseBox +{ +public: + CWeaponSelectBox( vgui::Panel *parent, WeaponSet *pWeaponSet, bool isSecondary ); + + virtual ~CWeaponSelectBox(); + + virtual void ActivateBuildMode(); + + void UpdateClips(); + +protected: + virtual void OnCommand( const char *command ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + +private: + void PopulateControls(); + void SetClipsVisible( bool visible ); + + CSWeaponID GetSelectedWeaponID(); + + typedef CCareerBaseBox BaseClass; + vgui::ComboBox *m_pClips; ///< Number of clips to purchase + vgui::Label *m_pBullets; ///< Label showing "M of N Bullets" + + WeaponSet *m_pWeaponSet; ///< WeaponSet being edited + bool m_isSecondary; ///< is this weapon primary or secondary? + BuyPresetListBox *m_pListBox; ///< List of weapons from which to choose + + int m_numWeapons; + CSWeaponID *m_weaponIDs; +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Base class for editing grenades and equipment + */ +class CBaseSelectBox : public CCareerBaseBox +{ + typedef CCareerBaseBox BaseClass; + +public: + CBaseSelectBox( vgui::Panel *parent, const char *panelName, bool loadResources = true ) : BaseClass( parent, panelName, loadResources ) {} + + virtual void OnControlChanged() = 0; +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Popup dialog for editing grenades in a buy preset fallback + */ +class CGrenadeSelectBox : public CBaseSelectBox +{ + typedef CBaseSelectBox BaseClass; + +public: + CGrenadeSelectBox( vgui::Panel *parent, WeaponSet *pWeaponSet ); + + void OnControlChanged(); ///< Updates item costs + +private: + WeaponSet *m_pWeaponSet; ///< WeaponSet being edited + + // Equipment controls + vgui::ComboBox *m_pHEGrenade; + EquipmentLabel *m_pHEGrenadeImage; + vgui::Label *m_pHELabel; + + vgui::ComboBox *m_pSmokeGrenade; + EquipmentLabel *m_pSmokeGrenadeImage; + vgui::Label *m_pSmokeLabel; + + vgui::ComboBox *m_pFlashbangs; + EquipmentLabel *m_pFlashbangImage; + vgui::Label *m_pFlashLabel; + + virtual void OnCommand( const char *command ); +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Popup dialog for selecting an equipment set + */ +class CEquipmentSelectBox : public CBaseSelectBox +{ + typedef CBaseSelectBox BaseClass; + +public: + CEquipmentSelectBox( vgui::Panel *parent, WeaponSet *pWeaponSet ); + + void OnControlChanged(); + +private: + WeaponSet *m_pWeaponSet; ///< WeaponSet being edited + + // Equipment controls + vgui::ComboBox *m_pKevlar; + vgui::Label *m_pKevlarLabel; + EquipmentLabel *m_pKevlarImage; + + vgui::ComboBox *m_pHelmet; + vgui::Label *m_pHelmetLabel; + EquipmentLabel *m_pHelmetImage; + + vgui::ComboBox *m_pDefuser; + vgui::Label *m_pDefuserLabel; + EquipmentLabel *m_pDefuserImage; + + vgui::ComboBox *m_pNightvision; + vgui::Label *m_pNightvisionLabel; + EquipmentLabel *m_pNightvisionImage; + + virtual void OnCommand( const char *command ); +}; + +#endif // CAREER_BOX_H diff --git a/game/client/cstrike/VGUI/career_button.cpp b/game/client/cstrike/VGUI/career_button.cpp new file mode 100644 index 0000000..24bf779 --- /dev/null +++ b/game/client/cstrike/VGUI/career_button.cpp @@ -0,0 +1,189 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#include <convar.h> +#include "cdll_int.h" +#include <ienginevgui.h> +#include "filesystem.h" +#include <vgui/ISystem.h> +#include "career_button.h" + +#include <vgui_controls/Label.h> +#include <vgui_controls/URLLabel.h> +#include <vgui_controls/ComboBox.h> +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include <vgui_controls/ScrollBar.h> +#include <vgui_controls/BuildGroup.h> +#include <vgui_controls/ImageList.h> +#include <vgui_controls/TextImage.h> +#include <vgui_controls/Button.h> + +#include "KeyValues.h" + +using namespace vgui; + +#ifndef _DEBUG +#define PROPORTIONAL_CAREER_FRAMES 1 +#endif + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +CCareerButton::CCareerButton(vgui::Panel *parent, const char *buttonName, const char *buttonText, const char *image, bool textFirst) : BaseClass( parent, buttonName, "" ) +{ + m_armedBorder = NULL; + + m_textImage = new TextImage(buttonText); + m_image = scheme()->GetImage(image, true); + m_textFirst = textFirst; +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerButton::SetImage( const char *image ) +{ + m_image = scheme()->GetImage(image, true); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerButton::Paint() +{ + int buttonWide, buttonTall; + GetSize(buttonWide, buttonTall); + + int imageWide, imageTall; + if ( m_image ) + { + m_image->GetSize(imageWide, imageTall); + } + else + { + imageWide = imageTall = 0; + } + + int textOffset = m_textPad; + + if (m_textFirst) + { + if ( m_image ) + { + m_image->SetPos(buttonWide - imageWide - m_imagePad, (buttonTall - imageTall)/2); + m_image->Paint(); + } + } + else + { + if ( m_image ) + { + m_image->SetPos(m_imagePad, (buttonTall - imageTall)/2); + m_image->Paint(); + } + textOffset += imageWide + m_imagePad; + } + + int textTall, textWide; + m_textImage->GetSize(textWide, textTall); + + int textSpace = buttonWide - imageWide - m_imagePad - 2*m_textPad; + + if ( IsEnabled() ) + { + m_textImage->SetColor( m_textNormalColor ); + } + else + { + m_textImage->SetColor( m_textDisabledColor ); + } + m_textImage->SetPos(textOffset + (textSpace - textWide)/2, (buttonTall - textTall)/2); + m_textImage->Paint(); + + if (HasFocus() && IsEnabled() ) + { + int x0, y0, x1, y1; + x0 = 3, y0 = 3, x1 = buttonWide - 4 , y1 = buttonTall - 2; + DrawFocusBorder(x0, y0, x1, y1); + } +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerButton::SetArmedBorder(IBorder *border) +{ + m_armedBorder = border; + InvalidateLayout(false); +} + +//-------------------------------------------------------------------------------------------------------------- +IBorder* CCareerButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + if ( /*_buttonBorderEnabled &&*/ armed && !depressed && IsEnabled() ) + { + return m_armedBorder; + } + + return BaseClass::GetBorder( depressed, armed, selected, keyfocus ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CCareerButton::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetDefaultBorder( pScheme->GetBorder("CareerButtonBorder") ); + SetDepressedBorder( pScheme->GetBorder("CareerButtonDepressedBorder") ); + m_armedBorder = pScheme->GetBorder("CareerButtonArmedBorder"); + + m_textNormalColor = pScheme->GetColor( "Label.TextBrightColor", Color(255, 255, 255, 255) ); + m_textDisabledColor = pScheme->GetColor( "Label.DisabledFgColor2", Color(128, 128, 128, 255) ); + m_textImage->SetColor( m_textNormalColor ); + if ( m_image ) + { + m_image->SetColor( GetFgColor() ); + } + + m_textImage->SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + + m_textPad = atoi(pScheme->GetResourceString( "CareerButtonTextPad" )); + m_imagePad = atoi(pScheme->GetResourceString( "CareerButtonImagePad" )); + if (IsProportional()) + { + m_textPad = scheme()->GetProportionalScaledValueEx( GetScheme(),m_textPad); + m_imagePad = scheme()->GetProportionalScaledValueEx( GetScheme(),m_imagePad); + } + + const int BufLen = 128; + char buf[BufLen]; + GetText(buf, BufLen); + m_textImage->SetText(buf); + m_textImage->ResizeImageToContent(); + + int buttonWide, buttonTall; + GetSize(buttonWide, buttonTall); + + int imageWide, imageTall; + if ( m_image ) + { + m_image->GetSize(imageWide, imageTall); + } + else + { + imageWide = imageTall = 0; + } + + int textSpace = buttonWide - imageWide - m_imagePad - 2*m_textPad; + + int textWide, textTall; + m_textImage->GetContentSize(textWide, textTall); + if (textSpace < textWide) + m_textImage->SetSize(textSpace, textTall); + + Color bgColor = pScheme->GetColor( "CareerButtonBG", Color(0, 0, 0, 0) ); + SetDefaultColor( bgColor, bgColor ); + SetArmedColor( bgColor, bgColor ); + SetDepressedColor( bgColor, bgColor ); +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/VGUI/career_button.h b/game/client/cstrike/VGUI/career_button.h new file mode 100644 index 0000000..96dabdd --- /dev/null +++ b/game/client/cstrike/VGUI/career_button.h @@ -0,0 +1,57 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef CAREER_BUTTON_H +#define CAREER_BUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui_controls/Frame.h> +#include <vgui_controls/Controls.h> +#include <vgui_controls/BitmapImagePanel.h> +#include <vgui_controls/PanelListPanel.h> +#include <vgui_controls/Button.h> +#include <vgui/KeyCode.h> + +//-------------------------------------------------------------------------------------------------------------- +/** + * This class adds border functionality necessary for next/prev/start buttons on map and bot screens + */ +class CCareerButton : public vgui::Button +{ +public: + CCareerButton(vgui::Panel *parent, const char *buttonName, const char *buttonText, const char *image, bool textFirst ); + + // Set armed button border attributes. Added in CCareerButton. + virtual void SetArmedBorder(vgui::IBorder *border); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void Paint(); + + void SetImage( const char *image ); + +protected: + + // Get button border attributes. + virtual vgui::IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus); + + vgui::IBorder *m_armedBorder; + vgui::IImage *m_image; + vgui::TextImage *m_textImage; + bool m_textFirst; + int m_textPad; + int m_imagePad; + + Color m_textNormalColor; + Color m_textDisabledColor; + +private: + typedef vgui::Button BaseClass; +}; + +#endif // CAREER_BUTTON_H diff --git a/game/client/cstrike/VGUI/counterstrikeviewport.cpp b/game/client/cstrike/VGUI/counterstrikeviewport.cpp new file mode 100644 index 0000000..d1f8577 --- /dev/null +++ b/game/client/cstrike/VGUI/counterstrikeviewport.cpp @@ -0,0 +1,322 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Client DLL VGUI2 Viewport +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#pragma warning( disable : 4800 ) // disable forcing int to bool performance warning + +// VGUI panel includes +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include <KeyValues.h> +#include <vgui/Cursor.h> +#include <vgui/IScheme.h> +#include <vgui/IVGui.h> +#include <vgui/ILocalize.h> +#include <vgui/VGUI.h> + +// client dll/engine defines +#include "hud.h" +#include <voice_status.h> + +// cstrike specific dialogs +#include "cstriketextwindow.h" +#include "cstriketeammenu.h" +#include "cstrikeclassmenu.h" +#include "cstrikebuymenu.h" +#include "cstrikebuyequipmenu.h" +#include "cstrikespectatorgui.h" +#include "cstrikeclientscoreboard.h" +#include "clientmode_csnormal.h" +#include "IGameUIFuncs.h" + +// viewport definitions +#include <baseviewport.h> +#include "counterstrikeviewport.h" +#include "cs_gamerules.h" +// #include "c_user_message_register.h" +#include "vguicenterprint.h" +#include "text_message.h" + + +static void OpenPanelWithCheck( const char *panelToOpen, const char *panelToCheck ) +{ + IViewPortPanel *checkPanel = gViewPortInterface->FindPanelByName( panelToCheck ); + if ( !checkPanel || !checkPanel->IsVisible() ) + { + gViewPortInterface->ShowPanel( panelToOpen, true ); + } +} + + +CON_COMMAND( buyequip, "Show equipment buy menu" ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if( pPlayer && pPlayer->m_lifeState == LIFE_ALIVE && pPlayer->State_Get() == STATE_ACTIVE ) + { + if( !pPlayer->IsInBuyZone() ) + { + internalCenterPrint->Print( "#Cstrike_NotInBuyZone" ); + } + else if( CSGameRules()->IsBuyTimeElapsed() ) + { + char strBuyTime[16]; + Q_snprintf( strBuyTime, sizeof( strBuyTime ), "%d", (int)CSGameRules()->GetBuyTimeLength() ); + + wchar_t buffer[128]; + wchar_t buytime[16]; + g_pVGuiLocalize->ConvertANSIToUnicode( strBuyTime, buytime, sizeof(buytime) ); + g_pVGuiLocalize->ConstructString( buffer, sizeof(buffer), g_pVGuiLocalize->Find("#Cstrike_TitlesTXT_Cant_buy"), 1, buytime ); + internalCenterPrint->Print( buffer ); + } + else + { + if( pPlayer->GetTeamNumber() == TEAM_CT ) + { + OpenPanelWithCheck( PANEL_BUY_EQUIP_CT, PANEL_BUY_CT ); + } + else if( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) + { + OpenPanelWithCheck( PANEL_BUY_EQUIP_TER, PANEL_BUY_TER ); + } + } + } +} + +CON_COMMAND( buymenu, "Show main buy menu" ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if( pPlayer ) + { + if ( pPlayer->m_lifeState != LIFE_ALIVE && pPlayer->State_Get() != STATE_ACTIVE ) + return; + + if( !pPlayer->IsInBuyZone() ) + { + internalCenterPrint->Print( "#Cstrike_NotInBuyZone" ); + } + else if( CSGameRules()->IsBuyTimeElapsed() ) + { + char strBuyTime[16]; + Q_snprintf( strBuyTime, sizeof( strBuyTime ), "%d", (int)CSGameRules()->GetBuyTimeLength() ); + + wchar_t buffer[128]; + wchar_t buytime[16]; + g_pVGuiLocalize->ConvertANSIToUnicode( strBuyTime, buytime, sizeof(buytime) ); + g_pVGuiLocalize->ConstructString( buffer, sizeof(buffer), g_pVGuiLocalize->Find("#Cstrike_TitlesTXT_Cant_buy"), 1, buytime ); + internalCenterPrint->Print( buffer ); + } + else + { + if( pPlayer->GetTeamNumber() == TEAM_CT ) + { + OpenPanelWithCheck( PANEL_BUY_CT, PANEL_BUY_EQUIP_CT ); + } + else if( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) + { + OpenPanelWithCheck( PANEL_BUY_TER, PANEL_BUY_EQUIP_TER ); + } + } + } +} + +CON_COMMAND( chooseteam, "Choose a new team" ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( pPlayer && pPlayer->CanShowTeamMenu() ) + { + gViewPortInterface->ShowPanel( PANEL_TEAM, true ); + } +} + +CON_COMMAND_F( spec_help, "Show spectator help screen", FCVAR_CLIENTCMD_CAN_EXECUTE) +{ + if ( gViewPortInterface ) + gViewPortInterface->ShowPanel( PANEL_INFO, true ); +} + +CON_COMMAND_F( spec_menu, "Activates spectator menu", FCVAR_CLIENTCMD_CAN_EXECUTE) +{ + bool bShowIt = true; + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( pPlayer && !pPlayer->IsObserver() ) + return; + + if ( args.ArgC() == 2 ) + { + bShowIt = atoi( args[ 1 ] ) == 1; + } + + if ( gViewPortInterface ) + gViewPortInterface->ShowPanel( PANEL_SPECMENU, bShowIt ); +} + +CON_COMMAND_F( togglescores, "Toggles score panel", FCVAR_CLIENTCMD_CAN_EXECUTE) +{ + if ( !gViewPortInterface ) + return; + + IViewPortPanel *scoreboard = gViewPortInterface->FindPanelByName( PANEL_SCOREBOARD ); + + if ( !scoreboard ) + return; + + if ( scoreboard->IsVisible() ) + { + gViewPortInterface->ShowPanel( scoreboard, false ); + GetClientVoiceMgr()->StopSquelchMode(); + } + else + { + gViewPortInterface->ShowPanel( scoreboard, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: called when the VGUI subsystem starts up +// Creates the sub panels and initialises them +//----------------------------------------------------------------------------- +void CounterStrikeViewport::Start( IGameUIFuncs *pGameUIFuncs, IGameEventManager2 * pGameEventManager ) +{ + BaseClass::Start( pGameUIFuncs, pGameEventManager ); +} + +void CounterStrikeViewport::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + gHUD.InitColors( pScheme ); + + SetPaintBackgroundEnabled( false ); +} + + +IViewPortPanel* CounterStrikeViewport::CreatePanelByName(const char *szPanelName) +{ + IViewPortPanel* newpanel = NULL; + + // overwrite MOD specific panel creation + + if ( Q_strcmp(PANEL_SCOREBOARD, szPanelName) == 0) + { + newpanel = new CCSClientScoreBoardDialog( this ); + } + + else if ( Q_strcmp(PANEL_SPECGUI, szPanelName) == 0 ) + { + newpanel = new CCSSpectatorGUI( this ); + } + + else if ( Q_strcmp(PANEL_CLASS_CT, szPanelName) == 0 ) + { + newpanel = new CClassMenu_CT( this ); + } + + else if ( Q_strcmp(PANEL_CLASS_TER, szPanelName) == 0 ) + { + newpanel = new CClassMenu_TER( this ); + } + + else if ( Q_strcmp(PANEL_BUY_CT, szPanelName) == 0 ) + { + newpanel = new CCSBuyMenu_CT( this ); + } + + else if ( Q_strcmp(PANEL_BUY_TER, szPanelName) == 0 ) + { + newpanel = new CCSBuyMenu_TER( this ); + } + + else if ( Q_strcmp(PANEL_BUY_EQUIP_CT, szPanelName) == 0 ) + { + newpanel = new CCSBuyEquipMenu_CT( this ); + } + + else if ( Q_strcmp(PANEL_BUY_EQUIP_TER, szPanelName) == 0 ) + { + newpanel = new CCSBuyEquipMenu_TER( this ); + } + + else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 ) + { + newpanel = new CCSTeamMenu( this ); + } + + else if ( Q_strcmp(PANEL_INFO, szPanelName) == 0 ) + { + newpanel = new CCSTextWindow( this ); + } + + else + { + // create a generic base panel, don't add twice + newpanel = BaseClass::CreatePanelByName( szPanelName ); + } + + return newpanel; +} + +void CounterStrikeViewport::CreateDefaultPanels( void ) +{ + AddNewPanel( CreatePanelByName( PANEL_TEAM ), "PANEL_TEAM" ); + AddNewPanel( CreatePanelByName( PANEL_CLASS_CT ), "PANEL_CLASS_CT" ); + AddNewPanel( CreatePanelByName( PANEL_CLASS_TER ), "PANEL_CLASS_TER" ); + + AddNewPanel( CreatePanelByName( PANEL_BUY_CT ), "PANEL_BUY_CT" ); + AddNewPanel( CreatePanelByName( PANEL_BUY_TER ), "PANEL_BUY_TER" ); + AddNewPanel( CreatePanelByName( PANEL_BUY_EQUIP_CT ), "PANEL_BUY_EQUIP_CT" ); + AddNewPanel( CreatePanelByName( PANEL_BUY_EQUIP_TER ), "PANEL_BUY_EQUIP_TER" ); + + BaseClass::CreateDefaultPanels(); + +} + +int CounterStrikeViewport::GetDeathMessageStartHeight( void ) +{ + int x = YRES(2); + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + x += g_pSpectatorGUI->GetTopBarHeight(); + } + + return x; +} + +/* +========================== +HUD_ChatInputPosition + +Sets the location of the input for chat text +========================== +*/ +//MIKETODO: positioning of chat text (and other engine output) +/* + #include "Exports.h" + + void CL_DLLEXPORT HUD_ChatInputPosition( int *x, int *y ) + { + RecClChatInputPosition( x, y ); + if ( gViewPortInterface ) + { + gViewPortInterface->ChatInputPosition( x, y ); + } + } + + EXPOSE_SINGLE_INTERFACE(CounterStrikeViewport, IClientVGUI, CLIENTVGUI_INTERFACE_VERSION); +*/ diff --git a/game/client/cstrike/VGUI/counterstrikeviewport.h b/game/client/cstrike/VGUI/counterstrikeviewport.h new file mode 100644 index 0000000..2559167 --- /dev/null +++ b/game/client/cstrike/VGUI/counterstrikeviewport.h @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef COUNTERSTRIKEVIEWPORT_H +#define COUNTERSTRIKEVIEWPORT_H + +#include "cs_shareddefs.h" +#include "baseviewport.h" + + +using namespace vgui; + +namespace vgui +{ + class Panel; + class Label; + class CBitmapImagePanel; +} + +class CCSTeamMenu; +class CCSClassMenu; +class CCSSpectatorGUI; +class CCSClientScoreBoard; +class CBuyMenu; +class CCSClientScoreBoardDialog; + + + +//============================================================================== +class CounterStrikeViewport : public CBaseViewport +{ + +private: + DECLARE_CLASS_SIMPLE( CounterStrikeViewport, CBaseViewport ); + +public: + + IViewPortPanel* CreatePanelByName(const char *szPanelName); + void CreateDefaultPanels( void ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void Start( IGameUIFuncs *pGameUIFuncs, IGameEventManager2 * pGameEventManager ); + + int GetDeathMessageStartHeight( void ); + + virtual void ShowBackGround(bool bShow) + { + m_pBackGround->SetVisible( false ); // CS:S menus paint their own backgrounds... + } + +private: + void CenterWindow( vgui::Frame *win ); + +}; + + +#endif // COUNTERSTRIKEVIEWPORT_H diff --git a/game/client/cstrike/VGUI/cstrikebuyequipmenu.cpp b/game/client/cstrike/VGUI/cstrikebuyequipmenu.cpp new file mode 100644 index 0000000..ed8f013 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikebuyequipmenu.cpp @@ -0,0 +1,111 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstrikebuyequipmenu.h" +#include "cs_shareddefs.h" +#include "cstrikebuysubmenu.h" +#include "backgroundpanel.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor for CT Equipment menu +//----------------------------------------------------------------------------- +CCSBuyEquipMenu_CT::CCSBuyEquipMenu_CT(IViewPort *pViewPort) : CBuyMenu( pViewPort ) +{ + SetTitle( "#Cstrike_Buy_Menu", true); + + SetProportional( true ); + + m_pMainMenu = new CCSBuySubMenu( this, "BuySubMenu" ); + m_pMainMenu->LoadControlSettings( "Resource/UI/BuyEquipment_CT.res" ); + m_pMainMenu->SetVisible( false ); + + m_iTeam = TEAM_CT; + + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor for Terrorist Equipment menu +//----------------------------------------------------------------------------- +CCSBuyEquipMenu_TER::CCSBuyEquipMenu_TER(IViewPort *pViewPort) : CBuyMenu( pViewPort ) +{ + SetTitle( "#Cstrike_Buy_Menu", true); + + SetProportional( true ); + + m_pMainMenu = new CCSBuySubMenu( this, "BuySubMenu" ); + m_pMainMenu->LoadControlSettings( "Resource/UI/BuyEquipment_TER.res" ); + m_pMainMenu->SetVisible( false ); + + m_iTeam = TEAM_TERRORIST; + + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CCSBuyEquipMenu_CT::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CCSBuyEquipMenu_CT::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBuyEquipMenu_CT::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CCSBuyEquipMenu_TER::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CCSBuyEquipMenu_TER::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBuyEquipMenu_TER::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); +} + diff --git a/game/client/cstrike/VGUI/cstrikebuyequipmenu.h b/game/client/cstrike/VGUI/cstrikebuyequipmenu.h new file mode 100644 index 0000000..425bd88 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikebuyequipmenu.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSTRIKEBUYEQUIPMENU_H +#define CSTRIKEBUYEQUIPMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/WizardPanel.h> + +#include <buymenu.h> + +namespace vgui +{ + class Panel; +} + +//============================ +// CT Equipment menu +//============================ +class CCSBuyEquipMenu_CT : public CBuyMenu +{ +private: + typedef vgui::WizardPanel BaseClass; + +public: + CCSBuyEquipMenu_CT(IViewPort *pViewPort); + + virtual const char *GetName( void ) { return PANEL_BUY_EQUIP_CT; } + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- +}; + + +//============================ +// Terrorist Equipment menu +//============================ + +class CCSBuyEquipMenu_TER : public CBuyMenu +{ +private: + typedef vgui::WizardPanel BaseClass; + +public: + CCSBuyEquipMenu_TER(IViewPort *pViewPort); + + virtual const char *GetName( void ) { return PANEL_BUY_EQUIP_TER; } + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- +}; + +#endif // CSTRIKEBUYEQUIPMENU_H diff --git a/game/client/cstrike/VGUI/cstrikebuymenu.cpp b/game/client/cstrike/VGUI/cstrikebuymenu.cpp new file mode 100644 index 0000000..e637c89 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikebuymenu.cpp @@ -0,0 +1,883 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstrikebuysubmenu.h" +#include "cstrikebuymenu.h" +#include "cs_shareddefs.h" +#include "backgroundpanel.h" +#include "buy_presets/buy_presets.h" +#include "cstrike/bot/shared_util.h" +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include "buypreset_weaponsetlabel.h" +#include "career_box.h" +#include "cs_gamerules.h" +#include "vgui_controls/RichText.h" +#include "cs_weapon_parse.h" +#include "c_cs_player.h" +#include "cs_ammodef.h" + + +using namespace vgui; + +//----------------------------------------------------------------------------- +/** + * This button resizes any images to fit in the width/height constraints + */ +class BuyPresetButton : public vgui::Button +{ + typedef vgui::Button BaseClass; + +public: + BuyPresetButton(vgui::Panel *parent, const char *buttonName, const char *buttonText ); + virtual ~BuyPresetButton(); + + virtual void PerformLayout( void ); + virtual void ClearImages( void ); + + virtual void SetFgColor( Color c ) + { + BaseClass::SetFgColor( c ); + } + + void SetAvailable( bool available ) + { + m_available = available; + } + + virtual int AddImage( vgui::IImage *image, int offset ) + { + if ( image ) + { + if ( !m_available ) + { + image->SetColor( Color( 128, 128, 128, 255 ) ); + } + } + return BaseClass::AddImage( image, offset ); + } + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + m_availableColor = pScheme->GetColor( "Label.TextColor", Color( 0, 0, 0, 0 ) ); + m_unavailableColor = pScheme->GetColor( "Label.DisabledFgColor2", Color( 0, 0, 0, 0 ) ); + } + + virtual Color GetButtonFgColor( void ) + { + return ( m_available ) ? m_availableColor : m_unavailableColor; + } + +private: + + bool m_available; + Color m_availableColor; + Color m_unavailableColor; +}; + +//----------------------------------------------------------------------------- +BuyPresetButton::BuyPresetButton(vgui::Panel *parent, const char *buttonName, const char *buttonText ) : Button( parent, buttonName, buttonText ) +{ + m_available = false; +} + +//----------------------------------------------------------------------------- +BuyPresetButton::~BuyPresetButton() +{ + ClearImages(); +} + +//----------------------------------------------------------------------------- +void BuyPresetButton::ClearImages( void ) +{ + int imageCount = GetImageCount(); + for ( int i=0; i<imageCount; ++i ) + { + BuyPresetImage *image = dynamic_cast< BuyPresetImage * >(GetImageAtIndex( i )); + if ( image ) + { + delete image; + } + } + + Button::ClearImages(); +} + +//----------------------------------------------------------------------------- +void BuyPresetButton::PerformLayout( void ) +{ + // resize images + int imageCount = GetImageCount(); + if ( imageCount > 1 ) + { + int wide, tall; + GetSize( wide, tall ); + + for ( int i=1; i<imageCount; ++i ) + { + IImage *image = GetImageAtIndex( i ); + if ( image ) + { + int imageWide, imageTall; + image->GetSize( imageWide, imageTall ); + + float scaleX = 1.0f, scaleY = 1.0f; + float widthPercent = 0.2f; + if ( i == 1 ) + { + widthPercent = 0.6f; + } + + if ( imageWide > wide * widthPercent ) + { + scaleX = (float)wide * widthPercent / (float)imageWide; + } + if ( imageTall > tall ) + { + scaleY = (float)tall / (float)imageTall; + } + + float scale = MIN( scaleX, scaleY ); + if ( scale < 1.0f ) + { + imageWide *= scale; + imageTall *= scale; + image->SetSize( imageWide, imageTall ); + } + } + } + } + + Button::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSBuyMenu_CT::CCSBuyMenu_CT(IViewPort *pViewPort) : CCSBaseBuyMenu( pViewPort, "BuySubMenu_CT" ) +{ + m_pMainMenu->LoadControlSettings( "Resource/UI/BuyMenu_CT.res" ); + m_pMainMenu->SetVisible( false ); + + m_iTeam = TEAM_CT; + + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSBuyMenu_TER::CCSBuyMenu_TER(IViewPort *pViewPort) : CCSBaseBuyMenu( pViewPort, "BuySubMenu_TER" ) +{ + m_pMainMenu->LoadControlSettings( "Resource/UI/BuyMenu_TER.res" ); + m_pMainMenu->SetVisible( false ); + + m_iTeam = TEAM_TERRORIST; + + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSBaseBuyMenu::CCSBaseBuyMenu(IViewPort *pViewPort, const char *subPanelName) : CBuyMenu( pViewPort ) +{ + SetTitle( "#Cstrike_Buy_Menu", true); + + SetProportional( true ); + + m_pMainMenu = new CCSBuySubMenu( this, subPanelName ); + m_pMainMenu->SetSize( 10, 10 ); // Quiet "parent not sized yet" spew +#if USE_BUY_PRESETS + for ( int i=0; i<NUM_BUY_PRESET_BUTTONS; ++i ) + { + m_pBuyPresetButtons[i] = new BuyPresetButton( m_pMainMenu, VarArgs( "BuyPresetButton%c", 'A' + i ), "" ); + } + m_pMoney = new Label( m_pMainMenu, "money", "" ); + //============================================================================= + // HPE_BEGIN: + // [pfreese] mainBackground was the little orange box outside that buy window + // that shouldn't have been there. Maybe this was left over from some + // copied code. + //============================================================================= + + m_pMainBackground = NULL; +// m_pMainBackground = new Panel( m_pMainMenu, "mainBackground" ); + + //============================================================================= + // HPE_END + //============================================================================= + m_pLoadout = new BuyPresetEditPanel( m_pMainMenu, "loadoutPanel", "Resource/UI/Loadout.res", 0, false ); +#else + for ( int i=0; i<NUM_BUY_PRESET_BUTTONS; ++i ) + { + m_pBuyPresetButtons[i] = NULL; + } + m_pMoney = NULL; + m_pMainBackground = NULL; +#endif // USE_BUY_PRESETS + m_lastMoney = -1; + + m_pBlackMarket = new EditablePanel( m_pMainMenu, "BlackMarket_Bargains" ); + m_pBlackMarket->LoadControlSettings( "Resource/UI/BlackMarket_Bargains.res" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + if ( state ) + { + Panel *defaultButton = FindChildByName( "CancelButton" ); + if ( defaultButton ) + { + defaultButton->RequestFocus(); + } + SetMouseInputEnabled( true ); + m_pMainMenu->SetMouseInputEnabled( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: shows/hides the buy menu +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::ShowPanel(bool bShow) +{ + CBuyMenu::ShowPanel( bShow ); + +#if USE_BUY_PRESETS + if ( bShow ) + { + UpdateBuyPresets( true ); + } +#endif // USE_BUY_PRESETS +} + +//----------------------------------------------------------------------------- +static void GetPanelBounds( Panel *pPanel, wrect_t& bounds ) +{ + if ( !pPanel ) + { + bounds.bottom = bounds.left = bounds.right = bounds.top = 0; + } + else + { + pPanel->GetBounds( bounds.left, bounds.top, bounds.right, bounds.bottom ); + bounds.right += bounds.left; + bounds.bottom += bounds.top; + } +} + +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::Paint() +{ +#if USE_BUY_PRESETS + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + int account = (pPlayer) ? pPlayer->GetAccount() : 0; + + if ( m_pMoney && m_lastMoney != account ) + { + m_lastMoney = account; + + const int BufLen = 128; + wchar_t wbuf[BufLen] = L""; + const wchar_t *formatStr = g_pVGuiLocalize->Find("#Cstrike_Current_Money"); + if ( !formatStr ) + formatStr = L"%s1"; + g_pVGuiLocalize->ConstructString( wbuf, sizeof(wbuf), formatStr, 1, NumAsWString( m_lastMoney ) ); + m_pMoney->SetText( wbuf ); + } +#endif // USE_BUY_PRESETS + + CBuyMenu::Paint(); +} + +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::UpdateBuyPresets( bool showDefaultPanel ) +{ + bool setPanelVisible = false; + if ( !showDefaultPanel ) + { + setPanelVisible = true; + } + + if ( !TheBuyPresets ) + TheBuyPresets = new BuyPresetManager(); + + int i; + // show buy preset buttons + int numPresets = MIN( TheBuyPresets->GetNumPresets(), NUM_BUY_PRESET_BUTTONS ); + for ( i=0; i<numPresets; ++i ) + { + if ( !m_pBuyPresetButtons[i] ) + continue; + + const BuyPreset *preset = TheBuyPresets->GetPreset(i); + + int setIndex; + int currentCost = -1; + WeaponSet currentSet; + const WeaponSet *fullSet = NULL; + for ( setIndex = 0; setIndex < preset->GetNumSets(); ++setIndex ) + { + const WeaponSet *itemSet = preset->GetSet( setIndex ); + if ( itemSet ) + { + itemSet->GetCurrent( currentCost, currentSet ); + if ( currentCost >= 0 ) + { + fullSet = itemSet; + break; + } + } + } + + if ( !fullSet && preset->GetNumSets() ) + { + fullSet = preset->GetSet( 0 ); + } + + // set the button's images + m_pBuyPresetButtons[i]->ClearImages(); + m_pBuyPresetButtons[i]->SetTextImageIndex( 0 ); + m_pBuyPresetButtons[i]->SetText( "" ); + + m_pBuyPresetButtons[i]->SetAvailable( currentCost >= 0 ); + + const char *imageName = ""; + if ( fullSet ) + { + if ( fullSet->GetPrimaryWeapon().GetWeaponID() != WEAPON_NONE ) + { + imageName = ImageFnameFromWeaponID( fullSet->GetPrimaryWeapon().GetWeaponID(), true ); + BuyPresetImage * image = new BuyPresetImage( scheme()->GetImage(imageName, true) ); + m_pBuyPresetButtons[i]->AddImage( image, 0 ); + } + if ( fullSet->GetSecondaryWeapon().GetWeaponID() != WEAPON_NONE ) + { + imageName = ImageFnameFromWeaponID( fullSet->GetSecondaryWeapon().GetWeaponID(), false ); + BuyPresetImage * image = new BuyPresetImage( scheme()->GetImage(imageName, true) ); + m_pBuyPresetButtons[i]->AddImage( image, 0 ); + } + } + + int displayCost = currentCost; + if ( displayCost < 0 ) + displayCost = 0; + + const int BufLen = 1024; + char aBuf[BufLen]; + Q_snprintf(aBuf, BufLen, "#Cstrike_BuyMenuPreset%d", i + 1); + m_pBuyPresetButtons[i]->SetText( g_pVGuiLocalize->Find(aBuf) ); + Q_snprintf(aBuf, BufLen, "cl_buy_favorite %d", i + 1); + m_pBuyPresetButtons[i]->SetCommand( aBuf ); + m_pBuyPresetButtons[i]->SetVisible( true ); + m_pBuyPresetButtons[i]->SetEnabled( true ); + } + + // hide unused buy preset buttons + for ( i=numPresets+1; i<NUM_BUY_PRESET_BUTTONS; ++i ) + { + if ( m_pBuyPresetButtons[i] ) + { + m_pBuyPresetButtons[i]->SetVisible( false ); + m_pBuyPresetButtons[i]->SetEnabled( true ); + } + } + + HandleBlackMarket(); +} + +const char *g_pWeaponNames[] = +{ + " ", + "#Cstrike_TitlesTXT_P228", + "#Cstrike_TitlesTXT_Glock18", + "#Cstrike_TitlesTXT_Scout", + "#Cstrike_TitlesTXT_HE_Grenade", + "#Cstrike_TitlesTXT_XM1014", + " ", + "#Cstrike_TitlesTXT_Mac10", + "#Cstrike_TitlesTXT_Aug", + "#Cstrike_TitlesTXT_Smoke_Grenade", + "#Cstrike_TitlesTXT_Dual40", + "#Cstrike_TitlesTXT_FiveSeven", + "#Cstrike_TitlesTXT_UMP45", + "#Cstrike_TitlesTXT_SG550", + "#Cstrike_TitlesTXT_Galil", + "#Cstrike_TitlesTXT_Famas", + "#Cstrike_TitlesTXT_USP45", + "#Cstrike_TitlesTXT_Magnum", + "#Cstrike_TitlesTXT_mp5navy", + "#Cstrike_TitlesTXT_ESM249", + "#Cstrike_TitlesTXT_Leone12", + "#Cstrike_TitlesTXT_M4A1", + "#Cstrike_TitlesTXT_tmp", + "#Cstrike_TitlesTXT_G3SG1", + "#Cstrike_TitlesTXT_Flashbang", + "#Cstrike_TitlesTXT_DesertEagle", + "#Cstrike_TitlesTXT_SG552", + "#Cstrike_TitlesTXT_AK47", + " ", + "#Cstrike_TitlesTXT_FNP90", + " ", + "#Cstrike_TitlesTXT_Kevlar_Vest", + "#Cstrike_TitlesTXT_Kevlar_Vest_Ballistic_Helmet", + "#Cstrike_TitlesTXT_Nightvision_Goggles" +}; + +int GetWeeklyBargain( void ) +{ + if ( CSGameRules() == NULL || CSGameRules()->m_pPrices == NULL ) + return 0; + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( pPlayer == NULL ) + return 0; + + int iBestIndex = 0; + int iBestBargain = 99999; + + for ( int i = 1; i < WEAPON_MAX; i++ ) + { + if ( i == WEAPON_SHIELDGUN ) + continue; + + CCSWeaponInfo *info = GetWeaponInfo( (CSWeaponID)i ); + + if ( info == NULL ) + continue; + + if ( info->m_iTeam == TEAM_UNASSIGNED || info->m_iTeam == pPlayer->m_iTeamNum ) + { + int iBargain = info->GetWeaponPrice() - info->GetPrevousPrice(); + + if ( iBargain < iBestBargain ) + { + iBestIndex = i; + iBestBargain = iBargain; + } + } + } + + return iBestIndex; +} + +#ifdef _DEBUG +ConVar cs_testbargain( "cs_testbargain", "1" ); +#endif + +void CCSBaseBuyMenu::HandleBlackMarket( void ) +{ + if ( CSGameRules() == NULL ) + return; + + if ( m_pLoadout ) + { + if ( CSGameRules()->IsBlackMarket() ) + { + if ( CSGameRules()->m_pPrices == NULL ) + return; + + if ( m_pBlackMarket == NULL ) + return; + + int iBargain = GetWeeklyBargain(); + CCSWeaponInfo *info = GetWeaponInfo( (CSWeaponID)iBargain ); + + wchar_t *wszWeaponName = g_pVGuiLocalize->Find( g_pWeaponNames[iBargain]); + + if ( wszWeaponName == NULL ) + return; + + if ( info == NULL ) + return; + + m_pLoadout->SetVisible( false ); + Label *pLabel = dynamic_cast< Label * >(m_pMainMenu->FindChildByName( "loadoutLabel" )); + + if ( pLabel ) + { + pLabel->SetVisible( false ); + } + + pLabel = dynamic_cast< Label * >(m_pBlackMarket->FindChildByName( "MarketHeadline" )); + + if ( pLabel ) + { + const int BufLen = 2048; + + wchar_t wbuf[BufLen] = L""; + const wchar_t *formatStr = g_pVGuiLocalize->Find("#Cstrike_MarketHeadline"); + + if ( !formatStr ) + formatStr = L"%s1"; + + g_pVGuiLocalize->ConstructString( wbuf, sizeof(wbuf), formatStr, 1, wszWeaponName ); + pLabel->SetText( wbuf ); + } + + pLabel = dynamic_cast< Label * >(m_pBlackMarket->FindChildByName( "MarketBargain" )); + + if ( pLabel ) + { + const int BufLen = 2048; + wchar_t wbuf[BufLen] = L""; + const wchar_t *formatStr = g_pVGuiLocalize->Find("#Cstrike_MarketBargain"); + + if ( !formatStr ) + formatStr = L"%s1"; + + g_pVGuiLocalize->ConstructString( wbuf, sizeof(wbuf), formatStr, 1, wszWeaponName ); + pLabel->SetText( wbuf ); + } + + pLabel = dynamic_cast< Label * >(m_pBlackMarket->FindChildByName( "MarketStickerPrice" )); + + if ( pLabel ) + { + char wbuf[16]; + + Q_snprintf( wbuf, 16, "%d", CSGameRules()->m_pPrices->iCurrentPrice[iBargain] ); + + pLabel->SetText( wbuf ); + } + + RichText *pText = dynamic_cast< RichText * >(m_pBlackMarket->FindChildByName( "MarketDescription" )); + + if ( pText ) + { + char wbuf[2048]; + g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find("#Cstrike_MarketDescription"), wbuf, 2048 ); + + pText->SetText( "" ); + pText->InsertPossibleURLString( wbuf, Color( 255, 255, 255, 255 ), Color( 255, 176, 0, 255 ) ); + pText->SetVerticalScrollbar( false ); + pText->SetPaintBorderEnabled( false ); + pText->SetUnderlineFont( m_hUnderlineFont ); + } + + pLabel = dynamic_cast< Label * >(m_pBlackMarket->FindChildByName( "MarketBargainIcon" )); + + if ( pLabel ) + { + char wbuff[12]; + Q_snprintf( wbuff, 12, "%c", info->iconActive->cCharacterInFont ); + + pLabel->SetText( wbuff ); + } + + Button *pButton = dynamic_cast< Button * >(m_pMainMenu->FindChildByName( "BargainbuyButton" )); + + if ( pButton ) + { + char command[512]; + char *pWeaponName = Q_stristr( info->szClassName, "_" ); + + if ( pWeaponName ) + { + pWeaponName++; + + Q_snprintf( command, 512, "buy %s", pWeaponName ); + } + + pButton->SetCommand( command ); + pButton->SetVisible( true ); + } + + + m_pBlackMarket->SetVisible( true ); + m_pBlackMarket->SetZPos( -2 ); + } + else + { + WeaponSet ws; + + TheBuyPresets->GetCurrentLoadout( &ws ); + m_pLoadout->SetWeaponSet( &ws, true ); + + m_pLoadout->SetVisible( true ); + Panel *pLabel = dynamic_cast< Label * >(m_pMainMenu->FindChildByName( "loadoutLabel" )); + + if ( pLabel ) + { + pLabel->SetVisible( true ); + } + + if ( m_pBlackMarket ) + { + m_pBlackMarket->SetVisible( false ); + + Button *pButton = dynamic_cast< Button * >(m_pMainMenu->FindChildByName( "BargainbuyButton" )); + + if ( pButton ) + { + pButton->SetVisible( false ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBaseBuyMenu::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); + + if ( m_pMainBackground ) + { + m_pMainBackground->SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + m_pMainBackground->SetBgColor( GetSchemeColor( "Button.BgColor", GetBgColor(), pScheme ) ); + } + + m_hUnderlineFont = pScheme->GetFont( "CSUnderline", IsProportional() ); + +#if USE_BUY_PRESETS + UpdateBuyPresets( true ); +#endif // USE_BUY_PRESETS +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static bool IsWeaponInvalid( CSWeaponID weaponID ) +{ + if ( weaponID == WEAPON_NONE ) + return false; + + return !CanBuyWeapon( WEAPON_NONE, WEAPON_NONE, weaponID ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBuySubMenu::OnThink() +{ + UpdateVestHelmPrice(); + BaseClass::OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: When buying vest+helmet, if you already have a vest with no damage +// then the price is reduced to just the helmet. Because this can change during +// the game, we need to update the enable/disable state of the menu item dynamically. +//----------------------------------------------------------------------------- +void CCSBuySubMenu::UpdateVestHelmPrice() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer == NULL ) + return; + + BuyMouseOverPanelButton *pButton = dynamic_cast< BuyMouseOverPanelButton * > ( FindChildByName( "kevlar_helmet", false ) ); + if ( pButton ) + { + // Set its price to the current value from the player. + pButton->SetCurrentPrice( pPlayer->GetCurrentAssaultSuitPrice() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBuySubMenu::OnCommand( const char *command ) +{ +#if USE_BUY_PRESETS + const char *buyPresetSetString = "cl_buy_favorite_query_set "; + if ( !strnicmp( command, buyPresetSetString, strlen( buyPresetSetString ) ) ) + { + bool invalid = IsWeaponInvalid( GetClientWeaponID( true ) ) || IsWeaponInvalid( GetClientWeaponID( false ) ); + if ( invalid ) + { + // can't save the favorite because it has an invalid weapon (colt for a T, etc) + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pPlayer->EmitSound( "BuyPreset.CantBuy" ); + } + + if ( cl_buy_favorite_nowarn.GetBool() ) + { + BaseClass::OnCommand( "vguicancel" ); + } + else + { + CCareerQueryBox *pBox = new CCareerQueryBox( this, "SetLoadoutError", "Resource/UI/SetLoadoutError.res" ); + pBox->AddActionSignalTarget( this ); + pBox->DoModal(); + } + } + else + { + // can save + if ( cl_buy_favorite_quiet.GetBool() ) + { + BaseClass::OnCommand( VarArgs( "cl_buy_favorite_set %d", atoi( command + strlen( buyPresetSetString ) ) ) ); + } + else + { + CCareerQueryBox *pBox = new CCareerQueryBox( this, "SetLoadoutQuery", "Resource/UI/SetLoadoutQuery.res" ); + pBox->SetCancelButtonAsDefault(); + if ( pBox->GetOkButton() ) + { + pBox->GetOkButton()->SetCommand( VarArgs( "cl_buy_favorite_set %d", atoi( command + strlen( buyPresetSetString ) ) ) ); + } + pBox->AddActionSignalTarget( this ); + pBox->DoModal(); + } + } + return; + } +#endif // USE_BUY_PRESETS + + if ( FStrEq( command, "buy_unavailable" ) ) + { + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pPlayer->EmitSound( "BuyPreset.CantBuy" ); + } + BaseClass::OnCommand( "vguicancel" ); + return; + } + + BaseClass::OnCommand( command ); +} + +void CCSBuySubMenu::OnSizeChanged(int newWide, int newTall) +{ + m_backgroundLayoutFinished = false; + BaseClass::OnSizeChanged( newWide, newTall ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSBuySubMenu::PerformLayout() +{ + BaseClass::PerformLayout(); + + // Buy submenus need to be shoved over for widescreen + int screenW, screenH; + GetHudSize( screenW, screenH ); + + int fullW, fullH; + fullW = scheme()->GetProportionalScaledValueEx( GetScheme(), 640 ); + fullH = scheme()->GetProportionalScaledValueEx( GetScheme(), 480 ); + + fullW = GetAlternateProportionalValueFromScaled( GetScheme(), fullW ); + fullH = GetAlternateProportionalValueFromScaled( GetScheme(), fullH ); + + int offsetX = (screenW - fullW)/2; + int offsetY = (screenH - fullH)/2; + + if ( !m_backgroundLayoutFinished ) + ResizeWindowControls( this, GetWide(), GetTall(), offsetX, offsetY ); + m_backgroundLayoutFinished = true; + + HandleBlackMarket(); +} + +void CCSBuySubMenu::HandleBlackMarket( void ) +{ + if ( CSGameRules() == NULL ) + return; + + int iBestBargain = 99999; + BuyMouseOverPanelButton *pButtonBargain = NULL; + + for (int i = 0; i < GetChildCount(); i++) + { + BuyMouseOverPanelButton *pButton = dynamic_cast< BuyMouseOverPanelButton * > ( GetChild(i) ); + if (!pButton) + continue; + + pButton->SetBargainButton( false ); + + const char *pWeaponName = Q_stristr( pButton->GetBuyCommand(), " " ); + + if ( pWeaponName ) + { + pWeaponName++; + + int iWeaponID = AliasToWeaponID(GetTranslatedWeaponAlias(pWeaponName)); + + if ( iWeaponID == 0 ) + continue; + + CCSWeaponInfo *info = GetWeaponInfo( (CSWeaponID)iWeaponID ); + + if ( info == NULL ) + continue; + + if ( CSGameRules()->IsBlackMarket() == false ) + { + //============================================================================= + // HPE_BEGIN: + // [dwenger] Removed to avoid clearing of default price when not in black market mode + //============================================================================= + + // pButton->SetCurrentPrice( info->GetDefaultPrice() ); + + //============================================================================= + // HPE_END + //============================================================================= + } + else + { + int iBargain = info->GetWeaponPrice() - info->GetPrevousPrice(); + + pButton->SetCurrentPrice( info->GetWeaponPrice() ); + pButton->SetPreviousPrice( info->GetPrevousPrice() ); + + if ( iBargain < iBestBargain ) + { + iBestBargain = iBargain; + pButtonBargain = pButton; + } + } + } + } + + if ( pButtonBargain ) + { + pButtonBargain->SetBargainButton( true ); + } +} + + + diff --git a/game/client/cstrike/VGUI/cstrikebuymenu.h b/game/client/cstrike/VGUI/cstrikebuymenu.h new file mode 100644 index 0000000..a97e07b --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikebuymenu.h @@ -0,0 +1,102 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSTRIKEBUYMENU_H +#define CSTRIKEBUYMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/WizardPanel.h> + +#include <buymenu.h> + +class BuyPresetEditPanel; +class BuyPresetButton; + +namespace vgui +{ + class Panel; + class Button; + class Label; +} + +enum +{ + NUM_BUY_PRESET_BUTTONS = 4, +}; + +//============================ +// Base CS buy Menu +//============================ +class CCSBaseBuyMenu : public CBuyMenu +{ +private: + typedef vgui::WizardPanel BaseClass; + +public: + CCSBaseBuyMenu(IViewPort *pViewPort, const char *subPanelName); + + virtual void ShowPanel( bool bShow ); + virtual void Paint( void ); + virtual void SetVisible( bool state ); + + void HandleBlackMarket( void ); + +private: + void UpdateBuyPresets( bool showDefaultPanel = false ); ///< Update the Buy Preset buttons and their info panels on the main buy menu + vgui::Panel *m_pMainBackground; + BuyPresetButton *m_pBuyPresetButtons[NUM_BUY_PRESET_BUTTONS]; + BuyPresetEditPanel *m_pLoadout; + vgui::Label *m_pMoney; + int m_lastMoney; + + vgui::EditablePanel *m_pBlackMarket; + HFont m_hUnderlineFont; + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- +}; + +//============================ +// CT main buy Menu +//============================ +class CCSBuyMenu_CT : public CCSBaseBuyMenu +{ +private: + typedef vgui::WizardPanel BaseClass; + +public: + CCSBuyMenu_CT(IViewPort *pViewPort); + + virtual const char *GetName( void ) { return PANEL_BUY_CT; } +}; + + +//============================ +// Terrorist main buy Menu +//============================ +class CCSBuyMenu_TER : public CCSBaseBuyMenu +{ +private: + typedef vgui::WizardPanel BaseClass; + +public: + CCSBuyMenu_TER(IViewPort *pViewPort); + + virtual const char *GetName( void ) { return PANEL_BUY_TER; } +}; + +#endif // CSTRIKEBUYMENU_H diff --git a/game/client/cstrike/VGUI/cstrikebuysubmenu.h b/game/client/cstrike/VGUI/cstrikebuysubmenu.h new file mode 100644 index 0000000..ccd0edd --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikebuysubmenu.h @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSBUYSUBMENU_H +#define CSBUYSUBMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <buysubmenu.h> +#include <buymouseoverpanelbutton.h> + +using namespace vgui; + +class CCSBuySubMenu : public CBuySubMenu +{ +private: + DECLARE_CLASS_SIMPLE(CCSBuySubMenu, CBuySubMenu); + +public: + CCSBuySubMenu (vgui::Panel *parent,const char *name = "BuySubMenu") : CBuySubMenu( parent, name ) + { + m_backgroundLayoutFinished = false; + }; + +protected: + + virtual void OnThink(); + void UpdateVestHelmPrice(); + + virtual void OnCommand( const char *command ); + + MouseOverPanelButton* CreateNewMouseOverPanelButton(EditablePanel * panel) + { + return new BuyMouseOverPanelButton(this, NULL, panel); + } + + CBuySubMenu* CreateNewSubMenu() { return new CCSBuySubMenu( this, "BuySubMenu" ); } + + // Background panel ------------------------------------------------------- + + virtual void PerformLayout(); + virtual void OnSizeChanged(int newWide, int newTall); // called after the size of a panel has been changed + + void HandleBlackMarket( void ); + + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- +}; + +#endif //CSBUYSUBMENU_H diff --git a/game/client/cstrike/VGUI/cstrikeclassmenu.cpp b/game/client/cstrike/VGUI/cstrikeclassmenu.cpp new file mode 100644 index 0000000..374f7ce --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikeclassmenu.cpp @@ -0,0 +1,272 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstrikeclassmenu.h" + +#include <KeyValues.h> +#include <filesystem.h> +#include <vgui_controls/Button.h> +#include <vgui/IVGui.h> + +#include "hud.h" // for gEngfuncs +#include "cs_gamerules.h" + +using namespace vgui; + + +// ----------------------------------------------------------------------------- // +// Class image panels. These maintain a list of the class image panels so +// it can render 3D images into them. +// ----------------------------------------------------------------------------- // + +CUtlVector<CCSClassImagePanel*> g_ClassImagePanels; + + +CCSClassImagePanel::CCSClassImagePanel( vgui::Panel *pParent, const char *pName ) + : vgui::ImagePanel( pParent, pName ) +{ + g_ClassImagePanels.AddToTail( this ); + m_ModelName[0] = 0; +} + +CCSClassImagePanel::~CCSClassImagePanel() +{ + g_ClassImagePanels.FindAndRemove( this ); +} + +void CCSClassImagePanel::ApplySettings( KeyValues *inResourceData ) +{ + const char *pName = inResourceData->GetString( "3DModel" ); + if ( pName ) + { + Q_strncpy( m_ModelName, pName, sizeof( m_ModelName ) ); + } + + BaseClass::ApplySettings( inResourceData ); +} + + +void CCSClassImagePanel::Paint() +{ + BaseClass::Paint(); +} + + +// ----------------------------------------------------------------------------- // +// CClassMenu_TER +// ----------------------------------------------------------------------------- // + +CClassMenu_TER::CClassMenu_TER(IViewPort *pViewPort) : CClassMenu(pViewPort, PANEL_CLASS_TER) +{ + LoadControlSettings( "Resource/UI/ClassMenu_TER.res" ); + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +const char *CClassMenu_TER::GetName( void ) +{ + return PANEL_CLASS_TER; +} + +void CClassMenu_TER::ShowPanel(bool bShow) +{ + if ( bShow) + { + engine->CheckPoint( "ClassMenu" ); + } + + BaseClass::ShowPanel( bShow ); + +} + +void CClassMenu_TER::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + if ( state ) + { + Panel *pAutoButton = FindChildByName( "autoselect_t" ); + if ( pAutoButton ) + { + pAutoButton->RequestFocus(); + } + } +} + +bool modelExists( const char *search, const CUtlVector< const char * > &names ) +{ + for ( int i=0; i<names.Count(); ++i ) + { + if ( Q_stristr( names[i], search ) != NULL ) + { + return true; + } + } + + return false; +} + +void CClassMenu_TER::Update() +{ + C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( pLocalPlayer && pLocalPlayer->PlayerClass() >= FIRST_T_CLASS && pLocalPlayer->PlayerClass() <= LAST_T_CLASS ) + { + SetVisibleButton( "CancelButton", true ); + } + else + { + SetVisibleButton( "CancelButton", false ); + } + + // if we don't have the new models installed, + // turn off the militia and spetsnaz buttons + SetVisibleButton( "militia", false ); +} + + +Panel *CClassMenu_TER::CreateControlByName(const char *controlName) +{ + if ( Q_stricmp( controlName, "CSClassImagePanel" ) == 0 ) + { + return new CCSClassImagePanel( NULL, controlName ); + } + + return BaseClass::CreateControlByName( controlName ); +} + + + +// ----------------------------------------------------------------------------- // +// CClassMenu_CT +// ----------------------------------------------------------------------------- // + +CClassMenu_CT::CClassMenu_CT(IViewPort *pViewPort) : CClassMenu(pViewPort, PANEL_CLASS_CT) +{ + LoadControlSettings( "Resource/UI/ClassMenu_CT.res" ); + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +Panel *CClassMenu_CT::CreateControlByName(const char *controlName) +{ + if ( Q_stricmp( controlName, "CSClassImagePanel" ) == 0 ) + { + return new CCSClassImagePanel( NULL, controlName ); + } + + return BaseClass::CreateControlByName( controlName ); +} + +const char *CClassMenu_CT::GetName( void ) +{ + return PANEL_CLASS_CT; +} + +void CClassMenu_CT::ShowPanel(bool bShow) +{ + if ( bShow) + { + engine->CheckPoint( "ClassMenu" ); + } + + BaseClass::ShowPanel( bShow ); + +} + +void CClassMenu_CT::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + if ( state ) + { + Panel *pAutoButton = FindChildByName( "autoselect_ct" ); + if ( pAutoButton ) + { + pAutoButton->RequestFocus(); + } + } +} + +void CClassMenu_CT::Update() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( pPlayer && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS ) + { + SetVisibleButton( "CancelButton", true ); + } + else + { + SetVisibleButton( "CancelButton", false ); + } + + // if we don't have the new models installed, + // turn off the militia and spetsnaz buttons + SetVisibleButton( "spetsnaz", false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CClassMenu_TER::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CClassMenu_TER::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClassMenu_TER::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CClassMenu_CT::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CClassMenu_CT::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClassMenu_CT::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); +} + diff --git a/game/client/cstrike/VGUI/cstrikeclassmenu.h b/game/client/cstrike/VGUI/cstrikeclassmenu.h new file mode 100644 index 0000000..96a991f --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikeclassmenu.h @@ -0,0 +1,107 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSCLASSMENU_H +#define CSCLASSMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <classmenu.h> +#include <vgui_controls/EditablePanel.h> +#include <filesystem.h> +#include <cs_shareddefs.h> +#include "cbase.h" +#include "cs_gamerules.h" +#include "vgui_controls/ImagePanel.h" +#include "backgroundpanel.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// These are maintained in a list so the renderer can draw a 3D character +// model on top of them. +//----------------------------------------------------------------------------- + +class CCSClassImagePanel : public vgui::ImagePanel +{ +public: + + typedef vgui::ImagePanel BaseClass; + + CCSClassImagePanel( vgui::Panel *pParent, const char *pName ); + virtual ~CCSClassImagePanel(); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void Paint(); + + +public: + char m_ModelName[128]; +}; + +extern CUtlVector<CCSClassImagePanel*> g_ClassImagePanels; + + +//----------------------------------------------------------------------------- +// Purpose: Draws the Terrorist class menu +//----------------------------------------------------------------------------- + +class CClassMenu_TER : public CClassMenu +{ +private: + DECLARE_CLASS_SIMPLE( CClassMenu_TER, CClassMenu ); + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- + +public: + CClassMenu_TER(IViewPort *pViewPort); + virtual Panel* CreateControlByName(const char *controlName); + const char *GetName( void ); + void ShowPanel(bool bShow); + void Update(); + virtual void SetVisible(bool state); +}; + + +//----------------------------------------------------------------------------- +// Purpose: Draws the Counter-Terrorist class menu +//----------------------------------------------------------------------------- + +class CClassMenu_CT : public CClassMenu +{ +private: + DECLARE_CLASS_SIMPLE( CClassMenu_CT, CClassMenu ); + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- + +public: + CClassMenu_CT(IViewPort *pViewPort); + virtual Panel *CreateControlByName(const char *controlName); + const char *GetName( void ); + void ShowPanel(bool bShow); + void Update(); + virtual void SetVisible(bool state); +}; + +#endif // CSCLASSMENU_H diff --git a/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp b/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp new file mode 100644 index 0000000..eb2a652 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp @@ -0,0 +1,1596 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "hud.h" +#include "cstrikeclientscoreboard.h" +#include "c_team.h" +#include "c_cs_playerresource.h" +#include "c_cs_player.h" +#include "cs_gamerules.h" +#include "backgroundpanel.h" +#include "clientmode.h" + +#include <KeyValues.h> + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui_controls/SectionedListPanel.h> +#include <vgui_controls/ScalableImagePanel.h> +#include "VGuiMatSurface/IMatSystemSurface.h" + +#include "voice_status.h" +#include "vgui_avatarimage.h" + +#include "achievementmgr.h" +#include "engine/imatchmaking.h" + +using namespace vgui; + +const int kInvalidImageID = -1; +const int kMaxMVPCount = 9999; +const float kScaleMVP = 0.75f; // scale of the MVP star relative to the player name height +const float kUpdateInterval = 1.0f; // how often the scoreboard refreshes +const float kTeamScoreMargin = 0.15f; // margin as a ratio of avatar height +const float kTeamScoreLineLeadingRatio = 0.25f; // padding as a ratio of avatar height + +// CT player data colors +ConVar cl_scoreboard_ct_color_red( "cl_scoreboard_ct_color_red", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player data red channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_ct_color_green( "cl_scoreboard_ct_color_green", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player data green channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_ct_color_blue( "cl_scoreboard_ct_color_blue", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player data blue channel", true, 0.0f, true, 255.0f ); + +// T player data colors +ConVar cl_scoreboard_t_color_red( "cl_scoreboard_t_color_red", "240", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player data red channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_t_color_green( "cl_scoreboard_t_color_green", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player data green channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_t_color_blue( "cl_scoreboard_t_color_blue", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player data blue channel", true, 0.0f, true, 255.0f ); + +// Dead player data colors +ConVar cl_scoreboard_dead_color_red( "cl_scoreboard_dead_color_red", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player data red channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_dead_color_green( "cl_scoreboard_dead_color_green", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player data green channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_dead_color_blue( "cl_scoreboard_dead_color_blue", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player data blue channel", true, 0.0f, true, 255.0f ); + +// Clan colors +ConVar cl_scoreboard_clan_ct_color_red( "cl_scoreboard_clan_ct_color_red", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player clan tag red channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_clan_ct_color_green( "cl_scoreboard_clan_ct_color_green", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player clan tag green channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_clan_ct_color_blue( "cl_scoreboard_clan_ct_color_blue", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player clan tag blue channel", true, 0.0f, true, 255.0f ); + +ConVar cl_scoreboard_clan_t_color_red( "cl_scoreboard_clan_t_color_red", "240", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player clan tag red channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_clan_t_color_green( "cl_scoreboard_clan_t_color_green", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player clan tag green channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_clan_t_color_blue( "cl_scoreboard_clan_t_color_blue", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player clan tag blue channel", true, 0.0f, true, 255.0f ); + +ConVar cl_scoreboard_dead_clan_color_red( "cl_scoreboard_dead_clan_color_red", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player clan tag red channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_dead_clan_color_green( "cl_scoreboard_dead_clan_color_green", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player clan tag green channel", true, 0.0f, true, 255.0f ); +ConVar cl_scoreboard_dead_clan_color_blue( "cl_scoreboard_dead_clan_color_blue", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player clan tag blue channel", true, 0.0f, true, 255.0f ); + + +// [tj] These ConVars are defined at various places in the global scope. Just declaring them here so we can use them +extern ConVar mp_winlimit; +extern ConVar mp_maxrounds; +extern ConVar mp_timelimit; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSClientScoreBoardDialog::CCSClientScoreBoardDialog( IViewPort *pViewPort ) : CClientScoreBoardDialog( pViewPort ), + m_DeadPlayerDataColor( 125, 125, 125, 125 ), + m_PlayerDataBgColor( 0, 0, 0, 0 ), + m_DeadPlayerClanColor( 125, 125, 125, 125 ) +{ + m_teamDisplayT.playerDataColor = Color( 240, 90, 90, 255 ); + m_teamDisplayT.playerClanColor = Color( 240, 90, 90, 255 ); + + m_teamDisplayCT.playerDataColor = Color( 150, 200, 255, 255 ); + m_teamDisplayCT.playerClanColor = Color( 150, 200, 255, 255 ); + + m_iImageDead = kInvalidImageID; + m_iImageDominated = kInvalidImageID; + m_iImageNemesis = kInvalidImageID; + m_iImageBomb = kInvalidImageID; + m_iImageVIP = kInvalidImageID; + m_iImageFriend = kInvalidImageID; + m_iImageNemesisDead = kInvalidImageID; + m_iImageDominationDead = kInvalidImageID; + + m_pWinConditionLabel = new Label( this, "WinConditionLabel", "" ); + m_pClockLabel = new Label( this, "Icon_Clock", "" ); + + m_pLabelMapName = new Label( this, "MapName", "" ); + m_pServerLabel = new Label( this, "ServerNameLabel", "" ); + + ListenForGameEvent( "server_spawn" ); + ListenForGameEvent( "game_newmap" ); + ListenForGameEvent( "match_end_conditions" ); + ListenForGameEvent( "cs_win_panel_match" ); + + SetDialogVariable( "server", "" ); + SetVisible( false ); + SetProportional(true); + SetPaintBorderEnabled(false); + SetScheme( "ClientScheme" ); + + // [pfreese] Make the scoreboard a popup so it renders over the chat interface (which is also a popup). Hacky. + MakePopup(); + + m_listItemFont = NULL; + + m_LocalPlayerItemID = -1; + m_iImageMVP = -1; + + m_gameOver = false; + + if ( g_pClientMode && + g_pClientMode->GetMapName() ) + { + V_wcsncpy( m_pMapName, g_pClientMode->GetMapName(), sizeof( m_pMapName ) ); + SetDialogVariable( "mapname", m_pMapName ); + m_pLabelMapName->SetVisible( true ); + } + + SetKeyBoardInputEnabled( true ); + + for ( int i = 0; i < cMaxScoreLines; ++i ) + { + PlayerDisplay* pPlayerDisplay; + + pPlayerDisplay = &m_teamDisplayCT.playerDisplay[i]; + pPlayerDisplay->pClanLabel = NULL; + pPlayerDisplay->pNameLabel = NULL; + pPlayerDisplay->pScoreLabel = NULL; + pPlayerDisplay->pDeathsLabel = NULL; + pPlayerDisplay->pPingLabel = NULL; + pPlayerDisplay->pMVPCountLabel = NULL; + pPlayerDisplay->pAvatar = NULL; + pPlayerDisplay->pStatusImage = NULL; + pPlayerDisplay->pMVPImage = NULL; + pPlayerDisplay->pSelect = NULL; + + pPlayerDisplay = &m_teamDisplayT.playerDisplay[i]; + pPlayerDisplay->pClanLabel = NULL; + pPlayerDisplay->pNameLabel = NULL; + pPlayerDisplay->pScoreLabel = NULL; + pPlayerDisplay->pDeathsLabel = NULL; + pPlayerDisplay->pPingLabel = NULL; + pPlayerDisplay->pMVPCountLabel = NULL; + pPlayerDisplay->pAvatar = NULL; + pPlayerDisplay->pStatusImage = NULL; + pPlayerDisplay->pMVPImage = NULL; + pPlayerDisplay->pSelect = NULL; + } + + m_pServerName[0] = L'\0'; + m_pStatsEnabled[0] = L'\0'; + m_pStatsDisabled[0] = L'\0'; + + m_MVPXOffset = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCSClientScoreBoardDialog::~CCSClientScoreBoardDialog() +{ + for (int i = 0; i < cMaxScoreLines; ++i) + { + PlayerDisplay* pPlayerDisplay; + + pPlayerDisplay = &m_teamDisplayCT.playerDisplay[i]; + delete pPlayerDisplay->pClanLabel; + delete pPlayerDisplay->pNameLabel; + delete pPlayerDisplay->pScoreLabel; + delete pPlayerDisplay->pDeathsLabel; + delete pPlayerDisplay->pPingLabel; + delete pPlayerDisplay->pMVPCountLabel; + delete pPlayerDisplay->pAvatar; + delete pPlayerDisplay->pStatusImage; + delete pPlayerDisplay->pMVPImage; + delete pPlayerDisplay->pSelect; + + pPlayerDisplay = &m_teamDisplayT.playerDisplay[i]; + delete pPlayerDisplay->pClanLabel; + delete pPlayerDisplay->pNameLabel; + delete pPlayerDisplay->pScoreLabel; + delete pPlayerDisplay->pDeathsLabel; + delete pPlayerDisplay->pPingLabel; + delete pPlayerDisplay->pMVPCountLabel; + delete pPlayerDisplay->pAvatar; + delete pPlayerDisplay->pStatusImage; + delete pPlayerDisplay->pMVPImage; + delete pPlayerDisplay->pSelect; + } +} + +const wchar_t *LocalizeFindSafe( const char *pTokenName ) +{ + const wchar_t *pStr = g_pVGuiLocalize->Find( pTokenName ); + return pStr ? pStr : L"\0"; +} + +//----------------------------------------------------------------------------- +// Purpose: Apply scheme settings +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + // + // [smessick] Note: ApplySchemeSettings is called multiple times for the scoreboard. + // Therefore, we must make sure to delete previously allocated items. + // + + LoadControlSettings( "Resource/UI/scoreboard.res" ); + + //Just used for a background alpha value. 50% opacity + m_listItemFont = pScheme->GetFont( "ScoreboardBody_1", IsProportional() ); + m_listItemFontSmaller = pScheme->GetFont( "ScoreboardBody_2", IsProportional() ); + m_listItemFontSmallest = pScheme->GetFont( "ScoreboardBody_3", IsProportional() ); + m_MVPFont = pScheme->GetFont( "ScoreboardMVP", IsProportional() ); + + SetBgColor( Color( 0, 0, 0, 0 ) ); + SetBorder( pScheme->GetBorder( "BaseBorder" ) ); + + // turn off the default player list since we have our own + if ( m_pPlayerList ) + { + m_pPlayerList->SetVisible( false ); + } + + // Set the player colors from the convars. + UpdatePlayerColors(); + + m_MVPXOffset = scheme()->GetProportionalScaledValueEx( GetScheme(), 2 ); + + SetupTeamDisplay( m_teamDisplayCT, "CT" ); + SetupTeamDisplay( m_teamDisplayT, "T" ); + + // Set the server name (in the case of a resolution change). + if ( m_pServerName[0] == L'\0' && + g_pClientMode->GetServerName() != NULL ) + { + V_wcsncpy( m_pServerName, g_pClientMode->GetServerName(), sizeof( m_pServerName ) ); + } + + // Cache the stats enabled string. + if ( m_pStatsEnabled[0] == L'\0' ) + { + V_wcsncpy( m_pStatsEnabled, LocalizeFindSafe( "#Cstrike_Scoreboard_StatsEnabled" ), sizeof( m_pStatsEnabled ) ); + } + + // Cache the stats disabled string. + if ( m_pStatsDisabled[0] == L'\0' ) + { + V_wcsncpy( m_pStatsDisabled, LocalizeFindSafe( "#Cstrike_Scoreboard_StatsDisabled" ), sizeof( m_pStatsDisabled ) ); + } + + SetVisible( false ); +} + + +void CCSClientScoreBoardDialog::SetupTeamDisplay( TeamDisplayInfo& teamDisplay, const char* szTeamPrefix ) +{ + const int selectMargin = scheme()->GetProportionalScaledValueEx( GetScheme(), 1 ); + const int mvpLabelWidth = scheme()->GetProportionalScaledValueEx( GetScheme(), 20 ); + const int mvpLabelYOffset = scheme()->GetProportionalScaledValueEx( GetScheme(), 2 ); + + char tmpName[32]; + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerArea", szTeamPrefix ); + Panel *pPlayerArea = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerAvatar0", szTeamPrefix ); + Panel *pPlayerAvatar0 = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerClan0", szTeamPrefix ); + Panel *pPlayerClan0 = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerName0", szTeamPrefix ); + Panel *pPlayerName0 = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerStatus0", szTeamPrefix ); + Panel *pPlayerStatus0 = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerScore0", szTeamPrefix ); + Panel *pPlayerScore0 = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerDeaths0", szTeamPrefix ); + Panel *pPlayerDeaths0 = FindChildByName( tmpName ); + + V_snprintf( tmpName, sizeof(tmpName), "%sPlayerLatency0", szTeamPrefix ); + Panel *pPlayerLatency0 = FindChildByName( tmpName ); + + // Get the bounds of the player area. + int playerX = 0; + int playerY = 0; + int playerWide = 0; + int playerTall = 0; + if ( pPlayerArea ) + pPlayerArea->GetBounds( playerX, playerY, playerWide, playerTall ); + + // determine the line height needed + int x, y, wide, tall; + teamDisplay.scoreAreaLineHeight = 0; + teamDisplay.scoreAreaMinX = INT_MAX; + teamDisplay.scoreAreaMaxX = INT_MIN; + + if ( pPlayerAvatar0 != NULL ) + { + pPlayerAvatar0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + if ( pPlayerClan0 != NULL ) + { + pPlayerClan0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + if ( pPlayerName0 != NULL ) + { + pPlayerName0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + if ( pPlayerScore0 != NULL ) + { + pPlayerScore0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + if ( pPlayerDeaths0 != NULL ) + { + pPlayerDeaths0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + if ( pPlayerLatency0 != NULL ) + { + pPlayerLatency0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + if ( pPlayerStatus0 != NULL ) + { + pPlayerStatus0->GetBounds( x, y, wide, tall ); + teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight ); + teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x ); + teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide ); + } + + int marginY = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kTeamScoreMargin); + + teamDisplay.scoreAreaInnerHeight = playerTall - 2 * marginY; + teamDisplay.scoreAreaLinePreferredLeading = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kTeamScoreLineLeadingRatio); + teamDisplay.scoreAreaStartY = playerY + marginY; + teamDisplay.maxPlayersVisible = MIN(cMaxScoreLines, teamDisplay.scoreAreaInnerHeight / teamDisplay.scoreAreaLineHeight); + + // Calculate the starting point for player data. + int startY = teamDisplay.scoreAreaStartY; + int spacingY = teamDisplay.scoreAreaLineHeight + teamDisplay.scoreAreaLinePreferredLeading; + + for ( int i = 0; i < cMaxScoreLines; ++i, startY += spacingY ) + { + PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i]; + + int wide = 0; + int tall = 0; + int x = 0; + int y = 0; + + // avatar + if ( pPlayerAvatar0 != NULL ) + { + pPlayerAvatar0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playeravatar%d", szTeamPrefix, i ); + delete playerDisplay.pAvatar; + playerDisplay.pAvatar = (CAvatarImagePanel*)SETUP_PANEL( new CAvatarImagePanel( this, tmpName ) ); + playerDisplay.pAvatar->SetBounds( x, startY, wide, tall ); + playerDisplay.pAvatar->SetDefaultAvatar( scheme()->GetImage( &teamDisplay == &m_teamDisplayCT ? CSTRIKE_DEFAULT_CT_AVATAR : CSTRIKE_DEFAULT_T_AVATAR, true ) ); + playerDisplay.pAvatar->SetShouldDrawFriendIcon( false ); + playerDisplay.pAvatar->SetShouldScaleImage( true ); + playerDisplay.pAvatar->SetVisible( false ); + } + + // clan + if ( pPlayerClan0 != NULL ) + { + pPlayerClan0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playerclan%d", szTeamPrefix, i ); + delete playerDisplay.pClanLabel; + playerDisplay.pClanLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, tmpName ) ); + playerDisplay.pClanLabel->SetFont( m_listItemFont ); + playerDisplay.pClanLabel->SetBounds( x, startY, wide, tall ); + playerDisplay.pClanLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pClanLabel->SetFgColor( m_teamDisplayCT.playerClanColor ); + playerDisplay.pClanLabel->SetContentAlignment( Label::a_east ); + playerDisplay.pClanLabel->SetVisible( false ); + } + + // name + if ( pPlayerName0 != NULL ) + { + pPlayerName0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playername%d", szTeamPrefix, i ); + delete playerDisplay.pNameLabel; + playerDisplay.pNameLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, tmpName ) ); + playerDisplay.pNameLabel->SetFont( m_listItemFont ); + playerDisplay.pNameLabel->SetBounds( x, startY, wide, tall); + playerDisplay.pNameLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pNameLabel->SetFgColor( m_teamDisplayCT.playerDataColor ); + playerDisplay.pNameLabel->SetContentAlignment( Label::a_west ); + playerDisplay.pNameLabel->SetVisible( false ); + + // mvp + int wideMVP = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kScaleMVP); + int tallMVP = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kScaleMVP); + int yMVP = ( teamDisplay.scoreAreaLineHeight - tallMVP ) / 2; + + V_snprintf( tmpName, 32, "_%s_mvp%d", szTeamPrefix, i ); + delete playerDisplay.pMVPImage; + playerDisplay.pMVPImage = (ImagePanel*)SETUP_PANEL( new ImagePanel( this, tmpName ) ); + playerDisplay.pMVPImage->SetBounds( x, startY + yMVP, wideMVP, tallMVP ); + playerDisplay.pMVPImage->SetImage( "../hud/scoreboard_mvp" ); + playerDisplay.pMVPImage->SetShouldScaleImage( true ); + playerDisplay.pMVPImage->SetVisible( false ); + + // mvp count + V_snprintf( tmpName, 32, "_%s_mvpcount%d", szTeamPrefix, i ); + delete playerDisplay.pMVPCountLabel; + playerDisplay.pMVPCountLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) ); + playerDisplay.pMVPCountLabel->SetFont( m_MVPFont ); + playerDisplay.pMVPCountLabel->SetBounds( 0, -mvpLabelYOffset, mvpLabelWidth, tallMVP ); + playerDisplay.pMVPCountLabel->SetVisible( false ); + + // Pin to the mvp image. + V_snprintf( tmpName, 32, "_%s_mvp%d", szTeamPrefix, i ); + playerDisplay.pMVPCountLabel->PinToSibling( tmpName, PIN_CENTER_LEFT, PIN_CENTER_RIGHT ); + } + + // score + if ( pPlayerScore0 != NULL ) + { + pPlayerScore0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playerscore%d", szTeamPrefix, i ); + delete playerDisplay.pScoreLabel; + playerDisplay.pScoreLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) ); + playerDisplay.pScoreLabel->SetFont( m_listItemFont ); + playerDisplay.pScoreLabel->SetBounds( x, startY, wide, tall ); + playerDisplay.pScoreLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pScoreLabel->SetFgColor( m_teamDisplayCT.playerDataColor ); + playerDisplay.pScoreLabel->SetContentAlignment( Label::a_center ); + playerDisplay.pScoreLabel->SetVisible( false ); + } + + // deaths + if ( pPlayerDeaths0 != NULL ) + { + pPlayerDeaths0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playerdeaths%d", szTeamPrefix, i ); + delete playerDisplay.pDeathsLabel; + playerDisplay.pDeathsLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) ); + playerDisplay.pDeathsLabel->SetFont( m_listItemFont ); + playerDisplay.pDeathsLabel->SetBounds( x, startY, wide, tall ); + playerDisplay.pDeathsLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pDeathsLabel->SetFgColor( m_teamDisplayCT.playerDataColor ); + playerDisplay.pDeathsLabel->SetContentAlignment( Label::a_center ); + playerDisplay.pDeathsLabel->SetVisible( false ); + } + + // latency + if ( pPlayerLatency0 != NULL ) + { + pPlayerLatency0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playerping%d", szTeamPrefix, i ); + delete playerDisplay.pPingLabel; + playerDisplay.pPingLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) ); + playerDisplay.pPingLabel->SetFont( m_listItemFont ); + playerDisplay.pPingLabel->SetBounds( x, startY, wide, tall ); + playerDisplay.pPingLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pPingLabel->SetFgColor( m_teamDisplayCT.playerDataColor ); + playerDisplay.pPingLabel->SetContentAlignment( Label::a_center ); + playerDisplay.pPingLabel->SetVisible( false ); + } + + // status + if ( pPlayerStatus0 != NULL ) + { + pPlayerStatus0->GetBounds( x, y, wide, tall ); + V_snprintf( tmpName, 32, "_%s_playerstatus%d", szTeamPrefix, i ); + delete playerDisplay.pStatusImage; + playerDisplay.pStatusImage = (ImagePanel*)SETUP_PANEL( new ImagePanel( this, tmpName ) ); + playerDisplay.pStatusImage->SetBounds( x, startY, wide, tall ); + playerDisplay.pStatusImage->SetImage( "../hud/scoreboard_dead" ); + playerDisplay.pStatusImage->SetShouldScaleImage( true ); + playerDisplay.pStatusImage->SetVisible( false ); + } + + // select + { + int x1 = teamDisplay.scoreAreaMinX - selectMargin; + int y1 = startY - selectMargin; + int x2 = teamDisplay.scoreAreaMaxX + selectMargin; + int y2 = startY + teamDisplay.scoreAreaLineHeight + selectMargin; + + V_snprintf( tmpName, 32, "_%s_playerselect%d", szTeamPrefix, i ); + delete playerDisplay.pSelect; + playerDisplay.pSelect = (ImagePanel*)SETUP_PANEL( new ImagePanel( this, tmpName ) ); + playerDisplay.pSelect->SetBounds( x1, y1, x2 - x1, y2 - y1 ); + playerDisplay.pSelect->SetImage( "../vgui/scoreboard/scoreboard-select" ); + playerDisplay.pSelect->SetShouldScaleImage( true ); + playerDisplay.pSelect->SetVisible( false ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Used for sorting players +//----------------------------------------------------------------------------- +int CCSClientScoreBoardDialog::PlayerSortFunction( PlayerScoreInfo* const* pLeft, PlayerScoreInfo* const* pRight ) +{ + // a return value < 0 puts pLeft earlier in the list, > 0 puts pRight earlier + const PlayerScoreInfo* pPlayer1 = *pLeft; + const PlayerScoreInfo* pPlayer2 = *pRight; + Assert( pPlayer1 && pPlayer2 ); + + // bail out early if either player is an empty slot, i.e. has a player index of -1 + if( pPlayer1->playerIndex == -1 ) + return 1; + if( pPlayer2->playerIndex == -1 ) + return -1; + + // first compare scores + if ( pPlayer1->frags > pPlayer2->frags ) + return -1; + if ( pPlayer1->frags < pPlayer2->frags ) + return 1; + + // second compare deaths + if ( pPlayer1->deaths > pPlayer2->deaths ) + return 1; + if ( pPlayer1->deaths < pPlayer2->deaths ) + return -1; + + // if score and deaths are the same, use player index to get deterministic sort + if ( pPlayer1->playerIndex < pPlayer2->playerIndex ) + return -1; + else + return 1; +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates the dialog +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::Update() +{ + if ( m_pServerLabel ) + { + m_pServerLabel->SetText( m_pServerName ); + } + + // Update the stats status. + CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr*>( engine->GetAchievementMgr() ); + if ( pAchievementMgr != NULL && + pAchievementMgr->CheckAchievementsEnabled() ) + { + SetDialogVariable( "statsstatus", m_pStatsEnabled ); + } + else + { + SetDialogVariable( "statsstatus", m_pStatsDisabled ); + } + + UpdateTeamInfo(); + UpdatePlayerList(); + UpdateSpectatorList(); + UpdateHLTVList(); + UpdateMatchEndText(); + UpdateMvpElements(); + + // update every second + m_fNextUpdateTime = gpGlobals->curtime + kUpdateInterval; + + // Catch the case where we call ShowPanel before ApplySchemeSettings, eg when going from windowed <-> fullscreen + if ( m_pImageList == NULL ) + { + InvalidateLayout( true, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates information about teams +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::UpdateTeamInfo() +{ + if ( g_PR == NULL ) + { + return; + } + + // update the team sections in the scoreboard + for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ ) + { + wchar_t *teamName = NULL; + C_Team *team = GetGlobalTeam( teamIndex ); + if ( team ) + { + // choose dialog variables to set depending on team + const char *pDialogVarTeamName = NULL; + const char *pDialogVarAliveCount = NULL; + const char *pDialogVarTeamScore = NULL; + switch ( teamIndex ) + { + case TEAM_TERRORIST: + teamName = g_pVGuiLocalize->Find( "#Cstrike_Team_T" ); + pDialogVarTeamName = "t_teamname"; + pDialogVarAliveCount = "t_alivecount"; + pDialogVarTeamScore = "t_totalteamscore"; + break; + case TEAM_CT: + teamName = g_pVGuiLocalize->Find( "#Cstrike_Team_CT" ); + pDialogVarTeamName = "ct_teamname"; + pDialogVarAliveCount = "ct_alivecount"; + pDialogVarTeamScore = "ct_totalteamscore"; + break; + default: + Assert( false ); + break; + } + + // Set the team name if it hasn't been set. + wchar_t name[64]; + if ( !teamName && team && team->Get_Name() != NULL ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( team->Get_Name(), name, sizeof( name ) ); + teamName = name; + } + + // Count the players on the team. + int numPlayers = 0; + int numAlive = 0; + for ( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ ) + { + if ( g_PR->IsConnected( playerIndex ) && g_PR->GetTeam( playerIndex ) == teamIndex ) + { + numPlayers++; + if ( g_PR->IsAlive( playerIndex ) ) + { + ++numAlive; + } + } + } + + SetDialogVariable( pDialogVarTeamName, teamName ); + + // Team score + wchar_t wNumScore[16]; + V_snwprintf( wNumScore, ARRAYSIZE( wNumScore ), L"%i", team->Get_Score() ); + SetDialogVariable( pDialogVarTeamScore, wNumScore ); + + // Number of alive players + wchar_t numAliveString[32]; + V_snwprintf( numAliveString, ARRAYSIZE( numAliveString ), L"%i / %i", numAlive, numPlayers); + SetDialogVariable( pDialogVarAliveCount, numAliveString ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Helper to shrink font size when a string gets too long for its label +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::AdjustFontToFit( const char *pString, vgui::Label *pLabel ) +{ + if ( !pString || !pLabel ) + return; + + int len = Q_strlen( pString ); + if ( !len ) + return; + + int arraySize = len + 1; + wchar_t *pWideString = new wchar_t[arraySize]; + V_UTF8ToUnicode( pString, pWideString, (arraySize * sizeof(wchar_t)) ); + + int stringWidth, stringHeight; + g_pMatSystemSurface->GetTextSize( m_listItemFont, pWideString, stringWidth, stringHeight ); + + int labelWidth = pLabel->GetWide(); + if ( stringWidth <= labelWidth ) + { + pLabel->SetFont( m_listItemFont ); + } + else + { + g_pMatSystemSurface->GetTextSize( m_listItemFontSmaller, pWideString, stringWidth, stringHeight ); + if ( stringWidth <= labelWidth ) + { + pLabel->SetFont( m_listItemFontSmaller ); + } + else + { + pLabel->SetFont(m_listItemFontSmallest); + } + } + delete [] pWideString; +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the player list +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::UpdatePlayerList() +{ + m_teamDisplayT.playerScores.PurgeAndDeleteElements(); + m_teamDisplayCT.playerScores.PurgeAndDeleteElements(); + + C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pLocalPlayer ) + return; + + // Set the player colors from the convars. + UpdatePlayerColors(); + + for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ ) + { + if( g_PR->IsConnected( playerIndex ) ) + { + PlayerScoreInfo* playerScoreInfo = new PlayerScoreInfo; + if ( !GetPlayerScoreInfo( playerIndex, *playerScoreInfo ) ) + { + delete playerScoreInfo; + continue; + } + + if ( g_PR->GetTeam( playerIndex ) == TEAM_TERRORIST ) + { + m_teamDisplayT.playerScores.AddToTail(playerScoreInfo); + } + else if ( g_PR->GetTeam( playerIndex ) == TEAM_CT ) + { + m_teamDisplayCT.playerScores.AddToTail(playerScoreInfo); + } + else + { + // [mhansen] make sure we don't leak here + delete playerScoreInfo; + } + } + } + + // Sort the lists of players + m_teamDisplayT.playerScores.Sort(PlayerSortFunction); + m_teamDisplayCT.playerScores.Sort(PlayerSortFunction); + + // Force the local player to be visible when he is below the visible portion of the sorted list + if ( pLocalPlayer->GetTeamNumber() == TEAM_TERRORIST ) + ForceLocalPlayerVisible(m_teamDisplayT); + else if ( pLocalPlayer->GetTeamNumber() == TEAM_CT ) + ForceLocalPlayerVisible(m_teamDisplayCT); + + UpdateTeamPlayerDisplay(m_teamDisplayT); + UpdateTeamPlayerDisplay(m_teamDisplayCT); +} + +void CCSClientScoreBoardDialog::UpdateTeamPlayerDisplay( TeamDisplayInfo& teamDisplay ) +{ + const int selectMargin = scheme()->GetProportionalScaledValueEx( GetScheme(), 1 ); + + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + if ( !cs_PR ) + return; + int iLocalPlayerIndex = GetLocalPlayerIndex(); + + int maxTeamSize = MAX(m_teamDisplayT.playerScores.Count(), m_teamDisplayCT.playerScores.Count()); + + // adjust spacing + int leadingAvailable = teamDisplay.scoreAreaInnerHeight - maxTeamSize * teamDisplay.scoreAreaLineHeight; + int leading = 0; + if ( maxTeamSize > 1 ) // only makes sense if we have more than one player + { + leading = clamp(leadingAvailable / (maxTeamSize - 1), 0, teamDisplay.scoreAreaLinePreferredLeading); + } + int spacingY = teamDisplay.scoreAreaLineHeight + leading; + + // temp values for updating just the y position of elements + int xPos, yPos; + + int i = 0; + int startY = teamDisplay.scoreAreaStartY; + for ( i = 0; i < MIN( cMaxScoreLines, teamDisplay.playerScores.Count() ); ++i, startY += spacingY ) + { + if ( startY + teamDisplay.scoreAreaLineHeight > teamDisplay.scoreAreaStartY + teamDisplay.scoreAreaInnerHeight ) + break; + + PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i]; + + const PlayerScoreInfo* pPlayerScore = teamDisplay.playerScores[i]; + if ( pPlayerScore ) + { + int playerIndex = pPlayerScore->playerIndex; + + const char* pUTF8Name = pPlayerScore->szName; + +// int bufsize; +// if ( g_PR->IsFakePlayer( playerIndex ) ) +// bufsize = strlen( oldName ) * 2 + 14 + 1; +// else +// bufsize = strlen( oldName ) * 2 + 1; +// +// char *newName = (char *)_alloca( bufsize ); +// UTIL_MakeSafeName( oldName, newName, bufsize ); + + if ( pUTF8Name != NULL && V_strlen( pUTF8Name ) > 0 ) + { + bool isAlive = cs_PR->IsAlive( playerIndex ); + Color fgColor = ( isAlive ? teamDisplay.playerDataColor : m_DeadPlayerDataColor ); + Color fgClanColor = ( isAlive ? teamDisplay.playerClanColor : m_DeadPlayerClanColor ); + + if ( playerDisplay.pNameLabel != NULL ) + { + AdjustFontToFit( pUTF8Name, playerDisplay.pNameLabel ); + + playerDisplay.pNameLabel->SetVisible( true ); + playerDisplay.pNameLabel->SetText( pUTF8Name ); + playerDisplay.pNameLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pNameLabel->SetFgColor( fgColor ); + playerDisplay.pNameLabel->GetPos(xPos, yPos); + playerDisplay.pNameLabel->SetPos(xPos, startY); + + int tallMVP = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kScaleMVP); + int yMVP = ( teamDisplay.scoreAreaLineHeight - tallMVP ) / 2; + playerDisplay.pMVPImage->SetPos( xPos, startY + yMVP); + } + + if ( playerDisplay.pClanLabel != NULL ) + { + const char* pUTF8Clan = pPlayerScore->szClanTag; + AdjustFontToFit( pUTF8Clan, playerDisplay.pClanLabel ); + + playerDisplay.pClanLabel->SetVisible( true ); + playerDisplay.pClanLabel->SetText( pUTF8Clan ); + playerDisplay.pClanLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pClanLabel->SetFgColor( fgClanColor ); + playerDisplay.pClanLabel->GetPos(xPos, yPos); + playerDisplay.pClanLabel->SetPos(xPos, startY); + } + + char tmpbuf[16]; + + if ( playerDisplay.pScoreLabel != NULL ) + { + Q_snprintf( tmpbuf, sizeof( tmpbuf ), "%d", pPlayerScore->frags); + playerDisplay.pScoreLabel->SetVisible( true ); + playerDisplay.pScoreLabel->SetText( tmpbuf ); + playerDisplay.pScoreLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pScoreLabel->SetFgColor( fgColor ); + playerDisplay.pScoreLabel->GetPos(xPos, yPos); + playerDisplay.pScoreLabel->SetPos(xPos, startY); + } + + if ( playerDisplay.pDeathsLabel != NULL ) + { + Q_snprintf( tmpbuf, sizeof( tmpbuf ), "%d", pPlayerScore->deaths); + playerDisplay.pDeathsLabel->SetVisible( true ); + playerDisplay.pDeathsLabel->SetText( tmpbuf ); + playerDisplay.pDeathsLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pDeathsLabel->SetFgColor( fgColor ); + playerDisplay.pDeathsLabel->GetPos(xPos, yPos); + playerDisplay.pDeathsLabel->SetPos(xPos, startY); + + } + + if ( playerDisplay.pPingLabel != NULL ) + { + if ( pPlayerScore->ping >= 0 ) + Q_snprintf( tmpbuf, sizeof( tmpbuf ), "%d", pPlayerScore->ping); + else + Q_strcpy( tmpbuf, "BOT"); + + playerDisplay.pPingLabel->SetVisible( true ); + playerDisplay.pPingLabel->SetText( tmpbuf ); + playerDisplay.pPingLabel->SetBgColor( m_PlayerDataBgColor ); + playerDisplay.pPingLabel->SetFgColor( fgColor ); + playerDisplay.pPingLabel->GetPos(xPos, yPos); + playerDisplay.pPingLabel->SetPos(xPos, startY); + + } + } + + if ( playerDisplay.pStatusImage != NULL ) + { + if ( pPlayerScore->szStatus == NULL ) + { + playerDisplay.pStatusImage->SetVisible( false ); + } + else + { + playerDisplay.pStatusImage->SetVisible( true ); + playerDisplay.pStatusImage->SetImage( pPlayerScore->szStatus ); + playerDisplay.pStatusImage->GetPos(xPos, yPos); + playerDisplay.pStatusImage->SetPos(xPos, startY); + playerDisplay.pStatusImage->SetDrawColor(pPlayerScore->bStatusPlayerColor ? teamDisplay.playerDataColor : COLOR_WHITE); + } + } + + if ( playerDisplay.pAvatar != NULL ) + { + playerDisplay.pAvatar->SetVisible( true ); + playerDisplay.pAvatar->SetPlayer( playerIndex, k_EAvatarSize32x32 ); + playerDisplay.pAvatar->GetPos(xPos, yPos); + playerDisplay.pAvatar->SetPos(xPos, startY); + } + + if ( playerDisplay.pSelect != NULL ) + { + playerDisplay.pSelect->SetVisible( pPlayerScore->playerIndex == iLocalPlayerIndex ); + playerDisplay.pSelect->SetPos(teamDisplay.scoreAreaMinX - selectMargin, startY - selectMargin); + } + } + } + + // set any remaining entries to non-visible + for ( ; i < cMaxScoreLines; ++i ) + { + PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i]; + + if ( playerDisplay.pClanLabel != NULL ) + playerDisplay.pClanLabel->SetVisible(false); + + if ( playerDisplay.pNameLabel != NULL ) + playerDisplay.pNameLabel->SetVisible(false); + + if ( playerDisplay.pScoreLabel != NULL ) + playerDisplay.pScoreLabel->SetVisible(false); + + if ( playerDisplay.pDeathsLabel != NULL ) + playerDisplay.pDeathsLabel->SetVisible(false); + + if ( playerDisplay.pPingLabel != NULL ) + playerDisplay.pPingLabel->SetVisible(false); + + if ( playerDisplay.pStatusImage != NULL ) + playerDisplay.pStatusImage->SetVisible(false); + + if ( playerDisplay.pAvatar != NULL ) + playerDisplay.pAvatar->SetVisible( false ); + + if ( playerDisplay.pMVPImage != NULL ) + playerDisplay.pMVPImage->SetVisible( false ); + + if ( playerDisplay.pMVPCountLabel != NULL ) + playerDisplay.pMVPCountLabel->SetVisible( false ); + + if ( playerDisplay.pSelect != NULL ) + playerDisplay.pSelect->SetVisible( false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates the spectator list +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::UpdateSpectatorList() +{ + if ( g_PR == NULL ) + { + return; + } + + const int listTextLen = 100; + wchar_t listText[listTextLen]; + listText[0] = L'\0'; + + const wchar_t *delimText = L", "; + const int delimTextLen = V_wcslen( delimText ); + + // Count the number of spectators and build up a list. + int nSpectators = 0; + for ( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; ++playerIndex ) + { + if ( ShouldShowAsSpectator( playerIndex ) ) + { + const char *playerName = g_PR->GetPlayerName( playerIndex ); + if ( playerName != NULL ) + { + // Convert the name to wide char. + wchar_t playerBuf[MAX_PLAYER_NAME_LENGTH]; + playerBuf[0] = L'\0'; + V_UTF8ToUnicode( playerName, playerBuf, sizeof( playerBuf ) ); + + // + // Check to see if there is space for the player name and delimiter. + // + + bool addDelim = ( nSpectators > 0 ); + + int playerNameLen = V_wcslen( playerBuf ); + if ( addDelim ) + { + playerNameLen += delimTextLen; + } + + int currentLen = V_wcslen( listText ); + int remainingLen = listTextLen - currentLen - playerNameLen - 1; + if ( remainingLen >= 0 ) + { + // Append the delimiter. + if ( addDelim ) + { + V_wcscat_safe( listText, delimText ); + } + + // Append the player name. + V_wcscat_safe( listText, playerBuf ); + } + } + + ++nSpectators; + } + } + + wchar_t labelText[512]; + labelText[0] = L'\0'; + + if ( nSpectators == 0 ) + { + // No spectators. + wchar_t *noSpectators = g_pVGuiLocalize->Find( "#Cstrike_Scoreboard_NoSpectators" ); + if ( noSpectators != NULL ) + { + V_wcsncpy( labelText, noSpectators, sizeof( labelText ) ); + } + } + else + { + // Build the text for the number of spectators. + const int countTextLen = 16; + wchar_t countText[countTextLen]; + countText[0] = L'\0'; + V_snwprintf( countText, countTextLen, L"%i", nSpectators ); + countText[countTextLen - 1] = L'\0'; + + // Build the combined count and spectator list text. + wchar_t *formatLabel = g_pVGuiLocalize->Find( ( nSpectators == 1 ) ? "#Cstrike_Scoreboard_Spectator" : "#Cstrike_Scoreboard_Spectators" ); + if ( formatLabel != NULL ) + { + g_pVGuiLocalize->ConstructString( labelText, sizeof( labelText ), formatLabel, 2, countText, listText ); + } + } + + SetDialogVariable( "spectators", labelText ); +} + +//----------------------------------------------------------------------------- +// Purpose: Display the number of HLTV viewers +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::UpdateHLTVList( void ) +{ + // Build the text for the number of viewers. + const int countTextLen = 16; + wchar_t countText[countTextLen]; + countText[0] = L'\0'; + V_snwprintf( countText, countTextLen, L"%i", m_HLTVSpectators ); + countText[countTextLen - 1] = L'\0'; + + // Build the combined text. + wchar_t labelText[512]; + labelText[0] = L'\0'; + wchar_t *formatLabel = g_pVGuiLocalize->Find( "#Cstrike_Scoreboard_HLTV" ); + if ( formatLabel != NULL ) + { + g_pVGuiLocalize->ConstructString( labelText, sizeof( labelText ), formatLabel, 1, countText ); + } + + SetDialogVariable( "sourcetv", labelText ); +} + +/** +* Special processing for MVP UI elements +*/ +void CCSClientScoreBoardDialog::UpdateMvpElements() +{ + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + if ( cs_PR == NULL ) + { + return; + } + + for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; ++teamIndex ) + { + TeamDisplayInfo& teamDisplay = ( teamIndex == TEAM_TERRORIST ) ? m_teamDisplayT : m_teamDisplayCT; + + for ( int i = 0; i < cMaxScoreLines && i < teamDisplay.playerScores.Count(); ++i ) + { + PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i]; + + // Get the player index. + int playerIndex = -1; + if ( teamDisplay.playerScores[i] != NULL ) + { + playerIndex = teamDisplay.playerScores[i]->playerIndex; + } + + if ( playerIndex == -1 ) + continue; + + // early out fail conditions + if ( playerDisplay.pNameLabel == NULL || playerDisplay.pMVPImage == NULL || playerDisplay.pMVPCountLabel == NULL ) + continue; + + // Get the number of MVPs and cap it. + int numMVPs = MIN( kMaxMVPCount, cs_PR->GetNumMVPs( playerIndex ) ); + + // No MVPs. + if ( numMVPs == 0 ) + { + playerDisplay.pMVPImage->SetVisible( false ); + playerDisplay.pMVPCountLabel->SetVisible( false ); + continue; + } + + // Get the dimensions of the player label. + int xName = 0; + int yName = 0; + int wideName = 0; + int tallName = 0; + playerDisplay.pNameLabel->GetBounds( xName, yName, wideName, tallName ); + + wchar_t playerName[64]; + playerDisplay.pNameLabel->GetText( playerName, sizeof(playerName) ); + + // Find the actual width of the rendered player name. + int actualWideName = 0; + int actualTallName = 0; + g_pMatSystemSurface->GetTextSize( playerDisplay.pNameLabel->GetFont(), playerName, actualWideName, actualTallName ); + if ( actualWideName > wideName ) + { + actualWideName = wideName; + } + + // Get the dimensions of the mvp image. + int xMVPImage = 0; + int yMVPImage = 0; + int wideMVPImage = 0; + int tallMVPImage = 0; + playerDisplay.pMVPImage->GetBounds( xMVPImage, yMVPImage, wideMVPImage, tallMVPImage ); + + // The MVP label is hidden for only one star. + bool showMVPNumber = ( numMVPs > 1 ); + + // Set the MVP label text and get the actual size. + int actualWideMVPLabel = 0; + if ( showMVPNumber ) + { + const int mvpTextSize = 8; + wchar_t mvpText[mvpTextSize]; + V_snwprintf( mvpText, ARRAYSIZE( mvpText ), L"%i", numMVPs ); + playerDisplay.pMVPCountLabel->SetText( mvpText ); + playerDisplay.pMVPCountLabel->SetVisible( true ); + + int actualTallMVPLabel = 0; + g_pMatSystemSurface->GetTextSize( playerDisplay.pMVPCountLabel->GetFont(), mvpText, actualWideMVPLabel, actualTallMVPLabel ); + } + + // Calculate the total width of the mvp stuff. + int wideMVPTotal = wideMVPImage; + if ( showMVPNumber ) + { + wideMVPTotal += actualWideMVPLabel; + } + + // Calculate the optimal place for the mvp. + int x = xName + actualWideName + m_MVPXOffset; + + // Get the position of the status image. + if ( playerDisplay.pStatusImage ) + { + int xStatus = 0; + int yStatus = 0; + playerDisplay.pStatusImage->GetPos( xStatus, yStatus ); + + if ( x + wideMVPTotal > xStatus ) + { + // Don't run over the status image. Back up. + x = xStatus - wideMVPTotal; + } + } + + playerDisplay.pMVPImage->SetPos( x, yMVPImage ); + playerDisplay.pMVPImage->SetVisible( true ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns whether the specified player index is a spectator +//----------------------------------------------------------------------------- +bool CCSClientScoreBoardDialog::ShouldShowAsSpectator( int iPlayerIndex ) +{ + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + if ( !cs_PR ) + return false; + + // see if player is connected + if ( cs_PR->IsConnected( iPlayerIndex ) ) + { + // either spectator or unassigned team should show in spectator list + int iTeam = cs_PR->GetTeam( iPlayerIndex ); + if ( TEAM_SPECTATOR == iTeam || TEAM_UNASSIGNED == iTeam ) + return true; + } + return false; +} + + +void CCSClientScoreBoardDialog::FireGameEvent( IGameEvent *event ) +{ + if ( event == NULL ) + return; + + const char *pEventName = event->GetName(); + if ( pEventName == NULL ) + return; + + if ( Q_strcmp( pEventName, "server_spawn" ) == 0 ) + { + // set server name in scoreboard + const char *hostname = event->GetString( "hostname" ); + if ( hostname != NULL ) + { + wchar_t wzHostName[256]; + g_pVGuiLocalize->ConvertANSIToUnicode( hostname, wzHostName, sizeof( wzHostName ) ); + g_pVGuiLocalize->ConstructString( m_pServerName, sizeof(m_pServerName), g_pVGuiLocalize->Find( "#Cstrike_SB_Server" ), 1, wzHostName ); + + if ( m_pServerLabel ) + { + m_pServerLabel->SetText( m_pServerName ); + } + + if ( m_gameOver ) + { + ResetFromGameOverState(); + } + + // Save the server name for use after this panel is reconstructed + if ( g_pClientMode ) + { + g_pClientMode->SetServerName( m_pServerName ); + } + } + } + else if ( Q_strcmp( pEventName, "game_newmap" ) == 0 ) + { + const char *mapName = event->GetString( "mapname" ); + if ( mapName != NULL ) + { + g_pVGuiLocalize->ConvertANSIToUnicode( mapName, m_pMapName, sizeof( m_pMapName ) ); + SetDialogVariable( "mapname", m_pMapName ); + + if ( m_gameOver ) + { + ResetFromGameOverState(); + } + + // Save the map name for use after this panel is reconstructed + if ( g_pClientMode ) + { + g_pClientMode->SetMapName( m_pMapName ); + } + } + } + else if ( Q_strcmp( pEventName, "match_end_conditions" ) == 0 ) + { + UpdateMatchEndText(); + } + else if ( Q_strcmp( pEventName, "cs_win_panel_match" ) == 0 ) + { + m_gameOver = true; + UpdateMatchEndText(); + } + + BaseClass::FireGameEvent( event ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a new row to the scoreboard, from the playerinfo structure +//----------------------------------------------------------------------------- +bool CCSClientScoreBoardDialog::GetPlayerScoreInfo( int playerIndex, PlayerScoreInfo& playerScoreInfo ) +{ + if ( g_PR == NULL ) + return false; + + // Clean up the player name + const char *oldName = g_PR->GetPlayerName( playerIndex ); + if ( oldName == NULL ) + return false; + + playerScoreInfo.szName = g_PR->GetPlayerName( playerIndex ); + + playerScoreInfo.playerIndex = playerIndex; + playerScoreInfo.frags = g_PR->GetPlayerScore( playerIndex ); + playerScoreInfo.deaths = g_PR->GetDeaths( playerIndex ); + + if ( g_PR->GetPing( playerIndex ) < 1 ) + { + if ( g_PR->IsFakePlayer( playerIndex ) ) + { + playerScoreInfo.ping = -1; + } + else + { + playerScoreInfo.ping = 0; + } + } + else + { + playerScoreInfo.ping = g_PR->GetPing( playerIndex ); + } + + // get CS specific infos + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + + C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !cs_PR || !pLocalPlayer ) + { + return false; + } + + // Get the clan tag + playerScoreInfo.szClanTag = cs_PR->GetClanTag( playerIndex ); + + bool bShowExtraInfo = + ( pLocalPlayer->GetTeamNumber() == TEAM_UNASSIGNED ) || // we're not spawned yet + ( pLocalPlayer->GetTeamNumber() == TEAM_SPECTATOR ) || // we are a spectator + ( pLocalPlayer->IsPlayerDead() && mp_forcecamera.GetInt() == OBS_ALLOW_ALL ) || // we are dead and allowed to spectate opponents + ( pLocalPlayer->GetTeamNumber() == g_PR->GetTeam( playerIndex ) ); // we're on the same team + + playerScoreInfo.szStatus = NULL; + playerScoreInfo.bStatusPlayerColor = false; + + // set the status icon; lowest priority icons are tested first, and highest last + + if ( cs_PR->IsVIP( playerIndex ) && bShowExtraInfo ) + { + playerScoreInfo.szStatus = "../hud/scoreboard_clock"; + playerScoreInfo.bStatusPlayerColor = true; + } + + if ( !g_PR->IsAlive( playerIndex ) && g_PR->GetTeam( playerIndex ) > TEAM_SPECTATOR ) + { + playerScoreInfo.szStatus = "../hud/scoreboard_dead"; + } + + if ( g_PR->IsHLTV( playerIndex ) ) + { +// // show #spectators in class field, it's transmitted as player's score +// char numspecs[32]; +// Q_snprintf( numspecs, sizeof( numspecs ), "%i Spectators", m_HLTVSpectators ); +// kv->SetString( "class", numspecs ); + } + + // Set the dominated icon + if ( pLocalPlayer->IsPlayerDominatingMe( playerIndex ) ) + { + if ( g_PR->IsAlive( playerIndex ) ) + { + playerScoreInfo.szStatus = "../hud/scoreboard_nemesis"; + } + else + { + playerScoreInfo.szStatus = "../hud/scoreboard_nemesis-dead"; + } + } + + if ( pLocalPlayer->IsPlayerDominated(playerIndex) ) + { + if ( g_PR->IsAlive( playerIndex ) ) + { + playerScoreInfo.szStatus = "../hud/scoreboard_dominated"; + } + else + { + playerScoreInfo.szStatus = "../hud/scoreboard_domination-dead"; + } + } + + if ( cs_PR->HasC4( playerIndex ) && bShowExtraInfo ) + { + playerScoreInfo.szStatus = "../hud/scoreboard_bomb"; + playerScoreInfo.bStatusPlayerColor = true; + } + + if ( cs_PR->HasDefuser( playerIndex ) && bShowExtraInfo ) + { + playerScoreInfo.szStatus = "../hud/scoreboard_defuser"; + playerScoreInfo.bStatusPlayerColor = true; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the time/round remaining display and server and map name +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::UpdateMatchEndText() +{ + // Hide the win condition. + if ( m_pWinConditionLabel ) + { + m_pWinConditionLabel->SetVisible( false ); + } + + // Hide the clock. + if ( m_pClockLabel ) + { + m_pClockLabel->SetVisible( false ); + } + + if ( !m_gameOver ) + { + SetDialogVariable( "mapname", m_pMapName ); + + wchar_t wzMatchEndCausesLabel[128], wzMatchEndCause[32]; + + // Time limit + if ( mp_timelimit.GetInt() != 0 ) + { + int timeTillEndOfMatch = CSGameRules()->GetMapRemainingTime(); + bool showTime = ( timeTillEndOfMatch != -1 ); + if ( showTime ) + { + if ( m_pWinConditionLabel ) + { + V_snwprintf( wzMatchEndCause, ARRAYSIZE( wzMatchEndCause ), L"%.2i:%.2i", timeTillEndOfMatch / 60, timeTillEndOfMatch % 60 ); + g_pVGuiLocalize->ConstructString( wzMatchEndCausesLabel, sizeof( wzMatchEndCausesLabel ), + g_pVGuiLocalize->Find( "#Cstrike_Time_LeftVariable" ), 1, wzMatchEndCause ); + m_pWinConditionLabel->SetText( wzMatchEndCausesLabel ); + m_pWinConditionLabel->SetVisible( true ); + } + + if ( m_pClockLabel ) + { + m_pClockLabel->SetVisible( true ); + } + } + } + // Round limit + else if ( mp_maxrounds.GetInt() != 0 ) + { + if ( m_pWinConditionLabel ) + { + // Get the number of rounds played. + int roundsPlayed = 0; + for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ ) + { + C_Team *team = GetGlobalTeam( teamIndex ); + if ( team ) + { + roundsPlayed += team->Get_Score(); + } + } + + V_snwprintf( wzMatchEndCause, ARRAYSIZE( wzMatchEndCause ), L"%d", mp_maxrounds.GetInt() - roundsPlayed ); + g_pVGuiLocalize->ConstructString( wzMatchEndCausesLabel, sizeof( wzMatchEndCausesLabel ), + g_pVGuiLocalize->Find( "#Cstrike_Rounds_LeftVariable" ), 1, wzMatchEndCause ); + m_pWinConditionLabel->SetText( wzMatchEndCausesLabel ); + m_pWinConditionLabel->SetVisible( true ); + } + } + // Win limit + else if ( mp_winlimit.GetInt() != 0 ) + { + if ( m_pWinConditionLabel ) + { + V_snwprintf( wzMatchEndCause, ARRAYSIZE( wzMatchEndCause ), L"%d", mp_winlimit.GetInt() ); + g_pVGuiLocalize->ConstructString( wzMatchEndCausesLabel, sizeof( wzMatchEndCausesLabel ), + g_pVGuiLocalize->Find( "#Cstrike_Wins_NeededVariable" ), 1, wzMatchEndCause ); + m_pWinConditionLabel->SetText( wzMatchEndCausesLabel ); + m_pWinConditionLabel->SetVisible( true ); + } + } + } +} + +// Resets all changes made by the scoreboard's state at the match end +void CCSClientScoreBoardDialog::ResetFromGameOverState() +{ + m_gameOver = false; + + if ( m_pLabelMapName ) + { + m_pLabelMapName->SetVisible( true ); + } + + UpdateMatchEndText(); +} + +// [tj] We hook into the show command so we can lock or unlock all the elements that need to be hidden +// +// [pfreese] This used to enable/disable keyboard input, but since the scoreboard is now a popup, we have +// to leave the keyboard disabled +void CCSClientScoreBoardDialog::ShowPanel( bool state ) +{ + BaseClass::ShowPanel(state); + + int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "hide_for_scoreboard" ); + + if ( state ) + { + gHUD.LockRenderGroup( iRenderGroup ); + } + else + { + gHUD.UnlockRenderGroup( iRenderGroup ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Grabs the player data colors from the convars. +//----------------------------------------------------------------------------- +void CCSClientScoreBoardDialog::UpdatePlayerColors( void ) +{ + m_teamDisplayT.playerDataColor.SetColor( cl_scoreboard_t_color_red.GetInt(), cl_scoreboard_t_color_green.GetInt(), cl_scoreboard_t_color_blue.GetInt(), 255 ); + m_teamDisplayCT.playerDataColor.SetColor( cl_scoreboard_ct_color_red.GetInt(), cl_scoreboard_ct_color_green.GetInt(), cl_scoreboard_ct_color_blue.GetInt(), 255 ); + m_DeadPlayerDataColor.SetColor( cl_scoreboard_dead_color_red.GetInt(), cl_scoreboard_dead_color_green.GetInt(), cl_scoreboard_dead_color_blue.GetInt(), 255 ); + m_teamDisplayT.playerClanColor.SetColor( cl_scoreboard_clan_t_color_red.GetInt(), cl_scoreboard_clan_t_color_green.GetInt(), cl_scoreboard_clan_t_color_blue.GetInt(), 255 ); + m_teamDisplayCT.playerClanColor.SetColor( cl_scoreboard_clan_ct_color_red.GetInt(), cl_scoreboard_clan_ct_color_green.GetInt(), cl_scoreboard_clan_ct_color_blue.GetInt(), 255 ); + m_DeadPlayerClanColor.SetColor( cl_scoreboard_dead_clan_color_red.GetInt(), cl_scoreboard_dead_clan_color_green.GetInt(), cl_scoreboard_dead_clan_color_blue.GetInt(), 255 ); +} + +// [tj] Disabling joystick input if you are dead. +void CCSClientScoreBoardDialog::OnThink() +{ + BaseClass::OnThink(); + +#ifdef _XBOX + C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pLocalPlayer ) + { + bool mouseEnabled = IsMouseInputEnabled(); + if (pLocalPlayer->IsAlive() == mouseEnabled) + { + SetMouseInputEnabled( !mouseEnabled ); + } + } +#endif +} + +bool CCSClientScoreBoardDialog::ForceLocalPlayerVisible( TeamDisplayInfo& teamDisplay ) +{ + int iLocalPlayerIndex = GetLocalPlayerIndex(); + + // Look for the local player in the non-visible portion of the member list + for (int i = teamDisplay.maxPlayersVisible; i < teamDisplay.playerScores.Count(); ++i) + { + PlayerScoreInfo* pPlayerScore = teamDisplay.playerScores[i]; + Assert(pPlayerScore != NULL); + + // Determine if this is the local player's entry + if ( pPlayerScore->playerIndex == iLocalPlayerIndex ) + { + // Remove the local player entry from the current position + teamDisplay.playerScores.Remove(i); + + // Re-insert the local player entry at the end of the visible list + teamDisplay.playerScores.InsertBefore( teamDisplay.maxPlayersVisible - 1, pPlayerScore ); + + return true; + } + } + return false; +} diff --git a/game/client/cstrike/VGUI/cstrikeclientscoreboard.h b/game/client/cstrike/VGUI/cstrikeclientscoreboard.h new file mode 100644 index 0000000..2a2e409 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikeclientscoreboard.h @@ -0,0 +1,153 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSTRIKECLIENTSCOREBOARDDIALOG_H +#define CSTRIKECLIENTSCOREBOARDDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <clientscoreboarddialog.h> +#include <vgui_controls/ImagePanel.h> +#include "cs_shareddefs.h" +#include <vgui_controls/Frame.h> +#include "vgui_avatarimage.h" + + +const int cMaxScoreLines = 32; // This value must be > 2 + + +//----------------------------------------------------------------------------- +// Purpose: Game ScoreBoard +//----------------------------------------------------------------------------- +class CCSClientScoreBoardDialog : public CClientScoreBoardDialog +{ +private: + DECLARE_CLASS_SIMPLE( CCSClientScoreBoardDialog, CClientScoreBoardDialog ); + +public: + CCSClientScoreBoardDialog( IViewPort *pViewPort ); + ~CCSClientScoreBoardDialog(); + + virtual void Update(); + + // vgui overrides for rounded corner background + void UpdateMvpElements(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual void ResetFromGameOverState(); + + // [tj] Hook in here to hide other UI + virtual void ShowPanel( bool state ); + + // [tj] So we can do processing every frame + virtual void OnThink(); + +protected: + + struct PlayerScoreInfo + { + const char* szName; + const char* szClanTag; + int playerIndex; + int frags; + int deaths; + int ping; + const char* szStatus; + bool bStatusPlayerColor; + }; + + struct PlayerDisplay + { + vgui::Label* pNameLabel; + vgui::Label* pClanLabel; + vgui::Label* pScoreLabel; + vgui::Label* pDeathsLabel; + vgui::Label* pPingLabel; + vgui::Label* pMVPCountLabel; + CAvatarImagePanel* pAvatar; + vgui::ImagePanel* pStatusImage; + vgui::ImagePanel* pMVPImage; + vgui::ImagePanel* pSelect; + }; + + struct TeamDisplayInfo + { + Color playerDataColor; + Color playerClanColor; + PlayerDisplay playerDisplay[cMaxScoreLines]; + CUtlVector<PlayerScoreInfo*> playerScores; // For sorting team members outside of the listboxes + int scoreAreaInnerHeight; + int scoreAreaLineHeight; + int scoreAreaLinePreferredLeading; + int scoreAreaStartY; + int scoreAreaMinX; + int scoreAreaMaxX; + int maxPlayersVisible; + }; + + bool GetPlayerScoreInfo( int playerIndex, PlayerScoreInfo& playerScoreInfo ); + void UpdateTeamPlayerDisplay( TeamDisplayInfo& teamDisplay ); + void SetupTeamDisplay( TeamDisplayInfo& teamDisplay, const char* szTeamPrefix ); + + void UpdateTeamInfo(); + void UpdatePlayerList(); + + bool ForceLocalPlayerVisible( TeamDisplayInfo& teamDisplay ); + void UpdateSpectatorList(); + void UpdateHLTVList( void ); + void UpdateMatchEndText(); + + bool ShouldShowAsSpectator( int iPlayerIndex ); + void FireGameEvent( IGameEvent *event ); + + void UpdatePlayerColors( void ); + void AdjustFontToFit( const char *pString, vgui::Label *pLabel ); + + static int PlayerSortFunction( PlayerScoreInfo* const* pPS1, PlayerScoreInfo* const* pPS2 ); + +private: + vgui::HFont m_listItemFont; + vgui::HFont m_listItemFontSmaller; + vgui::HFont m_listItemFontSmallest; + vgui::HFont m_MVPFont; + + int m_iImageDead; + int m_iImageMVP; // Not used in the section list explicitly. Drawn over it + int m_iImageDominated; + int m_iImageNemesis; + int m_iImageBomb; + int m_iImageVIP; + int m_iImageFriend; + int m_iImageNemesisDead; + int m_iImageDominationDead; + + Color m_DeadPlayerDataColor; + Color m_PlayerDataBgColor; + Color m_DeadPlayerClanColor; + + vgui::Label* m_pWinConditionLabel; + vgui::Label* m_pClockLabel; + vgui::Label* m_pLabelMapName; + vgui::Label* m_pServerLabel; + + bool m_gameOver; + + wchar_t m_pMapName[256]; + wchar_t m_pServerName[256]; + wchar_t m_pStatsEnabled[256]; + wchar_t m_pStatsDisabled[256]; + + int m_LocalPlayerItemID; + int m_MVPXOffset; + + TeamDisplayInfo m_teamDisplayT; + TeamDisplayInfo m_teamDisplayCT; +}; + + +#endif // CSTRIKECLIENTSCOREBOARDDIALOG_H diff --git a/game/client/cstrike/VGUI/cstrikespectatorgui.cpp b/game/client/cstrike/VGUI/cstrikespectatorgui.cpp new file mode 100644 index 0000000..9c633c1 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikespectatorgui.cpp @@ -0,0 +1,2324 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstrikespectatorgui.h" +#include "hud.h" +#include "cs_shareddefs.h" + +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include "cs_gamerules.h" +#include "c_team.h" +#include "c_cs_playerresource.h" +#include "c_plantedc4.h" +#include "c_cs_hostage.h" +#include "vtf/vtf.h" +#include "clientmode.h" +#include <vgui_controls/AnimationController.h> +#include "voice_status.h" +#include "hud_radar.h" + +using namespace vgui; +DECLARE_HUDELEMENT( CCSMapOverview ) + +extern ConVar overview_health; +extern ConVar overview_names; +extern ConVar overview_tracks; +extern ConVar overview_locked; +extern ConVar overview_alpha; +extern ConVar cl_radaralpha; +ConVar cl_radar_locked( "cl_radar_locked", "0", FCVAR_ARCHIVE, "Lock the angle of the radar display?" ); + +void PreferredOverviewModeChanged( IConVar *pConVar, const char *oldString, float flOldValue ) +{ + ConVarRef var( pConVar ); + char cmd[32]; + V_snprintf( cmd, sizeof( cmd ), "overview_mode %d\n", var.GetInt() ); + engine->ClientCmd( cmd ); +} +ConVar overview_preferred_mode( "overview_preferred_mode", "1", FCVAR_ARCHIVE, "Preferred overview mode", PreferredOverviewModeChanged ); + +ConVar overview_preferred_view_size( "overview_preferred_view_size", "600", FCVAR_ARCHIVE, "Preferred overview view size" ); + +#define HOSTAGE_RESCUE_DURATION (2.5f) +#define BOMB_FADE_DURATION (2.5f) +#define DEATH_ICON_FADE (7.5f) +#define DEATH_ICON_DURATION (10.0f) +#define LAST_SEEN_ICON_DURATION (4.0f) +#define DIFFERENCE_THRESHOLD (200.0f) + +// To make your own green radar file from the map overview file, turn this on, and include vtf.lib +#define no_GENERATE_RADAR_FILE + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSSpectatorGUI::CCSSpectatorGUI(IViewPort *pViewPort) : CSpectatorGUI(pViewPort) +{ + m_pCTLabel = NULL; + m_pCTScore = NULL; + m_pTerLabel = NULL; + m_pTerScore = NULL; + m_pTimer = NULL; + m_pTimerLabel = NULL; + m_pDivider = NULL; + m_pExtraInfo = NULL; + + m_modifiedWidths = false; + + m_scoreWidth = 0; + m_extraInfoWidth = 0; + + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + // Grab some control pointers + m_pCTLabel = dynamic_cast<Label *>(FindChildByName("CTScoreLabel")); + m_pCTScore = dynamic_cast<Label *>(FindChildByName("CTScoreValue")); + m_pTerLabel = dynamic_cast<Label *>(FindChildByName("TerScoreLabel")); + m_pTerScore = dynamic_cast<Label *>(FindChildByName("TerScoreValue")); + + m_pTimer = dynamic_cast<Label *>(FindChildByName("timerclock")); + m_pTimerLabel = dynamic_cast<Label *>(FindChildByName("timerlabel")); + + m_pDivider = dynamic_cast<Panel *>(FindChildByName("DividerBar")); + + m_pExtraInfo = dynamic_cast<Label *>(FindChildByName("extrainfo")); +} + +//----------------------------------------------------------------------------- +// Purpose: Resets the list of players +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::UpdateSpectatorPlayerList() +{ + C_Team *cts = GetGlobalTeam( TEAM_CT ); + if ( cts ) + { + wchar_t frags[ 10 ]; + _snwprintf( frags, ARRAYSIZE( frags ), L"%i", cts->Get_Score() ); + + SetLabelText( "CTScoreValue", frags ); + } + + C_Team *ts = GetGlobalTeam( TEAM_TERRORIST ); + if ( ts ) + { + wchar_t frags[ 10 ]; + _snwprintf( frags, ARRAYSIZE( frags ), L"%i", ts->Get_Score() ); + + SetLabelText( "TERScoreValue", frags ); + } +} + +bool CCSSpectatorGUI::NeedsUpdate( void ) +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + if ( !player ) + return false; + + if ( m_nLastAccount != player->GetAccount() ) + return true; + + if ( m_nLastTime != (int)CSGameRules()->GetRoundRemainingTime() ) + return true; + + if ( m_nLastSpecMode != player->GetObserverMode() ) + return true; + + if ( m_nLastSpecTarget != player->GetObserverTarget() ) + return true; + + return BaseClass::NeedsUpdate(); +} + +//============================================================================= +// HPE_BEGIN: +// [smessick] +//============================================================================= +void CCSSpectatorGUI::ShowPanel( bool bShow ) +{ + BaseClass::ShowPanel( bShow ); + + if ( bShow ) + { + // Resend the overview command. + char cmd[32]; + V_snprintf( cmd, sizeof( cmd ), "overview_mode %d\n", overview_preferred_mode.GetInt() ); + engine->ClientCmd( cmd ); + } +} +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: Updates the timer label if one exists +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::UpdateTimer() +{ + // these could be NULL if players modified the UI + if ( !ControlsPresent() ) + return; + + Color timerColor = m_pTimer->GetFgColor(); + if( g_PlantedC4s.Count() > 0 ) + { + m_pTimer->SetText( "\\" ); // bomb icon + m_pTimerLabel->SetVisible( false ); + + if( g_PlantedC4s[0]->m_flNextGlow > gpGlobals->curtime + 0.1f ) + timerColor[3] = 80; + else + timerColor[3] = 255; + + m_pTimer->SetFgColor( timerColor ); + return; + } + + timerColor[3] = 255; + m_pTimer->SetFgColor( timerColor ); + m_pTimer->SetText( "e" ); // clock icon + + m_nLastTime = (int)( CSGameRules()->GetRoundRemainingTime() ); + + if ( m_nLastTime < 0 ) + m_nLastTime = 0; + + wchar_t szText[ 63 ]; + _snwprintf ( szText, ARRAYSIZE( szText ), L"%d:%02d", (m_nLastTime / 60), (m_nLastTime % 60) ); + szText[62] = 0; + + SetLabelText("timerlabel", szText ); + m_pTimerLabel->SetVisible( true ); +} + +void CCSSpectatorGUI::UpdateAccount() +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + + if ( !player ) + return; + + m_nLastAccount = player->GetAccount(); + + if ( (player->GetTeamNumber() == TEAM_TERRORIST) || (player->GetTeamNumber() == TEAM_CT) ) + { + wchar_t szText[ 63 ]; + _snwprintf ( szText, ARRAYSIZE( szText ), L"$%i", m_nLastAccount ); + szText[62] = 0; + + SetLabelText( "extrainfo", szText ); + } +} + + +/*bool CCSSpectatorGUI::CanSpectateTeam( int iTeam ) +{ + bool bRetVal = true; + int iTeamOnly = 0;// TODO = gCSViewPortInterface->GetForceCamera(); + + // if we're not a spectator or HLTV and iTeamOnly is set + if ( C_BasePlayer::GetLocalPlayer()->GetTeamNumber() // && !gEngfuncs.IsSpectateOnly() + && iTeamOnly ) + { + // then we want to force the same team + if ( C_BasePlayer::GetLocalPlayer()->GetTeamNumber() != iTeam ) + { + bRetVal = false; + } + } + + return bRetVal; +}*/ + +void CCSSpectatorGUI::Update() +{ + BaseClass::Update(); + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if( pLocalPlayer ) + { + m_nLastSpecMode = pLocalPlayer->GetObserverMode(); + m_nLastSpecTarget = pLocalPlayer->GetObserverTarget(); + } + + UpdateTimer(); + + UpdateAccount(); + + UpdateSpectatorPlayerList(); + + if ( pLocalPlayer ) + { + ResizeControls(); + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: Save off widths for sizing calculations +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::StoreWidths( void ) +{ + if ( !ControlsPresent() ) + return; + + if ( !m_modifiedWidths ) + { + m_scoreWidth = m_pCTScore->GetWide(); + int terScoreWidth = m_pTerScore->GetWide(); + + m_extraInfoWidth = m_pExtraInfo->GetWide(); + + if ( m_scoreWidth != terScoreWidth ) + { + m_pTerScore = NULL; // We're working with a modified res file. Don't muck things up playing with positioning. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Resize controls so scores & map names are not cut off +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::ResizeControls( void ) +{ + if ( !ControlsPresent() ) + return; + + int x1, y1, w1, t1; + int x2, y2, w2, t2; + + StoreWidths(); + + // ensure scores are wide enough + int wCT, hCT, wTer, hTer; + m_pCTScore->GetBounds( x1, y1, w1, t1 ); + m_pCTScore->GetContentSize( wCT, hCT ); + m_pTerScore->GetBounds( x2, y2, w2, t2 ); + m_pTerScore->GetContentSize( wTer, hTer ); + + int desiredScoreWidth = m_scoreWidth; + desiredScoreWidth = MAX( desiredScoreWidth, wCT ); + desiredScoreWidth = MAX( desiredScoreWidth, wTer ); + + int diff = desiredScoreWidth - w1; + if ( diff != 0 ) + { + m_pCTScore->GetBounds( x1, y1, w1, t1 ); + m_pCTScore->SetBounds( x1 - diff, y1, w1 + diff, t1 ); + + m_pTerScore->GetBounds( x1, y1, w1, t1 ); + m_pTerScore->SetBounds( x1 - diff, y1, w1 + diff, t1 ); + + m_pCTLabel->GetPos( x1, y1 ); + m_pCTLabel->SetPos( x1 - diff, y1 ); + + m_pTerLabel->GetPos( x1, y1 ); + m_pTerLabel->SetPos( x1 - diff, y1 ); + + m_modifiedWidths = true; + } + + // ensure extra info is wide enough + int wExtra, hExtra; + m_pExtraInfo->GetBounds( x1, y1, w1, t1 ); + m_pExtraInfo->GetContentSize( wExtra, hExtra ); + + int desiredExtraWidth = m_extraInfoWidth; + desiredExtraWidth = MAX( desiredExtraWidth, wExtra ); + + diff = desiredExtraWidth - w1; + if ( diff != 0 ) + { + m_pExtraInfo->GetBounds( x1, y1, w1, t1 ); + m_pExtraInfo->SetBounds( x1 - diff, y1, w1 + diff, t1 ); + + m_pTimer->GetPos( x1, y1 ); + m_pTimer->SetPos( x1 - diff, y1 ); + + m_pTimerLabel->GetPos( x1, y1 ); + m_pTimerLabel->SetPos( x1 - diff, y1 ); + + m_pDivider->GetPos( x1, y1 ); + m_pDivider->SetPos( x1 - diff, y1 ); + + m_pCTScore->GetPos( x1, y1 ); + m_pCTScore->SetPos( x1 - diff, y1 ); + + m_pCTLabel->GetPos( x1, y1 ); + m_pCTLabel->SetPos( x1 - diff, y1 ); + + m_pTerScore->GetPos( x1, y1 ); + m_pTerScore->SetPos( x1 - diff, y1 ); + + m_pTerLabel->GetPos( x1, y1 ); + m_pTerLabel->SetPos( x1 - diff, y1 ); + + m_modifiedWidths = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Verify controls are present to resize +//----------------------------------------------------------------------------- +bool CCSSpectatorGUI::ControlsPresent( void ) const +{ + return ( m_pCTLabel != NULL && + m_pCTScore != NULL && + m_pTerLabel != NULL && + m_pTerScore != NULL && + m_pTimer != NULL && + m_pTimerLabel != NULL && + m_pDivider != NULL && + m_pExtraInfo != NULL ); +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static int AdjustValue( int curValue, int targetValue, int amount ) +{ + if ( curValue > targetValue ) + { + curValue -= amount; + + if ( curValue < targetValue ) + curValue = targetValue; + } + else if ( curValue < targetValue ) + { + curValue += amount; + + if ( curValue > targetValue ) + curValue = targetValue; + } + + return curValue; +} + +void CCSMapOverview::InitTeamColorsAndIcons() +{ + BaseClass::InitTeamColorsAndIcons(); + + Q_memset( m_TeamIconsSelf, 0, sizeof(m_TeamIconsSelf) ); + Q_memset( m_TeamIconsDead, 0, sizeof(m_TeamIconsDead) ); + Q_memset( m_TeamIconsOffscreen, 0, sizeof(m_TeamIconsOffscreen) ); + Q_memset( m_TeamIconsDeadOffscreen, 0, sizeof(m_TeamIconsDeadOffscreen) ); + + m_bombIconPlanted = -1; + m_bombIconDropped = -1; + m_bombIconCarried = -1; + m_bombRingPlanted = -1; + m_bombRingDropped = -1; + m_bombRingCarried = -1; + m_bombRingCarriedOffscreen = -1; + m_radioFlash = -1; + m_radioFlashOffscreen = -1; + m_radarTint = -1; + m_hostageFollowing = -1; + m_hostageFollowingOffscreen = -1; + m_playerFacing = -1; + m_cameraIconFirst = -1; + m_cameraIconThird = -1; + m_cameraIconFree = -1; + m_hostageRescueIcon = -1; + m_bombSiteIconA = -1; + m_bombSiteIconB = -1; + + + //setup team red + m_TeamColors[MAP_ICON_T] = COLOR_RED; + m_TeamIcons[MAP_ICON_T] = AddIconTexture( "sprites/player_red_small" ); + m_TeamIconsSelf[MAP_ICON_T] = AddIconTexture( "sprites/player_red_self" ); + m_TeamIconsDead[MAP_ICON_T] = AddIconTexture( "sprites/player_red_dead" ); + m_TeamIconsOffscreen[MAP_ICON_T] = AddIconTexture( "sprites/player_red_offscreen" ); + m_TeamIconsDeadOffscreen[MAP_ICON_T] = AddIconTexture( "sprites/player_red_dead_offscreen" ); + + // setup team blue + m_TeamColors[MAP_ICON_CT] = COLOR_BLUE; + m_TeamIcons[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_small" ); + m_TeamIconsSelf[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_self" ); + m_TeamIconsDead[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_dead" ); + m_TeamIconsOffscreen[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_offscreen" ); + m_TeamIconsDeadOffscreen[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_dead_offscreen" ); + + // setup team other + m_TeamColors[MAP_ICON_HOSTAGE] = COLOR_GREY; + m_TeamIcons[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_small" ); + m_TeamIconsSelf[MAP_ICON_HOSTAGE] = -1; + m_TeamIconsDead[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_dead" ); + m_TeamIconsOffscreen[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_offscreen" ); + m_TeamIconsDeadOffscreen[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_dead_offscreen" ); + + m_bombIconPlanted = AddIconTexture( "sprites/bomb_planted" ); + m_bombIconDropped = AddIconTexture( "sprites/bomb_dropped" ); + m_bombIconCarried = AddIconTexture( "sprites/bomb_carried" ); + + m_bombRingPlanted = AddIconTexture( "sprites/bomb_planted_ring" ); + m_bombRingDropped = AddIconTexture( "sprites/bomb_dropped_ring" ); + m_bombRingCarried = AddIconTexture( "sprites/bomb_carried_ring" ); + m_bombRingCarriedOffscreen = AddIconTexture( "sprites/bomb_carried_ring_offscreen" ); + + m_hostageFollowing = AddIconTexture( "sprites/hostage_following" ); + m_hostageFollowingOffscreen = AddIconTexture( "sprites/hostage_following_offscreen" ); + m_playerFacing = AddIconTexture( "sprites/player_tick" ); + m_cameraIconFirst = AddIconTexture( "sprites/spectator_eye" ); + m_cameraIconThird = AddIconTexture( "sprites/spectator_3rdcam" ); + m_cameraIconFree = AddIconTexture( "sprites/spectator_freecam" ); + m_hostageRescueIcon = AddIconTexture( "sprites/objective_rescue" );; + m_bombSiteIconA = AddIconTexture( "sprites/objective_site_a" );; + m_bombSiteIconB = AddIconTexture( "sprites/objective_site_b" );; + + m_radioFlash = AddIconTexture("sprites/player_radio_ring"); + m_radioFlashOffscreen = AddIconTexture("sprites/player_radio_ring_offscreen"); + + m_radarTint = AddIconTexture("sprites/radar_trans"); + +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::ApplySchemeSettings(vgui::IScheme *scheme) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_hIconFont = scheme->GetFont( "DefaultSmall", true ); +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::Update( void ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer ) + return; + + int team = pPlayer->GetTeamNumber(); + + // if dead with fadetoblack on, we can't show anything + if ( mp_fadetoblack.GetBool() && team > TEAM_SPECTATOR && !pPlayer->IsAlive() ) + { + SetMode( MAP_MODE_OFF ); + return; + } + + bool inRadarMode = (GetMode() == MAP_MODE_RADAR); + int specmode = pPlayer->GetObserverMode(); + // if alive, we can only be in radar mode + if( !inRadarMode && pPlayer->IsAlive()) + { + SetMode( MAP_MODE_RADAR ); + inRadarMode = true; + } + + if( inRadarMode ) + { + if( specmode > OBS_MODE_DEATHCAM ) + { + // If fully dead, we don't want to be radar any more + SetMode( m_playerPreferredMode ); + m_flChangeSpeed = 0; + } + else + { + SetFollowEntity(pPlayer->entindex()); + UpdatePlayers(); + } + } + + BaseClass::Update(); + + if ( GetSpectatorMode() == OBS_MODE_CHASE ) + { + // Follow the local player in chase cam, so the map rotates using the local player's angles + SetFollowEntity( pPlayer->entindex() ); + } +} + +//----------------------------------------------------------------------------- +CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForPlayerIndex( int index ) +{ + if ( index < 0 || index >= MAX_PLAYERS ) + return NULL; + + return &m_PlayersCSInfo[ index ]; +} + +//----------------------------------------------------------------------------- +CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForPlayer(MapPlayer_t *player) +{ + if( player == NULL ) + return NULL; + + for( int i = 0; i < MAX_PLAYERS; i++ ) + { + if( &m_Players[i] == player ) + return &m_PlayersCSInfo[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForHostage(MapPlayer_t *hostage) +{ + if( hostage == NULL ) + return NULL; + + for( int i = 0; i < MAX_HOSTAGES; i++ ) + { + if( &m_Hostages[i] == hostage ) + return &m_HostagesCSInfo[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +#define TIME_SPOTS_STAY_SEEN (0.5f) +#define TIME_UNTIL_ENEMY_SEEN (0.5f) +// rules that define if you can see a player on the overview or not +bool CCSMapOverview::CanPlayerBeSeen( MapPlayer_t *player ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if (!localPlayer || !player ) + return false; + + CSMapPlayer_t *csPlayer = GetCSInfoForPlayer(player); + + if ( !csPlayer ) + return false; + + if( GetMode() == MAP_MODE_RADAR ) + { + // This level will be for all the RadarMode thinking. Base class will be the old way for the other modes. + float now = gpGlobals->curtime; + + if( player->position == Vector(0,0,0) ) + return false; // Invalid guy. + + // draw special icons if within time + if ( csPlayer->overrideExpirationTime != -1 && csPlayer->overrideExpirationTime > gpGlobals->curtime ) + return true; + + // otherwise, not dead people + if( player->health <= 0 ) + return false; + + if( localPlayer->GetTeamNumber() == player->team ) + return true;// always yes for teammates. + + // and a living enemy needs to have been seen recently, and have been for a while + if( csPlayer->timeLastSeen != -1 + && ( now - csPlayer->timeLastSeen < TIME_SPOTS_STAY_SEEN ) + && ( now - csPlayer->timeFirstSeen > TIME_UNTIL_ENEMY_SEEN ) + ) + return true; + + return false; + } + else if( player->health <= 0 ) + { + // Have to be under the overriden icon time to draw when dead. + if ( csPlayer->overrideExpirationTime == -1 || csPlayer->overrideExpirationTime <= gpGlobals->curtime ) + return false; + } + + return BaseClass::CanPlayerBeSeen(player); +} + +bool CCSMapOverview::CanHostageBeSeen( MapPlayer_t *hostage ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !localPlayer || !hostage ) + return false; + + + CSMapPlayer_t *csHostage = GetCSInfoForHostage(hostage); + + if ( !csHostage ) + return false; + + if( GetMode() == MAP_MODE_RADAR ) + { + // This level will be for all the RadarMode thinking. Base class will be the old way for the other modes. + float now = gpGlobals->curtime; + + if( hostage->position == Vector(0,0,0) ) + return false; // Invalid guy. + + // draw special icons if within time + if ( csHostage->overrideExpirationTime != -1 && csHostage->overrideExpirationTime > gpGlobals->curtime ) + return true; + + // otherwise, not dead people + if( hostage->health <= 0 ) + return false; + + if( localPlayer->GetTeamNumber() == hostage->team ) + return true;// always yes for teammates. + + // and a living enemy needs to have been seen recently + if( csHostage->timeLastSeen != -1 && now - csHostage->timeLastSeen < TIME_SPOTS_STAY_SEEN ) + return true; + + return false; + } + else if( hostage->health <= 0 ) + { + // Have to be under the overriden icon time to draw when dead. + if ( csHostage->overrideExpirationTime == -1 || csHostage->overrideExpirationTime <= gpGlobals->curtime ) + return false; + } + + return BaseClass::CanPlayerBeSeen(hostage); +} + +CCSMapOverview::CCSMapOverview( const char *pElementName ) : BaseClass( pElementName ) +{ + m_nRadarMapTextureID = -1; + + g_pMapOverview = this; // for cvars access etc + + // restore non-radar modes + switch ( overview_preferred_mode.GetInt() ) + { + case MAP_MODE_INSET: + m_playerPreferredMode = MAP_MODE_INSET; + break; + + case MAP_MODE_FULL: + m_playerPreferredMode = MAP_MODE_FULL; + break; + + default: + m_playerPreferredMode = MAP_MODE_OFF; + break; + } +} + +void CCSMapOverview::Init( void ) +{ + BaseClass::Init(); + + // register for events as client listener + ListenForGameEvent( "hostage_killed" ); + ListenForGameEvent( "hostage_rescued" ); + ListenForGameEvent( "bomb_defused" ); + ListenForGameEvent( "bomb_exploded" ); +} + +CCSMapOverview::~CCSMapOverview() +{ + g_pMapOverview = NULL; + + //TODO release Textures ? clear lists +} + +void CCSMapOverview::UpdatePlayers() +{ + if( !m_goalIconsLoaded ) + UpdateGoalIcons(); + + UpdateHostages();// Update before players so players can spot them + + UpdateBomb();// Before players so player can properly spot where it is in this update + + UpdateFlashes(); + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + float now = gpGlobals->curtime; + + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( localPlayer == NULL ) + return; + + MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID()); + if( localMapPlayer == NULL ) + return; + + for ( int i = 1; i<= gpGlobals->maxClients; i++) + { + MapPlayer_t *player = &m_Players[i-1]; + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i-1); + + if ( !playerCS ) + continue; + + // update from global player resources + if ( pCSPR->IsConnected(i) ) + { + player->health = pCSPR->GetHealth( i ); + + if ( !pCSPR->IsAlive( i ) ) + { + // Safety actually happens after a TKPunish. + player->health = 0; + playerCS->isDead = true; + } + + if ( player->team != pCSPR->GetTeam( i ) ) + { + player->team = pCSPR->GetTeam( i ); + + if( player == localMapPlayer ) + player->icon = m_TeamIconsSelf[ GetIconNumberFromTeamNumber(player->team) ]; + else + player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ]; + + player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ]; + } + } + + Vector position = player->position; + QAngle angles = player->angle; + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer && !pPlayer->IsDormant() ) + { + // update position of active players in our PVS + position = pPlayer->EyePosition(); + angles = pPlayer->EyeAngles(); + + SetPlayerPositions( i-1, position, angles ); + } + } + + if ( GetMode() == MAP_MODE_RADAR ) + { + // Check for teammates spotting the bomb + if ( m_bomb.state != CSMapBomb_t::BOMB_INVALID && pCSPR->IsBombSpotted() ) + { + SetBombSeen( true ); + } + + // Check for teammates spotting enemy players + for ( int i = 1; i<= gpGlobals->maxClients; ++i ) + { + if ( !pCSPR->IsConnected(i) ) + continue; + + if ( !pCSPR->IsAlive(i) ) + continue; + + if ( pCSPR->GetTeam(i) == localMapPlayer->team ) + continue; + + if ( pCSPR->IsPlayerSpotted(i) ) + { + SetPlayerSeen( i-1 ); + + MapPlayer_t *baseEnemy = &m_Players[i-1]; + if( baseEnemy->health > 0 ) + { + // They were just seen, so if they are alive get rid of their "last known" icon + CSMapPlayer_t *csEnemy = GetCSInfoForPlayerIndex(i-1); + + if ( csEnemy ) + { + csEnemy->overrideIcon = -1; + csEnemy->overrideIconOffscreen = -1; + csEnemy->overridePosition = Vector(0, 0, 0); + csEnemy->overrideAngle = QAngle(0, 0, 0); + csEnemy->overrideFadeTime = -1; + csEnemy->overrideExpirationTime = -1; + } + } + } + } + + for( int i = 1; i<= gpGlobals->maxClients; i++ ) + { + MapPlayer_t *player = &m_Players[i-1]; + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i-1); + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer || !playerCS ) + continue; + + float timeSinceLastSeen = now - playerCS->timeLastSeen; + if( timeSinceLastSeen < 0.25f ) + continue; + if( player->health <= 0 ) + continue;// We don't need to spot dead guys, since they always show + if( player->team == localMapPlayer->team ) + continue;// We don't need to spot our own guys + + // Now that everyone has had a say on people they can see for us, go through and handle baddies that can no longer be seen. + if( playerCS->timeLastSeen != now && player->health > 0 ) + { + // We are not seen now, but if we were seen recently (and for long enough), + // put up a "last known" icon and clear timelastseen + // if they are alive. Death icon is more important, which is why the health check above. + if( timeSinceLastSeen < 0.5f && ( playerCS->timeLastSeen != -1 ) ) + { + if( now - playerCS->timeFirstSeen > TIME_UNTIL_ENEMY_SEEN ) + { + playerCS->overrideIcon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];; + playerCS->overrideIconOffscreen = m_TeamIconsOffscreen[ GetIconNumberFromTeamNumber(player->team) ]; + playerCS->overridePosition = player->position; + playerCS->overrideFadeTime = -1; + playerCS->overrideExpirationTime = now + LAST_SEEN_ICON_DURATION; + playerCS->overrideAngle = player->angle; + } + playerCS->timeLastSeen = -1; + playerCS->timeFirstSeen = -1; + } + } + } + } +} + +void CCSMapOverview::UpdateHostages() +{ + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + for( int i=0; i < MAX_HOSTAGES; i++ ) + { + if( pCSPR->IsHostageAlive( i ) ) + { + MapPlayer_t *hostage = GetHostageByEntityID( pCSPR->GetHostageEntityID(i) ); + if( hostage == NULL ) + hostage = &m_Hostages[i];// Don't have entry yet, so need one. This'll only happen once, at start of map + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + return; + + if( !hostageCS->isDead ) + { + hostage->index = pCSPR->GetHostageEntityID(i); + hostage->position = pCSPR->GetHostagePosition( i ); + hostage->health = 100; // Hostages don't have health available from pCSPR. + hostage->angle = QAngle(0, 0, 0); // No facing, like no health + hostage->team = TEAM_CT; // CT in terms of who sees them + hostage->icon = m_TeamIcons[ MAP_ICON_HOSTAGE ]; // But hostage for icon. + hostage->color = m_TeamColors[ MAP_ICON_HOSTAGE ]; + hostageCS->isHostage = true; + +// engine->Con_NPrintf( i + 15, "ID:%d Pos:(%.0f,%.0f,%.0f)", hostage->index, hostage->position.x, hostage->position.y, hostage->position.z ); + } + else + { +// engine->Con_NPrintf( i + 15, "Mostly Dead" ); + } + } + else + { +// engine->Con_NPrintf( i + 15, "Dead" ); + } + } +} + +void CCSMapOverview::UpdateBomb() +{ + if( m_bomb.state == CSMapBomb_s::BOMB_GONE ) + return;// no more updates until map restart + + float now = gpGlobals->curtime; + + // First, decide if it has been too long since the bomb has been seen to clear visibility timers. + if( now - m_bomb.timeLastSeen >= TIME_SPOTS_STAY_SEEN && m_bomb.timeFirstSeen != -1 ) + { + SetBombSeen( false ); + } + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + float biggestRadius = 0, smallestRadius = 0; + if ( g_PlantedC4s.Count() > 0 ) + { + // bomb is planted + C_PlantedC4 *pC4 = g_PlantedC4s[0]; + + if( pC4->IsBombActive() ) + { + m_bomb.position = pC4->GetAbsOrigin(); + m_bomb.state = CSMapBomb_t::BOMB_PLANTED; + m_bomb.ringTravelTime = 3.0f; + smallestRadius = m_flIconSize; + biggestRadius = m_flIconSize * 15.0f; + } + else + { + // Defused or exploded + m_bomb.state = CSMapBomb_t::BOMB_GONE; + } + } + else if ( pCSPR->HasC4( 0 ) ) + { + // bomb dropped + Vector pos = pCSPR->GetC4Postion(); + + if ( pos.x != 0 || pos.y != 0 || pos.z != 0 ) + { + m_bomb.position = pos; + m_bomb.state = CSMapBomb_t::BOMB_DROPPED; + m_bomb.ringTravelTime = 6.0f; + smallestRadius = m_flIconSize; + biggestRadius = m_flIconSize * 10.0f; + } + else + { + m_bomb.state = CSMapBomb_t::BOMB_INVALID; + //Not a bomb map. Man, what a weird system instead of IsBombMap. If nobody has it, and it isn't on the ground, then it isn't a bomb map. + } + } + else + { + for( int i = 1; i<= gpGlobals->maxClients; i++ ) + { + if( pCSPR->HasC4(i) ) + { + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if( pPlayer == NULL || pPlayer->IsDormant() ) + { + // Dormant or no player means we are relying on RadarUpdate messages so we can trust the MapOverview position. + MapPlayer_t *player = &m_Players[i-1]; + m_bomb.position = player->position; + } + else + { + // Update players is about to put this Real Data in the player sturct, and we don't want the bomb pos lagged one update behind + m_bomb.position = pPlayer->GetAbsOrigin(); + } + + m_bomb.state = CSMapBomb_t::BOMB_CARRIED; + m_bomb.ringTravelTime = 0; + smallestRadius = m_flIconSize * 1.2f; + biggestRadius = m_flIconSize * 1.2f; + break; + } + } + } + + int alpha = GetMasterAlpha(); + + if( m_bomb.currentRingRadius == m_bomb.maxRingRadius || m_bomb.ringTravelTime == 0 ) + { + m_bomb.currentRingRadius = smallestRadius; + m_bomb.maxRingRadius = biggestRadius; + m_bomb.currentRingAlpha = alpha; + } + else + { + m_bomb.currentRingRadius += (m_bomb.maxRingRadius - m_flIconSize) * gpGlobals->frametime / m_bomb.ringTravelTime; + m_bomb.currentRingRadius = MIN( m_bomb.currentRingRadius, m_bomb.maxRingRadius ); + m_bomb.currentRingAlpha = (alpha - 55) * ((m_bomb.maxRingRadius - m_bomb.currentRingRadius) / (m_bomb.maxRingRadius - m_flIconSize)) + 55; + } +} + +bool CCSMapOverview::ShouldDraw( void ) +{ + int alpha = GetMasterAlpha(); + if( alpha == 0 ) + return false;// we have been set to fully transparent + + //============================================================================= + // HPE_BEGIN: + // [smessick] Turn off large map display when in freezecam. + //============================================================================= + if ( IsInFreezeCam() && GetMode() == MAP_MODE_FULL ) + { + return false; + } + //============================================================================= + // HPE_END + //============================================================================= + + float now = gpGlobals->curtime; + if( GetMode() == MAP_MODE_RADAR ) + { + if ( (GET_HUDELEMENT( CHudRadar ))->ShouldDraw() == false ) + { + return false; + } + + // We have to be alive and not blind to draw in this mode. + C_CSPlayer *pCSPlayer = C_CSPlayer::GetLocalCSPlayer(); + if( !pCSPlayer || pCSPlayer->GetObserverMode() == OBS_MODE_DEATHCAM ) + { + return false; + } + else if (pCSPlayer->m_flFlashBangTime > now) + { + return false; + } + } + + return BaseClass::ShouldDraw(); +} + +CCSMapOverview::MapPlayer_t* CCSMapOverview::GetHostageByEntityID( int entityID ) +{ + for (int i=0; i<MAX_HOSTAGES; i++) + { + if ( m_Hostages[i].index == entityID ) + return &m_Hostages[i]; + } + + return NULL; +} + +CCSMapOverview::MapPlayer_t* CCSMapOverview::GetPlayerByEntityID( int entityID ) +{ + C_BasePlayer *realPlayer = UTIL_PlayerByIndex(entityID); + + if( realPlayer == NULL ) + return NULL; + + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *player = &m_Players[i]; + + if ( player->userid == realPlayer->GetUserID() ) + return player; + } + + return NULL; +} + +#define BORDER_WIDTH 4 +bool CCSMapOverview::AdjustPointToPanel(Vector2D *pos) +{ + if( pos == NULL ) + return false; + + int mapInset = GetBorderSize();// This gives us the amount inside the panel that the background is drawing. + if( mapInset != 0 ) + mapInset += BORDER_WIDTH; // And this gives us the border inside the map edge to give us room for offscreen icons. + + int x,y,w,t; + + //MapTpPanel has already offset the x and y. That's why we use 0 for left and top. + GetBounds( x,y,w,t ); + + bool madeChange = false; + if( pos->x < mapInset ) + { + pos->x = mapInset; + madeChange = true; + } + if( pos->x > w - mapInset ) + { + pos->x = w - mapInset; + madeChange = true; + } + if( pos->y < mapInset ) + { + pos->y = mapInset; + madeChange = true; + } + if( pos->y > t - mapInset ) + { + pos->y = t - mapInset; + madeChange = true; + } + + return madeChange; +} + +void CCSMapOverview::DrawMapTexture() +{ + int alpha = GetMasterAlpha(); + + if( GetMode() == MAP_MODE_FULL ) + SetBgColor( Color(0,0,0,0) );// no background in big mode + else + SetBgColor( Color(0,0,0,alpha * 0.5) ); + + int textureIDToUse = m_nMapTextureID; + bool foundRadarVersion = false; + if( m_nRadarMapTextureID != -1 && GetMode() == MAP_MODE_RADAR ) + { + textureIDToUse = m_nRadarMapTextureID; + foundRadarVersion = true; + } + + int mapInset = GetBorderSize(); + int pwidth, pheight; + GetSize(pwidth, pheight); + + if ( textureIDToUse > 0 ) + { + // We are drawing to the whole panel with a little border + Vector2D panelTL = Vector2D( mapInset, mapInset ); + Vector2D panelTR = Vector2D( pwidth - mapInset, mapInset ); + Vector2D panelBR = Vector2D( pwidth - mapInset, pheight - mapInset ); + Vector2D panelBL = Vector2D( mapInset, pheight - mapInset ); + + // So where are those four points on the great big map? + Vector2D textureTL = PanelToMap( panelTL );// The top left corner of the display is where on the master map? + textureTL /= OVERVIEW_MAP_SIZE;// Texture Vec2D is 0 to 1 + Vector2D textureTR = PanelToMap( panelTR ); + textureTR /= OVERVIEW_MAP_SIZE; + Vector2D textureBR = PanelToMap( panelBR ); + textureBR /= OVERVIEW_MAP_SIZE; + Vector2D textureBL = PanelToMap( panelBL ); + textureBL /= OVERVIEW_MAP_SIZE; + + Vertex_t points[4] = + { + // To draw a textured polygon, the first column is where you want to draw (to), and the second is what you want to draw (from). + // We want to draw to the panel (pulled in for a border), and we want to draw the part of the map texture that should be seen. + // First column is in panel coords, second column is in 0-1 texture coords + Vertex_t( panelTL, textureTL ), + Vertex_t( panelTR, textureTR ), + Vertex_t( panelBR, textureBR ), + Vertex_t( panelBL, textureBL ) + }; + + surface()->DrawSetColor( 255, 255, 255, alpha ); + surface()->DrawSetTexture( textureIDToUse ); + surface()->DrawTexturedPolygon( 4, points ); + } + + // If we didn't find the greenscale version of the map, then at least do a tint. + if( !foundRadarVersion && GetMode() == MAP_MODE_RADAR ) + { + surface()->DrawSetColor( 0,255,0, alpha / 4 ); + surface()->DrawFilledRect( mapInset, mapInset, m_vSize.x - 1 - mapInset, m_vSize.y - 1 - mapInset ); + } +} + +void CCSMapOverview::DrawBomb() +{ + if( m_bomb.state == CSMapBomb_t::BOMB_INVALID ) + return; + + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( localPlayer == NULL ) + return; + MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID()); + if( localMapPlayer == NULL ) + return; + float now = gpGlobals->curtime; + + if( localMapPlayer->team == TEAM_CT ) + { + // CT's only get to see it if... + + if( localMapPlayer->health <= 0 ) + { + if ( mp_forcecamera.GetInt() != OBS_ALLOW_ALL ) + return;// They're dead and spectating isn't restricted + } + else if( (m_bomb.timeLastSeen == -1) + || ( now - m_bomb.timeLastSeen >= TIME_SPOTS_STAY_SEEN ) + || ( now - m_bomb.timeFirstSeen < TIME_UNTIL_ENEMY_SEEN ) + ) + { + return;// It's in view + } + } + // else if you aren't CT you can always see it + + int bombIcon; + int bombRing; + int bombRingOffscreen; + switch(m_bomb.state) + { + case CSMapBomb_t::BOMB_DROPPED: + { + bombIcon = m_bombIconDropped; + bombRing = m_bombRingDropped; + bombRingOffscreen = m_bombRingDropped; + break; + } + case CSMapBomb_t::BOMB_CARRIED: + { + bombIcon = m_bombIconCarried; + bombRing = m_bombRingCarried; + bombRingOffscreen = m_bombRingCarriedOffscreen; + break; + } + case CSMapBomb_t::BOMB_PLANTED: + { + bombIcon = m_bombIconPlanted; + bombRing = m_bombRingPlanted; + bombRingOffscreen = m_bombRingPlanted; + break; + } + case CSMapBomb_t::BOMB_GONE: + { + bombIcon = m_bombIconPlanted; + bombRing = m_bombRingPlanted; + bombRingOffscreen = m_bombRingPlanted; + break; + } + default: + return; + } + + int alpha = 255; + + if( m_bomb.timeGone != -1 && m_bomb.timeFade <= gpGlobals->curtime ) + alpha *= 1 - ( (float)(gpGlobals->curtime - m_bomb.timeFade) / (float)(m_bomb.timeGone - m_bomb.timeFade) ); + + if( m_bomb.state != CSMapBomb_t::BOMB_GONE ) + DrawIconCS(bombRing, bombRingOffscreen, m_bomb.position, m_bomb.currentRingRadius, 0, m_bomb.currentRingAlpha); + DrawIconCS(bombIcon, bombIcon, m_bomb.position, m_flIconSize, 0, alpha); +} + +bool CCSMapOverview::DrawIconCS( int textureID, int offscreenTextureID, Vector pos, float scale, float angle, int alpha, bool allowRotation, const char *text, Color *textColor, float status, Color *statusColor ) +{ + if( GetMode() == MAP_MODE_RADAR && cl_radaralpha.GetInt() == 0 ) + return false; + + if( alpha <= 0 ) + return false; + + Vector2D pospanel = WorldToMap( pos ); + pospanel = MapToPanel( pospanel ); + + int idToUse = textureID; + float angleToUse = angle; + + Vector2D oldPos = pospanel; + Vector2D adjustment(0,0); + if( AdjustPointToPanel( &pospanel ) ) + { + if( offscreenTextureID == -1 ) + return false; //Doesn't want to draw if off screen. + + // Move it in to on panel, and change the icon. + idToUse = offscreenTextureID; + // And point towards the original spot + adjustment = Vector2D(pospanel.x - oldPos.x, pospanel.y - oldPos.y); + QAngle adjustmentAngles; + Vector adjustment3D(adjustment.x, -adjustment.y, 0); // Y gets flipped in WorldToMap + VectorAngles(adjustment3D, adjustmentAngles) ; + if( allowRotation ) + { + // Some icons don't want to rotate even when off radar + angleToUse = adjustmentAngles[YAW]; + + // And the angle needs to be in world space, not panel space. + if( m_bFollowAngle ) + { + angleToUse += m_fViewAngle; + } + else + { + if ( m_bRotateMap ) + angleToUse += 180.0f; + else + angleToUse += 90.0f; + } + } + + // Don't draw names for icons that are offscreen (bunches up and looks bad) + text = NULL; + } + + int d = GetPixelOffset( scale ); + + Vector offset; + + offset.x = -scale; offset.y = scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos1 = WorldToMap( pos + offset ); + Vector2D pos1Panel = MapToPanel(pos1); + pos1Panel.x += adjustment.x; + pos1Panel.y += adjustment.y; + + offset.x = scale; offset.y = scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos2 = WorldToMap( pos + offset ); + Vector2D pos2Panel = MapToPanel(pos2); + pos2Panel.x += adjustment.x; + pos2Panel.y += adjustment.y; + + offset.x = scale; offset.y = -scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos3 = WorldToMap( pos + offset ); + Vector2D pos3Panel = MapToPanel(pos3); + pos3Panel.x += adjustment.x; + pos3Panel.y += adjustment.y; + + offset.x = -scale; offset.y = -scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos4 = WorldToMap( pos + offset ); + Vector2D pos4Panel = MapToPanel(pos4); + pos4Panel.x += adjustment.x; + pos4Panel.y += adjustment.y; + + Vertex_t points[4] = + { + Vertex_t( pos1Panel, Vector2D(0,0) ), + Vertex_t( pos2Panel, Vector2D(1,0) ), + Vertex_t( pos3Panel, Vector2D(1,1) ), + Vertex_t( pos4Panel, Vector2D(0,1) ) + }; + + surface()->DrawSetColor( 255, 255, 255, alpha ); + surface()->DrawSetTexture( idToUse ); + surface()->DrawTexturedPolygon( 4, points ); + + pospanel.y += d + 4; + + if ( status >=0.0f && status <= 1.0f && statusColor ) + { + // health bar is 50x3 pixels + surface()->DrawSetColor( 0,0,0,255 ); + surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x+d, pospanel.y+1 ); + + int length = (float)(d*2)*status; + surface()->DrawSetColor( statusColor->r(), statusColor->g(), statusColor->b(), 255 ); + surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x-d+length, pospanel.y+1 ); + + pospanel.y += 3; + } + + if ( text && textColor ) + { + wchar_t iconText[ MAX_PLAYER_NAME_LENGTH*2 ]; + + g_pVGuiLocalize->ConvertANSIToUnicode( text, iconText, sizeof( iconText ) ); + + int wide, tall; + surface()->GetTextSize( m_hIconFont, iconText, wide, tall ); + + int x = pospanel.x-(wide/2); + int y = pospanel.y; + + // draw black shadow text + surface()->DrawSetTextColor( 0, 0, 0, 255 ); + surface()->DrawSetTextPos( x+1, y ); + surface()->DrawPrintText( iconText, wcslen(iconText) ); + + // draw name in color + surface()->DrawSetTextColor( textColor->r(), textColor->g(), textColor->b(), 255 ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawPrintText( iconText, wcslen(iconText) ); + } + + return true; +} + +void CCSMapOverview::DrawMapPlayers() +{ + DrawGoalIcons(); + DrawHostages(); + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + surface()->DrawSetTextFont( m_hIconFont ); + + Color colorGreen( 0, 255, 0, 255 ); // health bar color + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + for (int i=0; i < MAX_PLAYERS; i++) + { + int alpha = 255; + MapPlayer_t *player = &m_Players[i]; + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i); + + if ( !playerCS ) + continue; + + if ( !CanPlayerBeSeen( player ) ) + continue; + + float status = -1; + const char *name = NULL; + + if ( m_bShowNames && CanPlayerNameBeSeen( player ) ) + name = player->name; + + if ( m_bShowHealth && CanPlayerHealthBeSeen( player ) ) + status = player->health/100.0f; + + // Now draw them + if( playerCS->overrideExpirationTime > gpGlobals->curtime )// If dead, an X, if alive, an alpha'd normal icon + { + int alphaToUse = alpha; + if( playerCS->overrideFadeTime != -1 && playerCS->overrideFadeTime <= gpGlobals->curtime ) + { + // Fade linearly from fade start to disappear + alphaToUse *= 1 - (float)(gpGlobals->curtime - playerCS->overrideFadeTime) / (float)(playerCS->overrideExpirationTime - playerCS->overrideFadeTime); + } + + DrawIconCS( playerCS->overrideIcon, playerCS->overrideIconOffscreen, playerCS->overridePosition, m_flIconSize * 1.1f, GetViewAngle(), player->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &player->color, -1, &colorGreen ); + if( player->health > 0 ) + DrawIconCS( m_playerFacing, -1, playerCS->overridePosition, m_flIconSize * 1.1f, playerCS->overrideAngle[YAW], player->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &player->color, status, &colorGreen ); + } + else + { + float zDifference = 0; + if( localPlayer ) + { + if( (localPlayer->GetObserverMode() != OBS_MODE_NONE) && localPlayer->GetObserverTarget() ) + zDifference = player->position.z - localPlayer->GetObserverTarget()->GetAbsOrigin().z; + else + zDifference = player->position.z - localPlayer->GetAbsOrigin().z; + } + + float sizeForRing = m_flIconSize * 1.4f; + float sizeForPlayer = m_flIconSize * 1.1f; // The 1.1 is because the player dots are shrunken a little, so their facing pip can have some space to live + if ( zDifference > DIFFERENCE_THRESHOLD ) + { + // A dot above is bigger and a little fuzzy now. + sizeForRing *= 1.4f; + sizeForPlayer *= 1.4f; + alpha *= 0.5f; + } + else if ( zDifference < -DIFFERENCE_THRESHOLD ) + { + // A dot below is smaller. + sizeForRing *= 0.7f; + sizeForPlayer *= 0.7f; + } + + bool showTalkRing = localPlayer && (localPlayer->GetTeamNumber() == player->team || localPlayer->GetTeamNumber() == TEAM_SPECTATOR); + + if( showTalkRing && playerCS->currentFlashAlpha > 0 )// Flash type + { + // Make them flash a halo + DrawIconCS(m_radioFlash, m_radioFlashOffscreen, player->position, sizeForRing, player->angle[YAW], playerCS->currentFlashAlpha); + } + else if( showTalkRing && pCSPR->IsAlive( i + 1 ) && GetClientVoiceMgr()->IsPlayerSpeaking( i + 1) ) // Or solid on type + { + // Make them show a halo + DrawIconCS(m_radioFlash, m_radioFlashOffscreen, player->position, sizeForRing, player->angle[YAW], 255); + } + + bool doingLocalPlayer = GetPlayerByUserID(localPlayer->GetUserID()) == player; + float angleForPlayer = GetViewAngle(); + + if( doingLocalPlayer ) + { + sizeForPlayer *= 4.0f; // The self icon is really big since it has a camera view cone attached. + angleForPlayer = player->angle[YAW];// And, the self icon now rotates, natch. + } + + int offscreenIcon = m_TeamIconsOffscreen[GetIconNumberFromTeamNumber(player->team)]; + DrawIconCS( player->icon, offscreenIcon, player->position, sizeForPlayer, angleForPlayer, alpha, true, name, &player->color, status, &colorGreen ); + if( !doingLocalPlayer ) + { + // Draw the facing for everyone but the local player. + if( player->health > 0 ) + DrawIconCS( m_playerFacing, -1, player->position, sizeForPlayer, player->angle[YAW], alpha, true, name, &player->color, status, &colorGreen ); + } + } + } + + DrawBomb();// After players so it can draw on top +} + +void CCSMapOverview::DrawHostages() +{ + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + surface()->DrawSetTextFont( m_hIconFont ); + + Color colorGreen( 0, 255, 0, 255 ); // health bar color + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + for (int i=0; i < MAX_HOSTAGES; i++) + { + int alpha = 255; + MapPlayer_t *hostage = GetHostageByEntityID( pCSPR->GetHostageEntityID(i) ); + if( hostage == NULL ) + continue; + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + continue; + + if ( !CanHostageBeSeen( hostage ) ) + { +// engine->Con_NPrintf( i + 30, "Can't be seen." ); + continue; + } + + float status = -1; + const char *name = NULL; + + if( hostageCS->overrideExpirationTime > gpGlobals->curtime )// If dead, an X, if alive, an alpha'd normal icon + { +// engine->Con_NPrintf( i + 30, "ID:%d Override Pos:(%.0f,%.0f,%.0f)", hostage->index, hostageCS->overridePosition.x, hostageCS->overridePosition.y, hostageCS->overridePosition.z ); + int alphaToUse = alpha; + if( hostageCS->overrideFadeTime != -1 && hostageCS->overrideFadeTime <= gpGlobals->curtime ) + { + // Fade linearly from fade start to disappear + alphaToUse *= 1 - (float)(gpGlobals->curtime - hostageCS->overrideFadeTime) / (float)(hostageCS->overrideExpirationTime - hostageCS->overrideFadeTime); + } + + DrawIconCS( hostageCS->overrideIcon, hostageCS->overrideIconOffscreen, hostageCS->overridePosition, m_flIconSize, hostageCS->overrideAngle[YAW], hostage->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &hostage->color, status, &colorGreen ); + } + else + { + if( localPlayer && localPlayer->GetTeamNumber() == hostage->team && hostageCS->currentFlashAlpha > 0 ) + { + // Make them flash a halo + DrawIconCS(m_radioFlash, m_radioFlashOffscreen, hostage->position, m_flIconSize * 1.4f, hostage->angle[YAW], hostageCS->currentFlashAlpha); + } + +// engine->Con_NPrintf( i + 30, "ID:%d Pos:(%.0f,%.0f,%.0f)", hostage->index, hostage->position.x, hostage->position.y, hostage->position.z ); + int normalIcon, offscreenIcon; + float zDifference = 0; + if( localPlayer ) + { + if( (localPlayer->GetObserverMode() != OBS_MODE_NONE) && localPlayer->GetObserverTarget() ) + zDifference = hostage->position.z - localPlayer->GetObserverTarget()->GetAbsOrigin().z; + else + zDifference = hostage->position.z - localPlayer->GetAbsOrigin().z; + } + + float sizeForHostage = m_flIconSize; + if( zDifference > DIFFERENCE_THRESHOLD ) + { + // A dot above is bigger and a little fuzzy now. + sizeForHostage = m_flIconSize * 1.5f; + alpha *= 0.5f; + } + else if( zDifference < -DIFFERENCE_THRESHOLD ) + { + // A dot below is smaller. + sizeForHostage = m_flIconSize * 0.6f; + } + + normalIcon = hostage->icon; + offscreenIcon = m_TeamIconsOffscreen[ MAP_ICON_HOSTAGE ]; + DrawIconCS( normalIcon, offscreenIcon, hostage->position, sizeForHostage, GetViewAngle(), alpha, true, name, &hostage->color, status, &colorGreen ); + + if( pCSPR->IsHostageFollowingSomeone( i ) ) + { + // If they are following a CT, then give them a little extra symbol to show it. + DrawIconCS( m_hostageFollowing, m_hostageFollowingOffscreen, hostage->position, sizeForHostage, hostage->angle[YAW], alpha ); + } + } + } +} +void CCSMapOverview::SetMap(const char * levelname) +{ + BaseClass::SetMap(levelname); + + int wide, tall; + surface()->DrawGetTextureSize( m_nMapTextureID, wide, tall ); + if( wide == 0 && tall == 0 ) + { + m_nMapTextureID = -1; + m_nRadarMapTextureID = -1; + return;// No map image, so no radar image + } + + char radarFileName[MAX_PATH]; + Q_snprintf(radarFileName, MAX_PATH, "%s_radar", m_MapKeyValues->GetString("material")); + m_nRadarMapTextureID = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile(m_nRadarMapTextureID, radarFileName, true, false); + int radarWide = -1; + int radarTall = -1; + surface()->DrawGetTextureSize(m_nRadarMapTextureID, radarWide, radarTall); + bool radarTextureFound = false; + if( radarWide == wide && radarTall == tall ) + { + // Unbelievable that these is no failure return from SetTextureFile, and not + // even a ValidTexture check on the ID. So I can check if it is different from + // the original. It'll be a 32x32 default if not present. + radarTextureFound = true; + } + + if( !radarTextureFound ) + { + if( !CreateRadarImage(m_MapKeyValues->GetString("material"), radarFileName) ) + m_nRadarMapTextureID = -1; + } + + ClearGoalIcons(); +} + +bool CCSMapOverview::CreateRadarImage(const char *mapName, const char * radarFileName) +{ +#ifdef GENERATE_RADAR_FILE + char fullFileName[MAX_PATH]; + Q_snprintf(fullFileName, MAX_PATH, "materials/%s.vtf", mapName); + char fullRadarFileName[MAX_PATH]; + Q_snprintf(fullRadarFileName, MAX_PATH, "materials/%s.vtf", radarFileName); + + // Not found, so try to make one + FileHandle_t fp; + fp = ::filesystem->Open( fullFileName, "rb" ); + if( !fp ) + { + return false; + } + ::filesystem->Seek( fp, 0, FILESYSTEM_SEEK_TAIL ); + int srcVTFLength = ::filesystem->Tell( fp ); + ::filesystem->Seek( fp, 0, FILESYSTEM_SEEK_HEAD ); + + CUtlBuffer buf; + buf.EnsureCapacity( srcVTFLength ); + int overviewMapBytesRead = ::filesystem->Read( buf.Base(), srcVTFLength, fp ); + ::filesystem->Close( fp ); + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );// Need to set these explicitly since ->Read goes straight to memory and skips them. + buf.SeekPut( CUtlBuffer::SEEK_HEAD, overviewMapBytesRead ); + + IVTFTexture *radarTexture = CreateVTFTexture(); + if (radarTexture->Unserialize(buf)) + { + ImageFormat oldImageFormat = radarTexture->Format(); + radarTexture->ConvertImageFormat(IMAGE_FORMAT_RGBA8888, false); + unsigned char *imageData = radarTexture->ImageData(0,0,0); + int size = radarTexture->ComputeTotalSize(); // in bytes! + unsigned char *pEnd = imageData + size; + + for( ; imageData < pEnd; imageData += 4 ) + { + imageData[0] = 0; // R + imageData[2] = 0; // B + } + + radarTexture->ConvertImageFormat(oldImageFormat, false); + + buf.Clear(); + radarTexture->Serialize(buf); + + fp = ::filesystem->Open(fullRadarFileName, "wb"); + ::filesystem->Write(buf.Base(), buf.TellPut(), fp); + ::filesystem->Close(fp); + DestroyVTFTexture(radarTexture); + buf.Purge(); + + // And need a vmt file to go with it. + char vmtFilename[MAX_PATH]; + Q_snprintf(vmtFilename, MAX_PATH, "%s", fullRadarFileName); + char *extension = &vmtFilename[Q_strlen(vmtFilename) - 3]; + *extension++ = 'v'; + *extension++ = 'm'; + *extension++ = 't'; + *extension++ = '\0'; + fp = ::filesystem->Open(vmtFilename, "wt"); + ::filesystem->Write("\"UnlitGeneric\"\n", 15, fp); + ::filesystem->Write("{\n", 2, fp); + ::filesystem->Write("\t\"$translucent\" \"1\"\n", 20, fp); + ::filesystem->Write("\t\"$basetexture\" \"", 17, fp); + ::filesystem->Write(radarFileName, Q_strlen(radarFileName), fp); + ::filesystem->Write("\"\n", 2, fp); + ::filesystem->Write("\t\"$vertexalpha\" \"1\"\n", 20, fp); + ::filesystem->Write("\t\"$vertexcolor\" \"1\"\n", 20, fp); + ::filesystem->Write("\t\"$no_fullbright\" \"1\"\n", 22, fp); + ::filesystem->Write("\t\"$ignorez\" \"1\"\n", 16, fp); + ::filesystem->Write("}\n", 2, fp); + ::filesystem->Close(fp); + + m_nRadarMapTextureID = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( m_nRadarMapTextureID, radarFileName, true, true); + return true; + } +#endif + return false; +} + +void CCSMapOverview::ResetRound() +{ + BaseClass::ResetRound(); + + for (int i=0; i<MAX_PLAYERS; i++) + { + CSMapPlayer_t *p = &m_PlayersCSInfo[i]; + + p->isDead = false; + + p->overrideFadeTime = -1; + p->overrideExpirationTime = -1; + p->overrideIcon = -1; + p->overrideIconOffscreen = -1; + p->overridePosition = Vector( 0, 0, 0); + p->overrideAngle = QAngle(0, 0, 0); + + p->timeLastSeen = -1; + p->timeFirstSeen = -1; + p->isHostage = false; + + p->flashUntilTime = -1; + p->nextFlashPeakTime = -1; + p->currentFlashAlpha = 0; + } + + for (int i=0; i<MAX_HOSTAGES; i++) + { + MapPlayer_t *basep = &m_Hostages[i]; + CSMapPlayer_t *p = &m_HostagesCSInfo[i]; + + basep->health = 100; + Q_memset( basep->trail, 0, sizeof(basep->trail) ); + basep->position = Vector( 0, 0, 0 ); + basep->index = 0; + + p->isDead = false; + + p->overrideFadeTime = -1; + p->overrideExpirationTime = -1; + p->overrideIcon = -1; + p->overrideIconOffscreen = -1; + p->overridePosition = Vector( 0, 0, 0); + p->overrideAngle = QAngle(0, 0, 0); + + p->timeLastSeen = -1; + p->timeFirstSeen = -1; + p->isHostage = false; + + p->flashUntilTime = -1; + p->nextFlashPeakTime = -1; + p->currentFlashAlpha = 0; + } + + m_bomb.position = Vector(0,0,0); + m_bomb.state = CSMapBomb_t::BOMB_INVALID; + m_bomb.timeLastSeen = -1; + m_bomb.timeFirstSeen = -1; + m_bomb.timeFade = -1; + m_bomb.timeGone = -1; + + m_bomb.currentRingRadius = -1; + m_bomb.currentRingAlpha = -1; + m_bomb.maxRingRadius = -1; + m_bomb.ringTravelTime = -1; + + m_goalIconsLoaded = false; +} + +void CCSMapOverview::DrawCamera() +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if (!localPlayer) + return; + + if( localPlayer->GetObserverMode() == OBS_MODE_ROAMING ) + { + // Instead of the programmer-art red dot, we'll draw an icon for when our camera is roaming. + int alpha = 255; + DrawIconCS(m_cameraIconFree, m_cameraIconFree, localPlayer->GetAbsOrigin(), m_flIconSize * 3.0f, localPlayer->EyeAngles()[YAW], alpha); + } + else if( localPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + if( localPlayer->GetObserverTarget() ) + { + // Fade it if it is on top of a player dot. And don't rotate it. + int alpha = 255 * 0.5f; + DrawIconCS(m_cameraIconFirst, m_cameraIconFirst, localPlayer->GetObserverTarget()->GetAbsOrigin(), m_flIconSize * 1.5f, GetViewAngle(), alpha); + } + } + else if( localPlayer->GetObserverMode() == OBS_MODE_CHASE ) + { + if( localPlayer->GetObserverTarget() ) + { + // Or Draw the third-camera a little bigger. (Needs room to be off the dot being followed) + int alpha = 255; + DrawIconCS(m_cameraIconThird, m_cameraIconThird, localPlayer->GetObserverTarget()->GetAbsOrigin(), m_flIconSize * 3.0f, localPlayer->EyeAngles()[YAW], alpha); + } + } +} + +void CCSMapOverview::FireGameEvent( IGameEvent *event ) +{ + const char * type = event->GetName(); + + if ( Q_strcmp(type,"hostage_killed") == 0 ) + { + MapPlayer_t *hostage = GetHostageByEntityID( event->GetInt("hostage") ); + +// DevMsg("Hostage id %d just died.\n", event->GetInt("hostage")); + + if ( !hostage ) + return; + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + return; + + hostage->health = 0; + hostageCS->isDead = true; + Q_memset( hostage->trail, 0, sizeof(hostage->trail) ); // clear trails + + hostageCS->overrideIcon = m_TeamIconsDead[MAP_ICON_HOSTAGE]; + hostageCS->overrideIconOffscreen = hostageCS->overrideIcon; + hostageCS->overridePosition = hostage->position; + hostageCS->overrideAngle = hostage->angle; + hostageCS->overrideFadeTime = gpGlobals->curtime + DEATH_ICON_FADE; + hostageCS->overrideExpirationTime = gpGlobals->curtime + DEATH_ICON_DURATION; + } + else if ( Q_strcmp(type,"hostage_rescued") == 0 ) + { + MapPlayer_t *hostage = GetHostageByEntityID( event->GetInt("hostage") ); + +// DevMsg("Hostage id %d just got rescued.\n", event->GetInt("hostage")); + + if ( !hostage ) + return; + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + return; + + hostage->health = 0; + hostageCS->isDead = true; + Q_memset( hostage->trail, 0, sizeof(hostage->trail) ); // clear trails + + hostageCS->overrideIcon = hostage->icon; + hostageCS->overrideIconOffscreen = -1; + hostageCS->overridePosition = hostage->position; + hostageCS->overrideAngle = hostage->angle; + hostageCS->overrideFadeTime = gpGlobals->curtime; + hostageCS->overrideExpirationTime = gpGlobals->curtime + HOSTAGE_RESCUE_DURATION; + } + else if ( Q_strcmp(type,"bomb_defused") == 0 ) + { + m_bomb.state = CSMapBomb_t::BOMB_GONE; + m_bomb.timeFade = gpGlobals->curtime; + m_bomb.timeGone = gpGlobals->curtime + BOMB_FADE_DURATION; + } + else if ( Q_strcmp(type,"bomb_exploded") == 0 ) + { + m_bomb.state = CSMapBomb_t::BOMB_GONE; + m_bomb.timeFade = gpGlobals->curtime; + m_bomb.timeGone = gpGlobals->curtime + BOMB_FADE_DURATION; + } + else if ( Q_strcmp(type,"player_death") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + player->health = 0; + Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails + + CSMapPlayer_t *playerCS = GetCSInfoForPlayer(player); + + if ( !playerCS ) + return; + + playerCS->isDead = true; + playerCS->overrideIcon = m_TeamIconsDead[GetIconNumberFromTeamNumber(player->team)]; + playerCS->overrideIconOffscreen = playerCS->overrideIcon; + playerCS->overridePosition = player->position; + playerCS->overrideAngle = player->angle; + playerCS->overrideFadeTime = gpGlobals->curtime + DEATH_ICON_FADE; + playerCS->overrideExpirationTime = gpGlobals->curtime + DEATH_ICON_DURATION; + } + else if ( Q_strcmp(type,"player_team") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( localPlayer == NULL ) + return; + MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID()); + + player->team = event->GetInt("team"); + + if( player == localMapPlayer ) + player->icon = m_TeamIconsSelf[ GetIconNumberFromTeamNumber(player->team) ]; + else + player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ]; + + player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ]; + } + else + { + BaseClass::FireGameEvent(event); + } +} + +void CCSMapOverview::SetMode(int mode) +{ + if ( mode == MAP_MODE_RADAR ) + { + m_flChangeSpeed = 0; // change size instantly + // We want the _output_ of the radar to be consistant, so we need to take the map scale in to account. + float desiredZoom = (DESIRED_RADAR_RESOLUTION * m_fMapScale) / (OVERVIEW_MAP_SIZE * m_fFullZoom); + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0, 0, vgui::AnimationController::INTERPOLATOR_LINEAR ); + + if( CBasePlayer::GetLocalPlayer() ) + SetFollowEntity( CBasePlayer::GetLocalPlayer()->entindex() ); + + SetPaintBackgroundType( 2 );// rounded corners + ShowPanel( true ); + } + else if ( mode == MAP_MODE_INSET ) + { + SetPaintBackgroundType( 2 );// rounded corners + + float desiredZoom = (overview_preferred_view_size.GetFloat() * m_fMapScale) / (OVERVIEW_MAP_SIZE * m_fFullZoom); + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_LINEAR ); + } + else + { + SetPaintBackgroundType( 0 );// square corners + + float desiredZoom = 1.0f; + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_LINEAR ); + } + + BaseClass::SetMode(mode); +} + +void CCSMapOverview::UpdateSizeAndPosition() +{ + int x,y,w,h; + + vgui::surface()->GetScreenSize( w, h ); + + switch( m_nMode ) + { + case MAP_MODE_RADAR: + { + // To allow custom hud scripts to work, get our size from the HudRadar element that people already tweak. + int x, y, w, t; + (GET_HUDELEMENT( CHudRadar ))->GetBounds(x, y, w, t); + m_vPosition.x = x; + m_vPosition.y = y; + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight(); + } + + m_vSize.x = w; + m_vSize.y = w;// Intentionally not 't'. We need to enforce square-ness to prevent people from seeing more of the map by fiddling their HudLayout + break; + } + + case MAP_MODE_INSET: + { + m_vPosition.x = XRES(16); + m_vPosition.y = YRES(16); + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight(); + } + + m_vSize.x = w/4; + m_vSize.y = m_vSize.x/1.333; + break; + } + + case MAP_MODE_FULL: + default: + { + m_vSize.x = w; + m_vSize.y = h; + + m_vPosition.x = 0; + m_vPosition.y = 0; + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight(); + m_vSize.y -= g_pSpectatorGUI->GetTopBarHeight(); + m_vSize.y -= g_pSpectatorGUI->GetBottomBarHeight(); + } + break; + } + } + + GetBounds( x,y,w,h ); + + if ( m_flChangeSpeed > 0 ) + { + // adjust slowly + int pixels = m_flChangeSpeed * gpGlobals->frametime; + x = AdjustValue( x, m_vPosition.x, pixels ); + y = AdjustValue( y, m_vPosition.y, pixels ); + w = AdjustValue( w, m_vSize.x, pixels ); + h = AdjustValue( h, m_vSize.y, pixels ); + } + else + { + // set instantly + x = m_vPosition.x; + y = m_vPosition.y; + w = m_vSize.x; + h = m_vSize.y; + } + + SetBounds( x,y,w,h ); +} + +void CCSMapOverview::SetPlayerSeen( int index ) +{ + CSMapPlayer_t *pCS = GetCSInfoForPlayerIndex(index); + + float now = gpGlobals->curtime; + + if( pCS ) + { + if( pCS->timeLastSeen == -1 ) + pCS->timeFirstSeen = now; + + pCS->timeLastSeen = now; + } +} + +void CCSMapOverview::SetBombSeen( bool seen ) +{ + if( seen ) + { + float now = gpGlobals->curtime; + + if( m_bomb.timeLastSeen == -1 ) + m_bomb.timeFirstSeen = now; + + m_bomb.timeLastSeen = now; + } + else + { + m_bomb.timeFirstSeen = -1; + m_bomb.timeLastSeen = -1; + } +} + +void CCSMapOverview::FlashEntity( int entityID ) +{ + MapPlayer_t *player = GetPlayerByEntityID(entityID); + if( player == NULL ) + return; + + CSMapPlayer_t *playerCS = GetCSInfoForPlayer(player); + + if ( !playerCS ) + return; + + playerCS->flashUntilTime = gpGlobals->curtime + 2.0f; + playerCS->currentFlashAlpha = 255; + playerCS->nextFlashPeakTime = gpGlobals->curtime + 0.5f; +} + +void CCSMapOverview::UpdateFlashes() +{ + float now = gpGlobals->curtime; + for (int i=0; i<MAX_PLAYERS; i++) + { + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i); + if( playerCS->flashUntilTime <= now ) + { + // Flashing over. + playerCS->currentFlashAlpha = 0; + } + else + { + if( playerCS->nextFlashPeakTime <= now ) + { + // Time for a peak + playerCS->currentFlashAlpha = 255; + playerCS->nextFlashPeakTime = now + 0.5f; + playerCS->nextFlashPeakTime = MIN( playerCS->nextFlashPeakTime, playerCS->flashUntilTime ); + } + else + { + // Just fade away + playerCS->currentFlashAlpha -= ((playerCS->currentFlashAlpha * gpGlobals->frametime) / (playerCS->nextFlashPeakTime - now)); + playerCS->currentFlashAlpha = MAX( 0, playerCS->currentFlashAlpha ); + } + } + } +} + + +//----------------------------------------------------------------------------- +void CCSMapOverview::SetPlayerPreferredMode( int mode ) +{ + // A player has given an explicit overview_mode command, so we need to honor that when we are done being the radar. + m_playerPreferredMode = mode; + + // save off non-radar preferred modes + switch ( mode ) + { + case MAP_MODE_OFF: + overview_preferred_mode.SetValue( MAP_MODE_OFF ); + break; + + case MAP_MODE_INSET: + overview_preferred_mode.SetValue( MAP_MODE_INSET ); + break; + + case MAP_MODE_FULL: + overview_preferred_mode.SetValue( MAP_MODE_FULL ); + break; + } +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::SetPlayerPreferredViewSize( float viewSize ) +{ + overview_preferred_view_size.SetValue( viewSize ); +} + + +//----------------------------------------------------------------------------- +int CCSMapOverview::GetIconNumberFromTeamNumber( int teamNumber ) +{ + switch(teamNumber) + { + case TEAM_TERRORIST: + return MAP_ICON_T; + + case TEAM_CT: + return MAP_ICON_CT; + + default: + return MAP_ICON_HOSTAGE; + } +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::ClearGoalIcons() +{ + m_goalIcons.RemoveAll(); +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::UpdateGoalIcons() +{ + // The goal entities don't exist on the client, so we have to get them from the CS Resource. + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + Vector bombA = pCSPR->GetBombsiteAPosition(); + if( bombA != vec3_origin ) + { + CSMapGoal_t bombGoalA; + bombGoalA.position = bombA; + bombGoalA.iconToUse = m_bombSiteIconA; + m_goalIcons.AddToTail( bombGoalA ); + m_goalIconsLoaded = true; + } + + Vector bombB = pCSPR->GetBombsiteBPosition(); + if( bombB != vec3_origin ) + { + CSMapGoal_t bombGoalB; + bombGoalB.position = bombB; + bombGoalB.iconToUse = m_bombSiteIconB; + m_goalIcons.AddToTail( bombGoalB ); + m_goalIconsLoaded = true; + } + + for( int rescueIndex = 0; rescueIndex < MAX_HOSTAGE_RESCUES; rescueIndex++ ) + { + Vector hostageI = pCSPR->GetHostageRescuePosition( rescueIndex ); + if( hostageI != vec3_origin ) + { + CSMapGoal_t hostageGoalI; + hostageGoalI.position = hostageI; + hostageGoalI.iconToUse = m_hostageRescueIcon; + m_goalIcons.AddToTail( hostageGoalI ); + m_goalIconsLoaded = true; + } + } +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::DrawGoalIcons() +{ + for( int iconIndex = 0; iconIndex < m_goalIcons.Count(); iconIndex++ ) + { + // Goal icons are drawn without turning, but with edge adjustment. + CSMapGoal_t *currentIcon = &(m_goalIcons[iconIndex]); + int alpha = 255; + DrawIconCS(currentIcon->iconToUse, currentIcon->iconToUse, currentIcon->position, m_flIconSize, GetViewAngle(), alpha, false); + } +} + +//----------------------------------------------------------------------------- +bool CCSMapOverview::IsRadarLocked() +{ + return cl_radar_locked.GetBool(); +} + +//----------------------------------------------------------------------------- +int CCSMapOverview::GetMasterAlpha( void ) +{ + // The master alpha is the alpha that the map wants to draw at. The background will be at half that, and the icons + // will always be full. (The icons fade themselves for functional reasons like seen-recently.) + int alpha = 255; + if( GetMode() == MAP_MODE_RADAR ) + alpha = cl_radaralpha.GetInt(); + else + alpha = overview_alpha.GetFloat() * 255; + alpha = clamp( alpha, 0, 255 ); + + return alpha; +} + +//----------------------------------------------------------------------------- +int CCSMapOverview::GetBorderSize( void ) +{ + switch( GetMode() ) + { + case MAP_MODE_RADAR: + return 4; + case MAP_MODE_INSET: + return 4; + case MAP_MODE_FULL: + default: + return 0; + } +} + +//----------------------------------------------------------------------------- +Vector2D CCSMapOverview::PanelToMap( const Vector2D &panelPos ) +{ + // This is the reversing of baseclass's MapToPanel + int pwidth, pheight; + GetSize(pwidth, pheight); + float viewAngle = GetViewAngle(); + float fScale = (m_fZoom * m_fFullZoom) / OVERVIEW_MAP_SIZE; + + Vector offset; + offset.x = (panelPos.x - (pwidth * 0.5f)) / pheight; + offset.y = (panelPos.y - (pheight * 0.5f)) / pheight; + + offset.x /= fScale; + offset.y /= fScale; + + VectorYawRotate( offset, -viewAngle, offset ); + + Vector2D mapPos; + mapPos.x = offset.x + m_MapCenter.x; + mapPos.y = offset.y + m_MapCenter.y; + + return mapPos; +} diff --git a/game/client/cstrike/VGUI/cstrikespectatorgui.h b/game/client/cstrike/VGUI/cstrikespectatorgui.h new file mode 100644 index 0000000..5064812 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikespectatorgui.h @@ -0,0 +1,279 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSSPECTATORGUI_H +#define CSSPECTATORGUI_H +#ifdef _WIN32 +#pragma once +#endif + +#include "spectatorgui.h" +#include "mapoverview.h" +#include "cs_shareddefs.h" + +extern ConVar mp_playerid; // in cs_gamerules.h +extern ConVar mp_forcecamera; // in gamevars_shared.h +extern ConVar mp_fadetoblack; + + +//----------------------------------------------------------------------------- +// Purpose: Cstrike Spectator UI +//----------------------------------------------------------------------------- +class CCSSpectatorGUI : public CSpectatorGUI +{ +private: + DECLARE_CLASS_SIMPLE( CCSSpectatorGUI, CSpectatorGUI ); + +public: + CCSSpectatorGUI( IViewPort *pViewPort ); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void UpdateSpectatorPlayerList( void ); + virtual void Update( void ); + virtual bool NeedsUpdate( void ); + //============================================================================= + // HPE_BEGIN: + // [smessick] + //============================================================================= + virtual void ShowPanel( bool bShow ); + //============================================================================= + // HPE_END + //============================================================================= + +protected: + + void UpdateTimer(); + void UpdateAccount(); + + int m_nLastAccount; + int m_nLastTime; + int m_nLastSpecMode; + CBaseEntity *m_nLastSpecTarget; + + void StoreWidths( void ); + void ResizeControls( void ); + bool ControlsPresent( void ) const; + + vgui::Label *m_pCTLabel; + vgui::Label *m_pCTScore; + vgui::Label *m_pTerLabel; + vgui::Label *m_pTerScore; + vgui::Label *m_pTimer; + vgui::Label *m_pTimerLabel; + vgui::Panel *m_pDivider; + vgui::Label *m_pExtraInfo; + + bool m_modifiedWidths; + + int m_scoreWidth; + int m_extraInfoWidth; +}; + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#define DESIRED_RADAR_RESOLUTION 450 + +class CCSMapOverview : public CMapOverview +{ + DECLARE_CLASS_SIMPLE( CCSMapOverview, CMapOverview ); + +public: + + enum + { + MAP_ICON_T = 0, + MAP_ICON_CT, + MAP_ICON_HOSTAGE, + MAP_ICON_COUNT + }; + + CCSMapOverview( const char *pElementName ); + virtual ~CCSMapOverview(); + + virtual bool ShouldDraw( void ); + vgui::Panel *GetAsPanel(){ return this; } + virtual bool AllowConCommandsWhileAlive(){return false;} + virtual void SetPlayerPreferredMode( int mode ); + virtual void SetPlayerPreferredViewSize( float viewSize ); + virtual void ApplySchemeSettings( vgui::IScheme *scheme ); + +protected: // private structures & types + + // list of game events the hLTV takes care of + + typedef struct { + int xpos; + int ypos; + } FootStep_t; + + // Extra stuff in a this-level parallel array + typedef struct CSMapPlayer_s { + int overrideIcon; // if not -1, the icon to use instead + int overrideIconOffscreen; // to use with overrideIcon + float overrideFadeTime; // Time to start fading the override icon + float overrideExpirationTime; // Time to not use the override icon any more + Vector overridePosition; // Where the overridden icon will draw + QAngle overrideAngle; // And at what angle + bool isDead; // Death latch, since client can be behind the times on health messages. + float timeLastSeen; // curtime that we last saw this guy. + float timeFirstSeen; // curtime that we started seeing this guy + bool isHostage; // Not a full player, a hostage. Special icon, different death event + float flashUntilTime; + float nextFlashPeakTime; + int currentFlashAlpha; + } CSMapPlayer_t; + + typedef struct CSMapBomb_s + { + Vector position; + + enum BombState + { + BOMB_PLANTED, //planted and ticking down + BOMB_DROPPED, //dropped and lying loose + BOMB_CARRIED, //in the arms of a player + BOMB_GONE, //defused or exploded, but was planted + BOMB_INVALID //no bomb + }; + BombState state; + + float timeLastSeen; + float timeFirstSeen; + float timeFade; + float timeGone; + + float currentRingRadius; + float currentRingAlpha; + float maxRingRadius; + float ringTravelTime; + } CSMapBomb_t; + + typedef struct CSMapGoal_s + { + Vector position; + int iconToUse; + } CSMapGoal_t; + +public: // IViewPortPanel interface: + + virtual void Update(); + virtual void Init( void ); + + // both vgui::Frame and IViewPortPanel define these, so explicitly define them here as passthroughs to vgui + vgui::VPANEL GetVPanel( void ) { return BaseClass::GetVPanel(); } + virtual bool IsVisible() { return BaseClass::IsVisible(); } + virtual void SetParent(vgui::VPANEL parent) { BaseClass::SetParent(parent); } + + // IGameEventListener + + virtual void FireGameEvent( IGameEvent *event); + + // VGUI overrides + + // Player settings: + void SetPlayerSeen( int index ); + void SetBombSeen( bool seen ); + + // general settings: + virtual void SetMap(const char * map); + virtual void SetMode( int mode ); + + // Object settings + virtual void FlashEntity( int entityID ); + + // rules that define if you can see a player on the overview or not + virtual bool CanPlayerBeSeen(MapPlayer_t *player); + + virtual int GetIconNumberFromTeamNumber( int teamNumber ); + +protected: + + virtual void DrawCamera(); + virtual void DrawMapTexture(); + virtual void DrawMapPlayers(); + void DrawHostages(); + void DrawBomb(); + void DrawGoalIcons(); + virtual void ResetRound(); + virtual void InitTeamColorsAndIcons(); + virtual void UpdateSizeAndPosition(); + void UpdateGoalIcons(); + void ClearGoalIcons(); + virtual bool IsRadarLocked(); + Vector2D PanelToMap( const Vector2D &panelPos ); + + bool AdjustPointToPanel(Vector2D *pos); + MapPlayer_t* GetPlayerByEntityID( int entityID ); + MapPlayer_t* GetHostageByEntityID( int entityID ); + virtual void UpdatePlayers(); + void UpdateHostages();///< Update hostages in the MapPlayer list + void UpdateBomb(); + void UpdateFlashes(); + bool CreateRadarImage(const char *mapName, const char *radarFileName); + virtual bool RunHudAnimations(){ return false; } + +private: + bool DrawIconCS( int textureID, + int offscreenTextureID, + Vector pos, + float scale, + float angle, + int alpha, + bool allowRotation = true, + const char *text = NULL, + Color *textColor = NULL, + float status = -1, + Color *statusColor = NULL + ); + + int GetMasterAlpha( void );// The main alpha that the map part should be, determined by using the mode to look at the right convar + int GetBorderSize( void );// How far in from the edge of the panel we draw, based on mode. Let's the background fancy corners show. + CSMapPlayer_t* GetCSInfoForPlayerIndex( int index ); + CSMapPlayer_t* GetCSInfoForPlayer(MapPlayer_t *player); + CSMapPlayer_t* GetCSInfoForHostage(MapPlayer_t *hostage); + bool CanHostageBeSeen(MapPlayer_t *hostage); + + CSMapPlayer_t m_PlayersCSInfo[MAX_PLAYERS]; + CSMapBomb_t m_bomb; + MapPlayer_t m_Hostages[MAX_HOSTAGES]; + CSMapPlayer_t m_HostagesCSInfo[MAX_HOSTAGES]; + + CUtlVector< CSMapGoal_t > m_goalIcons; + bool m_goalIconsLoaded; + + int m_TeamIconsSelf[MAP_ICON_COUNT]; + int m_TeamIconsDead[MAP_ICON_COUNT]; + int m_TeamIconsOffscreen[MAP_ICON_COUNT]; + int m_TeamIconsDeadOffscreen[MAP_ICON_COUNT]; + + int m_bombIconPlanted; + int m_bombIconDropped; + int m_bombIconCarried; + int m_bombRingPlanted; + int m_bombRingDropped; + int m_bombRingCarried; + int m_bombRingCarriedOffscreen; + int m_radioFlash; + int m_radioFlashOffscreen; + int m_radarTint; + int m_hostageFollowing; + int m_hostageFollowingOffscreen; + int m_playerFacing; + int m_cameraIconFirst; + int m_cameraIconThird; + int m_cameraIconFree; + int m_hostageRescueIcon; + int m_bombSiteIconA; + int m_bombSiteIconB; + + int m_nRadarMapTextureID; // texture id for radar version of current overview image + + int m_playerPreferredMode; // The mode the player wants to be in for when we aren't being the radar +}; + +#endif // CSSPECTATORGUI_H diff --git a/game/client/cstrike/VGUI/cstriketeammenu.cpp b/game/client/cstrike/VGUI/cstriketeammenu.cpp new file mode 100644 index 0000000..4b39088 --- /dev/null +++ b/game/client/cstrike/VGUI/cstriketeammenu.cpp @@ -0,0 +1,201 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstriketeammenu.h" +#include "backgroundpanel.h" +#include <convar.h> +#include "hud.h" // for gEngfuncs +#include "c_cs_player.h" +#include "cs_gamerules.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSTeamMenu::CCSTeamMenu(IViewPort *pViewPort) : CTeamMenu(pViewPort) +{ + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCSTeamMenu::~CCSTeamMenu() +{ +} + +void CCSTeamMenu::ShowPanel(bool bShow) +{ + if ( bShow ) + { + engine->CheckPoint( "TeamMenu" ); + } + + BaseClass::ShowPanel( bShow ); +} + +//----------------------------------------------------------------------------- +// Purpose: called to update the menu with new information +//----------------------------------------------------------------------------- +void CCSTeamMenu::Update( void ) +{ + BaseClass::Update(); + + const ConVar *allowspecs = cvar->FindVar( "mp_allowspectators" ); + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !pPlayer || !CSGameRules() ) + return; + + if ( allowspecs && allowspecs->GetBool() ) + { + // if we're not already a CT or T...or the freeze time isn't over yet...or we're dead + if ( pPlayer->GetTeamNumber() == TEAM_UNASSIGNED || + CSGameRules()->IsFreezePeriod() || + ( pPlayer && pPlayer->IsPlayerDead() ) ) + { + SetVisibleButton("specbutton", true); + } + else + { + SetVisibleButton("specbutton", false); + } + } + else + { + SetVisibleButton("specbutton", false ); + } + + m_bVIPMap = false; + + char mapName[MAX_MAP_NAME]; + + Q_FileBase( engine->GetLevelName(), mapName, sizeof(mapName) ); + + if ( !Q_strncmp( mapName, "maps/as_", 8 ) ) + { + m_bVIPMap = true; + } + + // if this isn't a VIP map or we're a spectator/terrorist, then disable the VIP button + if ( !CSGameRules()->IsVIPMap() || ( pPlayer->GetTeamNumber() != TEAM_CT ) ) + { + SetVisibleButton("vipbutton", false); + } + else // this must be a VIP map and we must already be a CT + { + SetVisibleButton("vipbutton", true); + } + + if( pPlayer->GetTeamNumber() == TEAM_UNASSIGNED ) // we aren't on a team yet + { + SetVisibleButton("CancelButton", false); + } + else + { + SetVisibleButton("CancelButton", true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSTeamMenu::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + if ( state ) + { + Button *pAutoButton = dynamic_cast< Button* >( FindChildByName( "autobutton" ) ); + if ( pAutoButton ) + { + pAutoButton->RequestFocus(); + pAutoButton->SetArmed( true ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: When a team button is pressed it triggers this function to +// cause the player to join a team +//----------------------------------------------------------------------------- +void CCSTeamMenu::OnCommand( const char *command ) +{ + if ( Q_stricmp( command, "vguicancel" ) ) + { + engine->ClientCmd( command ); + } + + + BaseClass::OnCommand(command); + + gViewPortInterface->ShowBackGround( false ); + OnClose(); +} + + +void CCSTeamMenu::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_ENTER ) + { + Button *pAutoButton = dynamic_cast< Button* >( FindChildByName( "autobutton" ) ); + if ( pAutoButton ) + { + pAutoButton->DoClick(); + } + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the visibility of a button by name +//----------------------------------------------------------------------------- +void CCSTeamMenu::SetVisibleButton(const char *textEntryName, bool state) +{ + Button *entry = dynamic_cast<Button *>(FindChildByName(textEntryName)); + if (entry) + { + entry->SetVisible(state); + } +} + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CCSTeamMenu::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CCSTeamMenu::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSTeamMenu::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); +} + diff --git a/game/client/cstrike/VGUI/cstriketeammenu.h b/game/client/cstrike/VGUI/cstriketeammenu.h new file mode 100644 index 0000000..7b2ab0e --- /dev/null +++ b/game/client/cstrike/VGUI/cstriketeammenu.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSTEAMMENU_H +#define CSTEAMMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#include <teammenu.h> + +//----------------------------------------------------------------------------- +// Purpose: Displays the team menu +//----------------------------------------------------------------------------- +class CCSTeamMenu : public CTeamMenu +{ +private: + DECLARE_CLASS_SIMPLE( CCSTeamMenu, CTeamMenu ); + +public: + CCSTeamMenu(IViewPort *pViewPort); + ~CCSTeamMenu(); + + void Update(); + void ShowPanel( bool bShow ); + virtual void SetVisible(bool state); + +private: + enum { NUM_TEAMS = 3 }; + + // VGUI2 override + virtual void OnCommand( const char *command); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + // helper functions + void SetVisibleButton(const char *textEntryName, bool state); + + bool m_bVIPMap; + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- +}; + +#endif // CSTEAMMENU_H diff --git a/game/client/cstrike/VGUI/cstriketextwindow.cpp b/game/client/cstrike/VGUI/cstriketextwindow.cpp new file mode 100644 index 0000000..feedd23 --- /dev/null +++ b/game/client/cstrike/VGUI/cstriketextwindow.cpp @@ -0,0 +1,146 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstriketextwindow.h" +#include "backgroundpanel.h" +#include <cdll_client_int.h> + +#include <vgui/IScheme.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include <KeyValues.h> +#include <convar.h> +#include <vgui_controls/ImageList.h> + +#include <vgui_controls/TextEntry.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/BuildGroup.h> + +#include "IGameUIFuncs.h" // for key bindings +#include <igameresources.h> +extern IGameUIFuncs *gameuifuncs; // for key binding details + +#include <game/client/iviewport.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSTextWindow::CCSTextWindow(IViewPort *pViewPort) : CTextWindow( pViewPort ) +{ + SetProportional( true ); + + m_iScoreBoardKey = BUTTON_CODE_INVALID; // this is looked up in Activate() + + CreateBackground( this ); + m_backgroundLayoutFinished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCSTextWindow::~CCSTextWindow() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSTextWindow::Update() +{ + BaseClass::Update(); + + m_pOK->RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSTextWindow::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + if ( state ) + { + m_pOK->RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: shows the text window +//----------------------------------------------------------------------------- +void CCSTextWindow::ShowPanel(bool bShow) +{ + if ( bShow ) + { + // get key binding if shown + if ( m_iScoreBoardKey == BUTTON_CODE_INVALID ) // you need to lookup the jump key AFTER the engine has loaded + { + m_iScoreBoardKey = gameuifuncs->GetButtonCodeForBind( "showscores" ); + } + } + + BaseClass::ShowPanel( bShow ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSTextWindow::OnKeyCodePressed( KeyCode code ) +{ + //We manually intercept the ENTER key so in case the button loses focus + //ENTER still moves you through the MOTD screen. + if ( code == KEY_ENTER || code == KEY_XBUTTON_A || code == KEY_XBUTTON_B ) + { + m_pOK->DoClick(); + } + else if ( m_iScoreBoardKey != BUTTON_CODE_INVALID && m_iScoreBoardKey == code ) + { + gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, true ); + gViewPortInterface->PostMessageToPanel( PANEL_SCOREBOARD, new KeyValues( "PollHideCode", "code", code ) ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: The CS background is painted by image panels, so we should do nothing +//----------------------------------------------------------------------------- +void CCSTextWindow::PaintBackground() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Scale / center the window +//----------------------------------------------------------------------------- +void CCSTextWindow::PerformLayout() +{ + BaseClass::PerformLayout(); + + // stretch the window to fullscreen + if ( !m_backgroundLayoutFinished ) + LayoutBackgroundPanel( this ); + m_backgroundLayoutFinished = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSTextWindow::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + ApplyBackgroundSchemeSettings( this, pScheme ); +} + diff --git a/game/client/cstrike/VGUI/cstriketextwindow.h b/game/client/cstrike/VGUI/cstriketextwindow.h new file mode 100644 index 0000000..bd7c707 --- /dev/null +++ b/game/client/cstrike/VGUI/cstriketextwindow.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSTEXTWINDOW_H +#define CSTEXTWINDOW_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vguitextwindow.h" + +//----------------------------------------------------------------------------- +// Purpose: displays the MOTD +//----------------------------------------------------------------------------- + +class CCSTextWindow : public CTextWindow +{ +private: + DECLARE_CLASS_SIMPLE( CCSTextWindow, CTextWindow ); + +public: + CCSTextWindow(IViewPort *pViewPort); + virtual ~CCSTextWindow(); + + virtual void Update(); + virtual void SetVisible(bool state); + virtual void ShowPanel( bool bShow ); + virtual void OnKeyCodePressed(vgui::KeyCode code); + +protected: + ButtonCode_t m_iScoreBoardKey; + + // Background panel ------------------------------------------------------- + +public: + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + bool m_backgroundLayoutFinished; + + // End background panel --------------------------------------------------- +}; + + +#endif // CSTEXTWINDOW_H diff --git a/game/client/cstrike/VGUI/lifetime_stats_page.cpp b/game/client/cstrike/VGUI/lifetime_stats_page.cpp new file mode 100644 index 0000000..89c0129 --- /dev/null +++ b/game/client/cstrike/VGUI/lifetime_stats_page.cpp @@ -0,0 +1,257 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// +//=============================================================================// + +#include "cbase.h" +#include "lifetime_stats_page.h" +#include <vgui_controls/SectionedListPanel.h> +#include "cs_client_gamestats.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CLifetimeStatsPage::CLifetimeStatsPage(vgui::Panel *parent, const char *name) : BaseClass(parent, "CSLifetimeStatsDialog") +{ + m_allStatsGroupPanel = AddGroup( L"all", "Stats_Button_All", L"All" ); + m_detailedWeaponStatsGroupPanel = AddGroup( L"weapon", "Stats_Button_Weapon", L"Weapon Stats" ); + m_specialSkillsStatsGroupPanel = AddGroup( L"skills", "Stats_Button_Skills", L"Special Skills" ); + m_mapAndMiscellanyStatsGroupPanel = AddGroup( L"map", "Stats_Button_Misc", L"Miscellaneous" ); + m_mapVictoryStatsGroupPanel = AddGroup( L"map", "Stats_Button_Victories", L"Map Victories" ); + m_missionAndObjectiveStatsGroupPanel = AddGroup( L"mission", "Stats_Button_Mission", L"Mission && Objectives" ); + + m_allStatsGroupPanel->SetGroupActive(true); +} +//----------------------------------------------------------------------------- +// Purpose: Loads settings from statsdialog.res in hl2/resource/ui/ +//----------------------------------------------------------------------------- +void CLifetimeStatsPage::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings("resource/ui/CSLifetimeStatsDialog.res"); + + m_statsList->AddColumnToSection( 0, "name", "", SectionedListPanel::COLUMN_CENTER, 280); + m_statsList->AddColumnToSection( 0, "playerValue", "", SectionedListPanel::COLUMN_CENTER, 250); +} + +void CLifetimeStatsPage::RepopulateStats() +{ + m_statsList->RemoveAll(); + + const StatsCollection_t& personalLifetimeStats = g_CSClientGameStats.GetLifetimeStats(); + + if (m_missionAndObjectiveStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_ROUNDS_WON, personalLifetimeStats); + AddSimpleStat(CSSTAT_KILLS, personalLifetimeStats); + AddSimpleStat(CSSTAT_DEATHS, personalLifetimeStats); + AddKillToDeathStat(personalLifetimeStats); + AddSimpleStat(CSSTAT_DAMAGE, personalLifetimeStats); + AddSimpleStat(CSSTAT_NUM_BOMBS_PLANTED, personalLifetimeStats); + AddSimpleStat(CSSTAT_NUM_BOMBS_DEFUSED, personalLifetimeStats); + AddSimpleStat(CSSTAT_NUM_HOSTAGES_RESCUED, personalLifetimeStats); + AddSimpleStat(CSSTAT_PISTOLROUNDS_WON, personalLifetimeStats); + } + + if (m_specialSkillsStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_MVPS, personalLifetimeStats); + AddFavoriteWeaponStat(personalLifetimeStats); + AddSimpleStat(CSSTAT_SHOTS_FIRED, personalLifetimeStats); + AddSimpleStat(CSSTAT_SHOTS_HIT, personalLifetimeStats); + AddAccuracyStat(personalLifetimeStats); + AddSimpleStat(CSSTAT_DOMINATIONS, personalLifetimeStats); + AddSimpleStat(CSSTAT_DOMINATION_OVERKILLS, personalLifetimeStats); + AddSimpleStat(CSSTAT_REVENGES, personalLifetimeStats); + AddSimpleStat(CSSTAT_KILLS_HEADSHOT, personalLifetimeStats); + AddSimpleStat(CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, personalLifetimeStats); + AddSimpleStat(CSSTAT_KILLS_ENEMY_BLINDED, personalLifetimeStats); + AddSimpleStat(CSSTAT_KILLS_ENEMY_WEAPON, personalLifetimeStats); + AddSimpleStat(CSSTAT_KILLS_KNIFE_FIGHT, personalLifetimeStats); + } + + if (m_mapAndMiscellanyStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_MONEY_EARNED, personalLifetimeStats); + AddSimpleStat(CSSTAT_DECAL_SPRAYS, personalLifetimeStats); + AddSimpleStat(CSSTAT_NIGHTVISION_DAMAGE, personalLifetimeStats); + AddSimpleStat(CSSTAT_NUM_BROKEN_WINDOWS, personalLifetimeStats); + AddSimpleStat(CSSTAT_WEAPONS_DONATED, personalLifetimeStats); + } + + if (m_mapVictoryStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_MAP_WINS_CS_ASSAULT, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_CS_COMPOUND, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_CS_HAVANA, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_CS_ITALY, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_CS_MILITIA, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_CS_OFFICE, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_AZTEC, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_CBBLE, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_CHATEAU, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_DUST2, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_DUST, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_INFERNO, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_NUKE, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_PIRANESI, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_PORT, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_PRODIGY, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_TIDES, personalLifetimeStats); + AddSimpleStat(CSSTAT_MAP_WINS_DE_TRAIN, personalLifetimeStats); + } + + + if (m_detailedWeaponStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + CSStatType_t hitsStat = CSSTAT_HITS_DEAGLE; + CSStatType_t shotsStat = CSSTAT_SHOTS_DEAGLE; + for (CSStatType_t killStat = CSSTAT_KILLS_DEAGLE ; killStat <= CSSTAT_KILLS_HEGRENADE ; killStat = (CSStatType_t)(killStat + 1)) + { + if (shotsStat <= CSSTAT_SHOTS_M249) + { + AddSimpleStat(shotsStat, personalLifetimeStats); + } + if (hitsStat <= CSSTAT_HITS_M249) + { + AddSimpleStat(hitsStat, personalLifetimeStats); + } + AddSimpleStat(killStat, personalLifetimeStats); + + hitsStat = (CSStatType_t)(hitsStat + 1); + shotsStat = (CSStatType_t)(shotsStat + 1); + } + } + + int itemCount = m_statsList->GetItemCount(); + for (int i = 0; i < itemCount ; ++i) + { + m_statsList->SetItemBgColor(i, Color(0,0,0,0)); + } +} + +int CLifetimeStatsPage::AddSimpleStat( int desiredStat, const StatsCollection_t& personalLifetimeStats) +{ + PlayerStatData_t stat = g_CSClientGameStats.GetStatById(desiredStat); + + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetWString( "name", stat.pStatDisplayName ); + pKeyValues->SetFloat( "playerValue", 0 ); + + char buf[64]; + Q_snprintf( buf, sizeof( buf ), "%d", personalLifetimeStats[stat.iStatId] ); + pKeyValues->SetString( "playerValue", (personalLifetimeStats[stat.iStatId])?buf:"" ); + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + +int CLifetimeStatsPage::AddFavoriteWeaponStat(const StatsCollection_t& personalLifetimeStats) +{ + PlayerStatData_t statPlayerBestWeapon; + statPlayerBestWeapon.iStatId = CSSTAT_UNDEFINED; + + int previousBestKills = 0; + + for (CSStatType_t statId = CSSTAT_KILLS_DEAGLE ; statId <= CSSTAT_KILLS_M249; statId = (CSStatType_t)(statId + 1)) + { + PlayerStatData_t stat = g_CSClientGameStats.GetStatById( statId ); + + //Compare this to previous Weapons + int playerKillsWithWeapon = personalLifetimeStats[statId]; + { + if (playerKillsWithWeapon > previousBestKills) + { + statPlayerBestWeapon = stat; + previousBestKills = playerKillsWithWeapon; + } + } + } + + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetWString( "name", LocalizeTagOrUseDefault( "Stats_FavoriteWeapon", L"Favorite Weapon" ) ); + + pKeyValues->SetWString( "playerValue", TranslateWeaponKillIDToAlias(statPlayerBestWeapon.iStatId) ); + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + + +int CLifetimeStatsPage::AddKillToDeathStat(const StatsCollection_t& personalLifetimeStats) +{ + PlayerStatData_t statKills = g_CSClientGameStats.GetStatById(CSSTAT_KILLS); + PlayerStatData_t statDeaths = g_CSClientGameStats.GetStatById(CSSTAT_DEATHS); + + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetWString( "name", LocalizeTagOrUseDefault( "Stats_KillToDeathRatio", L"Kill to Death Ratio" ) ); + pKeyValues->SetFloat( "playerValue", 0 ); + + float playerKills = personalLifetimeStats[statKills.iStatId]; + float playerDeaths = personalLifetimeStats[statDeaths.iStatId]; + float playerKillToDeathRatio = 1.0f; + + if (playerDeaths > 0) + { + playerKillToDeathRatio = playerKills / playerDeaths; + } + + char buf[64]; + Q_snprintf( buf, sizeof( buf ), "%.2f", playerKillToDeathRatio ); + pKeyValues->SetString( "playerValue", (playerKillToDeathRatio)?buf:"" ); + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + +int CLifetimeStatsPage::AddAccuracyStat(const StatsCollection_t& personalLifetimeStats) +{ + PlayerStatData_t statHits = g_CSClientGameStats.GetStatById(CSSTAT_SHOTS_HIT); + PlayerStatData_t statShots = g_CSClientGameStats.GetStatById(CSSTAT_SHOTS_FIRED); + + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetWString( "name", LocalizeTagOrUseDefault( "Stats_Accuracy", L"Accuracy" ) ); + pKeyValues->SetFloat( "playerValue", 0 ); + + float playerHits = personalLifetimeStats[statHits.iStatId]; + float playerShots = personalLifetimeStats[statShots.iStatId]; + float playerAccuracy = 0.0; + + if (playerShots > 0) + { + playerAccuracy = 100.0f * playerHits / playerShots; + } + + char buf[64]; + Q_snprintf( buf, sizeof( buf ), "%.1f%%", playerAccuracy ); + pKeyValues->SetString( "playerValue", (playerAccuracy)?buf:"" ); + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + diff --git a/game/client/cstrike/VGUI/lifetime_stats_page.h b/game/client/cstrike/VGUI/lifetime_stats_page.h new file mode 100644 index 0000000..8a2af6b --- /dev/null +++ b/game/client/cstrike/VGUI/lifetime_stats_page.h @@ -0,0 +1,40 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSLifetimeSTATSPAGE_H +#define CSLifetimeSTATSPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "base_stats_page.h" + +class CLifetimeStatsPage : public CBaseStatsPage +{ + DECLARE_CLASS_SIMPLE ( CLifetimeStatsPage, CBaseStatsPage ); + +public: + CLifetimeStatsPage( vgui::Panel *parent, const char *name ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + void RepopulateStats(); + int AddSimpleStat( int desiredStat, const StatsCollection_t& personalLifetimeStats); + int AddAccuracyStat(const StatsCollection_t& personalLifetimeStats); + int AddFavoriteWeaponStat(const StatsCollection_t& personalLifetimeStats); + int AddKillToDeathStat(const StatsCollection_t& personalLifetimeStats); + + CBaseStatGroupPanel* m_allStatsGroupPanel; + CBaseStatGroupPanel* m_detailedWeaponStatsGroupPanel; + CBaseStatGroupPanel* m_specialSkillsStatsGroupPanel; + CBaseStatGroupPanel* m_mapAndMiscellanyStatsGroupPanel; + CBaseStatGroupPanel* m_mapVictoryStatsGroupPanel; + CBaseStatGroupPanel* m_missionAndObjectiveStatsGroupPanel; + + +}; + +#endif // CSLifetimeSTATSPAGE_H diff --git a/game/client/cstrike/VGUI/match_stats_page.cpp b/game/client/cstrike/VGUI/match_stats_page.cpp new file mode 100644 index 0000000..9130176 --- /dev/null +++ b/game/client/cstrike/VGUI/match_stats_page.cpp @@ -0,0 +1,334 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// +//=============================================================================// + +#include "cbase.h" +#include "match_stats_page.h" +#include <vgui_controls/SectionedListPanel.h> +#include "c_team.h" +#include "cs_client_gamestats.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CMatchStatsPage::CMatchStatsPage(vgui::Panel *parent, const char *name) : BaseClass(parent, "CSMatchStatsDialog") +{ + m_allStatsGroupPanel = AddGroup( L"all", "Stats_Button_All", L"All" ); + m_detailedWeaponStatsGroupPanel = AddGroup( L"weapon", "Stats_Button_Weapon", L"Weapon Stats" ); + m_specialSkillsStatsGroupPanel = AddGroup( L"skills", "Stats_Button_Skills", L"Special Skills" ); + m_mapAndMiscellanyStatsGroupPanel = AddGroup( L"map", "Stats_Button_Misc", L"Miscellaneous" ); + m_missionAndObjectiveStatsGroupPanel = AddGroup( L"mission", "Stats_Button_Mission", L"Mission && Objectives" ); + + m_allStatsGroupPanel->SetGroupActive(true); + + m_ResetNotice = new Label( this, "ResetNotice", ""); +} +//----------------------------------------------------------------------------- +// Purpose: Loads settings from statsdialog.res in hl2/resource/ui/ +//----------------------------------------------------------------------------- +void CMatchStatsPage::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings("resource/ui/CSMatchStatsDialog.res"); + m_statsList->AddColumnToSection( 0, "name", "", SectionedListPanel::COLUMN_CENTER, 180); + m_statsList->AddColumnToSection( 0, "playerValue", "", SectionedListPanel::COLUMN_CENTER, 100); + m_statsList->AddColumnToSection( 0, "ctValue", "", SectionedListPanel::COLUMN_CENTER, 100); + m_statsList->AddColumnToSection( 0, "tValue", " ", SectionedListPanel::COLUMN_CENTER, 100); + m_statsList->AddColumnToSection( 0, "serverValue", "", SectionedListPanel::COLUMN_CENTER, 100); +} + +void CMatchStatsPage::RepopulateStats() +{ + m_statsList->RemoveAll(); + + RoundStatsDirectAverage_t* tStats = g_CSClientGameStats.GetDirectTStatsAverages(); + RoundStatsDirectAverage_t* ctStats = g_CSClientGameStats.GetDirectCTStatsAverages(); + RoundStatsDirectAverage_t* serverStats = g_CSClientGameStats.GetDirectPlayerStatsAverages(); + const StatsCollection_t& personalMatchStats = g_CSClientGameStats.GetMatchStats(); + + if (m_missionAndObjectiveStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_ROUNDS_WON, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_KILLS, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_DEATHS, personalMatchStats, tStats, ctStats, serverStats); + AddKillToDeathStat(personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_DAMAGE, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_NUM_BOMBS_PLANTED, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_NUM_BOMBS_DEFUSED, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_NUM_HOSTAGES_RESCUED, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_PISTOLROUNDS_WON, personalMatchStats, tStats, ctStats, serverStats); + } + + if (m_specialSkillsStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_MVPS, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_SHOTS_FIRED, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_SHOTS_HIT, personalMatchStats, tStats, ctStats, serverStats); + AddAccuracyStat(personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_DOMINATIONS, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_DOMINATION_OVERKILLS, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_REVENGES, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_KILLS_HEADSHOT, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_KILLS_ENEMY_BLINDED, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_KILLS_ENEMY_WEAPON, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_KILLS_KNIFE_FIGHT, personalMatchStats, tStats, ctStats, serverStats); + } + + if (m_mapAndMiscellanyStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + AddSimpleStat(CSSTAT_MONEY_EARNED, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_DECAL_SPRAYS, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_NIGHTVISION_DAMAGE, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_NUM_BROKEN_WINDOWS, personalMatchStats, tStats, ctStats, serverStats); + AddSimpleStat(CSSTAT_WEAPONS_DONATED, personalMatchStats, tStats, ctStats, serverStats); + } + + if (m_detailedWeaponStatsGroupPanel->IsGroupActive() || m_allStatsGroupPanel->IsGroupActive()) + { + CSStatType_t hitsStat = CSSTAT_HITS_DEAGLE; + CSStatType_t shotsStat = CSSTAT_SHOTS_DEAGLE; + for (CSStatType_t killStat = CSSTAT_KILLS_DEAGLE ; killStat <= CSSTAT_KILLS_HEGRENADE ; killStat = (CSStatType_t)(killStat + 1)) + { + if (shotsStat <= CSSTAT_SHOTS_M249) + { + AddSimpleStat(shotsStat, personalMatchStats, tStats, ctStats, serverStats); + } + + if (hitsStat <= CSSTAT_HITS_M249) + { + AddSimpleStat(hitsStat, personalMatchStats, tStats, ctStats, serverStats); + } + AddSimpleStat(killStat, personalMatchStats, tStats, ctStats, serverStats); + + hitsStat = (CSStatType_t)(hitsStat + 1); + shotsStat = (CSStatType_t)(shotsStat + 1); + } + } + + int itemCount = m_statsList->GetItemCount(); + for (int i = 0; i < itemCount ; ++i) + { + m_statsList->SetItemBgColor(i, Color(0,0,0,0)); + } +} + +int CMatchStatsPage::AddSimpleStat( int desiredStat, const StatsCollection_t& personalMatchStats, RoundStatsDirectAverage_t* tStats, RoundStatsDirectAverage_t* ctStats, RoundStatsDirectAverage_t* serverStats ) +{ + PlayerStatData_t stat = g_CSClientGameStats.GetStatById(desiredStat); + + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetWString( "name", stat.pStatDisplayName ); + pKeyValues->SetFloat( "playerValue", 0 ); + + char buf[64]; + Q_snprintf( buf, sizeof( buf ), "%d", personalMatchStats[stat.iStatId] ); + pKeyValues->SetString( "playerValue", (personalMatchStats[stat.iStatId])?buf:"" ); + + if (desiredStat == CSSTAT_ROUNDS_WON) + { + C_Team *ts = GetGlobalTeam(TEAM_TERRORIST); + if (ts) + { + Q_snprintf( buf, sizeof( buf ), "%d", ts->Get_Score() ); + pKeyValues->SetString( "tValue", buf ); + } + + C_Team *cts = GetGlobalTeam(TEAM_CT); + if (cts){ + Q_snprintf( buf, sizeof( buf ), "%d", cts->Get_Score()); + pKeyValues->SetString( "ctValue", buf ); + } + } + else + { + Q_snprintf( buf, sizeof( buf ), "%.1f", tStats->m_fStat[stat.iStatId] ); + pKeyValues->SetString( "tValue", (tStats->m_fStat[stat.iStatId]||personalMatchStats[stat.iStatId])?buf:"" ); + + Q_snprintf( buf, sizeof( buf ), "%.1f", ctStats->m_fStat[stat.iStatId] ); + pKeyValues->SetString( "ctValue", (ctStats->m_fStat[stat.iStatId]||personalMatchStats[stat.iStatId])?buf:"" ); + } + + Q_snprintf( buf, sizeof( buf ), "%.1f", serverStats->m_fStat[stat.iStatId] ); + pKeyValues->SetString( "serverValue", (serverStats->m_fStat[stat.iStatId]||personalMatchStats[stat.iStatId])?buf:"" ); + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + + +int CMatchStatsPage::AddKillToDeathStat(const StatsCollection_t& personalMatchStats, RoundStatsDirectAverage_t* tStats, RoundStatsDirectAverage_t* ctStats, RoundStatsDirectAverage_t* serverStats ) +{ + PlayerStatData_t statKills = g_CSClientGameStats.GetStatById(CSSTAT_KILLS); + PlayerStatData_t statDeaths = g_CSClientGameStats.GetStatById(CSSTAT_DEATHS); + + KeyValues *pKeyValues = new KeyValues( "data" ); + + pKeyValues->SetWString( "name", LocalizeTagOrUseDefault( "Stats_KillToDeathRatio", L"Kill to Death Ratio" ) ); + pKeyValues->SetFloat( "playerValue", 0 ); + + float playerKills = personalMatchStats[statKills.iStatId]; + float tKills = tStats->m_fStat[statKills.iStatId]; + float ctKills = ctStats->m_fStat[statKills.iStatId]; + float serverKills = serverStats->m_fStat[statKills.iStatId]; + + float playerDeaths = personalMatchStats[statDeaths.iStatId]; + float tDeaths = tStats->m_fStat[statDeaths.iStatId]; + float ctDeaths = ctStats->m_fStat[statDeaths.iStatId]; + float serverDeaths = serverStats->m_fStat[statDeaths.iStatId]; + + char buf[64]; + + if (playerDeaths > 0) + { + float playerKillToDeathRatio = playerKills / playerDeaths; + Q_snprintf( buf, sizeof( buf ), "%.2f", playerKillToDeathRatio ); + pKeyValues->SetString( "playerValue", (playerKillToDeathRatio)?buf:"" ); + } + else + { + pKeyValues->SetString( "playerValue", "" ); + } + + + if (tDeaths > 0) + { + float tKillToDeathRatio = tKills / tDeaths; + Q_snprintf( buf, sizeof( buf ), "%.2f", tKillToDeathRatio ); + pKeyValues->SetString( "tValue", ((playerKills&&playerDeaths)||tKillToDeathRatio)?buf:"" ); + + } + else + { + pKeyValues->SetString( "tValue", "" ); + } + + if (ctDeaths > 0) + { + float ctKillToDeathRatio = ctKills / ctDeaths; + Q_snprintf( buf, sizeof( buf ), "%.2f", ctKillToDeathRatio); + pKeyValues->SetString( "ctValue", ((playerKills&&playerDeaths)||ctKillToDeathRatio)?buf:"" ); + } + else + { + pKeyValues->SetString( "ctValue", "" ); + } + + if (serverDeaths > 0) + { + float serverKillToDeathRatio = serverKills / serverDeaths; + Q_snprintf( buf, sizeof( buf ), "%.2f", serverKillToDeathRatio ); + pKeyValues->SetString( "serverValue", ((playerKills&&playerDeaths)||serverKillToDeathRatio)?buf:"" ); + } + else + { + pKeyValues->SetString( "serverValue", "" ); + } + + + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + + + +int CMatchStatsPage::AddAccuracyStat(const StatsCollection_t& personalMatchStats, RoundStatsDirectAverage_t* tStats, RoundStatsDirectAverage_t* ctStats, RoundStatsDirectAverage_t* serverStats ) +{ + PlayerStatData_t statHits = g_CSClientGameStats.GetStatById(CSSTAT_SHOTS_HIT); + PlayerStatData_t statShots = g_CSClientGameStats.GetStatById(CSSTAT_SHOTS_FIRED); + + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetWString( "name", LocalizeTagOrUseDefault( "Stats_Accuracy", L"Accuracy" ) ); + pKeyValues->SetFloat( "playerValue", 0 ); + + float playerHits = personalMatchStats[statHits.iStatId]; + float tHits = tStats->m_fStat[statHits.iStatId]; + float ctHits = ctStats->m_fStat[statHits.iStatId]; + float serverHits = serverStats->m_fStat[statHits.iStatId]; + + float playerShots = personalMatchStats[statShots.iStatId]; + float tShots = tStats->m_fStat[statShots.iStatId]; + float ctShots = ctStats->m_fStat[statShots.iStatId]; + float serverShots = serverStats->m_fStat[statShots.iStatId]; + + float playerAccuracy = 0.0; + float tAccuracy = 0.0; + float ctAccuracy = 0.0; + float serverAccuracy = 0.0; + + if (playerShots > 0) + { + playerAccuracy = 100.0f * playerHits / playerShots; + } + + if (tShots > 0) + { + tAccuracy = 100.0f * tHits / tShots; + } + + if (ctShots > 0) + { + ctAccuracy = 100.0f * ctHits / ctShots; + } + + if (serverShots > 0) + { + serverAccuracy = 100.0f * serverHits / serverShots; + } + + char buf[64]; + Q_snprintf( buf, sizeof( buf ), "%.1f%%", playerAccuracy ); + pKeyValues->SetString( "playerValue", (playerAccuracy)?buf:"" ); + + Q_snprintf( buf, sizeof( buf ), "%.1f%%", tAccuracy); + pKeyValues->SetString( "tValue", (playerAccuracy||tAccuracy)?buf:"" ); + + Q_snprintf( buf, sizeof( buf ), "%.1f%%", ctAccuracy ); + pKeyValues->SetString( "ctValue", (playerAccuracy||ctAccuracy)?buf:"" ); + + Q_snprintf( buf, sizeof( buf ), "%.1f%%", serverAccuracy ); + pKeyValues->SetString( "serverValue", (playerAccuracy||serverAccuracy)?buf:"" ); + + int newItem = m_statsList->AddItem(0, pKeyValues); + pKeyValues->deleteThis(); + + m_statsList->SetItemFont(newItem , m_listItemFont); + m_statsList->SetItemFgColor(newItem, Color(197,197,197,255)); + + return newItem; +} + + +void CMatchStatsPage::OnSizeChanged(int newWide, int newTall) +{ + BaseClass::OnSizeChanged(newWide, newTall); + + if (m_statsList) + { + if (m_ResetNotice) + { + int labelX, labelY, listX, listY, listWide, listTall; + m_statsList->GetBounds(listX, listY, listWide, listTall); + + m_ResetNotice->GetPos(labelX, labelY); + m_ResetNotice->SetPos(labelX, listY + listTall + 4); + } + } +} diff --git a/game/client/cstrike/VGUI/match_stats_page.h b/game/client/cstrike/VGUI/match_stats_page.h new file mode 100644 index 0000000..9c85902 --- /dev/null +++ b/game/client/cstrike/VGUI/match_stats_page.h @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSMATCHSTATSPAGE_H +#define CSMATCHSTATSPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "base_stats_page.h" + +class CMatchStatsPage : public CBaseStatsPage +{ + DECLARE_CLASS_SIMPLE ( CMatchStatsPage, CBaseStatsPage ); + +public: + CMatchStatsPage( vgui::Panel *parent, const char *name ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + void RepopulateStats(); + int AddSimpleStat( int desiredStat, const StatsCollection_t& personalMatchStats, RoundStatsDirectAverage_t* tStats, RoundStatsDirectAverage_t* ctStats, RoundStatsDirectAverage_t* serverStats ); + int AddAccuracyStat(const StatsCollection_t& personalMatchStats, RoundStatsDirectAverage_t* tStats, RoundStatsDirectAverage_t* ctStats, RoundStatsDirectAverage_t* serverStats ); + int AddKillToDeathStat(const StatsCollection_t& personalMatchStats, RoundStatsDirectAverage_t* tStats, RoundStatsDirectAverage_t* ctStats, RoundStatsDirectAverage_t* serverStats ); + virtual void OnSizeChanged(int wide, int tall); + + CBaseStatGroupPanel* m_allStatsGroupPanel; + CBaseStatGroupPanel* m_detailedWeaponStatsGroupPanel; + CBaseStatGroupPanel* m_specialSkillsStatsGroupPanel; + CBaseStatGroupPanel* m_mapAndMiscellanyStatsGroupPanel; + CBaseStatGroupPanel* m_missionAndObjectiveStatsGroupPanel; + + vgui::Label* m_ResetNotice; +}; + +#endif // CSMATCHSTATSPAGE_H diff --git a/game/client/cstrike/VGUI/stat_card.cpp b/game/client/cstrike/VGUI/stat_card.cpp new file mode 100644 index 0000000..648b286 --- /dev/null +++ b/game/client/cstrike/VGUI/stat_card.cpp @@ -0,0 +1,106 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Create and display a win panel at the end of a round displaying interesting stats and info about the round. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "stat_card.h" +#include "vgui_controls/AnimationController.h" +#include "iclientmode.h" +#include "c_playerresource.h" +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include "fmtstr.h" +#include "../../public/steam/steam_api.h" +#include "c_cs_player.h" +#include "cs_gamestats_shared.h" +#include "cs_client_gamestats.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +StatCard::StatCard(vgui::Panel *parent, const char *name) : BaseClass(parent, "CSStatCard") +{ + //m_pAvatarDefault = new ImagePanel(this, ""); + //m_pBackgroundArt = new ImagePanel(this, "BackgroundArt"); + m_pAvatar = new CAvatarImagePanel(this, "Avatar"); + m_pAvatar->SetShouldScaleImage(true); + m_pAvatar->SetShouldDrawFriendIcon(false); + m_pAvatar->SetSize(64,64); + + m_pName= new Label(this, "Name", "Name"); + m_pKillToDeathRatio = new Label(this, "KillToDeath", "KillToDeath"); + m_pStars = new Label(this, "Stars", "Stars"); +} + +StatCard::~StatCard() +{ + +} + + +void StatCard::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings("Resource/UI/CSStatCard.res"); + + //m_pBackgroundArt->SetShouldScaleImage(true); + //m_pBackgroundArt->SetImage("../VGUI/achievements/achievement-btn-up"); + + SetBgColor(Color(0,0,0,0)); + + UpdateInfo(); +} + +void StatCard::UpdateInfo() +{ + const StatsCollection_t& personalLifetimeStats = g_CSClientGameStats.GetLifetimeStats(); + + int stars = personalLifetimeStats[CSSTAT_MVPS]; + float kills = personalLifetimeStats[CSSTAT_KILLS]; + float deaths = personalLifetimeStats[CSSTAT_DEATHS]; + wchar_t buf[64], numBuf[64]; + + if (deaths > 0) + { + float killToDeath = kills / deaths; + + _snwprintf( numBuf, ARRAYSIZE( numBuf ), L"%.2f", killToDeath); + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), + g_pVGuiLocalize->Find( "#GameUI_Stats_LastMatch_KDRatio" ), 1, numBuf ); + m_pKillToDeathRatio->SetText( buf ); + } + else + { + m_pKillToDeathRatio->SetText(""); + } + + _snwprintf( numBuf, ARRAYSIZE( numBuf ), L"%i", stars); + g_pVGuiLocalize->ConstructString( buf, sizeof(buf), + g_pVGuiLocalize->Find( "#GameUI_Stats_LastMatch_MVPS" ), 1, numBuf ); + m_pStars->SetText( buf ); + + if (steamapicontext) + { + ISteamFriends* friends = steamapicontext->SteamFriends(); + if (friends) + { + m_pName->SetText(friends->GetPersonaName()); + } + } + + // Display the player avatar + if (m_pAvatar && steamapicontext && steamapicontext->SteamUser()) + { + m_pAvatar->SetPlayer( steamapicontext->SteamUser()->GetSteamID(), k_EAvatarSize64x64 ); + m_pAvatar->SetVisible( true ); + } +} diff --git a/game/client/cstrike/VGUI/stat_card.h b/game/client/cstrike/VGUI/stat_card.h new file mode 100644 index 0000000..5a393ab --- /dev/null +++ b/game/client/cstrike/VGUI/stat_card.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STATCARD_H +#define STATCARD_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/IScheme.h> +#include "hud.h" +#include "hudelement.h" +#include "vgui_avatarimage.h" +#include "cs_shareddefs.h" +#include <vgui_controls/EditablePanel.h> + +using namespace vgui; + +class StatCard : public EditablePanel +{ +private: + DECLARE_CLASS_SIMPLE( StatCard, EditablePanel ); + +public: + StatCard(vgui::Panel *parent, const char *name); + ~StatCard(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + void UpdateInfo(); + +protected: + + + ImagePanel* m_pAvatarDefault; + //ImagePanel* m_pBackgroundArt; + CAvatarImagePanel* m_pAvatar; + + Label* m_pName; + Label* m_pKillToDeathRatio; + Label* m_pStars; + + +private: +}; + +#endif //STATCARD_H
\ No newline at end of file diff --git a/game/client/cstrike/VGUI/stats_summary.cpp b/game/client/cstrike/VGUI/stats_summary.cpp new file mode 100644 index 0000000..b2224c0 --- /dev/null +++ b/game/client/cstrike/VGUI/stats_summary.cpp @@ -0,0 +1,684 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Display a list of achievements for the current game +// +//=============================================================================// + +#include "cbase.h" +#include "stats_summary.h" +#include "vgui_controls/Button.h" +#include "vgui/ILocalize.h" +#include "ixboxsystem.h" +#include "iachievementmgr.h" +#include "filesystem.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/ImageList.h" +#include "fmtstr.h" +#include "c_cs_playerresource.h" + +#include "../../../public/steam/steam_api.h" +#include "achievementmgr.h" +#include "../../../../public/vgui/IScheme.h" +#include "../vgui_controls/ScrollBar.h" +#include "stat_card.h" + +#include "weapon_csbase.h" +#include "cs_weapon_parse.h" +#include "buy_presets/buy_presets.h" +#include "win_panel_round.h" +#include "cs_client_gamestats.h" +#include "achievements_cs.h" +#include "ienginevgui.h" + + +#define STAT_NUM_ARRAY_LENGTH 8 + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" +#include <locale.h> + +extern CAchievementMgr g_AchievementMgrCS; + +const int maxRecentAchievementToDisplay = 10; + +/** + * public ConvertNumberToMantissaSuffixForm() + * + * Convert a floating point number to a string form incorporating a mantissa and a magnitude suffix (such as "K" for thousands). + * + * Parameters: + * fNum - + * textBuffer - output buffer + * textBufferLen - size of output buffer in characters + * bTrimTrailingZeros - indicates whether to remove trailing zeros of numbers without suffixes (e.g., "32.00000" becomes "32") + * iSignificantDigits - the desired number of significant digits in the result (e.g, "1.23" has 3 significant digits) + * + * Returns: + * bool - true on success + */ +bool ConvertNumberToMantissaSuffixForm(float fNum, wchar_t* textBuffer, int textBufferLen, bool bTrimTrailingZeros = true, int iSignificantDigits = 3) +{ + // we need room for at least iSignificantDigits + 3 characters + Assert(textBufferLen >= iSignificantDigits + 3); + + // find the correct power of 1000 exponent + Assert(fNum >= 0.0f); + int iExponent = 0; + while (fNum >= powf(10.0f, iExponent + 3)) + { + iExponent += 3; + } + + // look in the localization table for the matching suffix; fallback to lower suffixes when necessary + wchar_t *pSuffix = NULL; + for (; iExponent > 0; iExponent -= 3) + { + char localizationToken[64]; + V_snprintf(localizationToken, sizeof(localizationToken), "#GameUI_NumSuffix_E%i", iExponent); + pSuffix = g_pVGuiLocalize->Find(localizationToken); + if (pSuffix != NULL) + break; + } + + V_snwprintf(textBuffer, textBufferLen, L"%f", fNum / powf(10.0f, iExponent)); + textBuffer[textBufferLen - 1] = L'\0'; + + lconv* pLocaleSettings = localeconv(); + wchar_t decimalWChar = *pLocaleSettings->decimal_point; + wchar_t* pDecimalPos = wcschr(textBuffer, decimalWChar); + if (pDecimalPos == NULL) + { + Msg("ConvertNumberToMantissaSuffixForm(): decimal point not found in string %S for value %f\n", textBuffer, fNum); + return false; + } + + // trim trailing zeros + int iNumCharsInBuffer = wcslen(textBuffer); + if (pSuffix == NULL && bTrimTrailingZeros) + { + wchar_t* pLastChar; + for (pLastChar = &textBuffer[iNumCharsInBuffer - 1]; pLastChar > pDecimalPos; --pLastChar) + { + if (*pLastChar != L'0') // note that this is checking for '0', not a NULL terminator + break; + + *pLastChar = L'\0'; + } + if (pLastChar == pDecimalPos) + *pLastChar = L'\0'; + } + + // truncate the mantissa to the right of the decimal point so that it doesn't exceed the significant digits + if (pDecimalPos != NULL && iNumCharsInBuffer > iSignificantDigits + 1) + { + if (pDecimalPos - &textBuffer[0] < iSignificantDigits) + textBuffer[iSignificantDigits + 1] = L'\0'; // truncate at the correct number of significant digits + else + *pDecimalPos = L'\0'; // no room for any digits to the right of the decimal point, just truncate it at the "." + } + + if (pSuffix != NULL) + { +#ifdef WIN32 + int retVal = V_snwprintf( textBuffer, textBufferLen, L"%s%s", textBuffer, pSuffix ); +#else + int retVal = V_snwprintf( textBuffer, textBufferLen, L"%S%S", textBuffer, pSuffix ); +#endif + + if ( retVal < 0 ) + { + Msg("ConvertNumberToMantissaSuffixForm(): unable to add suffix %S for value %f (buffer size was %i)\n", pSuffix, fNum, textBufferLen); + return false; + } + } + + return true; +} + + +CStatsSummary::CStatsSummary(vgui::Panel *parent, const char *name) : BaseClass(parent, "CSAchievementsDialog"), + m_StatImageMap(DefLessFunc(CSStatType_t)) +{ + m_iFixedWidth = 900; // Give this an initial value in order to set a proper size + SetBounds(0, 0, 900, 780); + SetMinimumSize(256, 780); + + m_pStatCard = new StatCard(this, "ignored"); + + m_pImageList = NULL; + m_iDefaultWeaponImage = -1; + m_iDefaultMapImage = -1; + + m_pImagePanelFavWeapon = new ImagePanel(this, "FavoriteWeaponImage"); + m_pImagePanelLastMapFavWeapon = new ImagePanel(this, "FavWeaponIcon"); + m_pImagePanelFavMap = new ImagePanel(this, "FavoriteMapImage"); + + //Setup the recent achievement list by adding all achieved achievements to the list + m_pRecentAchievementsList = new vgui::PanelListPanel(this, "RecentAchievementsListPanel"); + m_pRecentAchievementsList->SetFirstColumnWidth(0); + + ListenForGameEvent( "player_stats_updated" ); + ListenForGameEvent( "achievement_earned_local" ); + + m_bRecentAchievementsDirty = true; + m_bStatsDirty = true; +} + +CStatsSummary::~CStatsSummary() +{ + if (m_pRecentAchievementsList) + { + m_pRecentAchievementsList->DeleteAllItems(); + } + + delete m_pRecentAchievementsList; + delete m_pImageList; +} + +void CStatsSummary::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + LoadControlSettings("Resource/UI/CSStatsSummary.res"); + + SetBgColor(Color(86,86,86,255)); + + if (m_pImageList) + delete m_pImageList; + m_pImageList = new ImageList(false); + + if (m_pImageList) + { + //Load defaults + m_iDefaultWeaponImage = m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/defaultweapon", true)); + m_iDefaultMapImage = m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_generic_map", true)); + + //load weapon images + m_StatImageMap.Insert(CSSTAT_KILLS_DEAGLE, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/deserteagle", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_USP, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/usp45", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_GLOCK, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/glock18", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_P228, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/p228", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_ELITE, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/elites", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_FIVESEVEN, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/fiveseven", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_AWP, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/awp", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_AK47, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/ak47", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_M4A1, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/m4a1", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_AUG, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/aug", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_SG552, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/sg552", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_SG550, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/sg550", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_GALIL, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/galil", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_FAMAS, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/famas", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_SCOUT, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/scout", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_G3SG1, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/g3sg1", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_P90, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/p90", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_MP5NAVY, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/mp5", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_TMP, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/tmp", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_MAC10, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/mac10", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_UMP45, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/ump45", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_M3, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/m3", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_XM1014, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/xm1014", true))); + m_StatImageMap.Insert(CSSTAT_KILLS_M249, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/fav_weap/m249", true))); + + //load map images + m_StatImageMap.Insert(CSSTAT_MAP_WINS_CS_ASSAULT, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_assault", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_CS_COMPOUND, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_compound", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_CS_HAVANA, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_havana", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_CS_ITALY, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_italy", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_CS_MILITIA, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_militia", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_CS_OFFICE, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_cs_office", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_AZTEC, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_aztec", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_CBBLE, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_cbble", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_CHATEAU, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_chateau", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_DUST2, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_dust2", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_DUST, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_dust", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_INFERNO, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_inferno", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_NUKE, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_nuke", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_PIRANESI, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_piranesi", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_PORT, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_port", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_PRODIGY, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_prodigy", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_TIDES, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_tides", true))); + m_StatImageMap.Insert(CSSTAT_MAP_WINS_DE_TRAIN, m_pImageList->AddImage(scheme()->GetImage("gfx/vgui/summary_maps/summary_de_train", true))); + } +} +//---------------------------------------------------------- +// Get the width we're going to lock at +//---------------------------------------------------------- +void CStatsSummary::ApplySettings(KeyValues *pResourceData) +{ + m_iFixedWidth = pResourceData->GetInt("wide", 512); + + BaseClass::ApplySettings(pResourceData); +} + +//---------------------------------------------------------- +// Preserve our width to the one in the .res file +//---------------------------------------------------------- +void CStatsSummary::OnSizeChanged(int newWide, int newTall) +{ + // Lock the width, but allow height scaling + if (newWide != m_iFixedWidth) + { + SetSize(m_iFixedWidth, newTall); + return; + } + + BaseClass::OnSizeChanged(newWide, newTall); +} + + +void CStatsSummary::OnThink() +{ + if ( m_bRecentAchievementsDirty ) + UpdateRecentAchievements(); + + if ( m_bStatsDirty ) + UpdateStatsData(); +} + + +void CStatsSummary::OnPageShow() +{ + UpdateStatsData(); + UpdateRecentAchievements(); +} + +//---------------------------------------------------------- +// Update all of the stats displays +//---------------------------------------------------------- +void CStatsSummary::UpdateStatsData() +{ + UpdateStatsSummary(); + UpdateKillHistory(); + UpdateFavoriteWeaponData(); + UpdateMapsData(); + + + UpdateLastMatchStats(); + m_pStatCard->UpdateInfo(); + + m_bStatsDirty = false; +} + +//---------------------------------------------------------- +// Update the stats located in the stats summary section +//---------------------------------------------------------- +void CStatsSummary::UpdateStatsSummary() +{ + DisplayCompressedLocalizedStat(CSSTAT_ROUNDS_PLAYED, "roundsplayed"); + DisplayCompressedLocalizedStat(CSSTAT_ROUNDS_WON, "roundswon"); + DisplayCompressedLocalizedStat(CSSTAT_SHOTS_HIT, "shotshit"); + DisplayCompressedLocalizedStat(CSSTAT_SHOTS_FIRED, "shotsfired"); + + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + + //Calculate Win Ratio + int wins = g_CSClientGameStats.GetStatById(CSSTAT_ROUNDS_WON).iStatValue; + int rounds = g_CSClientGameStats.GetStatById(CSSTAT_ROUNDS_PLAYED).iStatValue; + float winRatio = 0; + if (rounds > 0) + { + winRatio = ((float)wins / (float)rounds) * 100.0f; + } + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f%%", winRatio); + SetDialogVariable("winratio", tempWNumString); + + + //Calculate accuracy + int hits = g_CSClientGameStats.GetStatById(CSSTAT_SHOTS_HIT).iStatValue; + int shots = g_CSClientGameStats.GetStatById(CSSTAT_SHOTS_FIRED).iStatValue; + float accuracy = 0; + if (shots > 0) + { + accuracy = ((float)hits / (float)shots) * 100.0f; + } + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f%%", accuracy); + SetDialogVariable("hitratio", tempWNumString); +} + +//---------------------------------------------------------- +// Update the stats located in the stats summary section +//---------------------------------------------------------- +void CStatsSummary::UpdateKillHistory() +{ + DisplayCompressedLocalizedStat(CSSTAT_KILLS, "kills"); + DisplayCompressedLocalizedStat(CSSTAT_DEATHS, "deaths"); + + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + + //Calculate Win Ratio + int kills = g_CSClientGameStats.GetStatById(CSSTAT_KILLS).iStatValue; + int deaths = g_CSClientGameStats.GetStatById(CSSTAT_DEATHS).iStatValue; + float killDeathRatio = 0; + if (deaths > 0) + { + killDeathRatio = (float)kills / (float)deaths; + } + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f", killDeathRatio); + SetDialogVariable("killdeathratio", tempWNumString); +} +//---------------------------------------------------------- +// Update the stats located in the favorite weapon section +//---------------------------------------------------------- +void CStatsSummary::UpdateFavoriteWeaponData() +{ + // First set the image to the favorite weapon + if (m_pImageList) + { + //start with a dummy stat + const WeaponName_StatId* pFavoriteWeaponStatEntry = NULL; + + //Find the weapon stat with the most kills + int numKills = 0; + for (int i = 0; WeaponName_StatId_Table[i].killStatId != CSSTAT_UNDEFINED; ++i) + { + // ignore weapons with no hit counts (knife and grenade) + if (WeaponName_StatId_Table[i].hitStatId == CSSTAT_UNDEFINED ) + continue; + + const WeaponName_StatId& weaponStatEntry = WeaponName_StatId_Table[i]; + PlayerStatData_t stat = g_CSClientGameStats.GetStatById(weaponStatEntry.killStatId); + if (stat.iStatValue > numKills) + { + pFavoriteWeaponStatEntry = &weaponStatEntry; + numKills = stat.iStatValue; + } + } + + if (pFavoriteWeaponStatEntry) + { + CUtlMap<CSStatType_t, int>::IndexType_t idx = m_StatImageMap.Find((CSStatType_t)pFavoriteWeaponStatEntry->killStatId); + if (m_StatImageMap.IsValidIndex(idx)) + { + m_pImagePanelFavWeapon->SetImage(m_pImageList->GetImage(m_StatImageMap[idx])); + } + + DisplayCompressedLocalizedStat(pFavoriteWeaponStatEntry->hitStatId, "weaponhits", "#GameUI_Stats_WeaponShotsHit"); + DisplayCompressedLocalizedStat(pFavoriteWeaponStatEntry->shotStatId, "weaponshotsfired", "#GameUI_Stats_WeaponShotsFired"); + DisplayCompressedLocalizedStat(pFavoriteWeaponStatEntry->killStatId, "weaponkills", "#GameUI_Stats_WeaponKills"); + + //Calculate accuracy + int kills = g_CSClientGameStats.GetStatById(pFavoriteWeaponStatEntry->killStatId).iStatValue; + int shots = g_CSClientGameStats.GetStatById(pFavoriteWeaponStatEntry->shotStatId).iStatValue; + float killsPerShot = 0; + if (shots > 0) + { + killsPerShot = (float)kills / (float)shots; + } + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f", (killsPerShot)); + DisplayFormattedLabel("#GameUI_Stats_WeaponKillRatio", tempWNumString, "weaponkillratio"); + } + else + { + if (m_iDefaultWeaponImage != -1) + { + m_pImagePanelFavWeapon->SetImage(m_pImageList->GetImage(m_iDefaultWeaponImage)); + } + DisplayFormattedLabel("#GameUI_Stats_WeaponShotsHit", L"0", "weaponhits"); + DisplayFormattedLabel("#GameUI_Stats_WeaponShotsFired", L"0", "weaponshotsfired"); + DisplayFormattedLabel("#GameUI_Stats_WeaponKills", L"0", "weaponkills"); + DisplayFormattedLabel("#GameUI_Stats_WeaponKillRatio", L"0", "weaponkillratio"); + } + } +} + +//---------------------------------------------------------- +// Update the stats located in the favorite weapon section +//---------------------------------------------------------- +void CStatsSummary::UpdateMapsData() +{ + //start with a dummy stat + const MapName_MapStatId* pFavoriteMapStat = NULL; + int numRounds = 0; + for (int i = 0; MapName_StatId_Table[i].statWinsId != CSSTAT_UNDEFINED; ++i) + { + const MapName_MapStatId& mapStatId = MapName_StatId_Table[i]; + PlayerStatData_t stat = g_CSClientGameStats.GetStatById(mapStatId.statRoundsId); + if (stat.iStatValue > numRounds) + { + pFavoriteMapStat = &mapStatId; + numRounds = stat.iStatValue; + } + } + + if (pFavoriteMapStat) + { + CUtlMap<CSStatType_t, int>::IndexType_t idx = m_StatImageMap.Find((CSStatType_t)pFavoriteMapStat->statWinsId); + if (m_StatImageMap.IsValidIndex(idx)) + { + m_pImagePanelFavMap->SetImage(m_pImageList->GetImage(m_StatImageMap[idx])); + } + + // map name + SetDialogVariable("mapname", pFavoriteMapStat->szMapName); + + // rounds played + DisplayCompressedLocalizedStat(pFavoriteMapStat->statRoundsId,"mapplayed", "#GameUI_Stats_MapPlayed"); + DisplayCompressedLocalizedStat(pFavoriteMapStat->statWinsId, "mapwins", "#GameUI_Stats_MapWins"); + + //Calculate Win Ratio + int wins = g_CSClientGameStats.GetStatById(pFavoriteMapStat->statWinsId).iStatValue; + int rounds = g_CSClientGameStats.GetStatById(pFavoriteMapStat->statRoundsId).iStatValue; + float winRatio = 0; + if (rounds > 0) + { + winRatio = ((float)wins / (float)rounds) * 100.0f; + } + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f%%", winRatio); + DisplayFormattedLabel("#GameUI_Stats_MapWinRatio", tempWNumString, "mapwinratio"); + } + else + { + if (m_iDefaultMapImage != -1) + { + m_pImagePanelFavMap->SetImage(m_pImageList->GetImage(m_iDefaultMapImage)); + } + SetDialogVariable("mapname", L""); + DisplayFormattedLabel("#GameUI_Stats_MapPlayed", L"0", "mapplayed"); + DisplayFormattedLabel("#GameUI_Stats_MapWins", L"0", "mapwins"); + DisplayFormattedLabel("#GameUI_Stats_MapWinRatio", L"0", "mapwinratio"); + } +} + +int CStatsSummary::AchivementDateSortPredicate( CCSBaseAchievement* const* pLeft, CCSBaseAchievement* const* pRight ) +{ + if (!pLeft || !pRight || !(*pLeft) || ! (*pRight)) + { + return 0; + } + + if ((*pLeft)->GetSortKey() < (*pRight)->GetSortKey()) + { + return 1; + } + else if ((*pLeft)->GetSortKey() > (*pRight)->GetSortKey()) + { + return -1; + } + else + { + return 0; + } +} + +void CStatsSummary::UpdateRecentAchievements() +{ + CUtlVector<CCSBaseAchievement*> sortedAchivementList; + + //Get a list of all the achievements that have been earned + int iCount = g_AchievementMgrCS.GetAchievementCount(); + sortedAchivementList.EnsureCapacity(iCount); + + for (int i = 0; i < iCount; ++i) + { + CCSBaseAchievement* pAchievement = (CCSBaseAchievement*)g_AchievementMgrCS.GetAchievementByIndex(i); + if (pAchievement && pAchievement->IsAchieved()) + { + sortedAchivementList.AddToTail(pAchievement); + } + } + + //Sort the earned achievements by time and date earned. + sortedAchivementList.Sort(AchivementDateSortPredicate); + + //Clear the UI list + m_pRecentAchievementsList->DeleteAllItems(); + + // Add each achievement in the sorted list as a panel. + int numPanelsToAdd = MIN(maxRecentAchievementToDisplay, sortedAchivementList.Count()); + for ( int i = 0; i < numPanelsToAdd; i++ ) + { + CCSBaseAchievement* pAchievement = sortedAchivementList[i]; + if (pAchievement) + { + CAchievementsPageItemPanel *achievementItemPanel = new CAchievementsPageItemPanel(m_pRecentAchievementsList, "AchievementDialogItemPanel"); + achievementItemPanel->SetAchievementInfo(pAchievement); + + // force all our new panel to have the correct internal layout and size so that our parent container can layout properly + achievementItemPanel->InvalidateLayout(true, true); + + m_pRecentAchievementsList->AddItem(NULL, achievementItemPanel); + } + } + + m_bRecentAchievementsDirty = false; +} + +void CStatsSummary::UpdateLastMatchStats() +{ + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_T_ROUNDS_WON, "lastmatchtwins", "#GameUI_Stats_LastMatch_TWins"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_CT_ROUNDS_WON, "lastmatchctwins", "#GameUI_Stats_LastMatch_CTWins"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_ROUNDS_WON, "lastmatchwins", "#GameUI_Stats_LastMatch_RoundsWon"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_MAX_PLAYERS, "lastmatchmaxplayers", "#GameUI_Stats_LastMatch_MaxPlayers"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_MVPS, "lastmatchstars", "#GameUI_Stats_LastMatch_MVPS"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_KILLS, "lastmatchkills", "#GameUI_Stats_WeaponKills"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_DEATHS, "lastmatchdeaths", "#GameUI_Stats_LastMatch_Deaths"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_DAMAGE, "lastmatchtotaldamage", "#GameUI_Stats_LastMatch_Damage"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_DOMINATIONS, "lastmatchdominations", "#GameUI_Stats_LastMatch_Dominations"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_REVENGES, "lastmatchrevenges", "#GameUI_Stats_LastMatch_Revenges"); + + UpdateLastMatchFavoriteWeaponStats(); + + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + + //Calculate Kill:Death ratio + int deaths = g_CSClientGameStats.GetStatById(CSSTAT_LASTMATCH_DEATHS).iStatValue; + int kills = g_CSClientGameStats.GetStatById(CSSTAT_LASTMATCH_KILLS).iStatValue; + float killDeathRatio = deaths; + if (deaths != 0) + { + killDeathRatio = (float)kills / (float)deaths; + } + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f", killDeathRatio); + DisplayFormattedLabel("#GameUI_Stats_LastMatch_KDRatio", tempWNumString, "lastmatchkilldeathratio"); + + //Calculate cost per kill + PlayerStatData_t statMoneySpent = g_CSClientGameStats.GetStatById(CSSTAT_LASTMATCH_MONEYSPENT); + int costPerKill = 0; + if (kills > 0) + { + costPerKill = statMoneySpent.iStatValue / kills; + } + ConvertNumberToMantissaSuffixForm(costPerKill, tempWNumString, STAT_NUM_ARRAY_LENGTH); + DisplayFormattedLabel("#GameUI_Stats_LastMatch_MoneySpentPerKill", tempWNumString, "lastmatchmoneyspentperkill"); +} + +void CStatsSummary::UpdateLastMatchFavoriteWeaponStats() +{ + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + + PlayerStatData_t statFavWeaponId = g_CSClientGameStats.GetStatById(CSSTAT_LASTMATCH_FAVWEAPON_ID); + const WeaponName_StatId& favoriteWeaponStat = GetWeaponTableEntryFromWeaponId((CSWeaponID)statFavWeaponId.iStatValue); + + // If we have a valid weapon use it's data; otherwise use nothing. We check the shot ID to make sure it is a weapon with bullets. + if (favoriteWeaponStat.hitStatId != CSSTAT_UNDEFINED) + { + // Set the image and (non-copyright) name of this weapon + CUtlMap<CSStatType_t, int>::IndexType_t idx = m_StatImageMap.Find((CSStatType_t)favoriteWeaponStat.killStatId); + if (m_StatImageMap.IsValidIndex(idx)) + { + m_pImagePanelLastMapFavWeapon->SetImage(m_pImageList->GetImage(m_StatImageMap[idx])); + } + + const wchar_t* tempName = WeaponIDToDisplayName(favoriteWeaponStat.weaponId); + if (tempName) + { + SetDialogVariable("lastmatchfavweaponname", tempName); + } + else + { + SetDialogVariable("lastmatchfavweaponname", L""); + } + + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_FAVWEAPON_KILLS, "lastmatchfavweaponkills", "#GameUI_Stats_WeaponKills"); + DisplayCompressedLocalizedStat(CSSTAT_LASTMATCH_FAVWEAPON_HITS, "lastmatchfavweaponhits", "#GameUI_Stats_WeaponShotsHit"); + + int shots = g_CSClientGameStats.GetStatById(CSSTAT_LASTMATCH_FAVWEAPON_SHOTS).iStatValue; + int hits = g_CSClientGameStats.GetStatById(CSSTAT_LASTMATCH_FAVWEAPON_HITS).iStatValue; + float accuracy = 0.0f; + if (shots != 0) + { + accuracy = ((float)hits / (float)shots) * 100.0f; + } + V_snwprintf(tempWNumString, ARRAYSIZE(tempWNumString), L"%.1f%%", accuracy); + DisplayFormattedLabel("#GameUI_Stats_LastMatch_FavWeaponAccuracy", tempWNumString, "lastmatchfavweaponaccuracy"); + } + else + { + if (m_iDefaultWeaponImage != -1) + { + m_pImagePanelLastMapFavWeapon->SetImage(m_pImageList->GetImage(m_iDefaultWeaponImage)); + } + + + //Set this label to diferent text, indicating that there is not a favorite weapon + SetDialogVariable("lastmatchfavweaponname", g_pVGuiLocalize->Find("#GameUI_Stats_LastMatch_NoFavWeapon")); + + DisplayFormattedLabel("#GameUI_Stats_WeaponKills", L"0", "lastmatchfavweaponkills"); + DisplayFormattedLabel("#GameUI_Stats_WeaponShotsHit", L"0", "lastmatchfavweaponhits"); + DisplayFormattedLabel("#GameUI_Stats_LastMatch_FavWeaponAccuracy", L"0", "lastmatchfavweaponaccuracy"); + } +} + +void CStatsSummary::FireGameEvent( IGameEvent *event ) +{ + const char *type = event->GetName(); + + if ( 0 == Q_strcmp( type, "achievement_earned_local" ) ) + { + m_bRecentAchievementsDirty = true; + } + + else if ( 0 == Q_strcmp( type, "player_stats_updated" ) ) + { + m_bStatsDirty = true; + } +} + +void CStatsSummary::DisplayCompressedLocalizedStat(CSStatType_t stat, const char* dialogVariable, const char* localizationToken /* = NULL */) +{ + wchar_t tempWNumString[STAT_NUM_ARRAY_LENGTH]; + int statValue = g_CSClientGameStats.GetStatById(stat).iStatValue; + ConvertNumberToMantissaSuffixForm(statValue, tempWNumString, STAT_NUM_ARRAY_LENGTH); + if (localizationToken) + { + DisplayFormattedLabel(localizationToken, tempWNumString, dialogVariable); + } + else + { + SetDialogVariable(dialogVariable, tempWNumString); + } +} + + +void CStatsSummary::DisplayFormattedLabel( const char* localizationToken, const wchar_t* valueText, const char* dialogVariable ) +{ + wchar_t tempWStatString[256]; + g_pVGuiLocalize->ConstructString(tempWStatString, sizeof(tempWStatString), + g_pVGuiLocalize->Find(localizationToken), 1, valueText); + SetDialogVariable(dialogVariable, tempWStatString); +} diff --git a/game/client/cstrike/VGUI/stats_summary.h b/game/client/cstrike/VGUI/stats_summary.h new file mode 100644 index 0000000..e4e0c41 --- /dev/null +++ b/game/client/cstrike/VGUI/stats_summary.h @@ -0,0 +1,84 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSSTATSSUMMARY_H +#define CSSTATSSUMMARY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/Label.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Button.h" +#include "c_cs_player.h" +#include "cs_gamestats_shared.h" +#include "achievements_page.h" +#include "GameEventListener.h" +#include "utlmap.h" + +class IAchievement; +class IScheme; +class CAchievementsPageGroupPanel; +class StatCard; +class CCSBaseAchievement; + +class CStatsSummary : public vgui::PropertyPage, public CGameEventListener +{ + DECLARE_CLASS_SIMPLE ( CStatsSummary, vgui::PropertyPage ); + +public: + CStatsSummary( vgui::Panel *parent, const char *name ); + ~CStatsSummary(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void OnSizeChanged( int newWide, int newTall ); + virtual void FireGameEvent( IGameEvent *event ); + + virtual void OnThink(); + virtual void OnPageShow(); + + void UpdateStatsData(); + void UpdateStatsSummary(); + void UpdateKillHistory(); + void UpdateFavoriteWeaponData(); + void UpdateMapsData(); + void UpdateRecentAchievements(); + void UpdateLastMatchStats(); + + void UpdateLastMatchFavoriteWeaponStats(); + +private: + + static int AchivementDateSortPredicate( CCSBaseAchievement* const* pLeft, CCSBaseAchievement* const* pRight); + void DisplayCompressedLocalizedStat(CSStatType_t stat, const char* dialogVariable, const char* localizationToken = NULL); + void DisplayFormattedLabel(const char* localizationToken, const wchar_t* valueText, const char* dialogVariable); + + int m_iFixedWidth; + int m_iDefaultWeaponImage; + int m_iDefaultMapImage; + + vgui::Label* m_pLabelRoundsPlayed; + vgui::Label* m_pLabelRoundsWon; + vgui::ImagePanel* m_pImagePanelFavWeapon; + vgui::ImagePanel* m_pImagePanelLastMapFavWeapon; + vgui::ImagePanel* m_pImagePanelFavMap; + + vgui::ImageList *m_pImageList; + + vgui::PanelListPanel *m_pRecentAchievementsList; + + StatCard* m_pStatCard; + + bool m_bRecentAchievementsDirty; + bool m_bStatsDirty; + + CUtlMap<CSStatType_t, int> m_StatImageMap; +}; +#endif // CSSTATSSUMMARY_H
\ No newline at end of file diff --git a/game/client/cstrike/VGUI/win_panel_round.cpp b/game/client/cstrike/VGUI/win_panel_round.cpp new file mode 100644 index 0000000..7c05f26 --- /dev/null +++ b/game/client/cstrike/VGUI/win_panel_round.cpp @@ -0,0 +1,470 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Create and display a win panel at the end of a round displaying interesting stats and info about the round. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "win_panel_round.h" +#include "vgui_controls/AnimationController.h" +#include "iclientmode.h" +#include "c_playerresource.h" +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include "fmtstr.h" +#include "cs_gamestats_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar cl_round_win_fade_time( "cl_round_win_fade_time", "1.5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + +DECLARE_HUDELEMENT_DEPTH( WinPanel_Round, 1 ); // 1 is foreground +extern const wchar_t *LocalizeFindSafe( const char *pTokenName ); + + +// helper function for converting wstrings to upper-case inline +// NB: this returns a pointer to a static buffer +wchar_t* UpperCaseWideString( const wchar_t* wszSource ) +{ + static wchar_t wszBuffer[256]; + V_wcsncpy(wszBuffer, wszSource, sizeof(wszBuffer)); + V_wcsupr(wszBuffer); + return wszBuffer; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +WinPanel_Round::WinPanel_Round( const char *pElementName ) : + BorderedPanel( NULL, pElementName ), + CHudElement( pElementName ), + m_bIsFading(false), + m_fFadeBeginTime(0.0f) +{ + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + SetParent(g_pClientMode->GetViewport()); + + SetScheme( "ClientScheme" ); + + RegisterForRenderGroup( "hide_for_scoreboard" ); + + m_bShouldBeVisible = false; +} + +WinPanel_Round::~WinPanel_Round() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WinPanel_Round::Reset() +{ + Hide(); +} + +void WinPanel_Round::Init() +{ + CHudElement::Init(); + + // listen for events + ListenForGameEvent( "round_end" ); + ListenForGameEvent( "round_start" ); + ListenForGameEvent( "cs_win_panel_round" ); + ListenForGameEvent( "cs_win_panel_match" ); + ListenForGameEvent( "round_mvp" ); + + InitLayout(); + + m_bShouldBeVisible = false; + m_bShowTimerDefend = false; + m_bShowTimerAttack = false; +} + + +void WinPanel_Round::InitLayout() +{ + // reload control settings when resolution changes to force update of proportional layout + LoadControlSettings("Resource/UI/Win_Round.res"); + + CAvatarImagePanel* pMVP_Avatar = dynamic_cast<CAvatarImagePanel*>(FindChildByName("MVP_Avatar")); + pMVP_Avatar->SetDefaultAvatar(scheme()->GetImage( CSTRIKE_DEFAULT_AVATAR, true)); + pMVP_Avatar->SetShouldDrawFriendIcon(false); +} + + +void WinPanel_Round::VidInit() +{ +} + +//============================================================================= +// HPE_BEGIN: +// [Forrest] Allow win panel to be turned off on client +//============================================================================= +ConVar cl_nowinpanel( + "cl_nowinpanel", + "0", + FCVAR_ARCHIVE, + "Turn on/off win panel on client" + ); +//============================================================================= +// HPE_END +//============================================================================= + +void WinPanel_Round::FireGameEvent( IGameEvent* event ) +{ + const char *pEventName = event->GetName(); + + if ( Q_strcmp( "round_end", pEventName ) == 0 ) + { + } + else if ( Q_strcmp( "round_start", pEventName ) == 0 ) + { + Hide(); + } + else if( Q_strcmp( "cs_win_panel_match", pEventName ) == 0 ) + { + Hide(); + } + else if( Q_strcmp( "round_mvp", pEventName ) == 0 ) + { + C_BasePlayer *basePlayer = UTIL_PlayerByUserId( event->GetInt( "userid" ) ); + CSMvpReason_t mvpReason = (CSMvpReason_t)event->GetInt( "reason" ); + + if( basePlayer ) + { + SetMVP( ToCSPlayer( basePlayer ), mvpReason ); + } + } + else if ( Q_strcmp( "cs_win_panel_round", pEventName ) == 0 ) + { + /* + "show_timer_defend" "bool" + "show_timer_attack" "bool" + "timer_time" "int" + + "final_event" "byte" // 0 - no event, 1 - bomb exploded, 2 - flag capped, 3 - timer expired + + "funfact_type" "byte" //WINPANEL_FUNFACT in cs_shareddef.h + "funfact_player" "byte" + "funfact_data1" "long" + "funfact_data2" "long" + "funfact_data3" "long" + */ + + if ( !g_PR ) + return; + + //============================================================================= + // HPE_BEGIN: + // [Forrest] Check if win panel is disabled. + //============================================================================= + static ConVarRef sv_nowinpanel( "sv_nowinpanel" ); + if ( sv_nowinpanel.GetBool() || cl_nowinpanel.GetBool() ) + return; + //============================================================================= + // HPE_END + //============================================================================= + + m_bShowTimerDefend = event->GetBool( "show_timer_defend" ); + m_bShowTimerAttack = event->GetBool( "show_timer_attack" ); + int iTimerTime = event->GetInt( "timer_time" ); + + int minutes = clamp( iTimerTime / 60, 0, 99 ); + int seconds = clamp( iTimerTime % 60, 0, 59 ); + + wchar_t time[8]; + _snwprintf( time, ARRAYSIZE( time ), L"%d:%02d", minutes, seconds ); + + SetDialogVariable("TIMER_TEXT", time); + + // Final Fun Fact + SetFunFactLabel( L""); + int iFunFactPlayer = event->GetInt("funfact_player"); + const char* funfactToken = event->GetString("funfact_token", ""); + + if (strlen(funfactToken) != 0) + { + wchar_t funFactText[256]; + wchar_t playerText[64]; + wchar_t dataText1[8], dataText2[8], dataText3[8]; + int param1 = event->GetInt("funfact_data1"); + int param2 = event->GetInt("funfact_data2"); + int param3 = event->GetInt("funfact_data3"); + if ( iFunFactPlayer >= 1 && iFunFactPlayer <= MAX_PLAYERS ) + { + const char* playerName = g_PR->GetPlayerName( iFunFactPlayer ); + if( playerName && Q_strcmp( playerName, PLAYER_UNCONNECTED_NAME ) != 0 && Q_strcmp( playerName, PLAYER_ERROR_NAME ) != 0 ) + { + V_strtowcs( g_PR->GetPlayerName( iFunFactPlayer ), 64, playerText, sizeof( playerText ) ); + } + else + { +#ifdef WIN32 + _snwprintf( playerText, ARRAYSIZE( playerText ), L"%s", LocalizeFindSafe( "#winpanel_former_player" ) ); +#else + _snwprintf( playerText, ARRAYSIZE( playerText ), L"%S", LocalizeFindSafe( "#winpanel_former_player" ) ); +#endif + } + } + else + { + _snwprintf( playerText, ARRAYSIZE( playerText ), L"" ); + } + _snwprintf( dataText1, ARRAYSIZE( dataText1 ), L"%i", param1 ); + _snwprintf( dataText2, ARRAYSIZE( dataText2 ), L"%i", param2 ); + _snwprintf( dataText3, ARRAYSIZE( dataText3 ), L"%i", param3 ); + g_pVGuiLocalize->ConstructString( funFactText, sizeof(funFactText), (wchar_t *)LocalizeFindSafe(funfactToken), 4, + playerText, dataText1, dataText2, dataText3 ); + SetFunFactLabel(funFactText); + } + + int iEndEvent = event->GetInt( "final_event" ); + + //Map the round end events onto localized strings + const char* endEventToString[RoundEndReason_Count]; + V_memset(endEventToString, 0, sizeof(endEventToString)); + + //terrorist win events + endEventToString[Target_Bombed] = "#winpanel_end_target_bombed"; + endEventToString[VIP_Assassinated] = "#winpanel_end_vip_assassinated"; + endEventToString[Terrorists_Escaped] = "#winpanel_end_terrorists_escaped"; + endEventToString[Terrorists_Win] = "#winpanel_end_terrorists__kill"; + endEventToString[Hostages_Not_Rescued] = "#winpanel_end_hostages_not_rescued"; + endEventToString[VIP_Not_Escaped] = "#winpanel_end_vip_not_escaped"; + + //CT win events + endEventToString[VIP_Escaped] = "#winpanel_end_vip_escaped"; + endEventToString[CTs_PreventEscape] = "#winpanel_end_cts_prevent_escape"; + endEventToString[Escaping_Terrorists_Neutralized] = "#winpanel_end_escaping_terrorists_neutralized"; + endEventToString[Bomb_Defused] = "#winpanel_end_bomb_defused"; + endEventToString[CTs_Win] = "#winpanel_end_cts_win"; + endEventToString[All_Hostages_Rescued] = "#winpanel_end_all_hostages_rescued"; + endEventToString[Target_Saved] = "#winpanel_end_target_saved"; + endEventToString[Terrorists_Not_Escaped] = "#winpanel_end_terrorists_not_escaped"; + + //We don't show a round end panel for these + endEventToString[Game_Commencing] = ""; + endEventToString[Round_Draw] = ""; + + const wchar_t* wszEventMessage = NULL; + if(iEndEvent >=0 && iEndEvent < RoundEndReason_Count) + wszEventMessage = LocalizeFindSafe(endEventToString[iEndEvent]); + + if ( wszEventMessage != NULL ) + { + SetDialogVariable("WIN_DESCRIPTION", UpperCaseWideString(wszEventMessage)); + } + else + { + SetDialogVariable("WIN_DESCRIPTION", ""); + } + + Label* pWinLabel = dynamic_cast<Label*>(FindChildByName("WinLabel")); + switch(iEndEvent) + { + case Target_Bombed: + case VIP_Assassinated: + case Terrorists_Escaped: + case Terrorists_Win: + case Hostages_Not_Rescued: + case VIP_Not_Escaped: + pWinLabel->SetText(UpperCaseWideString(LocalizeFindSafe("#winpanel_t_win"))); + pWinLabel->SetFgColor(Color(184,0,0,255)); + break; + + case VIP_Escaped: + case CTs_PreventEscape: + case Escaping_Terrorists_Neutralized: + case Bomb_Defused: + case CTs_Win: + case All_Hostages_Rescued: + case Target_Saved: + case Terrorists_Not_Escaped: + pWinLabel->SetText(UpperCaseWideString(LocalizeFindSafe("#winpanel_ct_win"))); + pWinLabel->SetFgColor(Color(71,152,237,255)); + break; + + case Round_Draw: + pWinLabel->SetText(UpperCaseWideString(LocalizeFindSafe("#winpanel_draw"))); + pWinLabel->SetFgColor(Color(204,204,204,255)); + break; + } + + //[tj] We set the icon to the generic one right before we show it. + // The expected result is that we replace it immediately with + // the round MVP. if there is none, we just use the generic. + SetMVP( NULL, CSMVP_UNDEFINED ); + + Show(); + } +} + +void WinPanel_Round::SetMVP( C_CSPlayer* pPlayer, CSMvpReason_t reason ) +{ + CAvatarImagePanel* pMVP_Avatar = dynamic_cast<CAvatarImagePanel*>(FindChildByName("MVP_Avatar")); + + if ( pMVP_Avatar ) + { + pMVP_Avatar->ClearAvatar(); + } + + //First set the text to the name of the player + //============================================================================= + // HPE_BEGIN: + // [Forrest] Allow MVP to be turned off for a server + //============================================================================= + bool isThereAnMVP = ( pPlayer != NULL ); + if ( isThereAnMVP ) + //============================================================================= + // HPE_END + //============================================================================= + { + + const char* mvpReasonToken = NULL; + switch ( reason ) + { + case CSMVP_ELIMINATION: + mvpReasonToken = "winpanel_mvp_award_kills"; + break; + case CSMVP_BOMBPLANT: + mvpReasonToken = "winpanel_mvp_award_bombplant"; + break; + case CSMVP_BOMBDEFUSE: + mvpReasonToken = "winpanel_mvp_award_bombdefuse"; + break; + case CSMVP_HOSTAGERESCUE: + mvpReasonToken = "winpanel_mvp_award_rescue"; + break; + default: + mvpReasonToken = "winpanel_mvp_award"; + break; + } + + wchar_t wszBuf[256], wszPlayerName[64]; + g_pVGuiLocalize->ConvertANSIToUnicode(UTIL_SafeName(pPlayer->GetPlayerName()), wszPlayerName, sizeof(wszPlayerName)); + + wchar_t *pReason = (wchar_t *)LocalizeFindSafe( mvpReasonToken ); + if ( !pReason ) + { + pReason = L"%s1"; + } + + g_pVGuiLocalize->ConstructString( wszBuf, sizeof( wszBuf ), pReason, 1, wszPlayerName ); + SetDialogVariable( "MVP_TEXT", wszBuf ); + + player_info_t pi; + if ( engine->GetPlayerInfo(pPlayer->entindex(), &pi) ) + { + if ( pMVP_Avatar ) + { + pMVP_Avatar->SetDefaultAvatar( GetDefaultAvatarImage( pPlayer ) ); + pMVP_Avatar->SetPlayer( pPlayer, k_EAvatarSize64x64 ); + } + } + } + else + { + SetDialogVariable( "MVP_TEXT", ""); + } + + //============================================================================= + // HPE_BEGIN: + // [Forrest] Allow MVP to be turned off for a server + //============================================================================= + // The avatar image and its accompanying elements should be hidden if there is no MVP for the round. + if ( pMVP_Avatar ) + { + pMVP_Avatar->SetVisible( isThereAnMVP ); + } + ImagePanel* pMVP_AvatarGlow = dynamic_cast<ImagePanel*>(FindChildByName("MVP_AvatarGlow")); + if ( pMVP_AvatarGlow ) + { + pMVP_AvatarGlow->SetVisible( isThereAnMVP ); + } + ImagePanel* pMVP_Foreground_Star = dynamic_cast<ImagePanel*>(FindChildByName("MVP_Foreground_Star")); + if ( pMVP_Foreground_Star ) + { + pMVP_Foreground_Star->SetVisible( isThereAnMVP ); + } + //============================================================================= + // HPE_END + //============================================================================= +} + +void WinPanel_Round::SetFunFactLabel( const wchar *szFunFact ) +{ + SetDialogVariable( "FUNFACT", szFunFact ); +} + +void WinPanel_Round::Show( void ) +{ + int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "hide_for_round_panel" ); + if ( iRenderGroup >= 0) + { + gHUD.LockRenderGroup( iRenderGroup ); + } + + m_bShouldBeVisible = true; + SetAlpha(255); + m_bIsFading = false; +} + +void WinPanel_Round::Hide( void ) +{ + if ( m_bShouldBeVisible && !m_bIsFading ) + { + m_bIsFading = true; + m_fFadeBeginTime = gpGlobals->realtime; + } +} + +void WinPanel_Round::OnThink() +{ + if ( m_bShouldBeVisible && m_bIsFading ) + { + float fAlpha = 1.0f - (gpGlobals->realtime - m_fFadeBeginTime) / cl_round_win_fade_time.GetFloat(); + + if (fAlpha >= 0.0f) + { + SetAlpha(RoundFloatToInt(fAlpha * 255.0f)); + } + else + { + int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "hide_for_round_panel" ); + if ( iRenderGroup >= 0 ) + { + gHUD.UnlockRenderGroup( iRenderGroup ); + } + m_bShouldBeVisible = false; + SetAlpha(0); + m_bIsFading = false; + } + } +} + +void WinPanel_Round::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetFgColor(Color(251,176,59,255)); + SetBgColor(Color(0,0,0,212)); +} + +void WinPanel_Round::OnScreenSizeChanged( int nOldWide, int nOldTall ) +{ + BaseClass::OnScreenSizeChanged(nOldWide, nOldTall); + + InitLayout(); + + +} + +bool WinPanel_Round::ShouldDraw( void ) +{ + return ( m_bShouldBeVisible && CHudElement::ShouldDraw()); +} diff --git a/game/client/cstrike/VGUI/win_panel_round.h b/game/client/cstrike/VGUI/win_panel_round.h new file mode 100644 index 0000000..f5a6d7e --- /dev/null +++ b/game/client/cstrike/VGUI/win_panel_round.h @@ -0,0 +1,65 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Create and display a win panel at the end of a round displaying interesting stats and info about the round. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CSWINPANEL_ROUND_H +#define CSWINPANEL_ROUND_H +#ifdef _WIN32 +#pragma once +#endif + +#include "VGUI/bordered_panel.h" +#include <game/client/iviewport.h> +#include <vgui/IScheme.h> +#include "hud.h" +#include "hudelement.h" +#include "c_cs_player.h" +#include "vgui_avatarimage.h" + +#include "cs_shareddefs.h" + +using namespace vgui; + +class WinPanel_Round : public BorderedPanel, public CHudElement +{ +private: + DECLARE_CLASS_SIMPLE( WinPanel_Round, BorderedPanel ); + +public: + WinPanel_Round(const char *pElementName); + ~WinPanel_Round(); + + virtual void Reset(); + virtual void Init(); + virtual void VidInit(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void FireGameEvent( IGameEvent * event ); + virtual bool ShouldDraw( void ); + virtual void Paint( void ) {}; + virtual void OnScreenSizeChanged(int nOldWide, int nOldTall); + + virtual void OnThink(); + + void InitLayout(); + void Show(); + void Hide(); + +protected: + void SetMVP( C_CSPlayer* pPlayer, CSMvpReason_t reason ); + void SetFunFactLabel( const wchar *szFunFact ); + +private: + bool m_bShowTimerDefend; + bool m_bShowTimerAttack; + + bool m_bShouldBeVisible; + + // fade tracking + bool m_bIsFading; + float m_fFadeBeginTime; +}; + +#endif //CSWINPANEL_ROUND_H
\ No newline at end of file |