diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/cstrike | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/cstrike')
116 files changed, 33211 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 diff --git a/game/client/cstrike/buy_presets/buy_preset.cpp b/game/client/cstrike/buy_presets/buy_preset.cpp new file mode 100644 index 0000000..369fccc --- /dev/null +++ b/game/client/cstrike/buy_presets/buy_preset.cpp @@ -0,0 +1,1195 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#include "buy_preset_debug.h" +#include "buy_presets.h" +#include "weapon_csbase.h" +#include "cs_ammodef.h" +#include "cs_gamerules.h" +#include "cstrike/bot/shared_util.h" +#include "vgui/ILocalize.h" +#include <vgui_controls/Controls.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar cl_rebuy; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the local player is a CT and the map is a defuse map. + */ +static bool CanBuyDefuser() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + return ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT && CSGameRules()->IsBombDefuseMap() ); +} + + +void BuyPresetManager::GetCurrentLoadout( WeaponSet *weaponSet ) +{ + if ( !weaponSet ) + return; + + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + if ( !player ) + return; + + CWeaponCSBase *pWeapon; + const CCSWeaponInfo *pInfo; + + int ammo[MAX_AMMO_TYPES]; + memset( ammo, 0, sizeof(ammo) ); + FillClientAmmo( ammo ); + + weaponSet->Reset(); + + // Grab current armor values + weaponSet->m_armor = ( player->ArmorValue() > 0 ) ? 100 : 0; + weaponSet->m_helmet = player->HasHelmet(); + + // Grab current smoke grenade + pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE )); + pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE ); + int ammoType = (pInfo)?pInfo->iAmmoType:0; + weaponSet->m_smokeGrenade = (pWeapon && player->GetAmmoCount( ammoType )); + + // Grab current HE grenade + pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE )); + pInfo = GetWeaponInfo( WEAPON_HEGRENADE ); + ammoType = (pInfo)?pInfo->iAmmoType:0; + weaponSet->m_HEGrenade = (pWeapon && player->GetAmmoCount( ammoType )); + + // Grab current flashbangs + pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG )); + pInfo = GetWeaponInfo( WEAPON_FLASHBANG ); + ammoType = (pInfo)?pInfo->iAmmoType:0; + weaponSet->m_flashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType ); + + // Grab current equipment + weaponSet->m_defuser = player->HasDefuser(); + weaponSet->m_nightvision = player->HasNightVision(); + + // Grab current primary weapon + CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon + pInfo = GetWeaponInfo( primaryID ); + if ( pInfo ) + { + int roundsNeeded = ammo[pInfo->iAmmoType]; + int buySize = GetCSAmmoDef()->GetBuySize( pInfo->iAmmoType ); + int numClips = ceil(roundsNeeded/(float)buySize); + + weaponSet->m_primaryWeapon.SetWeaponID( primaryID ); + weaponSet->m_primaryWeapon.SetAmmoType( AMMO_CLIPS ); + weaponSet->m_primaryWeapon.SetAmmoAmount( numClips ); + weaponSet->m_primaryWeapon.SetFillAmmo( false ); + } + + // Grab current pistol + CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol + pInfo = GetWeaponInfo( secondaryID ); + if ( pInfo ) + { + int roundsNeeded = ammo[pInfo->iAmmoType]; + int buySize = GetCSAmmoDef()->GetBuySize( pInfo->iAmmoType ); + int numClips = ceil(roundsNeeded/(float)buySize); + + weaponSet->m_secondaryWeapon.SetWeaponID( secondaryID ); + weaponSet->m_secondaryWeapon.SetAmmoType( AMMO_CLIPS ); + weaponSet->m_secondaryWeapon.SetAmmoAmount( numClips ); + weaponSet->m_secondaryWeapon.SetFillAmmo( false ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +WeaponSet::WeaponSet() +{ + Reset(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Sets reasonable defaults for an empty weapon set: no primary/secondary weapons, no equipment, and + * everything optional. + */ +void WeaponSet::Reset() +{ + BuyPresetWeapon blank; + m_primaryWeapon = blank; + m_secondaryWeapon = blank; + + m_armor = 0; + m_helmet = false; + m_smokeGrenade = false; + m_HEGrenade = false; + m_flashbangs = 0; + m_defuser = false; + m_nightvision = false; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Constructs a WeaponSet containing everything that could be purchased right now. The cost is also + * returned, and is -1 if the set is not purchasable (as opposed to completely purchased already, in + * which case cost is 0.) + */ +void WeaponSet::GetCurrent( int& cost, WeaponSet& ws ) const +{ + cost = -1; + ws.Reset(); + + if ( !engine->IsConnected() ) + return; + + C_CSPlayer *player = CCSPlayer::GetLocalCSPlayer(); + if ( !player ) + return; + + if ( player->GetTeamNumber() != TEAM_CT && player->GetTeamNumber() != TEAM_TERRORIST ) + return; + + // we're connected, and a valid team, so we can purchase + cost = 0; + + if ( player->IsVIP() ) + return; // can't buy anything as the VIP + + 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 ); + } + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + // Buy any required items + + //------------------------------------------------------------------------- + // Armor + if ( m_armor > player->ArmorValue() ) + { + cost += iKevlarPrice; + ws.m_armor = 100; + } + + if ( (m_helmet && m_armor > 0) && !player->HasHelmet() ) + { + cost += iHelmetPrice; + ws.m_armor = 100; + ws.m_helmet = true; + } + + CWeaponCSBase *pWeapon; + CCSWeaponInfo *pInfo; + + //------------------------------------------------------------------------- + // Smoke grenade + pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE )); + pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE ); + int ammoType = (pInfo)?pInfo->iAmmoType:0; + + bool hasSmokeGrenade = (pWeapon && player->GetAmmoCount( ammoType )); + if ( m_smokeGrenade && !hasSmokeGrenade ) + { + cost += pInfo->GetWeaponPrice(); + ws.m_smokeGrenade = true; + hasSmokeGrenade = true; + } + + //------------------------------------------------------------------------- + // HE grenade + pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE )); + pInfo = GetWeaponInfo( WEAPON_HEGRENADE ); + ammoType = (pInfo)?pInfo->iAmmoType:0; + + bool hasHEGrenade = (pWeapon && player->GetAmmoCount( ammoType )); + if ( m_HEGrenade && !hasHEGrenade ) + { + cost += pInfo->GetWeaponPrice(); + ws.m_HEGrenade = true; + hasHEGrenade = true; + } + + //------------------------------------------------------------------------- + // Flashbang grenades + pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG )); + pInfo = GetWeaponInfo( WEAPON_FLASHBANG ); + ammoType = (pInfo)?pInfo->iAmmoType:0; + + int numFlashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType ); + if ( m_flashbangs && numFlashbangs < m_flashbangs ) + { + int count = m_flashbangs - numFlashbangs; + cost += pInfo->GetWeaponPrice() * count; + ws.m_flashbangs = count; + numFlashbangs += count; + } + + //------------------------------------------------------------------------- + // defuser + if ( m_defuser && player->GetTeamNumber() == TEAM_CT && !player->HasDefuser() && CanBuyDefuser() ) + { + cost += DEFUSEKIT_PRICE; + ws.m_defuser = true; + } + + //------------------------------------------------------------------------- + // nightvision goggles + if ( m_nightvision && !player->HasNightVision() ) + { + cost += iNVGPrice; + ws.m_nightvision = true; + } + + //------------------------------------------------------------------------- + // Construct a list of weapons to buy from. + CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon + CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol + + BuyPresetWeaponList primaryWeapons; + primaryWeapons.AddToTail( m_primaryWeapon ); + + BuyPresetWeaponList secondaryWeapons; + secondaryWeapons.AddToTail( m_secondaryWeapon ); + + /** + * Determine best weapons & ammo to purchase + * + * For each primary weapon in the list, do the following until one satisfies the current money situation: + * 1. buy primary weapon and its ammo (NEED TO WATCH FOR WEAPONS PLAYER CANNOT BUY!!!) + * 2. for each non-optional secondary weapon (if any), + * try to buy it and its ammo + * 3. when a set can be purchased, add it to the total cost and stop processing + * If we can't purchase the primary weapon, or a required secondary weapon, the WeaponSet can't be bought, + * so we return -1. + * + * To find if a weapon is owned, we can compare it's weaponID to either primaryID or secondaryID from above. + */ + + int currentCost = cost; + int ammo[MAX_AMMO_TYPES]; + memset( ammo, 0, sizeof(ammo) ); + FillClientAmmo( ammo ); + const BuyPresetWeapon *primaryWeaponToBuy = NULL; + const BuyPresetWeapon *secondaryWeaponToBuy = NULL; + int primaryClipsToBuy = 0; + int secondaryClipsToBuy = 0; + int currentNonWeaponCost = currentCost; + bool doneBuyingWeapons = false; + + int currentCash = player->GetAccount(); + + //------------------------------------------------------------------------- + // Try to buy the primary weapon + int i; + for ( i=0; i<primaryWeapons.Count() && !doneBuyingWeapons; ++i ) + { + const BuyPresetWeapon *primaryWeapon = &primaryWeapons[i]; + primaryWeaponToBuy = NULL; + int primaryClips = 0; + primaryClipsToBuy = 0; + currentCost = currentNonWeaponCost; + FillClientAmmo( ammo ); + + CSWeaponID weaponID = primaryWeapon->GetWeaponID(); + if ( weaponID == WEAPON_NONE ) + weaponID = primaryID; + + CCSWeaponInfo *primaryInfo = GetWeaponInfo( weaponID ); + int primaryAmmoCost = 0; + int primaryAmmoBuySize = 0; + if ( primaryInfo ) + { + primaryClips = CalcClipsNeeded( primaryWeapon, primaryInfo, ammo ); + int primaryWeaponCost = ( weaponID != primaryID ) ? primaryInfo->GetWeaponPrice() : 0; + primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->iAmmoType ); + primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->iAmmoType ); + currentCost += primaryWeaponCost; + currentCost += primaryAmmoCost * primaryClips; + ammo[primaryInfo->iAmmoType] += primaryClips * primaryAmmoBuySize; + + CURRENTCOST_DEBUG("\t%5.5d (%s)\n", primaryWeaponCost, WeaponIDToAlias( weaponID )); + CURRENTCOST_DEBUG("\t%5.5d (ammo x %d)\n", primaryAmmoCost * primaryClips, primaryClips); + if ( currentCash < currentCost ) + { + currentCost = currentNonWeaponCost; // can't afford it, so try the next weapon + continue; + } + + // check for as_* maps, and CTs buying AK47s, etc + if ( !CanBuyWeapon(primaryID, secondaryID, weaponID) && weaponID != primaryID ) + { + currentCost = currentNonWeaponCost; // can't buy it, so try the next weapon + continue; + } + + primaryWeaponToBuy = primaryWeapon; + primaryClipsToBuy = primaryClips; + } + + if ( !secondaryWeapons.Count() ) + { + CURRENTCOST_DEBUG("\t\t\tDone buying weapons (no secondary)\n"); + break; + } + + //------------------------------------------------------------------------- + // Try to buy a (required) secondary weapon to go with primaryWeaponToBuy. + // If not, reset cost to not reflect either weapon, so we can look at buying + // the next primary weapon in the list. + int j; + for ( j=0; j<secondaryWeapons.Count(); ++j ) + { + const BuyPresetWeapon *secondaryWeapon = &secondaryWeapons[j]; + + // reset ammo counts and cost + secondaryWeaponToBuy = NULL; + secondaryClipsToBuy = 0; + currentCost = currentNonWeaponCost; + FillClientAmmo( ammo ); + + // add in ammo we're already buying for the primary weapon to the client's actual ammo + if ( primaryInfo ) + { + currentCost += ( weaponID != primaryID ) ? primaryInfo->GetWeaponPrice() : 0; + currentCost += primaryAmmoCost * primaryClips; + ammo[primaryInfo->iAmmoType] += primaryClips * primaryAmmoBuySize; + } + + int currentCostWithoutSecondary = currentCost; + + CSWeaponID weaponID = secondaryWeapon->GetWeaponID(); + if ( weaponID == WEAPON_NONE ) + weaponID = secondaryID; + + CCSWeaponInfo *secondaryInfo = GetWeaponInfo( weaponID ); + if ( !secondaryInfo ) + { + currentCost = currentCostWithoutSecondary; + continue; + } + + int secondaryClips = CalcClipsNeeded( secondaryWeapon, secondaryInfo, ammo ); + int secondaryWeaponCost = ( weaponID != secondaryID ) ? secondaryInfo->GetWeaponPrice() : 0; + int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->iAmmoType ); + int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->iAmmoType ); + currentCost += secondaryWeaponCost; + currentCost += secondaryAmmoCost * secondaryClips; + ammo[secondaryInfo->iAmmoType] += secondaryClips * secondaryAmmoBuySize; + + CURRENTCOST_DEBUG("\t%5.5d (%s)\n", secondaryWeaponCost, WeaponIDToAlias( weaponID )); + CURRENTCOST_DEBUG("\t%5.5d (ammo x %d)\n", secondaryAmmoCost * secondaryClips, secondaryClips); + if ( currentCash < currentCost ) + { + currentCost = currentCostWithoutSecondary; // can't afford it, so skip + continue; + } + + // check for as_* maps, and CTs buying elites, etc + if ( !CanBuyWeapon(primaryID, secondaryID, weaponID) && weaponID != secondaryID ) + { + currentCost = currentCostWithoutSecondary; // can't buy it, so skip + continue; + } + + // We found both a primary and secondary weapon to buy. Stop looking for more. + secondaryWeaponToBuy = secondaryWeapon; + secondaryClipsToBuy = secondaryClips; + doneBuyingWeapons = true; + CURRENTCOST_DEBUG("\t\t\tDone buying weapons (primary && secondary)\n"); + break; + } + } + + // Update our cost to reflect the weapons and ammo purchased + cost = currentCost; + + if ( primaryWeaponToBuy ) + { + BuyPresetWeapon weapon = *primaryWeaponToBuy; + weapon.SetAmmoAmount( primaryClipsToBuy ); + weapon.SetAmmoType( AMMO_CLIPS ); + ws.m_primaryWeapon = weapon; + } + else + { + // If we failed to buy a primary weapon, and one was in the list, bail - the player can't afford this WeaponSet. + for ( int i=0; i<primaryWeapons.Count(); ++i ) + { + int weaponID = primaryWeapons[i].GetWeaponID(); + if ( weaponID != WEAPON_NONE ) + { + cost = -1; + ws.Reset(); + return; + } + } + } + + if ( secondaryWeaponToBuy ) + { + BuyPresetWeapon weapon = *secondaryWeaponToBuy; + weapon.SetAmmoAmount( secondaryClipsToBuy ); + weapon.SetAmmoType( AMMO_CLIPS ); + ws.m_secondaryWeapon = weapon; + } + else + { + // If we failed to buy a required secondary weapon, and one was in the list, bail - the player can't afford this WeaponSet. + for ( int i=0; i<secondaryWeapons.Count(); ++i ) + { + int weaponID = secondaryWeapons[i].GetWeaponID(); + if ( weaponID != WEAPON_NONE ) + { + cost = -1; + ws.Reset(); + return; + } + } + } + + //------------------------------------------------------------------------- + // now any extra ammo, in rebuy order + const char *remainder = SharedParse( cl_rebuy.GetString() ); + const char *token; + + while ( remainder ) + { + token = SharedGetToken(); + if ( !token || !*token ) + return; + + if ( !stricmp( token, "PrimaryAmmo" ) ) + { + if ( primaryWeaponToBuy ) + { + CSWeaponID id = primaryWeaponToBuy->GetWeaponID(); + if ( id == WEAPON_NONE ) + id = primaryID; + + if ( primaryWeaponToBuy->GetFillAmmo() ) + { + const CCSWeaponInfo *info = GetWeaponInfo( id ); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int ammoCost = GetCSAmmoDef()->GetCost( info->iAmmoType ); + int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + int roundsLeft = maxRounds - ammo[info->iAmmoType]; + int clipsLeft = (ammoBuySize > 0) ? ceil(roundsLeft / (float)ammoBuySize) : 0; + while ( clipsLeft > 0 && cost + ammoCost <= currentCash ) + { + ws.m_primaryWeapon.SetAmmoAmount( ws.m_primaryWeapon.GetAmmoAmount() + 1 ); + --clipsLeft; + ammo[info->iAmmoType] += MIN(maxRounds, ammoBuySize); + cost += ammoCost; + } + } + } + } + } + else if ( !stricmp( token, "SecondaryAmmo" ) ) + { + if ( secondaryWeaponToBuy ) + { + if ( secondaryWeaponToBuy->GetFillAmmo() ) + { + CSWeaponID weaponID = secondaryWeaponToBuy->GetWeaponID(); + if ( weaponID == WEAPON_NONE ) + weaponID = secondaryID; + + const CCSWeaponInfo *info = GetWeaponInfo( weaponID ); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int ammoCost = GetCSAmmoDef()->GetCost( info->iAmmoType ); + int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + int roundsLeft = maxRounds - ammo[info->iAmmoType]; + int clipsLeft = (ammoBuySize > 0) ? ceil(roundsLeft / (float)ammoBuySize) : 0; + while ( clipsLeft > 0 && cost + ammoCost <= currentCash ) + { + ws.m_secondaryWeapon.SetAmmoAmount( ws.m_secondaryWeapon.GetAmmoAmount() + 1 ); + --clipsLeft; + ammo[info->iAmmoType] += MIN(maxRounds, ammoBuySize); + cost += ammoCost; + } + } + } + } + } + + remainder = SharedParse( remainder ); + } + + if ( cost > currentCash ) + { + cost = -1; // can't buy it + ws.Reset(); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Constructs a WeaponSet containing everything that can be purchased. + * The cost (including extra cash) is also calculated and returned. + */ +void WeaponSet::GetFromScratch( int& cost, WeaponSet& ws ) const +{ + cost = 0; + ws.Reset(); + + int ammo[MAX_AMMO_TYPES]; + memset( ammo, 0, sizeof(ammo) ); + + 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 ); + } + + //------------------------------------------------------------------------- + // Primary weapon + CCSWeaponInfo *primaryInfo = GetWeaponInfo( m_primaryWeapon.GetWeaponID() ); + if ( primaryInfo ) + { + int primaryClips = CalcClipsNeeded( &m_primaryWeapon, primaryInfo, ammo ); + int primaryWeaponCost = primaryInfo->GetWeaponPrice(); + int primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->iAmmoType ); + int primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->iAmmoType ); + cost += primaryWeaponCost; + cost += primaryAmmoCost * primaryClips; + ammo[primaryInfo->iAmmoType] += primaryClips * primaryAmmoBuySize; + + ws.m_primaryWeapon = m_primaryWeapon; + ws.m_primaryWeapon.SetAmmoAmount( primaryClips ); + ws.m_primaryWeapon.SetAmmoType( AMMO_CLIPS ); + ws.m_primaryWeapon.SetFillAmmo( false ); + } + + //------------------------------------------------------------------------- + // secondary weapon + CCSWeaponInfo *secondaryInfo = GetWeaponInfo( m_secondaryWeapon.GetWeaponID() ); + if ( secondaryInfo ) + { + int secondaryClips = CalcClipsNeeded( &m_secondaryWeapon, secondaryInfo, ammo ); + int secondaryWeaponCost = secondaryInfo->GetWeaponPrice(); + int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->iAmmoType ); + int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->iAmmoType ); + cost += secondaryWeaponCost; + cost += secondaryAmmoCost * secondaryClips; + ammo[secondaryInfo->iAmmoType] += secondaryClips * secondaryAmmoBuySize; + + ws.m_secondaryWeapon = m_secondaryWeapon; + ws.m_secondaryWeapon.SetAmmoAmount( secondaryClips ); + ws.m_secondaryWeapon.SetAmmoType( AMMO_CLIPS ); + ws.m_secondaryWeapon.SetFillAmmo( false ); + } + + //------------------------------------------------------------------------- + // equipment + if ( m_armor ) + { + cost += (m_helmet) ? (iKevlarPrice + iHelmetPrice) : iKevlarPrice; + ws.m_armor = m_armor; + ws.m_helmet = m_helmet; + } + + if ( m_smokeGrenade ) + { + CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE ); + cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0; + ws.m_smokeGrenade = m_smokeGrenade; + } + + if ( m_HEGrenade ) + { + CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_HEGRENADE ); + cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0; + ws.m_HEGrenade = m_HEGrenade; + } + + CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_FLASHBANG ); + cost += ( pInfo ) ? pInfo->GetWeaponPrice() * m_flashbangs : 0; + ws.m_flashbangs = m_flashbangs; + + if ( m_defuser ) + { + cost += DEFUSEKIT_PRICE; + ws.m_defuser = m_defuser; + } + + if ( m_nightvision ) + { + cost += iNVGPrice; + ws.m_nightvision = m_nightvision; + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Convenience function that wraps GetFromScratch() and discards the generated WeaponSet. Returns the cost + * to buy the full WeaponSet from scratch. + */ +int WeaponSet::FullCost() const +{ + WeaponSet fullSet; + int cost = 0; + GetFromScratch( cost, fullSet ); + return cost; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Generates a list of buy commands that will buy the current WeaponSet. + */ +void WeaponSet::GenerateBuyCommands( char command[BUY_PRESET_COMMAND_LEN] ) const +{ + command[0] = 0; + char *tmp = command; + int remainder = BUY_PRESET_COMMAND_LEN; + int i; + + CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon + CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol + + //------------------------------------------------------------------------- + // Primary weapon + CSWeaponID weaponID = m_primaryWeapon.GetWeaponID(); + if ( weaponID == WEAPON_NONE ) + weaponID = primaryID; + const CCSWeaponInfo *primaryInfo = GetWeaponInfo( weaponID ); + if ( primaryInfo ) + { + if ( weaponID != primaryID ) + { + tmp = BufPrintf( tmp, remainder, "buy %s\n", WeaponIDToAlias(weaponID) ); + } + for ( i=0; i<m_primaryWeapon.GetAmmoAmount(); ++i ) + { + tmp = BufPrintf( tmp, remainder, "buyammo1\n" ); + } + } + + //------------------------------------------------------------------------- + // secondary weapon + weaponID = m_secondaryWeapon.GetWeaponID(); + if ( weaponID == WEAPON_NONE ) + weaponID = secondaryID; + const CCSWeaponInfo *secondaryInfo = GetWeaponInfo( weaponID ); + if ( secondaryInfo ) + { + if ( weaponID != secondaryID ) + { + tmp = BufPrintf( tmp, remainder, "buy %s\n", WeaponIDToAlias(weaponID) ); + } + for ( i=0; i<m_secondaryWeapon.GetAmmoAmount(); ++i ) + { + tmp = BufPrintf( tmp, remainder, "buyammo2\n" ); + } + } + + //------------------------------------------------------------------------- + // equipment + if ( m_armor ) + { + if ( m_helmet ) + { + tmp = BufPrintf( tmp, remainder, "buy vesthelm\n" ); + } + else + { + tmp = BufPrintf( tmp, remainder, "buy vest\n" ); + } + } + + if ( m_smokeGrenade ) + { + tmp = BufPrintf( tmp, remainder, "buy smokegrenade\n" ); + } + + if ( m_HEGrenade ) + { + tmp = BufPrintf( tmp, remainder, "buy hegrenade\n" ); + } + + for ( i=0; i<m_flashbangs; ++i ) + { + tmp = BufPrintf( tmp, remainder, "buy flashbang\n" ); + } + + if ( m_defuser ) + { + tmp = BufPrintf( tmp, remainder, "buy defuser\n" ); + } + + if ( m_nightvision ) + { + tmp = BufPrintf( tmp, remainder, "buy nvgs\n" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return images out of a subdir, so when the buy preset system resizes the images, they don't resize in the + * main buy menu. + */ +const char *ImageFnameFromWeaponID( CSWeaponID weaponID, bool isPrimary ) +{ + switch (weaponID) + { + case WEAPON_NONE: + return "gfx/vgui/defaultweapon"; + case WEAPON_SCOUT: + return "gfx/vgui/scout"; + case WEAPON_XM1014: + return "gfx/vgui/xm1014"; + case WEAPON_MAC10: + return "gfx/vgui/mac10"; + case WEAPON_AUG: + return "gfx/vgui/aug"; + case WEAPON_UMP45: + return "gfx/vgui/ump45"; + case WEAPON_SG550: + return "gfx/vgui/sg550"; + case WEAPON_GALIL: + return "gfx/vgui/galil"; + case WEAPON_FAMAS: + return "gfx/vgui/famas"; + case WEAPON_AWP: + return "gfx/vgui/awp"; + case WEAPON_MP5NAVY: + return "gfx/vgui/mp5"; + case WEAPON_M249: + return "gfx/vgui/m249"; + case WEAPON_M3: + return "gfx/vgui/m3"; + case WEAPON_M4A1: + return "gfx/vgui/m4a1"; + case WEAPON_TMP: + return "gfx/vgui/tmp"; + case WEAPON_G3SG1: + return "gfx/vgui/g3sg1"; + case WEAPON_SG552: + return "gfx/vgui/sg552"; + case WEAPON_AK47: + return "gfx/vgui/ak47"; + case WEAPON_P90: + return "gfx/vgui/p90"; + case WEAPON_SHIELDGUN: + return "gfx/vgui/shield"; + + case WEAPON_USP: + return "gfx/vgui/usp45"; + case WEAPON_GLOCK: + return "gfx/vgui/glock18"; + case WEAPON_DEAGLE: + return "gfx/vgui/deserteagle"; + case WEAPON_ELITE: + return "gfx/vgui/elites"; + case WEAPON_P228: + return "gfx/vgui/p228"; + case WEAPON_FIVESEVEN: + return "gfx/vgui/fiveseven"; + + default: + return ""; + } +} + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +BuyPreset::BuyPreset() +{ + SetName( L"" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +BuyPreset::BuyPreset( const BuyPreset& other ) +{ + *this = other; +} + + +//-------------------------------------------------------------------------------------------------------------- +BuyPreset::~BuyPreset() +{ +} + + +//-------------------------------------------------------------------------------------------------------------- +void BuyPreset::SetName( const wchar_t *name ) +{ + wcsncpy( m_name, name, MaxBuyPresetName ); + if ( m_name[0] == 0 ) + { + const wchar_t * defaultName = g_pVGuiLocalize->Find( "#Cstrike_BuyPresetBlank" ); + if ( defaultName ) + { + wcsncpy( m_name, defaultName, MaxBuyPresetName ); + } + } + m_name[MaxBuyPresetName-1] = 0; +} + + +//-------------------------------------------------------------------------------------------------------------- +// Parse KeyValues string into a BuyPresetWeapon vector +static void ParseWeaponString( const char *str, BuyPresetWeaponList& weapons, bool isPrimary ) +{ + weapons.RemoveAll(); + + if ( !str ) + return; + + const char *remainder = SharedParse( str ); + const char *token; + + const int BufLen = 32; + char tmpBuf[BufLen]; + tmpBuf[0] = '\0'; + char weaponBuf[BufLen]; + weaponBuf[0] = '\0'; + char clipModifier = 0; + int numClips = 0; + + while ( remainder ) + { + token = SharedGetToken(); + if ( !token || strlen(token) >= BufLen ) + return; + + Q_strncpy( tmpBuf, token, BufLen ); + tmpBuf[BufLen - 1] = 0; + char *tmp = tmpBuf; + while ( *tmp ) + { + if ( *tmp == '/' ) + { + *tmp = ' '; + } + ++tmp; + } + // sscanf is safe, since the size of the string being scanned is at least as small as any individual destination buffer + if ( sscanf( tmpBuf, "%s %d%c", weaponBuf, &numClips, &clipModifier ) != 3 ) + return; + + CSWeaponID weaponID = AliasToWeaponID( weaponBuf ); + if ( weaponID != WEAPON_NONE ) + { + const CCSWeaponInfo *info = GetWeaponInfo( weaponID ); + if ( info ) + { + int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType ); + int buySize = GetCSAmmoDef()->GetBuySize( info->iAmmoType ); + numClips = MIN(ceil(maxRounds/(float)buySize), MAX(0, numClips)); + if ( ((!isPrimary) ^ IsPrimaryWeapon( weaponID )) == 0 ) + return; + } + else + { + numClips = MIN(NUM_CLIPS_FOR_CURRENT, MAX(0, numClips)); + } + } + else + { + numClips = MIN(NUM_CLIPS_FOR_CURRENT, MAX(0, numClips)); + } + + BuyPresetWeapon weapon( weaponID ); + weapon.SetAmmoType( AMMO_CLIPS ); + weapon.SetAmmoAmount( numClips ); + weapon.SetFillAmmo( (clipModifier == '+') ); + weapons.AddToTail( weapon ); + + remainder = SharedParse( remainder ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Populates data from a KeyValues structure, for loading + */ +void BuyPreset::Parse( KeyValues *data ) +{ + m_name[0] = 0; + m_weaponList.RemoveAll(); + + if (!data) + return; + + //------------------------------------------------------------------------- + // Read name + wcsncpy( m_name, data->GetWString( "PresetName", L""), MaxBuyPresetName ); + m_name[ MaxBuyPresetName - 1 ] = 0; + PRESET_DEBUG( "Parsing Buy Preset %ls\n", m_name ); + + int version = data->GetInt( "Version", 0 ); + if ( version < 4 || version > 4 ) + { + PRESET_DEBUG( "Invalid preset version %d\n", version ); + return; + } + + const char *primaryString = data->GetString( "Primary", NULL ); + const char *secondaryString = data->GetString( "Secondary", NULL ); + const char *equipmentString = data->GetString( "Equipment", NULL ); + + CUtlVector< BuyPresetWeapon > weapons; + ParseWeaponString( primaryString, weapons, true ); + + WeaponSet ws; + if ( weapons.Count() ) + ws.m_primaryWeapon = weapons[0]; + + weapons.RemoveAll(); + ParseWeaponString( secondaryString, weapons, false ); + if ( weapons.Count() ) + ws.m_secondaryWeapon = weapons[0]; + + // parse equipment + if ( equipmentString ) + { + const char *remainder = SharedParse( equipmentString ); + const char *token; + + const int BufLen = 32; + char tmpBuf[BufLen]; + char itemBuf[BufLen]; + int intVal; + + while ( remainder ) + { + token = SharedGetToken(); + if ( !token || Q_strlen(token) >= BufLen ) + break; + + Q_strncpy( tmpBuf, token, BufLen ); + tmpBuf[BufLen - 1] = 0; + char *tmp = tmpBuf; + while ( *tmp ) + { + if ( *tmp == '/' ) + { + *tmp = ' '; + } + ++tmp; + } + // sscanf is safe, since the size of the string being scanned is at least as small as any individual destination buffer + if ( sscanf( tmpBuf, "%s %d", itemBuf, &intVal ) != 2 ) + break; + + if ( !strcmp( itemBuf, "vest" ) ) + { + ws.m_armor = (intVal > 0) ? 100 : 0; + ws.m_helmet = false; + } + else if ( !strcmp( itemBuf, "vesthelm" ) ) + { + ws.m_armor = (intVal > 0) ? 100 : 0; + ws.m_helmet = true; + } + else if ( !strcmp( itemBuf, "defuser" ) ) + { + ws.m_defuser = (intVal > 0); + } + else if ( !strcmp( itemBuf, "nvgs" ) ) + { + ws.m_nightvision = (intVal > 0); + } + else if ( !strcmp( itemBuf, "sgren" ) ) + { + ws.m_smokeGrenade = (intVal > 0); + } + else if ( !strcmp( itemBuf, "hegren" ) ) + { + ws.m_HEGrenade = (intVal > 0); + } + else if ( !strcmp( itemBuf, "flash" ) ) + { + ws.m_flashbangs = MIN( 2, MAX( 0, intVal ) ); + } + + remainder = SharedParse( remainder ); + } + + m_weaponList.AddToTail( ws ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +// Build a string of weapons+ammo for KeyValues saving/loading +static const char* ConstructWeaponString( const BuyPresetWeapon& weapon ) +{ + const int WeaponLen = 1024; + static char weaponString[WeaponLen]; + weaponString[0] = 0; + int remainder = WeaponLen; + char *tmp = weaponString; + + tmp = BufPrintf( tmp, remainder, "%s/%d%c", + WeaponIDToAlias( weapon.GetWeaponID() ), + weapon.GetAmmoAmount(), + (weapon.GetFillAmmo()) ? '+' : '=' ); + return weaponString; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Fills in a KeyValues structure with data, for saving + */ +void BuyPreset::Save( KeyValues *data ) +{ + //------------------------------------------------------------------------- + // Save name and version + KeyValues *presetKey = data->CreateNewKey(); + presetKey->SetWString( "PresetName", m_name ); + + /** + * Version History + * --------------- + * + * PRE-RELEASE + * 1. 2/2004 + * First-pass implementation. Allowed for multiple weapons per fallback. + * Individual items could be marked optional. Cash reserve could be specified. + * 2. 3/2004 + * Second-pass implementation. Removed multiple weapons per fallback. The + * pistol could be marked optional. A preset could be marked as 'use for + * Autobuy'. + * + * CZ RELEASE + * 3. 4/22/2004 + * The first released version. Removed optional/required status. Also + * removed the cash reserve and autobuy status. Corresponds to streamlined + * editing interface mocked up by Greg Coomer. + * + * CS:S RELEASE + * 4. 3/10/2004 + * Removed fallbacks to correspond to new UI. + */ + presetKey->SetInt( "Version", 4 ); + if ( m_weaponList.Count() > 0 ) + { + //------------------------------------------------------------------------- + // Save a fallback WeaponSet + const WeaponSet& ws = m_weaponList[0]; + + presetKey->SetString( "Primary", ConstructWeaponString( ws.m_primaryWeapon ) ); + presetKey->SetString( "Secondary", ConstructWeaponString( ws.m_secondaryWeapon ) ); + + presetKey->SetString( "Equipment", + SharedVarArgs("vest%s/%d flash/%d sgren/%d hegren/%d defuser/%d nvgs/%d", + (ws.m_helmet)?"helm":"", ws.m_armor, + ws.m_flashbangs, + ws.m_smokeGrenade, + ws.m_HEGrenade, + ws.m_defuser, + ws.m_nightvision + ) ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Calculates the full cost of the preset, which is exactly the full cost of the first fallback WeaponSet + */ +int BuyPreset::FullCost() const +{ + if ( m_weaponList.Count() == 0 ) + return 0; + + return m_weaponList[0].FullCost(); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the specified fallback WeaponSet, or NULL if it doesn't exist + */ +const WeaponSet * BuyPreset::GetSet( int index ) const +{ + if ( index < 0 || index >= m_weaponList.Count() ) + return NULL; + + return &(m_weaponList[index]); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Deletes a fallback + */ +void BuyPreset::DeleteSet( int index ) +{ + if ( index < 0 || index >= m_weaponList.Count() ) + return; + + m_weaponList.Remove( index ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Switches the order of two fallbacks + */ +void BuyPreset::SwapSet( int firstIndex, int secondIndex ) +{ + if ( firstIndex < 0 || firstIndex >= m_weaponList.Count() ) + return; + + if ( secondIndex < 0 || secondIndex >= m_weaponList.Count() ) + return; + + WeaponSet tmpSet = m_weaponList[firstIndex]; + m_weaponList[firstIndex] = m_weaponList[secondIndex]; + m_weaponList[secondIndex] = tmpSet; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Replaces a fallback with the supplied WeaponSet + */ +void BuyPreset::ReplaceSet( int index, const WeaponSet& weaponSet ) +{ + if ( index < 0 || index > m_weaponList.Count() ) + return; + + if ( index == m_weaponList.Count() ) + { + m_weaponList.AddToTail( weaponSet ); + } + else + { + m_weaponList[index] = weaponSet; + } +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/buy_presets/buy_preset_debug.cpp b/game/client/cstrike/buy_presets/buy_preset_debug.cpp new file mode 100644 index 0000000..afe3e23 --- /dev/null +++ b/game/client/cstrike/buy_presets/buy_preset_debug.cpp @@ -0,0 +1,65 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +// Author: Matthew D. Campbell ([email protected]), 2004 + +#include "cbase.h" +#include "buy_preset_debug.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef _DEBUG +#define BUY_PRESET_DEBUGGING 1 +#else +#define BUY_PRESET_DEBUGGING 0 +#endif + +#if BUY_PRESET_DEBUGGING + +static ConVar cl_preset_debug( "cl_preset_debug", "0", 0, "Controls debug information about buy presets" ); + +//-------------------------------------------------------------------------------------------------------------- +bool IsPresetDebuggingEnabled() +{ + return cl_preset_debug.GetInt() >= 1.0f; +} + +//-------------------------------------------------------------------------------------------------------------- +bool IsPresetFullCostDebuggingEnabled() +{ + return cl_preset_debug.GetInt() == 2.0f; +} + +//-------------------------------------------------------------------------------------------------------------- +bool IsPresetCurrentCostDebuggingEnabled() +{ + return cl_preset_debug.GetInt() == 3.0f; +} + +#else // ! BUY_PRESET_DEBUGGING + +//-------------------------------------------------------------------------------------------------------------- +bool IsPresetDebuggingEnabled() +{ + return false; +} + +//-------------------------------------------------------------------------------------------------------------- +bool IsPresetFullCostDebuggingEnabled() +{ + return false; +} + +//-------------------------------------------------------------------------------------------------------------- +bool IsPresetCurrentCostDebuggingEnabled() +{ + return false; +} + +#endif // ! BUY_PRESET_DEBUGGING + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/buy_presets/buy_preset_debug.h b/game/client/cstrike/buy_presets/buy_preset_debug.h new file mode 100644 index 0000000..ded580b --- /dev/null +++ b/game/client/cstrike/buy_presets/buy_preset_debug.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef BUY_PRESET_DEBUG_H +#define BUY_PRESET_DEBUG_H +#ifdef _WIN32 +#pragma once +#endif + +//-------------------------------------------------------------------------------------------------------------- +// Utility functions for the debugging macros below +bool IsPresetDebuggingEnabled(); ///< cl_preset_debug >= 1.0f +bool IsPresetFullCostDebuggingEnabled(); ///< cl_preset_debug == 2.0f +bool IsPresetCurrentCostDebuggingEnabled(); ///< cl_preset_debug == 3.0f + +//-------------------------------------------------------------------------------------------------------------- +// Macros for conditional debugging of buy presets +#define PRESET_DEBUG if (IsPresetDebuggingEnabled()) DevMsg +#define FULLCOST_DEBUG if (IsPresetFullCostDebuggingEnabled()) DevMsg +#define CURRENTCOST_DEBUG if (IsPresetCurrentCostDebuggingEnabled()) DevMsg + +#endif // BUY_PRESET_DEBUG_H diff --git a/game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp b/game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp new file mode 100644 index 0000000..24c4751 --- /dev/null +++ b/game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp @@ -0,0 +1,364 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: BuyPresetWeapon implementation, and misc knowledge relating to weapons +// +//============================================================================= + +#include "cbase.h" + +#include "buy_preset_debug.h" +#include "buy_presets.h" +#include "weapon_csbase.h" +#include "cs_ammodef.h" +#include "cs_gamerules.h" +#include "cstrike/bot/shared_util.h" +#include <vgui/ILocalize.h> +#include <vgui_controls/Controls.h> +#include "c_cs_player.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//-------------------------------------------------------------------------------------------------------------- +struct WeaponDisplayNameInfo +{ + CSWeaponID id; + const char *displayName; +}; + + +//-------------------------------------------------------------------------------------------------------------- +// NOTE: Array must be NULL-terminated +static WeaponDisplayNameInfo weaponDisplayNameInfo[] = +{ + { WEAPON_P228, "#Cstrike_TitlesTXT_P228" }, + { WEAPON_GLOCK, "#Cstrike_TitlesTXT_Glock18" }, + { WEAPON_SCOUT, "#Cstrike_TitlesTXT_Scout" }, + { WEAPON_XM1014, "#Cstrike_TitlesTXT_AutoShotgun" }, + { WEAPON_MAC10, "#Cstrike_TitlesTXT_Mac10_Short" }, + { WEAPON_AUG, "#Cstrike_TitlesTXT_Aug" }, + { WEAPON_ELITE, "#Cstrike_TitlesTXT_Beretta96G" }, + { WEAPON_FIVESEVEN, "#Cstrike_TitlesTXT_ESFiveSeven" }, + { WEAPON_UMP45, "#Cstrike_TitlesTXT_KMUMP45" }, + { WEAPON_SG550, "#Cstrike_TitlesTXT_SG550" }, + { WEAPON_GALIL, "#Cstrike_TitlesTXT_Galil" }, + { WEAPON_FAMAS, "#Cstrike_TitlesTXT_Famas" }, + { WEAPON_USP, "#Cstrike_TitlesTXT_USP45" }, + { WEAPON_AWP, "#Cstrike_TitlesTXT_ArcticWarfareMagnum" }, + { WEAPON_MP5NAVY, "#Cstrike_TitlesTXT_mp5navy" }, + { WEAPON_M249, "#Cstrike_TitlesTXT_ESM249" }, + { WEAPON_M3, "#Cstrike_TitlesTXT_Leone12" }, + { WEAPON_M4A1, "#Cstrike_TitlesTXT_M4A1_Short" }, + { WEAPON_TMP, "#Cstrike_TitlesTXT_tmp" }, + { WEAPON_G3SG1, "#Cstrike_TitlesTXT_G3SG1" }, + { WEAPON_DEAGLE, "#Cstrike_TitlesTXT_DesertEagle" }, + { WEAPON_SG552, "#Cstrike_TitlesTXT_SG552" }, + { WEAPON_AK47, "#Cstrike_TitlesTXT_AK47" }, + { WEAPON_P90, "#Cstrike_TitlesTXT_ESC90" }, + { WEAPON_SHIELDGUN, "#Cstrike_TitlesTXT_TactShield" }, + + { WEAPON_NONE, "#Cstrike_CurrentWeapon" } +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the display name for a weapon, based on it's weaponID + */ +const wchar_t* WeaponIDToDisplayName( CSWeaponID weaponID ) +{ + for( int i=0; weaponDisplayNameInfo[i].displayName; ++i ) + if ( weaponDisplayNameInfo[i].id == weaponID ) + return g_pVGuiLocalize->Find( weaponDisplayNameInfo[i].displayName ); + + return NULL; +} + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +BuyPresetWeapon::BuyPresetWeapon() +{ + m_name = NULL; + m_weaponID = WEAPON_NONE; + m_ammoType = AMMO_CLIPS; + m_ammoAmount = 0; + m_fillAmmo = true; +} + + +//-------------------------------------------------------------------------------------------------------------- +BuyPresetWeapon::BuyPresetWeapon( CSWeaponID weaponID ) +{ + m_name = WeaponIDToDisplayName( weaponID ); + m_weaponID = weaponID; + m_ammoType = AMMO_CLIPS; + m_ammoAmount = (weaponID == WEAPON_NONE) ? 0 : 1; + m_fillAmmo = true; +} + + +//-------------------------------------------------------------------------------------------------------------- +BuyPresetWeapon& BuyPresetWeapon::operator= (const BuyPresetWeapon& other) +{ + m_name = other.m_name; + m_weaponID = other.m_weaponID; + m_ammoType = other.m_ammoType; + m_ammoAmount = other.m_ammoAmount; + m_fillAmmo = other.m_fillAmmo; + + return *this; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Sets the WEAPON_* weapon ID (and resets ammo, etc) + */ +void BuyPresetWeapon::SetWeaponID( CSWeaponID weaponID ) +{ + m_name = WeaponIDToDisplayName( weaponID ); + m_weaponID = weaponID; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the number of clips that will have to be bought for the specified BuyPresetWeapon + */ +int CalcClipsNeeded( const BuyPresetWeapon *pWeapon, const CCSWeaponInfo *pInfo, const int ammo[MAX_AMMO_TYPES] ) +{ + if ( !pWeapon || !pInfo ) + return 0; + + int maxRounds = GetCSAmmoDef()->MaxCarry( pInfo->iAmmoType ); + int buySize = GetCSAmmoDef()->GetBuySize( pInfo->iAmmoType ); + + int numClips = 0; + if ( buySize && pInfo->iAmmoType >= 0 ) + { + switch ( pWeapon->GetAmmoType() ) + { + case AMMO_CLIPS: + numClips = pWeapon->GetAmmoAmount(); + if ( pWeapon->GetWeaponID() == WEAPON_NONE && numClips >= 4 ) + { + numClips = ceil(maxRounds/(float)buySize); + } + numClips = MIN( ceil(maxRounds/(float)buySize), numClips ); + + numClips -= ammo[pInfo->iAmmoType]/buySize; + if ( numClips < 0 || ammo[pInfo->iAmmoType] == maxRounds ) + { + numClips = 0; + } + break; + case AMMO_ROUNDS: + { + int roundsNeeded = pWeapon->GetAmmoAmount() - ammo[pInfo->iAmmoType]; + if ( roundsNeeded > 0 ) + { + numClips = ceil(roundsNeeded/(float)buySize); + } + else + { + numClips = 0; + } + } + break; + case AMMO_PERCENT: + { + int roundsNeeded = maxRounds*pWeapon->GetAmmoAmount() - ammo[pInfo->iAmmoType]; + roundsNeeded *= 0.01f; + if ( roundsNeeded > 0 ) + { + numClips = ceil(roundsNeeded/(float)buySize); + } + else + { + numClips = 0; + } + } + break; + } + } + return numClips; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the client can currently buy the weapon according to class/gamemode restrictions. Returns + * true also if the player owns the weapon already. + */ +bool CanBuyWeapon( CSWeaponID currentPrimaryID, CSWeaponID currentSecondaryID, CSWeaponID weaponID ) +{ + if ( currentPrimaryID == WEAPON_SHIELDGUN && weaponID == WEAPON_ELITE ) + { + return false; + } + + if ( currentSecondaryID == WEAPON_ELITE && weaponID == WEAPON_SHIELDGUN ) + { + return false; + } + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return false; + + CCSWeaponInfo *info = GetWeaponInfo( weaponID ); + if ( !info ) + return false; + + /// @TODO: assasination maps have a specific set of weapons that can be used in them. + if ( info->m_iTeam != TEAM_UNASSIGNED && pPlayer->GetTeamNumber() != info->m_iTeam ) + return false; + + return true; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the CSWeaponType is a primary class + */ +static bool IsPrimaryWeaponClassID( CSWeaponType classId ) +{ + switch (classId) + { + case WEAPONTYPE_SUBMACHINEGUN: + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_MACHINEGUN: + case WEAPONTYPE_RIFLE: + case WEAPONTYPE_SNIPER_RIFLE: + return true; + } + + return false; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the weapon ID is for a primary weapon + */ +static bool IsPrimaryWeaponID( CSWeaponID id ) +{ + if ( id == WEAPON_SHIELDGUN ) + return true; + + CCSWeaponInfo *info = GetWeaponInfo( id ); + if ( !info ) + return false; + + return IsPrimaryWeaponClassID( info->m_WeaponType ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the CSWeaponType is a secondary class + */ +static bool IsSecondaryWeaponClassID( CSWeaponType classId ) +{ + return (classId == WEAPONTYPE_PISTOL); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the weapon ID is for a secondary weapon + */ +static bool IsSecondaryWeaponID( CSWeaponID id ) +{ + if ( id == WEAPON_SHIELDGUN ) + return false; + + CCSWeaponInfo *info = GetWeaponInfo( id ); + if ( !info ) + return false; + + return IsSecondaryWeaponClassID( info->m_WeaponType ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Fills the ammo array with the ammo currently owned by the local player + */ +void FillClientAmmo( int ammo[MAX_AMMO_TYPES] ) +{ + int i; + for ( i=0; i<MAX_AMMO_TYPES; ++i ) + { + ammo[i] = 0; + } + + C_CSPlayer *localPlayer = CCSPlayer::GetLocalCSPlayer(); + if ( !localPlayer ) + return; + + for ( i=0; i<WEAPON_MAX; ++i ) + { + CSWeaponID gameWeaponID = (CSWeaponID)i; + if ( gameWeaponID == WEAPON_NONE || gameWeaponID >= WEAPON_SHIELDGUN ) + continue; + + const CCSWeaponInfo *info = GetWeaponInfo( gameWeaponID ); + if ( !info ) + continue; + + int clientAmmoType = info->iAmmoType; + int clientAmmoCount = localPlayer->GetAmmoCount( clientAmmoType ); + if ( clientAmmoCount > 0 ) + { + ammo[ clientAmmoType ] = MAX( ammo[ clientAmmoType ], clientAmmoCount ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the weapon in the specified slot +//----------------------------------------------------------------------------- +CWeaponCSBase *GetWeaponInSlot( int iSlot, int iSlotPos ) +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + if ( !player ) + return NULL; + + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetWeapon(i)); + + if ( pWeapon == NULL ) + continue; + + if ( pWeapon->GetSlot() == iSlot && pWeapon->GetPosition() == iSlotPos ) + return pWeapon; + } + + return NULL; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the client's WEAPON_* value for the currently owned weapon, or WEAPON_NONE if no weapon is owned + */ +CSWeaponID GetClientWeaponID( bool primary ) +{ + C_CSPlayer *localPlayer = CCSPlayer::GetLocalCSPlayer(); + if ( !localPlayer ) + return WEAPON_NONE; + + int slot = (primary)?0:1; + CWeaponCSBase *pWeapon = GetWeaponInSlot( slot, slot ); + if ( !pWeapon ) + return WEAPON_NONE; + + return pWeapon->GetWeaponID(); +} + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/buy_presets/buy_presets.cpp b/game/client/cstrike/buy_presets/buy_presets.cpp new file mode 100644 index 0000000..7badf8f --- /dev/null +++ b/game/client/cstrike/buy_presets/buy_presets.cpp @@ -0,0 +1,454 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#include "buy_preset_debug.h" +#include "buy_presets.h" +#include "weapon_csbase.h" +#include "game/client/iviewport.h" +#include "filesystem.h" +#include <vgui/ILocalize.h> +#include <vgui_controls/Controls.h> +#include "c_cs_player.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +BuyPresetManager *TheBuyPresets = NULL; + +#if USE_BUY_PRESETS +//-------------------------------------------------------------------------------------------------------------- +ConVar cl_buy_favorite_quiet( "cl_buy_favorite_quiet", "0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Skips the prompt when saving a buy favorite in the buy menu" ); +ConVar cl_buy_favorite_nowarn( "cl_buy_favorite_nowarn", "0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Skips the error prompt when saving an invalid buy favorite" ); + +//-------------------------------------------------------------------------------------------------------------- +static void PrintBuyPresetUsage( void ) +{ + if ( TheBuyPresets->GetNumPresets() ) + { + Msg( "usage: cl_buy_favorite <1...%d>\n", TheBuyPresets->GetNumPresets() ); + for ( int i=0; i<TheBuyPresets->GetNumPresets(); ++i ) + { + const BuyPreset *preset = TheBuyPresets->GetPreset( i ); + if ( preset && preset->GetName() && preset->GetName()[0] ) + { + char buffer[64]; + g_pVGuiLocalize->ConvertUnicodeToANSI( preset->GetName(), buffer, sizeof( buffer ) ); + Msg( " %d. %s\n", i+1, buffer ); + } + } + } + else + { + Msg( "cl_buy_favorite: no favorites are defined\n" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Callback function for handling the "cl_buy_favorite" command + */ +CON_COMMAND_F( cl_buy_favorite, "Purchase a favorite weapon/equipment loadout", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if ( !engine->IsConnected() ) + return; + + if ( !TheBuyPresets ) + TheBuyPresets = new BuyPresetManager(); + + if ( args.ArgC() != 2 ) + { + PRESET_DEBUG( "cl_buy_favorite: no favorite specified\n" ); + PrintBuyPresetUsage(); + return; + } + + int presetIndex = atoi( args[1] ) - 1; + if ( presetIndex < 0 || presetIndex >= TheBuyPresets->GetNumPresets() ) + { + PRESET_DEBUG( "cl_buy_favorite: favorite %d doesn't exist\n", presetIndex ); + PrintBuyPresetUsage(); + return; + } + + TheBuyPresets->PurchasePreset( presetIndex ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Callback function for handling the "cl_buy_favorite_set" command + */ +CON_COMMAND_F( cl_buy_favorite_set, "Saves the current loadout as a favorite", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if ( !engine->IsConnected() ) + return; + + if ( !TheBuyPresets ) + TheBuyPresets = new BuyPresetManager(); + + if ( args.ArgC() != 2 ) + { + PRESET_DEBUG( "cl_buy_favorite_set: no favorite specified\n" ); + PrintBuyPresetUsage(); + return; + } + + int presetIndex = atoi( args[ 1 ] ) - 1; + if ( presetIndex < 0 || presetIndex >= TheBuyPresets->GetNumPresets() ) + { + PRESET_DEBUG( "cl_buy_favorite_set: favorite %d doesn't exist\n", presetIndex ); + PrintBuyPresetUsage(); + return; + } + + const BuyPreset *preset = TheBuyPresets->GetPreset( presetIndex ); + if ( !preset ) + { + return; + } + + WeaponSet ws; + TheBuyPresets->GetCurrentLoadout( &ws ); + BuyPreset newPreset( *preset ); + newPreset.ReplaceSet( 0, ws ); + + TheBuyPresets->SetPreset( presetIndex, &newPreset ); + TheBuyPresets->Save(); + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pPlayer->EmitSound( "BuyPreset.Updated" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Callback function for handling the "cl_buy_favorite_reset" command + */ +void __CmdFunc_BuyPresetsReset(void) +{ + if ( !engine->IsConnected() ) + return; + + if ( !TheBuyPresets ) + TheBuyPresets = new BuyPresetManager(); + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer || ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST ) ) + { + return; + } + + TheBuyPresets->ResetEditToDefaults(); + TheBuyPresets->SetPresets( TheBuyPresets->GetEditPresets() ); + TheBuyPresets->Save(); +} +ConCommand cl_buy_favorite_reset( "cl_buy_favorite_reset", __CmdFunc_BuyPresetsReset, "Reset favorite loadouts to the default", FCVAR_CLIENTCMD_CAN_EXECUTE ); +#endif // USE_BUY_PRESETS + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +/** + * Creates the BuyPresetManager singleton + */ +BuyPresetManager::BuyPresetManager() +{ + m_loadedTeam = TEAM_UNASSIGNED; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Resets the BuyPresetManager, loading BuyPresets from disk. If no presets are defined, it loads the + * default presets instead. + */ +void BuyPresetManager::VerifyLoadedTeam( void ) +{ +#if USE_BUY_PRESETS + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return; + + int playerTeam = pPlayer->GetTeamNumber(); + + if ( playerTeam == m_loadedTeam ) + return; + + if ( playerTeam != TEAM_CT && playerTeam != TEAM_TERRORIST ) + return; + + m_presets.RemoveAll(); + + const char *filename = "cfg/BuyPresets_TER.vdf"; + if ( playerTeam == TEAM_CT ) + { + filename = "cfg/BuyPresets_CT.vdf"; + } + + KeyValues *data; + KeyValues *presetKey; + data = new KeyValues( "Presets" ); + bool fileExists = data->LoadFromFile( filesystem, filename, NULL ); + + presetKey = data->GetFirstSubKey(); + while ( presetKey ) + { + BuyPreset preset; + preset.Parse( presetKey ); + m_presets.AddToTail(preset); + + presetKey = presetKey->GetNextKey(); + } + + if ( !m_presets.Count() ) + { + const char *filename = "cfg/BuyPresetsDefault_TER.vdf"; + if ( playerTeam == TEAM_CT ) + { + filename = "cfg/BuyPresetsDefault_CT.vdf"; + } + + KeyValues *data; + KeyValues *presetKey; + data = new KeyValues( "Presets" ); + data->LoadFromFile( filesystem, filename, NULL ); + + presetKey = data->GetFirstSubKey(); + while ( presetKey ) + { + BuyPreset preset; + preset.Parse( presetKey ); + m_presets.AddToTail(preset); + + presetKey = presetKey->GetNextKey(); + } + + data->deleteThis(); + } + + // Guarantee we have at least this many presets, even if they are blank + while ( m_presets.Count() < NUM_PRESETS ) + { + BuyPreset preset; + m_presets.AddToTail(preset); + } + + data->deleteThis(); + m_editPresets = m_presets; + + if ( !fileExists ) + Save(); +#endif // USE_BUY_PRESETS +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Loads the default presets into the editing presets (e.g. when hitting the "Use Defaults" button) + */ +void BuyPresetManager::ResetEditToDefaults( void ) +{ +#if USE_BUY_PRESETS + VerifyLoadedTeam(); + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return; + + int playerTeam = pPlayer->GetTeamNumber(); + + if ( playerTeam != TEAM_CT && playerTeam != TEAM_TERRORIST ) + return; + + m_editPresets.RemoveAll(); + + const char *filename = "cfg/BuyPresetsDefault_TER.vdf"; + if ( playerTeam == TEAM_CT ) + { + filename = "cfg/BuyPresetsDefault_CT.vdf"; + } + + KeyValues *data; + KeyValues *presetKey; + data = new KeyValues( "Presets" ); + data->LoadFromFile( filesystem, filename, NULL ); + + presetKey = data->GetFirstSubKey(); + while ( presetKey ) + { + BuyPreset preset; + preset.Parse( presetKey ); + m_editPresets.AddToTail(preset); + + presetKey = presetKey->GetNextKey(); + } + + data->deleteThis(); +#endif // USE_BUY_PRESETS +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Saves the current BuyPresets to disk + */ +void BuyPresetManager::Save() +{ +#if USE_BUY_PRESETS + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return; + + const char *filename = "cfg/BuyPresets_TER.vdf"; + switch ( pPlayer->GetTeamNumber() ) + { + case TEAM_CT: + filename = "cfg/BuyPresets_CT.vdf"; + break; + case TEAM_TERRORIST: + filename = "cfg/BuyPresets_TER.vdf"; + break; + default: + return; // don't bother saving presets unless we're on a team + } + + KeyValues *data = new KeyValues( "Presets" ); + for( int i=0; i<m_presets.Count(); ++i ) + { + m_presets[i].Save( data ); + } + data->SaveToFile( filesystem, filename, NULL ); + data->deleteThis(); +#endif // USE_BUY_PRESETS +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the specified "live" preset, or NULL if it doesn't exist + */ +const BuyPreset * BuyPresetManager::GetPreset( int index ) const +{ + if ( index < 0 || index >= m_presets.Count() ) + { + return NULL; + } + + return &(m_presets[index]); +} + + +//-------------------------------------------------------------------------------------------------------------- +void BuyPresetManager::SetPreset( int index, const BuyPreset *preset ) +{ + if ( index < 0 || index >= m_presets.Count() || !preset ) + { + return; + } + + m_presets[index] = *preset; +} + + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the specified editing preset, or NULL if it doesn't exist + */ +BuyPreset * BuyPresetManager::GetEditPreset( int index ) +{ + if ( index < 0 || index >= m_editPresets.Count() ) + { + return NULL; + } + + return &(m_editPresets[index]); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Generates and sends buy commands to buy a specific preset + */ +void BuyPresetManager::PurchasePreset( int presetIndex ) +{ + if ( presetIndex >= 0 && presetIndex < m_presets.Count() ) + { + const BuyPreset *preset = &(m_presets[presetIndex]); + + int setIndex; + for ( setIndex = 0; setIndex < preset->GetNumSets(); ++setIndex ) + { + // Try to buy this weapon set. + const WeaponSet *itemSet = preset->GetSet( setIndex ); + if ( itemSet ) + { + int currentCost; + WeaponSet currentSet; + itemSet->GetCurrent( currentCost, currentSet ); + if ( currentCost > 0 ) + { + PRESET_DEBUG( "cl_buy_favorite: buying %ls for a total of $%d.\n", + preset->GetName(), + currentCost ); + char buf[BUY_PRESET_COMMAND_LEN]; + currentSet.GenerateBuyCommands( buf ); + + // Send completed string + PRESET_DEBUG( "%s\n", buf ); + engine->ClientCmd( buf ); + return; + } + else if ( currentCost == 0 ) + { + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pPlayer->EmitSound( "BuyPreset.AlreadyBought" ); + } + + // We have everything already. Let the player know. + if ( setIndex == 0 ) + { + PRESET_DEBUG( "cl_buy_favorite: already have a complete %ls set.\n", preset->GetName() ); + } + else + { + PRESET_DEBUG( "cl_buy_favorite: can't afford anything better from %ls.\n", preset->GetName() ); + } + return; + } + } + } + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pPlayer->EmitSound( "BuyPreset.CantBuy" ); + } + + PRESET_DEBUG( "cl_buy_favorite: can't afford anything better from %ls.\n", preset->GetName() ); + return; + } + + // We failed to buy anything. Let the player know. + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pPlayer->EmitSound( "BuyPreset.CantBuy" ); + } + + PRESET_DEBUG( "cl_buy_favorite: preset %d doesn't exist.\n", presetIndex ); +} + + +//-------------------------------------------------------------------------------------------------------------- diff --git a/game/client/cstrike/buy_presets/buy_presets.h b/game/client/cstrike/buy_presets/buy_presets.h new file mode 100644 index 0000000..086e6ba --- /dev/null +++ b/game/client/cstrike/buy_presets/buy_presets.h @@ -0,0 +1,276 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef BUY_PRESETS_H +#define BUY_PRESETS_H +#ifdef _WIN32 +#pragma once +#endif + +#define USE_BUY_PRESETS 1 + +/** + * CLASS STRUCTURE: + * Buy presets are managed by TheBuyPresetManager, which has a list of BuyPreset objects. + * Each BuyPreset has a list of fallback WeaponSets. Each WeaponSet is a complete set of + * weapons and equipment that could be purchased by a buy preset. + * + * BUYING: + * When purchasing a buy preset, the fallback WeaponSets are considered in order from first + * to last. When one is deemed to be purchasable, the buy commands are generated and + * processing stops. + * + * EDITING: + * When editing buy presets, TheBuyPresetManager maintains a second list of BuyPresets, to + * allow an all-or-nothing undo system. The editing is done on two main VGUI menus: a + * list of the four main buy presets (CBuyPresetEditMainMenu), and a menu showing the + * fallbacks for a single preset (CBuyPresetEditOutfitListMenu). + * + * Presets and fallbacks can be copied, created, deleted, etc from these pages. From the + * fallback menu, a wizard can edit the contents of a fallback, specifying primary weapon, + * pistol, and equipment. + */ + +#include "weapon_csbase.h" +#include <KeyValues.h> +#include <utlvector.h> + +class BuyPreset; +class CCSWeaponInfo; + +//-------------------------------------------------------------------------------------------------------------- +enum BuyPresetStringSizes +{ + BUY_PRESET_COMMAND_LEN = 256, + MaxBuyPresetName = 64, + MaxBuyPresetImageFname = 64, +}; + +enum AmmoSizeType +{ + AMMO_PERCENT, + AMMO_CLIPS, + AMMO_ROUNDS +}; + +enum { NUM_PRESETS = 4 }; + +//-------------------------------------------------------------------------------------------------------------- +/** + * A BuyPresetWeapon stores the mp.dll version of the WeaponID, the increment for buying ammo (clips, rounds, etc), + * the number of increments to buy, etc. This is the basic info for buying a weapon that is present in an + * item set. + */ +class BuyPresetWeapon +{ +public: + BuyPresetWeapon(); + BuyPresetWeapon( CSWeaponID weaponID ); + BuyPresetWeapon& operator= (const BuyPresetWeapon& other); + + const wchar_t* GetName() const { return m_name; } ///< Returns the display name corresponding to the weapon ID + CSWeaponID GetWeaponID() const { return m_weaponID; } ///< Returns the WEAPON_* weapon ID + void SetWeaponID( CSWeaponID weaponID ); ///< Sets the WEAPON_* weapon ID (and resets ammo, etc) + + void SetAmmoType( AmmoSizeType ammoType ) { m_ammoType = ammoType; } ///< Sets the ammo type - percent (unused), clips, or rounds (unused) + void SetAmmoAmount( int ammoAmount ) { m_ammoAmount = ammoAmount; } ///< Sets the amount of ammo, in units relevant to the ammo type + void SetFillAmmo( bool fill ) { m_fillAmmo = fill; } ///< Sets whether the weapon's ammo should be topped off + + AmmoSizeType GetAmmoType() const { return m_ammoType; } ///< Returns ammo type - percent (unused), clips, or rounds (unused) + int GetAmmoAmount() const { return m_ammoAmount; } ///< Returns the amount of ammo, in units relevant to the ammo type + bool GetFillAmmo() const { return m_fillAmmo; } ///< Returns true if the weapon's ammo should be topped off + +protected: + const wchar_t *m_name; + CSWeaponID m_weaponID; + AmmoSizeType m_ammoType; + int m_ammoAmount; + bool m_fillAmmo; +}; + +typedef CUtlVector< BuyPresetWeapon > BuyPresetWeaponList; + +//-------------------------------------------------------------------------------------------------------------- +/** + * A WeaponSet is part of a buy preset. Each buy preset is composed of a series of (fallback) WeaponSets. + * The WeaponSet contains a snapshot of a set of weapons and equipment that should be bought. + */ +class WeaponSet +{ +public: + WeaponSet(); + + void GetCurrent( int& cost, WeaponSet& ws ) const; ///< Generates a WeaponSet containing only items that would be bought right now + void GetFromScratch( int& cost, WeaponSet& ws ) const; ///< Generates a WeaponSet containing everything that would be bought from scratch + + void GenerateBuyCommands( char command[BUY_PRESET_COMMAND_LEN] ) const; ///< Generates a list of commands to buy the current WeaponSet. + + int FullCost() const; ///< Calculates the full cost of the WeaponSet, including all optional items + + void Reset( void ); + + const BuyPresetWeapon& GetPrimaryWeapon() const { return m_primaryWeapon; } + const BuyPresetWeapon& GetSecondaryWeapon() const { return m_secondaryWeapon; } + + BuyPresetWeapon m_primaryWeapon; + BuyPresetWeapon m_secondaryWeapon; + + // Equipment -------------------------------- + int m_armor; + bool m_helmet; + bool m_smokeGrenade; + bool m_HEGrenade; + int m_flashbangs; + bool m_defuser; + bool m_nightvision; +}; + +typedef CUtlVector< WeaponSet > WeaponSetList; + +//-------------------------------------------------------------------------------------------------------------- +/** + * The BuyPreset is a complete representation of a buy preset. Essentially, it consists of the preset name, + * whether or not the preset is used for autobuy, and a list of fallback WeaponSets. + */ +class BuyPreset +{ +public: + BuyPreset(); + ~BuyPreset(); + BuyPreset(const BuyPreset& other); + + void SetName( const wchar_t *name ); + const wchar_t * GetName() const { return m_name; } + + void Parse( KeyValues *data ); ///< Populates data from a KeyValues structure, for loading + void Save( KeyValues *data ); ///< Fills in a KeyValues structure with data, for saving + + int GetNumSets() const { return m_weaponList.Count(); } ///< Returns the number of fallback WeaponSets + const WeaponSet * GetSet( int index ) const; ///< Returns the specified fallback WeaponSet, or NULL if it doesn't exist + + int FullCost() const; ///< Calculates the full cost of the preset, which is exactly the full cost of the first fallback WeaponSet + + // editing functions -------------------- + void DeleteSet( int index ); ///< Deletes a fallback + void SwapSet( int firstIndex, int secondIndex ); ///< Switches the order of two fallbacks + void ReplaceSet( int index, const WeaponSet& weaponSet ); ///< Replaces a fallback with the supplied WeaponSet + +private: + wchar_t m_name[MaxBuyPresetName]; + + WeaponSetList m_weaponList; +}; + +typedef CUtlVector< BuyPreset > BuyPresetList; + +//-------------------------------------------------------------------------------------------------------------- +/** + * The BuyPresetManager singleton manages saving/loading/buying the individual BuyPresets. It also tracks the + * ownership of some items that aren't explicitly known by the client (defuser, nightvision). + * + * Two sets of BuyPresets are maintained - the live set used for purchasing, and a set that is acted upon for + * editing. This provides the basic save changes/abandon changes ability when editing presets. + */ +class BuyPresetManager +{ +public: + BuyPresetManager(); + + void Save(); + + void PurchasePreset( int presetIndex ); ///< Generates and sends buy commands to buy a specific preset + + int GetNumPresets() { VerifyLoadedTeam(); return m_presets.Count(); } + const BuyPreset * GetPreset( int index ) const; ///< Returns the specified "live" preset, or NULL if it doesn't exist + void SetPreset( int index, const BuyPreset *preset ); + + void SetPresets( const BuyPresetList& presets ) { m_presets = presets; } + + void SetEditPresets( const BuyPresetList& presets ) { m_editPresets = presets; } + int GetNumEditPresets() const { return m_editPresets.Count(); } + + BuyPreset * GetEditPreset( int index ); ///< Returns the specified editing preset, or NULL if it doesn't exist + const CUtlVector< BuyPreset >& GetEditPresets() const { return m_editPresets; } + + void ResetEditPresets() { m_editPresets = m_presets; } ///< Resets the editing presets to the live presets (e.g. when starting editing) + void ResetEditToDefaults( void ); ///< Loads the default presets into the editing presets (e.g. when hitting the "Use Defaults" button) + + void GetCurrentLoadout( WeaponSet *weaponSet ); + +private: + BuyPresetList m_presets; ///< Current (live) presets + BuyPresetList m_editPresets; ///< BuyPresets used when editing + + int m_loadedTeam; + void VerifyLoadedTeam( void ); +}; + +//-------------------------------------------------------------------------------------------------------------- +extern BuyPresetManager *TheBuyPresets; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Functions for controlling the display of the various buy preset editing menus. Opening one closes any + * others open. + */ +enum { REOPEN_YES, REOPEN_NO, REOPEN_DONTCHANGE }; ///< Determines if we need to re-open the buy menu when done editing. +void ShowBuyPresetMainMenu( bool runUpdate, int reopenBuyMenu ); ///< Open the main preset editing menu +void ShowBuyPresetEditMenu( int presetIndex ); ///< Open the menu for editing the list of fallbacks for an individual preset + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the image filename for a given WEAPON_* weapon ID. Primary is specified because WEAPON_NONE returns + * separate images for primary and pistol, to facilitate showing an outline version for "Current Weapon". + */ +const char *ImageFnameFromWeaponID( CSWeaponID weaponID, bool isPrimary ); + +//-------------------------------------------------------------------------------------------------------------- +/** + * Constructs a list of weapons that will satisfy the any unfinished weapon-specific career tasks: + * 1. If there are no unfinished career tasks, the source list is returned + * 2. If there are unfinished career tasks, all weapons that can be used for the tasks are added + * to the front of the list. This list of career weapons is sorted according to the order of + * weapons in the source list, so that if any weapons in the source list can be used for career + * tasks, they will be. + */ +BuyPresetWeaponList CareerWeaponList( const BuyPresetWeaponList& source, bool isPrimary, CSWeaponID currentClientID ); + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the number of clips that will have to be bought for the specified BuyPresetWeapon + */ +class CCSWeaponInfo; +int CalcClipsNeeded( const BuyPresetWeapon *pWeapon, const CCSWeaponInfo *pInfo, const int ammo[MAX_AMMO_TYPES] ); + +//-------------------------------------------------------------------------------------------------------------- +/** + * Fills the ammo array with the ammo currently owned by the local player + */ +void FillClientAmmo( int ammo[MAX_AMMO_TYPES] ); + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the client can currently buy the weapon according to class/gamemode restrictions. Returns + * true also if the player owns the weapon already. + */ +bool CanBuyWeapon( CSWeaponID currentPrimaryID, CSWeaponID currentSecondaryID, CSWeaponID weaponID ); + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns the display name for a weapon, based on it's weaponID + */ +const wchar_t* WeaponIDToDisplayName( CSWeaponID weaponID ); + +//-------------------------------------------------------------------------------------------------------------- +// If our weapon is NONE, we want to be able to buy up to this many clips for the current gun we're holding +enum { NUM_CLIPS_FOR_CURRENT = 4 }; + +#if USE_BUY_PRESETS +extern ConVar cl_buy_favorite_quiet; +extern ConVar cl_buy_favorite_nowarn; +#endif // USE_BUY_PRESETS + +#endif // BUY_PRESETS_H diff --git a/game/client/cstrike/c_cs_hostage.cpp b/game/client/cstrike/c_cs_hostage.cpp new file mode 100644 index 0000000..afbfe77 --- /dev/null +++ b/game/client/cstrike/c_cs_hostage.cpp @@ -0,0 +1,518 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Client side C_CHostage class +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "c_cs_hostage.h" +#include <bitbuf.h> +#include "ragdoll_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#undef CHostage + +//----------------------------------------------------------------------------- + +static float HOSTAGE_HEAD_TURN_RATE = 130; + + +CUtlVector< C_CHostage* > g_Hostages; +CUtlVector< EHANDLE > g_HostageRagdolls; + +extern ConVar g_ragdoll_fadespeed; + +//----------------------------------------------------------------------------- +const int NumInterestingPoseParameters = 6; +static const char* InterestingPoseParameters[NumInterestingPoseParameters] = +{ + "body_yaw", + "spine_yaw", + "neck_trans", + "head_pitch", + "head_yaw", + "head_roll" +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class C_LowViolenceHostageDeathModel : public C_BaseAnimating +{ +public: + DECLARE_CLASS( C_LowViolenceHostageDeathModel, C_BaseAnimating ); + + C_LowViolenceHostageDeathModel(); + ~C_LowViolenceHostageDeathModel(); + + bool SetupLowViolenceModel( C_CHostage *pHostage ); + + // fading out + void ClientThink( void ); + +private: + + void Interp_Copy( VarMapping_t *pDest, CBaseEntity *pSourceEntity, VarMapping_t *pSrc ); + + float m_flFadeOutStart; +}; + +//----------------------------------------------------------------------------- +C_LowViolenceHostageDeathModel::C_LowViolenceHostageDeathModel() +{ +} + +//----------------------------------------------------------------------------- +C_LowViolenceHostageDeathModel::~C_LowViolenceHostageDeathModel() +{ +} + +//----------------------------------------------------------------------------- +void C_LowViolenceHostageDeathModel::Interp_Copy( VarMapping_t *pDest, CBaseEntity *pSourceEntity, VarMapping_t *pSrc ) +{ + if ( !pDest || !pSrc ) + return; + + if ( pDest->m_Entries.Count() != pSrc->m_Entries.Count() ) + { + Assert( false ); + return; + } + + int c = pDest->m_Entries.Count(); + for ( int i = 0; i < c; i++ ) + { + pDest->m_Entries[ i ].watcher->Copy( pSrc->m_Entries[i].watcher ); + } +} + +//----------------------------------------------------------------------------- +bool C_LowViolenceHostageDeathModel::SetupLowViolenceModel( C_CHostage *pHostage ) +{ + const model_t *model = pHostage->GetModel(); + const char *pModelName = modelinfo->GetModelName( model ); + if ( InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false ) + { + Release(); + return false; + } + + // Play the low-violence death anim + if ( LookupSequence( "death1" ) == -1 ) + { + Release(); + return false; + } + + m_flFadeOutStart = gpGlobals->curtime + 5.0f; + SetNextClientThink( CLIENT_THINK_ALWAYS ); + + SetSequence( LookupSequence( "death1" ) ); + ForceClientSideAnimationOn(); + + if ( pHostage && !pHostage->IsDormant() ) + { + SetNetworkOrigin( pHostage->GetAbsOrigin() ); + SetAbsOrigin( pHostage->GetAbsOrigin() ); + SetAbsVelocity( pHostage->GetAbsVelocity() ); + + // move my current model instance to the ragdoll's so decals are preserved. + pHostage->SnatchModelInstance( this ); + + SetAbsAngles( pHostage->GetRenderAngles() ); + SetNetworkAngles( pHostage->GetRenderAngles() ); + + CStudioHdr *pStudioHdr = GetModelPtr(); + + // update pose parameters + float poseParameter[MAXSTUDIOPOSEPARAM]; + GetPoseParameters( pStudioHdr, poseParameter ); + for ( int i=0; i<NumInterestingPoseParameters; ++i ) + { + int poseParameterIndex = LookupPoseParameter( pStudioHdr, InterestingPoseParameters[i] ); + SetPoseParameter( pStudioHdr, poseParameterIndex, poseParameter[poseParameterIndex] ); + } + } + + Interp_Reset( GetVarMapping() ); + return true; +} + +//----------------------------------------------------------------------------- +void C_LowViolenceHostageDeathModel::ClientThink( void ) +{ + if ( m_flFadeOutStart > gpGlobals->curtime ) + { + return; + } + + int iAlpha = GetRenderColor().a; + + iAlpha = MAX( iAlpha - ( g_ragdoll_fadespeed.GetInt() * gpGlobals->frametime ), 0 ); + + SetRenderMode( kRenderTransAlpha ); + SetRenderColorA( iAlpha ); + + if ( iAlpha == 0 ) + { + Release(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void C_CHostage::RecvProxy_Rescued( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_CHostage *pHostage= (C_CHostage *) pStruct; + + bool isRescued = pData->m_Value.m_Int != 0; + + if ( isRescued && !pHostage->m_isRescued ) + { + // hostage was rescued + pHostage->m_flDeadOrRescuedTime = gpGlobals->curtime + 2; + pHostage->SetRenderMode( kRenderGlow ); + pHostage->SetNextClientThink( gpGlobals->curtime ); + } + + pHostage->m_isRescued = isRescued; +} + +//----------------------------------------------------------------------------- +IMPLEMENT_CLIENTCLASS_DT(C_CHostage, DT_CHostage, CHostage) + + RecvPropInt( RECVINFO( m_isRescued ), 0, C_CHostage::RecvProxy_Rescued ), + RecvPropInt( RECVINFO( m_iHealth ) ), + RecvPropInt( RECVINFO( m_iMaxHealth ) ), + RecvPropInt( RECVINFO( m_lifeState ) ), + + RecvPropEHandle( RECVINFO( m_leader ) ), + +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CHostage::C_CHostage() +{ + g_Hostages.AddToTail( this ); + + m_flDeadOrRescuedTime = 0.0; + m_flLastBodyYaw = 0; + m_createdLowViolenceRagdoll = false; + + // TODO: Get IK working on the steep slopes CS has, then enable it on characters. + m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK; + + // set the model so the PlayerAnimState uses the Hostage activities/sequences + SetModelName( "models/Characters/Hostage_01.mdl" ); + + m_PlayerAnimState = CreateHostageAnimState( this, this, LEGANIM_8WAY, false ); + + m_leader = NULL; + m_blinkTimer.Invalidate(); + m_seq = -1; + + m_flCurrentHeadPitch = 0; + m_flCurrentHeadYaw = 0; + + m_eyeAttachment = -1; + m_chestAttachment = -1; + m_headYawPoseParam = -1; + m_headPitchPoseParam = -1; + m_lookAt = Vector( 0, 0, 0 ); + m_isInit = false; + m_lookAroundTimer.Invalidate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CHostage::~C_CHostage() +{ + g_Hostages.FindAndRemove( this ); + m_PlayerAnimState->Release(); +} + +//----------------------------------------------------------------------------- +void C_CHostage::Spawn( void ) +{ + m_leader = NULL; + m_blinkTimer.Invalidate(); +} + +//----------------------------------------------------------------------------- +bool C_CHostage::ShouldDraw( void ) +{ + if ( m_createdLowViolenceRagdoll ) + return false; + + return BaseClass::ShouldDraw(); +} + +//----------------------------------------------------------------------------- +C_BaseAnimating * C_CHostage::BecomeRagdollOnClient() +{ + if ( g_RagdollLVManager.IsLowViolence() ) + { + // We can't just play the low-violence anim ourselves, since we're about to be deleted by the server. + // So, let's create another entity that can play the anim and stick around. + C_LowViolenceHostageDeathModel *pLowViolenceModel = new C_LowViolenceHostageDeathModel(); + m_createdLowViolenceRagdoll = pLowViolenceModel->SetupLowViolenceModel( this ); + if ( m_createdLowViolenceRagdoll ) + { + UpdateVisibility(); + g_HostageRagdolls.AddToTail( pLowViolenceModel ); + return pLowViolenceModel; + } + else + { + // if we don't have a low-violence death anim, don't create a ragdoll. + return NULL; + } + } + + C_BaseAnimating *pRagdoll = BaseClass::BecomeRagdollOnClient(); + if ( pRagdoll && pRagdoll != this ) + { + g_HostageRagdolls.AddToTail( pRagdoll ); + } + return pRagdoll; +} + +//----------------------------------------------------------------------------- +/** + * Set up attachment and pose param indices. + * We can't do this in the constructor or Spawn() because the data isn't + * there yet. + */ +void C_CHostage::Initialize( ) +{ + m_eyeAttachment = LookupAttachment( "eyes" ); + m_chestAttachment = LookupAttachment( "chest" ); + + m_headYawPoseParam = LookupPoseParameter( "head_yaw" ); + GetPoseParameterRange( m_headYawPoseParam, m_headYawMin, m_headYawMax ); + + m_headPitchPoseParam = LookupPoseParameter( "head_pitch" ); + GetPoseParameterRange( m_headPitchPoseParam, m_headPitchMin, m_headPitchMax ); + + m_bodyYawPoseParam = LookupPoseParameter( "body_yaw" ); + GetPoseParameterRange( m_bodyYawPoseParam, m_bodyYawMin, m_bodyYawMax ); + + Vector pos; + QAngle angles; + + if (!GetAttachment( m_eyeAttachment, pos, angles )) + { + m_vecViewOffset = Vector( 0, 0, 50.0f ); + } + else + { + m_vecViewOffset = pos - GetAbsOrigin(); + } + + + if (!GetAttachment( m_chestAttachment, pos, angles )) + { + m_lookAt = Vector( 0, 0, 0 ); + } + else + { + Vector forward; + AngleVectors( angles, &forward ); + m_lookAt = EyePosition() + 100.0f * forward; + } +} + +//----------------------------------------------------------------------------- +CWeaponCSBase* C_CHostage::CSAnim_GetActiveWeapon() +{ + return NULL; +} + +//----------------------------------------------------------------------------- +bool C_CHostage::CSAnim_CanMove() +{ + return true; +} + + +//----------------------------------------------------------------------------- +/** + * Orient head and eyes towards m_lookAt. + */ +void C_CHostage::UpdateLookAt( CStudioHdr *pStudioHdr ) +{ + if (!m_isInit) + { + m_isInit = true; + Initialize( ); + } + + // head yaw + if (m_headYawPoseParam < 0 || m_bodyYawPoseParam < 0 || m_headPitchPoseParam < 0) + return; + + if (GetLeader()) + { + m_lookAt = GetLeader()->EyePosition(); + } + + // orient eyes + m_viewtarget = m_lookAt; + + // blinking + if (m_blinkTimer.IsElapsed()) + { + m_blinktoggle = !m_blinktoggle; + m_blinkTimer.Start( RandomFloat( 1.5f, 4.0f ) ); + } + + // Figure out where we want to look in world space. + QAngle desiredAngles; + Vector to = m_lookAt - EyePosition(); + VectorAngles( to, desiredAngles ); + + // Figure out where our body is facing in world space. + float poseParams[MAXSTUDIOPOSEPARAM]; + GetPoseParameters( pStudioHdr, poseParams ); + QAngle bodyAngles( 0, 0, 0 ); + bodyAngles[YAW] = GetRenderAngles()[YAW] + RemapVal( poseParams[m_bodyYawPoseParam], 0, 1, m_bodyYawMin, m_bodyYawMax ); + + + float flBodyYawDiff = bodyAngles[YAW] - m_flLastBodyYaw; + m_flLastBodyYaw = bodyAngles[YAW]; + + + // Set the head's yaw. + float desired = AngleNormalize( desiredAngles[YAW] - bodyAngles[YAW] ); + desired = clamp( desired, m_headYawMin, m_headYawMax ); + m_flCurrentHeadYaw = ApproachAngle( desired, m_flCurrentHeadYaw, HOSTAGE_HEAD_TURN_RATE * gpGlobals->frametime ); + + // Counterrotate the head from the body rotation so it doesn't rotate past its target. + m_flCurrentHeadYaw = AngleNormalize( m_flCurrentHeadYaw - flBodyYawDiff ); + desired = clamp( desired, m_headYawMin, m_headYawMax ); + + SetPoseParameter( pStudioHdr, m_headYawPoseParam, m_flCurrentHeadYaw ); + + + // Set the head's yaw. + desired = AngleNormalize( desiredAngles[PITCH] ); + desired = clamp( desired, m_headPitchMin, m_headPitchMax ); + + m_flCurrentHeadPitch = ApproachAngle( desired, m_flCurrentHeadPitch, HOSTAGE_HEAD_TURN_RATE * gpGlobals->frametime ); + m_flCurrentHeadPitch = AngleNormalize( m_flCurrentHeadPitch ); + SetPoseParameter( pStudioHdr, m_headPitchPoseParam, m_flCurrentHeadPitch ); + + SetPoseParameter( pStudioHdr, "head_roll", 0.0f ); +} + + +//----------------------------------------------------------------------------- +/** + * Look around at various interesting things + */ +void C_CHostage::LookAround( void ) +{ + if (GetLeader() == NULL && m_lookAroundTimer.IsElapsed()) + { + m_lookAroundTimer.Start( RandomFloat( 3.0f, 15.0f ) ); + + Vector forward; + QAngle angles = GetAbsAngles(); + angles[ YAW ] += RandomFloat( m_headYawMin, m_headYawMax ); + angles[ PITCH ] += RandomFloat( m_headPitchMin, m_headPitchMax ); + AngleVectors( angles, &forward ); + m_lookAt = EyePosition() + 100.0f * forward; + } +} + +//----------------------------------------------------------------------------- +void C_CHostage::UpdateClientSideAnimation() +{ + if (IsDormant()) + { + return; + } + + m_PlayerAnimState->Update( GetAbsAngles()[YAW], GetAbsAngles()[PITCH] ); + + // initialize pose parameters + char *setToZero[] = + { + "spine_yaw", + "head_roll" + }; + CStudioHdr *pStudioHdr = GetModelPtr(); + for ( int i=0; i < ARRAYSIZE( setToZero ); i++ ) + { + int index = LookupPoseParameter( pStudioHdr, setToZero[i] ); + if ( index >= 0 ) + SetPoseParameter( pStudioHdr, index, 0 ); + } + + // orient head and eyes + LookAround(); + UpdateLookAt( pStudioHdr ); + + + BaseClass::UpdateClientSideAnimation(); +} + +//----------------------------------------------------------------------------- +void C_CHostage::ClientThink() +{ + C_BaseCombatCharacter::ClientThink(); + + int speed = 2; + int a = m_clrRender->a; + + a = MAX( 0, a - speed ); + + SetRenderColorA( a ); + + if ( m_clrRender->a > 0 ) + { + SetNextClientThink( gpGlobals->curtime + 0.001 ); + } +} + +//----------------------------------------------------------------------------- +bool C_CHostage::WasRecentlyKilledOrRescued( void ) +{ + return ( gpGlobals->curtime < m_flDeadOrRescuedTime ); +} + +//----------------------------------------------------------------------------- +void C_CHostage::OnPreDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnPreDataChanged( updateType ); + + m_OldLifestate = m_lifeState; +} + +//----------------------------------------------------------------------------- +void C_CHostage::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + if ( m_OldLifestate != m_lifeState ) + { + if( m_lifeState == LIFE_DEAD || m_lifeState == LIFE_DYING ) + m_flDeadOrRescuedTime = gpGlobals->curtime + 2; + } +} + +//----------------------------------------------------------------------------- +void C_CHostage::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ) +{ + static ConVar *violence_hblood = cvar->FindVar( "violence_hblood" ); + if ( violence_hblood && !violence_hblood->GetBool() ) + return; + + BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName ); +} + diff --git a/game/client/cstrike/c_cs_hostage.h b/game/client/cstrike/c_cs_hostage.h new file mode 100644 index 0000000..35ba2f9 --- /dev/null +++ b/game/client/cstrike/c_cs_hostage.h @@ -0,0 +1,123 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Client side CHostage class +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef C_CHOSTAGE_H +#define C_CHOSTAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "c_ai_basenpc.h" +#include "utlvector.h" +#include "util_shared.h" +#include "cs_playeranimstate.h" +#include "c_cs_player.h" + + +// for shared code +#define CHostage C_CHostage + + +//---------------------------------------------------------------------------------------------- +/** + * The client-side implementation of the Hostage + */ +class C_CHostage : public C_BaseCombatCharacter, public ICSPlayerAnimStateHelpers +{ +public: + DECLARE_CLASS( C_CHostage, C_BaseCombatCharacter ); + DECLARE_CLIENTCLASS(); + + C_CHostage(); + virtual ~C_CHostage(); + +// ICSPlayerAnimState overrides. +public: + virtual CWeaponCSBase* CSAnim_GetActiveWeapon(); + virtual bool CSAnim_CanMove(); + +public: + virtual void Spawn( void ); + virtual void UpdateClientSideAnimation(); + + void OnPreDataChanged( DataUpdateType_t updateType ); + void OnDataChanged( DataUpdateType_t updateType ); + + bool IsRescued( void ) { return m_isRescued; } + bool WasRecentlyKilledOrRescued( void ); + + int GetHealth( void ) const { return m_iHealth; } + int GetMaxHealth( void ) const { return m_iMaxHealth; } + + virtual void ClientThink( void ); + + C_CSPlayer *GetLeader( void ) const; // return who we are following or NULL + + virtual C_BaseAnimating * BecomeRagdollOnClient(); + virtual bool ShouldDraw( void ); + + void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ); +private: + int m_OldLifestate; + int m_iMaxHealth; + + ICSPlayerAnimState *m_PlayerAnimState; + + CNetworkVar( EHANDLE, m_leader ); // who we are following, or NULL + + CNetworkVar( bool, m_isRescued ); + float m_flDeadOrRescuedTime; + static void RecvProxy_Rescued( const CRecvProxyData *pData, void *pStruct, void *pOut ); + + CountdownTimer m_blinkTimer; + + Vector m_lookAt; // point in space we are looking at + void UpdateLookAt( CStudioHdr *pStudioHdr ); // orient head and eyes towards m_lookAt + void LookAround( void ); // look around at various interesting things + CountdownTimer m_lookAroundTimer; + + bool m_isInit; + void Initialize( void ); // set up attachment and pose param indices + + int m_eyeAttachment; + int m_chestAttachment; + + int m_bodyYawPoseParam; + float m_bodyYawMin; + float m_bodyYawMax; + + int m_headYawPoseParam; + float m_headYawMin; + float m_headYawMax; + float m_flCurrentHeadYaw; + float m_flLastBodyYaw; + + int m_headPitchPoseParam; + float m_headPitchMin; + float m_headPitchMax; + float m_flCurrentHeadPitch; + + int m_seq; + + bool m_createdLowViolenceRagdoll; + +private: + C_CHostage( const C_CHostage & ); // not defined, not accessible +}; + + +inline C_CSPlayer *C_CHostage::GetLeader( void ) const +{ + return ToCSPlayer( m_leader.m_Value ); +} + + +extern CUtlVector< C_CHostage* > g_Hostages; +extern CUtlVector< EHANDLE > g_HostageRagdolls; + + +#endif // C_CHOSTAGE_H diff --git a/game/client/cstrike/c_cs_player.cpp b/game/client/cstrike/c_cs_player.cpp new file mode 100644 index 0000000..974dfd4 --- /dev/null +++ b/game/client/cstrike/c_cs_player.cpp @@ -0,0 +1,2553 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "c_cs_player.h" +#include "c_user_message_register.h" +#include "view.h" +#include "iclientvehicle.h" +#include "ivieweffects.h" +#include "input.h" +#include "IEffects.h" +#include "fx.h" +#include "c_basetempentity.h" +#include "hud_macros.h" //HOOK_COMMAND +#include "engine/ivdebugoverlay.h" +#include "smoke_fog_overlay.h" +#include "bone_setup.h" +#include "in_buttons.h" +#include "r_efx.h" +#include "dlight.h" +#include "shake.h" +#include "cl_animevent.h" +#include "c_physicsprop.h" +#include "props_shared.h" +#include "obstacle_pushaway.h" +#include "death_pose.h" + +#include "effect_dispatch_data.h" //for water ripple / splash effect +#include "c_te_effect_dispatch.h" //ditto +#include "c_te_legacytempents.h" +#include "cs_gamerules.h" +#include "fx_cs_blood.h" +#include "c_cs_playerresource.h" +#include "c_team.h" + +#include "history_resource.h" +#include "ragdoll_shared.h" +#include "collisionutils.h" + +// NVNT - haptics system for spectating +#include "haptics/haptic_utils.h" + +#include "steam/steam_api.h" + +#include "cs_blackmarket.h" // for vest/helmet prices + +#if defined( CCSPlayer ) + #undef CCSPlayer +#endif + +#include "materialsystem/imesh.h" //for materials->FindMaterial +#include "iviewrender.h" //for view-> + +#include "iviewrender_beams.h" // flashlight beam + +//============================================================================= +// HPE_BEGIN: +// [menglish] Adding and externing variables needed for the freezecam +//============================================================================= + +static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET); +static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET); + +extern ConVar spec_freeze_time; +extern ConVar spec_freeze_traveltime; +extern ConVar spec_freeze_distance_min; +extern ConVar spec_freeze_distance_max; + +//============================================================================= +// HPE_END +//============================================================================= + +ConVar cl_left_hand_ik( "cl_left_hand_ik", "0", 0, "Attach player's left hand to rifle with IK." ); + +ConVar cl_ragdoll_physics_enable( "cl_ragdoll_physics_enable", "1", 0, "Enable/disable ragdoll physics." ); + +ConVar cl_minmodels( "cl_minmodels", "0", 0, "Uses one player model for each team." ); +ConVar cl_min_ct( "cl_min_ct", "1", 0, "Controls which CT model is used when cl_minmodels is set.", true, 1, true, 4 ); +ConVar cl_min_t( "cl_min_t", "1", 0, "Controls which Terrorist model is used when cl_minmodels is set.", true, 1, true, 4 ); +const float CycleLatchTolerance = 0.15; // amount we can diverge from the server's cycle before we're corrected + +extern ConVar mp_playerid_delay; +extern ConVar mp_playerid_hold; +extern ConVar sv_allowminmodels; + +class CAddonInfo +{ +public: + const char *m_pAttachmentName; + const char *m_pWeaponClassName; // The addon uses the w_ model from this weapon. + const char *m_pModelName; //If this is present, will use this model instead of looking up the weapon + const char *m_pHolsterName; +}; + + + +// These must follow the ADDON_ ordering. +CAddonInfo g_AddonInfo[] = +{ + { "grenade0", "weapon_flashbang", 0, 0 }, + { "grenade1", "weapon_flashbang", 0, 0 }, + { "grenade2", "weapon_hegrenade", 0, 0 }, + { "grenade3", "weapon_smokegrenade", 0, 0 }, + { "c4", "weapon_c4", 0, 0 }, + { "defusekit", 0, "models/weapons/w_defuser.mdl", 0 }, + { "primary", 0, 0, 0 }, // Primary addon model is looked up based on m_iPrimaryAddon + { "pistol", 0, 0, 0 }, // Pistol addon model is looked up based on m_iSecondaryAddon + { "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" }, +}; + +// -------------------------------------------------------------------------------- // +// Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. +// -------------------------------------------------------------------------------- // + +class C_TEPlayerAnimEvent : public C_BaseTempEntity +{ +public: + DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity ); + DECLARE_CLIENTCLASS(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ) + { + // Create the effect. + C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() ); + if ( pPlayer && !pPlayer->IsDormant() ) + { + pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData ); + } + } + +public: + CNetworkHandle( CBasePlayer, m_hPlayer ); + CNetworkVar( int, m_iEvent ); + CNetworkVar( int, m_nData ); +}; + +IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent ); + +BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent ) + RecvPropEHandle( RECVINFO( m_hPlayer ) ), + RecvPropInt( RECVINFO( m_iEvent ) ), + RecvPropInt( RECVINFO( m_nData ) ) +END_RECV_TABLE() + +BEGIN_PREDICTION_DATA( C_CSPlayer ) +#ifdef CS_SHIELD_ENABLED + DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), +#endif + DEFINE_PRED_FIELD_TOL( m_flStamina, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.1f ), + DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ), + DEFINE_PRED_FIELD( m_iShotsFired, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_iDirection, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bResumeZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_iLastZoom, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + +END_PREDICTION_DATA() + +vgui::IImage* GetDefaultAvatarImage( C_BasePlayer *pPlayer ) +{ + vgui::IImage* result = NULL; + + switch ( pPlayer ? pPlayer->GetTeamNumber() : TEAM_MAXCOUNT ) + { + case TEAM_TERRORIST: + result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_T_AVATAR, true ); + break; + + case TEAM_CT: + result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_CT_AVATAR, true ); + break; + + default: + result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_AVATAR, true ); + break; + } + + return result; +} + +// ----------------------------------------------------------------------------- // +// Client ragdoll entity. +// ----------------------------------------------------------------------------- // + +float g_flDieTranslucentTime = 0.6; + +class C_CSRagdoll : public C_BaseAnimatingOverlay +{ +public: + DECLARE_CLASS( C_CSRagdoll, C_BaseAnimatingOverlay ); + DECLARE_CLIENTCLASS(); + + C_CSRagdoll(); + ~C_CSRagdoll(); + + virtual void OnDataChanged( DataUpdateType_t type ); + + int GetPlayerEntIndex() const; + IRagdoll* GetIRagdoll() const; + bool GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt ) OVERRIDE; + + void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ); + + virtual void ComputeFxBlend(); + virtual bool IsTransparent(); + bool IsInitialized() { return m_bInitialized; } + // fading ragdolls don't cast shadows + virtual ShadowType_t ShadowCastType() + { + if ( m_flRagdollSinkStart == -1 ) + return BaseClass::ShadowCastType(); + return SHADOWS_NONE; + } + + virtual void ValidateModelIndex( void ); + +private: + + C_CSRagdoll( const C_CSRagdoll & ) {} + + void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity ); + + void CreateLowViolenceRagdoll( void ); + void CreateCSRagdoll( void ); + +private: + + EHANDLE m_hPlayer; + CNetworkVector( m_vecRagdollVelocity ); + CNetworkVector( m_vecRagdollOrigin ); + CNetworkVar(int, m_iDeathPose ); + CNetworkVar(int, m_iDeathFrame ); + float m_flRagdollSinkStart; + bool m_bInitialized; + bool m_bCreatedWhilePlaybackSkipping; +}; + + +IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_CSRagdoll, DT_CSRagdoll, CCSRagdoll ) + RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ), + RecvPropVector( RECVINFO(m_vecRagdollOrigin) ), + RecvPropEHandle( RECVINFO( m_hPlayer ) ), + RecvPropInt( RECVINFO( m_nModelIndex ) ), + RecvPropInt( RECVINFO(m_nForceBone) ), + RecvPropVector( RECVINFO(m_vecForce) ), + RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ), + RecvPropInt( RECVINFO(m_iDeathPose) ), + RecvPropInt( RECVINFO(m_iDeathFrame) ), + RecvPropInt(RECVINFO(m_iTeamNum)), + RecvPropInt( RECVINFO(m_bClientSideAnimation)), +END_RECV_TABLE() + + +C_CSRagdoll::C_CSRagdoll() +{ + m_flRagdollSinkStart = -1; + m_bInitialized = false; + m_bCreatedWhilePlaybackSkipping = engine->IsSkippingPlayback(); +} + +C_CSRagdoll::~C_CSRagdoll() +{ + PhysCleanupFrictionSounds( this ); +} + +bool C_CSRagdoll::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt ) +{ + // otherwise use the death pose to set up the ragdoll + ForceSetupBonesAtTime( pDeltaBones0, gpGlobals->curtime - boneDt ); + GetRagdollCurSequenceWithDeathPose( this, pDeltaBones1, gpGlobals->curtime, m_iDeathPose, m_iDeathFrame ); + return SetupBones( pCurrentBones, MAXSTUDIOBONES, BONE_USED_BY_ANYTHING, gpGlobals->curtime ); +} + +void C_CSRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity ) +{ + if ( !pSourceEntity ) + return; + + VarMapping_t *pSrc = pSourceEntity->GetVarMapping(); + VarMapping_t *pDest = GetVarMapping(); + + // Find all the VarMapEntry_t's that represent the same variable. + for ( int i = 0; i < pDest->m_Entries.Count(); i++ ) + { + VarMapEntry_t *pDestEntry = &pDest->m_Entries[i]; + for ( int j=0; j < pSrc->m_Entries.Count(); j++ ) + { + VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j]; + if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), + pDestEntry->watcher->GetDebugName() ) ) + { + pDestEntry->watcher->Copy( pSrcEntry->watcher ); + break; + } + } + } +} + +void C_CSRagdoll::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ) +{ + IPhysicsObject *pPhysicsObject = VPhysicsGetObject(); + + if( !pPhysicsObject ) + return; + + Vector dir = pTrace->endpos - pTrace->startpos; + + if ( iDamageType == DMG_BLAST ) + { + dir *= 4000; // adjust impact strenght + + // apply force at object mass center + pPhysicsObject->ApplyForceCenter( dir ); + } + else + { + Vector hitpos; + + VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos ); + VectorNormalize( dir ); + + dir *= 4000; // adjust impact strenght + + // apply force where we hit it + pPhysicsObject->ApplyForceOffset( dir, hitpos ); + + // Blood spray! + FX_CS_BloodSpray( hitpos, dir, 10 ); + } + + m_pRagdoll->ResetRagdollSleepAfterTime(); +} + + +void C_CSRagdoll::ValidateModelIndex( void ) +{ + if ( sv_allowminmodels.GetBool() && cl_minmodels.GetBool() ) + { + if ( GetTeamNumber() == TEAM_CT ) + { + int index = cl_min_ct.GetInt() - 1; + if ( index >= 0 && index < CTPlayerModels.Count() ) + { + m_nModelIndex = modelinfo->GetModelIndex(CTPlayerModels[index]); + } + } + else if ( GetTeamNumber() == TEAM_TERRORIST ) + { + int index = cl_min_t.GetInt() - 1; + if ( index >= 0 && index < TerroristPlayerModels.Count() ) + { + m_nModelIndex = modelinfo->GetModelIndex(TerroristPlayerModels[index]); + } + } + } + + BaseClass::ValidateModelIndex(); +} + + +void C_CSRagdoll::CreateLowViolenceRagdoll( void ) +{ + // Just play a death animation. + // Find a death anim to play. + int iMinDeathAnim = 9999, iMaxDeathAnim = -9999; + for ( int iAnim=1; iAnim < 100; iAnim++ ) + { + char str[512]; + Q_snprintf( str, sizeof( str ), "death%d", iAnim ); + if ( LookupSequence( str ) == -1 ) + break; + + iMinDeathAnim = MIN( iMinDeathAnim, iAnim ); + iMaxDeathAnim = MAX( iMaxDeathAnim, iAnim ); + } + + if ( iMinDeathAnim == 9999 ) + { + CreateCSRagdoll(); + return; + } + + SetNetworkOrigin( m_vecRagdollOrigin ); + SetAbsOrigin( m_vecRagdollOrigin ); + SetAbsVelocity( m_vecRagdollVelocity ); + + C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() ); + if ( pPlayer ) + { + if ( !pPlayer->IsDormant() ) + { + // move my current model instance to the ragdoll's so decals are preserved. + pPlayer->SnatchModelInstance( this ); + } + + SetAbsAngles( pPlayer->GetRenderAngles() ); + SetNetworkAngles( pPlayer->GetRenderAngles() ); + } + + int iDeathAnim = RandomInt( iMinDeathAnim, iMaxDeathAnim ); + char str[512]; + Q_snprintf( str, sizeof( str ), "death%d", iDeathAnim ); + SetSequence( LookupSequence( str ) ); + ForceClientSideAnimationOn(); + + Interp_Reset( GetVarMapping() ); +} + + +void C_CSRagdoll::CreateCSRagdoll() +{ + // First, initialize all our data. If we have the player's entity on our client, + // then we can make ourselves start out exactly where the player is. + C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() ); + + // mark this to prevent model changes from overwriting the death sequence with the server sequence + SetReceivedSequence(); + + if ( pPlayer && !pPlayer->IsDormant() ) + { + // move my current model instance to the ragdoll's so decals are preserved. + pPlayer->SnatchModelInstance( this ); + + VarMapping_t *varMap = GetVarMapping(); + + // Copy all the interpolated vars from the player entity. + // The entity uses the interpolated history to get bone velocity. + bool bRemotePlayer = (pPlayer != C_BasePlayer::GetLocalPlayer()); + if ( bRemotePlayer ) + { + Interp_Copy( pPlayer ); + + SetAbsAngles( pPlayer->GetRenderAngles() ); + GetRotationInterpolator().Reset(); + + m_flAnimTime = pPlayer->m_flAnimTime; + SetSequence( pPlayer->GetSequence() ); + m_flPlaybackRate = pPlayer->GetPlaybackRate(); + } + else + { + // This is the local player, so set them in a default + // pose and slam their velocity, angles and origin + SetAbsOrigin( m_vecRagdollOrigin ); + + SetAbsAngles( pPlayer->GetRenderAngles() ); + + SetAbsVelocity( m_vecRagdollVelocity ); + + int iSeq = LookupSequence( "walk_lower" ); + if ( iSeq == -1 ) + { + Assert( false ); // missing walk_lower? + iSeq = 0; + } + + SetSequence( iSeq ); // walk_lower, basic pose + SetCycle( 0.0 ); + + // go ahead and set these on the player in case the code below decides to set up bones using + // that entity instead of this one. The local player may not have valid animation + pPlayer->SetSequence( iSeq ); // walk_lower, basic pose + pPlayer->SetCycle( 0.0 ); + + Interp_Reset( varMap ); + } + } + else + { + // overwrite network origin so later interpolation will + // use this position + SetNetworkOrigin( m_vecRagdollOrigin ); + + SetAbsOrigin( m_vecRagdollOrigin ); + SetAbsVelocity( m_vecRagdollVelocity ); + + Interp_Reset( GetVarMapping() ); + } + + // Turn it into a ragdoll. + if ( cl_ragdoll_physics_enable.GetInt() ) + { + // Make us a ragdoll.. + m_nRenderFX = kRenderFxRagdoll; + + matrix3x4_t boneDelta0[MAXSTUDIOBONES]; + matrix3x4_t boneDelta1[MAXSTUDIOBONES]; + matrix3x4_t currentBones[MAXSTUDIOBONES]; + const float boneDt = 0.05f; + + //============================================================================= + // [pfreese], [tj] + // There are visual problems with the attempted blending of the + // death pose animations in C_CSRagdoll::GetRagdollInitBoneArrays. The version + // in C_BasePlayer::GetRagdollInitBoneArrays doesn't attempt to blend death + // poses, so if the player is relevant, use that one regardless of whether the + // player is the local one or not. + //============================================================================= + if ( pPlayer && !pPlayer->IsDormant() ) + { + pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt ); + } + else + { + GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt ); + } + + InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); + m_flRagdollSinkStart = -1; + } + else + { + m_flRagdollSinkStart = gpGlobals->curtime; + DestroyShadow(); + ClientLeafSystem()->SetRenderGroup( GetRenderHandle(), RENDER_GROUP_TRANSLUCENT_ENTITY ); + } + m_bInitialized = true; +} + + +void C_CSRagdoll::ComputeFxBlend( void ) +{ + if ( m_flRagdollSinkStart == -1 ) + { + BaseClass::ComputeFxBlend(); + } + else + { + float elapsed = gpGlobals->curtime - m_flRagdollSinkStart; + float flVal = RemapVal( elapsed, 0, g_flDieTranslucentTime, 255, 0 ); + flVal = clamp( flVal, 0, 255 ); + m_nRenderFXBlend = (int)flVal; + +#ifdef _DEBUG + m_nFXComputeFrame = gpGlobals->framecount; +#endif + } +} + + +bool C_CSRagdoll::IsTransparent( void ) +{ + if ( m_flRagdollSinkStart == -1 ) + { + return BaseClass::IsTransparent(); + } + else + { + return true; + } +} + + +void C_CSRagdoll::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if ( type == DATA_UPDATE_CREATED ) + { + // Prevent replays from creating ragdolls on the first frame of playback after skipping through playback. + // If a player died (leaving a ragdoll) previous to the first frame of replay playback, + // their ragdoll wasn't yet initialized because OnDataChanged events are queued but not processed + // until the first render. + if ( engine->IsPlayingDemo() && m_bCreatedWhilePlaybackSkipping ) + { + Release(); + return; + } + + if ( g_RagdollLVManager.IsLowViolence() ) + { + CreateLowViolenceRagdoll(); + } + else + { + CreateCSRagdoll(); + } + } + else + { + if ( !cl_ragdoll_physics_enable.GetInt() ) + { + // Don't let it set us back to a ragdoll with data from the server. + m_nRenderFX = kRenderFxNone; + } + } +} + +IRagdoll* C_CSRagdoll::GetIRagdoll() const +{ + return m_pRagdoll; +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the player toggles nightvision +// Input : *pData - the int value of the nightvision state +// *pStruct - the player +// *pOut - +//----------------------------------------------------------------------------- +void RecvProxy_NightVision( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_CSPlayer *pPlayerData = (C_CSPlayer *) pStruct; + + bool bNightVisionOn = ( pData->m_Value.m_Int > 0 ); + + if ( pPlayerData->m_bNightVisionOn != bNightVisionOn ) + { + if ( bNightVisionOn ) + pPlayerData->m_flNightVisionAlpha = 1; + } + + pPlayerData->m_bNightVisionOn = bNightVisionOn; +} + +void RecvProxy_FlashTime( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_CSPlayer *pPlayerData = (C_CSPlayer *) pStruct; + + if( pPlayerData != C_BasePlayer::GetLocalPlayer() ) + return; + + if ( (pPlayerData->m_flFlashDuration != pData->m_Value.m_Float) && pData->m_Value.m_Float > 0 ) + { + pPlayerData->m_flFlashAlpha = 1; + } + + pPlayerData->m_flFlashDuration = pData->m_Value.m_Float; + pPlayerData->m_flFlashBangTime = gpGlobals->curtime + pPlayerData->m_flFlashDuration; +} + +void RecvProxy_HasDefuser( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_CSPlayer *pPlayerData = (C_CSPlayer *)pStruct; + + if (pPlayerData == NULL) + { + return; + } + + bool drawIcon = false; + + if (pData->m_Value.m_Int == 0) + { + pPlayerData->RemoveDefuser(); + } + else + { + if (pPlayerData->HasDefuser() == false) + { + drawIcon = true; + } + pPlayerData->GiveDefuser(); + } + + if (pPlayerData->IsLocalPlayer() && drawIcon) + { + // add to pickup history + CHudHistoryResource *pHudHR = GET_HUDELEMENT( CHudHistoryResource ); + + if ( pHudHR ) + { + pHudHR->AddToHistory(HISTSLOT_ITEM, "defuser_pickup"); + } + } +} + +void C_CSPlayer::RecvProxy_CycleLatch( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + // This receive proxy looks to see if the server's value is close enough to what we think it should + // be. We've been running the same code; this is an error correction for changes we didn't simulate + // while they were out of PVS. + C_CSPlayer *pPlayer = (C_CSPlayer *)pStruct; + if( pPlayer->IsLocalPlayer() ) + return; // Don't need to fixup ourselves. + + float incomingCycle = (float)(pData->m_Value.m_Int) / 16; // Came in as 4 bit fixed point + float currentCycle = pPlayer->GetCycle(); + bool closeEnough = fabs(currentCycle - incomingCycle) < CycleLatchTolerance; + if( fabs(currentCycle - incomingCycle) > (1 - CycleLatchTolerance) ) + { + closeEnough = true;// Handle wrapping around 1->0 + } + + if( !closeEnough ) + { + // Server disagrees too greatly. Correct our value. + if ( pPlayer && pPlayer->GetTeam() ) + { + DevMsg( 2, "%s %s(%d): Cycle latch wants to correct %.2f in to %.2f.\n", + pPlayer->GetTeam()->Get_Name(), pPlayer->GetPlayerName(), pPlayer->entindex(), currentCycle, incomingCycle ); + } + pPlayer->SetServerIntendedCycle( incomingCycle ); + } +} + +void __MsgFunc_ReloadEffect( bf_read &msg ) +{ + int iPlayer = msg.ReadShort(); + C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( C_BaseEntity::Instance( iPlayer ) ); + if ( pPlayer ) + pPlayer->PlayReloadEffect(); + +} +USER_MESSAGE_REGISTER( ReloadEffect ); + +BEGIN_RECV_TABLE_NOBASE( C_CSPlayer, DT_CSLocalPlayerExclusive ) + RecvPropFloat( RECVINFO(m_flStamina) ), + RecvPropInt( RECVINFO( m_iDirection ) ), + RecvPropInt( RECVINFO( m_iShotsFired ) ), + RecvPropFloat( RECVINFO( m_flVelocityModifier ) ), + + RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ), + + //============================================================================= + // HPE_BEGIN: + // [tj]Set up the receive table for per-client domination data + //============================================================================= + + RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominated ), RecvPropBool( RECVINFO( m_bPlayerDominated[0] ) ) ), + RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominatingMe ), RecvPropBool( RECVINFO( m_bPlayerDominatingMe[0] ) ) ) + + //============================================================================= + // HPE_END + //============================================================================= + +END_RECV_TABLE() + + +BEGIN_RECV_TABLE_NOBASE( C_CSPlayer, DT_CSNonLocalPlayerExclusive ) + RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ), +END_RECV_TABLE() + + +IMPLEMENT_CLIENTCLASS_DT( C_CSPlayer, DT_CSPlayer, CCSPlayer ) + RecvPropDataTable( "cslocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_CSLocalPlayerExclusive) ), + RecvPropDataTable( "csnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_CSNonLocalPlayerExclusive) ), + RecvPropInt( RECVINFO( m_iAddonBits ) ), + RecvPropInt( RECVINFO( m_iPrimaryAddon ) ), + RecvPropInt( RECVINFO( m_iSecondaryAddon ) ), + RecvPropInt( RECVINFO( m_iThrowGrenadeCounter ) ), + RecvPropInt( RECVINFO( m_iPlayerState ) ), + RecvPropInt( RECVINFO( m_iAccount ) ), + RecvPropInt( RECVINFO( m_bInBombZone ) ), + RecvPropInt( RECVINFO( m_bInBuyZone ) ), + RecvPropInt( RECVINFO( m_iClass ) ), + RecvPropInt( RECVINFO( m_ArmorValue ) ), + RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ), + RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ), + RecvPropFloat( RECVINFO( m_flStamina ) ), + RecvPropInt( RECVINFO( m_bHasDefuser ), 0, RecvProxy_HasDefuser ), + RecvPropInt( RECVINFO( m_bNightVisionOn), 0, RecvProxy_NightVision ), + RecvPropBool( RECVINFO( m_bHasNightVision ) ), + + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Added for fun-fact support + //============================================================================= + + //RecvPropBool( RECVINFO( m_bPickedUpDefuser ) ), + //RecvPropBool( RECVINFO( m_bDefusedWithPickedUpKit ) ), + + //============================================================================= + // HPE_END + //============================================================================= + + RecvPropBool( RECVINFO( m_bInHostageRescueZone ) ), + RecvPropInt( RECVINFO( m_ArmorValue ) ), + RecvPropBool( RECVINFO( m_bIsDefusing ) ), + RecvPropBool( RECVINFO( m_bResumeZoom ) ), + RecvPropInt( RECVINFO( m_iLastZoom ) ), + +#ifdef CS_SHIELD_ENABLED + RecvPropBool( RECVINFO( m_bHasShield ) ), + RecvPropBool( RECVINFO( m_bShieldDrawn ) ), +#endif + RecvPropInt( RECVINFO( m_bHasHelmet ) ), + RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ), + RecvPropFloat( RECVINFO( m_flFlashDuration ), 0, RecvProxy_FlashTime ), + RecvPropFloat( RECVINFO( m_flFlashMaxAlpha)), + RecvPropInt( RECVINFO( m_iProgressBarDuration ) ), + RecvPropFloat( RECVINFO( m_flProgressBarStartTime ) ), + RecvPropEHandle( RECVINFO( m_hRagdoll ) ), + RecvPropInt( RECVINFO( m_cycleLatch ), 0, &C_CSPlayer::RecvProxy_CycleLatch ), + +END_RECV_TABLE() + + + +C_CSPlayer::C_CSPlayer() : + m_iv_angEyeAngles( "C_CSPlayer::m_iv_angEyeAngles" ) +{ + m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true ); + + m_angEyeAngles.Init(); + + AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); + + m_iLastAddonBits = m_iAddonBits = 0; + m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE; + m_iProgressBarDuration = 0; + m_flProgressBarStartTime = 0.0f; + m_ArmorValue = 0; + m_bHasHelmet = false; + m_iIDEntIndex = 0; + m_delayTargetIDTimer.Reset(); + m_iOldIDEntIndex = 0; + m_holdTargetIDTimer.Reset(); + m_iDirection = 0; + + m_Activity = ACT_IDLE; + + m_pFlashlightBeam = NULL; + m_fNextThinkPushAway = 0.0f; + + m_serverIntendedCycle = -1.0f; + + view->SetScreenOverlayMaterial( NULL ); + + m_bPlayingFreezeCamSound = false; +} + + +C_CSPlayer::~C_CSPlayer() +{ + RemoveAddonModels(); + + ReleaseFlashlight(); + + m_PlayerAnimState->Release(); +} + + +bool C_CSPlayer::HasDefuser() const +{ + return m_bHasDefuser; +} + +void C_CSPlayer::GiveDefuser() +{ + m_bHasDefuser = true; +} + +void C_CSPlayer::RemoveDefuser() +{ + m_bHasDefuser = false; +} + +bool C_CSPlayer::HasNightVision() const +{ + return m_bHasNightVision; +} + +bool C_CSPlayer::IsVIP() const +{ + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + + if ( !pCSPR ) + return false; + + return pCSPR->IsVIP( entindex() ); +} + +C_CSPlayer* C_CSPlayer::GetLocalCSPlayer() +{ + return (C_CSPlayer*)C_BasePlayer::GetLocalPlayer(); +} + + +CSPlayerState C_CSPlayer::State_Get() const +{ + return m_iPlayerState; +} + + +float C_CSPlayer::GetMinFOV() const +{ + // Min FOV for AWP. + return 10; +} + + +int C_CSPlayer::GetAccount() const +{ + return m_iAccount; +} + + +int C_CSPlayer::PlayerClass() const +{ + return m_iClass; +} + +bool C_CSPlayer::IsInBuyZone() +{ + return m_bInBuyZone; +} + +bool C_CSPlayer::CanShowTeamMenu() const +{ + return true; +} + + +int C_CSPlayer::ArmorValue() const +{ + return m_ArmorValue; +} + +bool C_CSPlayer::HasHelmet() const +{ + return m_bHasHelmet; +} + +int C_CSPlayer::GetCurrentAssaultSuitPrice() +{ + // WARNING: This price logic also exists in CCSPlayer::AttemptToBuyAssaultSuit + // and must be kept in sync if changes are made. + + int fullArmor = ArmorValue() >= 100 ? 1 : 0; + if ( fullArmor && !HasHelmet() ) + { + return HELMET_PRICE; + } + else if ( !fullArmor && HasHelmet() ) + { + return KEVLAR_PRICE; + } + else + { + // NOTE: This applies to the case where you already have both + // as well as the case where you have neither. In the case + // where you have both, the item should still have a price + // and become disabled when you have little or no money left. + return ASSAULTSUIT_PRICE; + } +} + +const QAngle& C_CSPlayer::GetRenderAngles() +{ + if ( IsRagdoll() ) + { + return vec3_angle; + } + else + { + return m_PlayerAnimState->GetRenderAngles(); + } +} + + +float g_flFattenAmt = 4; +void C_CSPlayer::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType ) +{ + if ( shadowType == SHADOWS_SIMPLE ) + { + // Don't let the render bounds change when we're using blobby shadows, or else the shadow + // will pop and stretch. + mins = CollisionProp()->OBBMins(); + maxs = CollisionProp()->OBBMaxs(); + } + else + { + GetRenderBounds( mins, maxs ); + + // We do this because the normal bbox calculations don't take pose params into account, and + // the rotation of the guy's upper torso can place his gun a ways out of his bbox, and + // the shadow will get cut off as he rotates. + // + // Thus, we give it some padding here. + mins -= Vector( g_flFattenAmt, g_flFattenAmt, 0 ); + maxs += Vector( g_flFattenAmt, g_flFattenAmt, 0 ); + } +} + + +void C_CSPlayer::GetRenderBounds( Vector& theMins, Vector& theMaxs ) +{ + // TODO POSTSHIP - this hack/fix goes hand-in-hand with a fix in CalcSequenceBoundingBoxes in utils/studiomdl/simplify.cpp. + // When we enable the fix in CalcSequenceBoundingBoxes, we can get rid of this. + // + // What we're doing right here is making sure it only uses the bbox for our lower-body sequences since, + // with the current animations and the bug in CalcSequenceBoundingBoxes, are WAY bigger than they need to be. + C_BaseAnimating::GetRenderBounds( theMins, theMaxs ); + + // If we're ducking, we should reduce the render height by the difference in standing and ducking heights. + // This prevents shadows from drawing above ducking players etc. + if ( GetFlags() & FL_DUCKING ) + { + theMaxs.z -= 18.5f; + } +} + + +bool C_CSPlayer::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const +{ + if ( shadowType == SHADOWS_SIMPLE ) + { + // Blobby shadows should sit directly underneath us. + pDirection->Init( 0, 0, -1 ); + return true; + } + else + { + return BaseClass::GetShadowCastDirection( pDirection, shadowType ); + } +} + + +void C_CSPlayer::VPhysicsUpdate( IPhysicsObject *pPhysics ) +{ + BaseClass::VPhysicsUpdate( pPhysics ); +} + + +int C_CSPlayer::GetIDTarget() const +{ + if ( !m_delayTargetIDTimer.IsElapsed() ) + return 0; + + if ( m_iIDEntIndex ) + { + return m_iIDEntIndex; + } + + if ( m_iOldIDEntIndex && !m_holdTargetIDTimer.IsElapsed() ) + { + return m_iOldIDEntIndex; + } + + return 0; +} + + +void InitializeAddonModelFromWeapon( CWeaponCSBase *weapon, C_BreakableProp *addon ) +{ + if ( !weapon ) + { + return; + } + + const CCSWeaponInfo& weaponInfo = weapon->GetCSWpnData(); + if ( weaponInfo.m_szAddonModel[0] == 0 ) + { + addon->InitializeAsClientEntity( weaponInfo.szWorldModel, RENDER_GROUP_OPAQUE_ENTITY ); + } + else + { + addon->InitializeAsClientEntity( weaponInfo.m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY ); + } +} + +void C_CSPlayer::CreateAddonModel( int i ) +{ + COMPILE_TIME_ASSERT( (sizeof( g_AddonInfo ) / sizeof( g_AddonInfo[0] )) == NUM_ADDON_BITS ); + + // Create the model entity. + CAddonInfo *pAddonInfo = &g_AddonInfo[i]; + + int iAttachment = LookupAttachment( pAddonInfo->m_pAttachmentName ); + if ( iAttachment <= 0 ) + return; + + C_BreakableProp *pEnt = new C_BreakableProp; + + int addonType = (1<<i); + if ( addonType == ADDON_PISTOL || addonType == ADDON_PRIMARY ) + { + CCSWeaponInfo *weaponInfo = GetWeaponInfo( (CSWeaponID)((addonType == ADDON_PRIMARY) ? m_iPrimaryAddon.Get() : m_iSecondaryAddon.Get()) ); + if ( !weaponInfo ) + { + Warning( "C_CSPlayer::CreateAddonModel: Unable to get weapon info.\n" ); + pEnt->Release(); + return; + } + if ( weaponInfo->m_szAddonModel[0] == 0 ) + { + pEnt->InitializeAsClientEntity( weaponInfo->szWorldModel, RENDER_GROUP_OPAQUE_ENTITY ); + } + else + { + pEnt->InitializeAsClientEntity( weaponInfo->m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY ); + } + } + else if( pAddonInfo->m_pModelName ) + { + if ( addonType == ADDON_PISTOL2 && !(m_iAddonBits & ADDON_PISTOL ) ) + { + pEnt->InitializeAsClientEntity( pAddonInfo->m_pHolsterName, RENDER_GROUP_OPAQUE_ENTITY ); + } + else + { + pEnt->InitializeAsClientEntity( pAddonInfo->m_pModelName, RENDER_GROUP_OPAQUE_ENTITY ); + } + } + else + { + WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pAddonInfo->m_pWeaponClassName ); + if ( hWpnInfo == GetInvalidWeaponInfoHandle() ) + { + Assert( false ); + return; + } + + CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); + if ( pWeaponInfo ) + { + if ( pWeaponInfo->m_szAddonModel[0] == 0 ) + pEnt->InitializeAsClientEntity( pWeaponInfo->szWorldModel, RENDER_GROUP_OPAQUE_ENTITY ); + else + pEnt->InitializeAsClientEntity( pWeaponInfo->m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY ); + } + else + { + pEnt->Release(); + Warning( "C_CSPlayer::CreateAddonModel: Unable to get weapon info for %s.\n", pAddonInfo->m_pWeaponClassName ); + return; + } + } + + if ( Q_strcmp( pAddonInfo->m_pAttachmentName, "c4" ) ) + { + // fade out all attached models except C4 + pEnt->SetFadeMinMax( 400, 500 ); + } + + // Create the addon. + CAddonModel *pAddon = &m_AddonModels[m_AddonModels.AddToTail()]; + + pAddon->m_hEnt = pEnt; + pAddon->m_iAddon = i; + pAddon->m_iAttachmentPoint = iAttachment; + pEnt->SetParent( this, pAddon->m_iAttachmentPoint ); + pEnt->SetLocalOrigin( Vector( 0, 0, 0 ) ); + pEnt->SetLocalAngles( QAngle( 0, 0, 0 ) ); + if ( IsLocalPlayer() ) + { + pEnt->SetSolid( SOLID_NONE ); + pEnt->RemoveEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); + } +} + + +void C_CSPlayer::UpdateAddonModels() +{ + int iCurAddonBits = m_iAddonBits; + + // Don't put addon models on the local player unless in third person. + if ( IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() ) + iCurAddonBits = 0; + + // If the local player is observing this entity in first-person mode, get rid of its addons. + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == this ) + iCurAddonBits = 0; + + // Any changes to the attachments we should have? + if ( m_iLastAddonBits == iCurAddonBits && + m_iLastPrimaryAddon == m_iPrimaryAddon && + m_iLastSecondaryAddon == m_iSecondaryAddon ) + { + return; + } + + bool rebuildPistol2Addon = false; + if ( m_iSecondaryAddon == WEAPON_ELITE && ((m_iLastAddonBits ^ iCurAddonBits) & ADDON_PISTOL) != 0 ) + { + rebuildPistol2Addon = true; + } + m_iLastAddonBits = iCurAddonBits; + m_iLastPrimaryAddon = m_iPrimaryAddon; + m_iLastSecondaryAddon = m_iSecondaryAddon; + + // Get rid of any old models. + int i,iNext; + for ( i=m_AddonModels.Head(); i != m_AddonModels.InvalidIndex(); i = iNext ) + { + iNext = m_AddonModels.Next( i ); + CAddonModel *pModel = &m_AddonModels[i]; + + int addonBit = 1<<pModel->m_iAddon; + if ( !( iCurAddonBits & addonBit ) || (rebuildPistol2Addon && addonBit == ADDON_PISTOL2) ) + { + if ( pModel->m_hEnt.Get() ) + pModel->m_hEnt->Release(); + + m_AddonModels.Remove( i ); + } + } + + // Figure out which models we have now. + int curModelBits = 0; + FOR_EACH_LL( m_AddonModels, j ) + { + curModelBits |= (1<<m_AddonModels[j].m_iAddon); + } + + // Add any new models. + for ( i=0; i < NUM_ADDON_BITS; i++ ) + { + if ( (iCurAddonBits & (1<<i)) && !( curModelBits & (1<<i) ) ) + { + // Ok, we're supposed to have this one. + CreateAddonModel( i ); + } + } +} + + +void C_CSPlayer::RemoveAddonModels() +{ + m_iAddonBits = 0; + UpdateAddonModels(); +} + + +void C_CSPlayer::NotifyShouldTransmit( ShouldTransmitState_t state ) +{ + // Remove all addon models if we go out of the PVS. + if ( state == SHOULDTRANSMIT_END ) + { + RemoveAddonModels(); + + if( m_pFlashlightBeam != NULL ) + { + ReleaseFlashlight(); + } + } + + BaseClass::NotifyShouldTransmit( state ); +} + + +void C_CSPlayer::UpdateSoundEvents() +{ + int iNext; + for ( int i=m_SoundEvents.Head(); i != m_SoundEvents.InvalidIndex(); i = iNext ) + { + iNext = m_SoundEvents.Next( i ); + + CCSSoundEvent *pEvent = &m_SoundEvents[i]; + if ( gpGlobals->curtime >= pEvent->m_flEventTime ) + { + CLocalPlayerFilter filter; + EmitSound( filter, GetSoundSourceIndex(), STRING( pEvent->m_SoundName ) ); + + m_SoundEvents.Remove( i ); + } + } +} + +//----------------------------------------------------------------------------- +void C_CSPlayer::UpdateMinModels( void ) +{ + int modelIndex = m_nModelIndex; + + // cl_minmodels convar dependent on sv_allowminmodels convar + + if ( !IsVIP() && sv_allowminmodels.GetBool() && cl_minmodels.GetBool() && !IsLocalPlayer() ) + { + if ( GetTeamNumber() == TEAM_CT ) + { + int index = cl_min_ct.GetInt() - 1; + if ( index >= 0 && index < CTPlayerModels.Count() ) + { + modelIndex = modelinfo->GetModelIndex( CTPlayerModels[index] ); + } + } + else if ( GetTeamNumber() == TEAM_TERRORIST ) + { + int index = cl_min_t.GetInt() - 1; + if ( index >= 0 && index < TerroristPlayerModels.Count() ) + { + modelIndex = modelinfo->GetModelIndex( TerroristPlayerModels[index] ); + } + } + } + + SetModelByIndex( modelIndex ); +} + +// NVNT gate for spectating. +static bool inSpectating_Haptics = false; +//----------------------------------------------------------------------------- +void C_CSPlayer::ClientThink() +{ + BaseClass::ClientThink(); + + UpdateSoundEvents(); + + UpdateAddonModels(); + + UpdateIDTarget(); + + if ( gpGlobals->curtime >= m_fNextThinkPushAway ) + { + PerformObstaclePushaway( this ); + m_fNextThinkPushAway = gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL; + } + + // NVNT - check for spectating forces + if ( IsLocalPlayer() ) + { + if ( GetTeamNumber() == TEAM_SPECTATOR || !this->IsAlive() || GetLocalOrInEyeCSPlayer() != this ) + { + if (!inSpectating_Haptics) + { + if ( haptics ) + haptics->SetNavigationClass("spectate"); + + inSpectating_Haptics = true; + } + } + else + { + if (inSpectating_Haptics) + { + if ( haptics ) + haptics->SetNavigationClass("on_foot"); + + inSpectating_Haptics = false; + } + } + + if ( m_iObserverMode == OBS_MODE_FREEZECAM ) + { + //============================================================================= + // HPE_BEGIN: + // [Forrest] Added sv_disablefreezecam check + //============================================================================= + static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" ); + if ( !m_bPlayingFreezeCamSound && !cl_disablefreezecam.GetBool() && !sv_disablefreezecam.GetBool() ) + //============================================================================= + // HPE_END + //============================================================================= + { + // Play sound + m_bPlayingFreezeCamSound = true; + + CLocalPlayerFilter filter; + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = "UI/freeze_cam.wav"; + ep.m_flVolume = VOL_NORM; + ep.m_SoundLevel = SNDLVL_NORM; + ep.m_bEmitCloseCaption = false; + + EmitSound( filter, GetSoundSourceIndex(), ep ); + } + } + else + { + m_bPlayingFreezeCamSound = false; + } + } +} + + +void C_CSPlayer::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if ( type == DATA_UPDATE_CREATED ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + + if ( IsLocalPlayer() ) + { + if ( CSGameRules() && CSGameRules()->IsBlackMarket() ) + { + CSGameRules()->m_pPrices = NULL; + CSGameRules()->m_StringTableBlackMarket = NULL; + CSGameRules()->GetBlackMarketPriceList(); + + CSGameRules()->SetBlackMarketPrices( false ); + } + } + } + + UpdateVisibility(); +} + + +void C_CSPlayer::ValidateModelIndex( void ) +{ + UpdateMinModels(); +} + + +void C_CSPlayer::PostDataUpdate( DataUpdateType_t updateType ) +{ + // C_BaseEntity assumes we're networking the entity's angles, so pretend that it + // networked the same value we already have. + SetNetworkAngles( GetLocalAngles() ); + + BaseClass::PostDataUpdate( updateType ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_CSPlayer::Interpolate( float currentTime ) +{ + if ( !BaseClass::Interpolate( currentTime ) ) + return false; + + if ( CSGameRules()->IsFreezePeriod() ) + { + // don't interpolate players position during freeze period + SetAbsOrigin( GetNetworkOrigin() ); + } + + return true; +} + +int C_CSPlayer::GetMaxHealth() const +{ + return 100; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the local player, or the player being spectated in-eye +//----------------------------------------------------------------------------- +C_CSPlayer* GetLocalOrInEyeCSPlayer( void ) +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + + if( player && player->GetObserverMode() == OBS_MODE_IN_EYE ) + { + C_BaseEntity *target = player->GetObserverTarget(); + + if( target && target->IsPlayer() ) + { + return ToCSPlayer( target ); + } + } + return player; +} + +#define MAX_FLASHBANG_OPACITY 75.0f + +//----------------------------------------------------------------------------- +// Purpose: Update this client's targetid entity +//----------------------------------------------------------------------------- +void C_CSPlayer::UpdateIDTarget() +{ + if ( !IsLocalPlayer() ) + return; + + // Clear old target and find a new one + m_iIDEntIndex = 0; + + // don't show IDs if mp_playerid == 2 + if ( mp_playerid.GetInt() == 2 ) + return; + + // don't show IDs if mp_fadetoblack is on + if ( mp_fadetoblack.GetBool() && !IsAlive() ) + return; + + // don't show IDs in chase spec mode + if ( GetObserverMode() == OBS_MODE_CHASE || + GetObserverMode() == OBS_MODE_DEATHCAM ) + return; + + //Check how much of a screen fade we have. + //if it's more than 75 then we can't see what's going on so we don't display the id. + byte color[4]; + bool blend; + vieweffects->GetFadeParams( &color[0], &color[1], &color[2], &color[3], &blend ); + + if ( color[3] > MAX_FLASHBANG_OPACITY && ( IsAlive() || GetObserverMode() == OBS_MODE_IN_EYE ) ) + return; + + trace_t tr; + Vector vecStart, vecEnd; + VectorMA( MainViewOrigin(), 2500, MainViewForward(), vecEnd ); + VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart ); + UTIL_TraceLine( vecStart, vecEnd, MASK_VISIBLE_AND_NPCS, GetLocalOrInEyeCSPlayer(), COLLISION_GROUP_NONE, &tr ); + if ( !tr.startsolid && !tr.DidHitNonWorldEntity() ) + { + CTraceFilterSimple filter( GetLocalOrInEyeCSPlayer(), COLLISION_GROUP_NONE ); + + // Check for player hitboxes extending outside their collision bounds + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers(vecStart, vecEnd + MainViewForward() * rayExtension, MASK_SOLID|CONTENTS_HITBOX, &filter, &tr ); + } + + if ( !tr.startsolid && tr.DidHitNonWorldEntity() ) + { + C_BaseEntity *pEntity = tr.m_pEnt; + + if ( pEntity && (pEntity != this) ) + { + if ( mp_playerid.GetInt() == 1 ) // only show team names + { + if ( pEntity->GetTeamNumber() != GetTeamNumber() ) + { + return; + } + } + + //Adrian: If there's a smoke cloud in my way, don't display the name + //We check this AFTER we found a player, just so we don't go thru this for nothing. + for ( int i = 0; i < m_SmokeGrenades.Count(); i++ ) + { + C_BaseParticleEntity *pSmokeGrenade = (C_BaseParticleEntity*)m_SmokeGrenades.Element( i ); + + if ( pSmokeGrenade ) + { + float flHit1, flHit2; + + float flRadius = ( SMOKEGRENADE_PARTICLERADIUS * NUM_PARTICLES_PER_DIMENSION + 1 ) * 0.5f; + + Vector vPos = pSmokeGrenade->GetAbsOrigin(); + + /*debugoverlay->AddBoxOverlay( pSmokeGrenade->GetAbsOrigin(), Vector( flRadius, flRadius, flRadius ), + Vector( -flRadius, -flRadius, -flRadius ), QAngle( 0, 0, 0 ), 255, 0, 0, 255, 0.2 );*/ + + if ( IntersectInfiniteRayWithSphere( MainViewOrigin(), MainViewForward(), vPos, flRadius, &flHit1, &flHit2 ) ) + { + return; + } + } + } + + if ( !GetIDTarget() && ( !m_iOldIDEntIndex || m_holdTargetIDTimer.IsElapsed() ) ) + { + // track when we first mouse over the target + m_delayTargetIDTimer.Start( mp_playerid_delay.GetFloat() ); + } + m_iIDEntIndex = pEntity->entindex(); + + m_iOldIDEntIndex = m_iIDEntIndex; + m_holdTargetIDTimer.Start( mp_playerid_hold.GetFloat() ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Input handling +//----------------------------------------------------------------------------- +bool C_CSPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd ) +{ + // Bleh... we will wind up needing to access bones for attachments in here. + C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true ); + + return BaseClass::CreateMove( flInputSampleTime, pCmd ); +} + +//----------------------------------------------------------------------------- +// Purpose: Flash this entity on the radar +//----------------------------------------------------------------------------- +bool C_CSPlayer::IsInHostageRescueZone() +{ + return m_bInHostageRescueZone; +} + +CWeaponCSBase* C_CSPlayer::GetActiveCSWeapon() const +{ + return dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() ); +} + +CWeaponCSBase* C_CSPlayer::GetCSWeapon( CSWeaponID id ) const +{ + for (int i=0;i<MAX_WEAPONS;i++) + { + CBaseCombatWeapon *weapon = GetWeapon( i ); + if ( weapon ) + { + CWeaponCSBase *csWeapon = dynamic_cast< CWeaponCSBase * >( weapon ); + if ( csWeapon ) + { + if ( id == csWeapon->GetWeaponID() ) + { + return csWeapon; + } + } + } + } + + return NULL; +} + +//REMOVEME +/* +void C_CSPlayer::SetFireAnimation( PLAYER_ANIM playerAnim ) +{ + Activity idealActivity = ACT_WALK; + + // Figure out stuff about the current state. + float speed = GetAbsVelocity().Length2D(); + bool isMoving = ( speed != 0.0f ) ? true : false; + bool isDucked = ( GetFlags() & FL_DUCKING ) ? true : false; + bool isStillJumping = false; //!( GetFlags() & FL_ONGROUND ); + bool isRunning = false; + + if ( speed > ARBITRARY_RUN_SPEED ) + { + isRunning = true; + } + + // Now figure out what to do based on the current state and the new state. + switch ( playerAnim ) + { + default: + case PLAYER_RELOAD: + case PLAYER_ATTACK1: + case PLAYER_IDLE: + case PLAYER_WALK: + // Are we still jumping? + // If so, keep playing the jump animation. + if ( !isStillJumping ) + { + idealActivity = ACT_WALK; + + if ( isDucked ) + { + idealActivity = !isMoving ? ACT_CROUCHIDLE : ACT_RUN_CROUCH; + } + else + { + if ( isRunning ) + { + idealActivity = ACT_RUN; + } + else + { + idealActivity = isMoving ? ACT_WALK : ACT_IDLE; + } + } + + // Allow body yaw to override for standing and turning in place + idealActivity = m_PlayerAnimState.BodyYawTranslateActivity( idealActivity ); + } + break; + + case PLAYER_JUMP: + idealActivity = ACT_HOP; + break; + + case PLAYER_DIE: + // Uses Ragdoll now??? + idealActivity = ACT_DIESIMPLE; + break; + + // FIXME: Use overlays for reload, start/leave aiming, attacking + case PLAYER_START_AIMING: + case PLAYER_LEAVE_AIMING: + idealActivity = ACT_WALK; + break; + } + + CWeaponCSBase *pWeapon = GetActiveCSWeapon(); + + if ( pWeapon ) + { + Activity aWeaponActivity = idealActivity; + + if ( playerAnim == PLAYER_ATTACK1 ) + { + switch ( idealActivity ) + { + case ACT_WALK: + default: + aWeaponActivity = ACT_PLAYER_WALK_FIRE; + break; + case ACT_RUN: + aWeaponActivity = ACT_PLAYER_RUN_FIRE; + break; + case ACT_IDLE: + aWeaponActivity = ACT_PLAYER_IDLE_FIRE; + break; + case ACT_CROUCHIDLE: + aWeaponActivity = ACT_PLAYER_CROUCH_FIRE; + break; + case ACT_RUN_CROUCH: + aWeaponActivity = ACT_PLAYER_CROUCH_WALK_FIRE; + break; + } + } + + m_PlayerAnimState.SetWeaponLayerSequence( pWeapon->GetCSWpnData().m_szAnimExtension, aWeaponActivity ); + } +} +*/ + +ShadowType_t C_CSPlayer::ShadowCastType( void ) +{ + if ( !IsVisible() ) + return SHADOWS_NONE; + + return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not we can switch to the given weapon. +// Input : pWeapon - +//----------------------------------------------------------------------------- +bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) +{ + if ( !pWeapon->CanDeploy() ) + return false; + + if ( GetActiveWeapon() ) + { + if ( !GetActiveWeapon()->CanHolster() ) + return false; + } + + return true; +} + + +void C_CSPlayer::UpdateClientSideAnimation() +{ + // We do this in a different order than the base class. + // We need our cycle to be valid for when we call the playeranimstate update code, + // or else it'll synchronize the upper body anims with the wrong cycle. + if ( GetSequence() != -1 ) + { + // move frame forward + FrameAdvance( 0.0f ); // 0 means to use the time we last advanced instead of a constant + } + + // Update the animation data. It does the local check here so this works when using + // a third-person camera (and we don't have valid player angles). + if ( this == C_CSPlayer::GetLocalCSPlayer() ) + m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] ); + else + m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ); + + if ( GetSequence() != -1 ) + { + // latch old values + OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR ); + } +} + + +float g_flMuzzleFlashScale=1; + +void C_CSPlayer::ProcessMuzzleFlashEvent() +{ + CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + // Reenable when the weapons have muzzle flash attachments in the right spot. + if ( this == pLocalPlayer ) + return; // don't show own world muzzle flashs in for localplayer + + if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + // also don't show in 1st person spec mode + if ( pLocalPlayer->GetObserverTarget() == this ) + return; + } + + CWeaponCSBase *pWeapon = GetActiveCSWeapon(); + + if ( !pWeapon ) + return; + + bool hasMuzzleFlash = (pWeapon->GetMuzzleFlashStyle() != CS_MUZZLEFLASH_NONE); + + Vector vector; + QAngle angles; + + int iAttachment = LookupAttachment( "muzzle_flash" ); + + if ( iAttachment >= 0 ) + { + bool bFoundAttachment = GetAttachment( iAttachment, vector, angles ); + // If we have an attachment, then stick a light on it. + if ( bFoundAttachment ) + { + if ( hasMuzzleFlash ) + { + dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH + index ); + el->origin = vector; + el->radius = 70; + el->decay = el->radius / 0.05f; + el->die = gpGlobals->curtime + 0.05f; + el->color.r = 255; + el->color.g = 192; + el->color.b = 64; + el->color.exponent = 5; + } + + int shellType = GetShellForAmmoType( pWeapon->GetCSWpnData().szAmmo1 ); + + QAngle playerAngle = EyeAngles(); + Vector vForward, vRight, vUp; + + AngleVectors( playerAngle, &vForward, &vRight, &vUp ); + + QAngle angVelocity; + Vector vVel = vRight * 100 + vUp * 20; + VectorAngles( vVel, angVelocity ); + + if ( pWeapon->GetMaxClip1() > 0 ) + { + tempents->CSEjectBrass( vector, angVelocity, 120, shellType, this ); + } + } + } + + if ( hasMuzzleFlash ) + { + iAttachment = pWeapon->GetMuzzleAttachment(); + + if ( iAttachment > 0 ) + { + float flScale = pWeapon->GetCSWpnData().m_flMuzzleScale; + flScale *= 0.75; + FX_MuzzleEffectAttached( flScale, pWeapon->GetRefEHandle(), iAttachment, NULL, false ); + + } + } +} + +const QAngle& C_CSPlayer::EyeAngles() +{ + if ( IsLocalPlayer() && !g_nKillCamMode ) + { + return BaseClass::EyeAngles(); + } + else + { + return m_angEyeAngles; + } +} + +bool C_CSPlayer::ShouldDraw( void ) +{ + // If we're dead, our ragdoll will be drawn for us instead. + if ( !IsAlive() ) + return false; + + if( GetTeamNumber() == TEAM_SPECTATOR ) + return false; + + if( IsLocalPlayer() ) + { + if ( IsRagdoll() ) + return true; + } + + return BaseClass::ShouldDraw(); +} + + +bool FindWeaponAttachmentBone( C_BaseCombatWeapon *pWeapon, int &iWeaponBone ) +{ + if ( !pWeapon ) + return false; + + CStudioHdr *pHdr = pWeapon->GetModelPtr(); + if ( !pHdr ) + return false; + + for ( iWeaponBone=0; iWeaponBone < pHdr->numbones(); iWeaponBone++ ) + { + if ( stricmp( pHdr->pBone( iWeaponBone )->pszName(), "L_Hand_Attach" ) == 0 ) + break; + } + + return iWeaponBone != pHdr->numbones(); +} + + +bool FindMyAttachmentBone( C_BaseAnimating *pModel, int &iBone, CStudioHdr *pHdr ) +{ + if ( !pHdr ) + return false; + + for ( iBone=0; iBone < pHdr->numbones(); iBone++ ) + { + if ( stricmp( pHdr->pBone( iBone )->pszName(), "Valvebiped.Bip01_L_Hand" ) == 0 ) + break; + } + + return iBone != pHdr->numbones(); +} + + +inline bool IsBoneChildOf( CStudioHdr *pHdr, int iBone, int iParent ) +{ + if ( iBone == iParent ) + return false; + + while ( iBone != -1 ) + { + if ( iBone == iParent ) + return true; + + iBone = pHdr->pBone( iBone )->parent; + } + return false; +} + +void ApplyDifferenceTransformToChildren( + C_BaseAnimating *pModel, + const matrix3x4_t &mSource, + const matrix3x4_t &mDest, + int iParentBone ) +{ + CStudioHdr *pHdr = pModel->GetModelPtr(); + if ( !pHdr ) + return; + + // Build a matrix to go from mOriginalHand to mHand. + // ( mDest * Inverse( mSource ) ) * mSource = mDest + matrix3x4_t mSourceInverse, mToDest; + MatrixInvert( mSource, mSourceInverse ); + ConcatTransforms( mDest, mSourceInverse, mToDest ); + + // Now multiply iMyBone and all its children by mToWeaponBone. + for ( int i=0; i < pHdr->numbones(); i++ ) + { + if ( IsBoneChildOf( pHdr, i, iParentBone ) ) + { + matrix3x4_t &mCur = pModel->GetBoneForWrite( i ); + matrix3x4_t mNew; + ConcatTransforms( mToDest, mCur, mNew ); + mCur = mNew; + } + } +} + + +void GetCorrectionMatrices( + const matrix3x4_t &mShoulder, + const matrix3x4_t &mElbow, + const matrix3x4_t &mHand, + matrix3x4_t &mShoulderCorrection, + matrix3x4_t &mElbowCorrection + ) +{ + extern void Studio_AlignIKMatrix( matrix3x4_t &mMat, const Vector &vAlignTo ); + + // Get the positions of each node so we can get the direction vectors. + Vector vShoulder, vElbow, vHand; + MatrixPosition( mShoulder, vShoulder ); + MatrixPosition( mElbow, vElbow ); + MatrixPosition( mHand, vHand ); + + // Get rid of the translation. + matrix3x4_t mOriginalShoulder = mShoulder; + matrix3x4_t mOriginalElbow = mElbow; + MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalShoulder ); + MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalElbow ); + + // Let the IK code align them like it would if we did IK on the joint. + matrix3x4_t mAlignedShoulder = mOriginalShoulder; + matrix3x4_t mAlignedElbow = mOriginalElbow; + Studio_AlignIKMatrix( mAlignedShoulder, vElbow-vShoulder ); + Studio_AlignIKMatrix( mAlignedElbow, vHand-vElbow ); + + // Figure out the transformation from the aligned bones to the original ones. + matrix3x4_t mInvAlignedShoulder, mInvAlignedElbow; + MatrixInvert( mAlignedShoulder, mInvAlignedShoulder ); + MatrixInvert( mAlignedElbow, mInvAlignedElbow ); + + ConcatTransforms( mInvAlignedShoulder, mOriginalShoulder, mShoulderCorrection ); + ConcatTransforms( mInvAlignedElbow, mOriginalElbow, mElbowCorrection ); +} + + +void C_CSPlayer::BuildTransformations( CStudioHdr *pHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ) +{ + // First, setup our model's transformations like normal. + BaseClass::BuildTransformations( pHdr, pos, q, cameraTransform, boneMask, boneComputed ); + + if ( IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() ) + return; + + if ( !cl_left_hand_ik.GetInt() ) + return; + + // If our current weapon has a bone named L_Hand_Attach, then we attach the player's + // left hand (Valvebiped.Bip01_L_Hand) to it. + C_BaseCombatWeapon *pWeapon = GetActiveWeapon(); + + if ( !pWeapon ) + return; + + // Have the weapon setup its bones. + pWeapon->SetupBones( NULL, 0, BONE_USED_BY_ANYTHING, gpGlobals->curtime ); + + int iWeaponBone = 0; + if ( FindWeaponAttachmentBone( pWeapon, iWeaponBone ) ) + { + int iMyBone = 0; + if ( FindMyAttachmentBone( this, iMyBone, pHdr ) ) + { + int iHand = iMyBone; + int iElbow = pHdr->pBone( iHand )->parent; + int iShoulder = pHdr->pBone( iElbow )->parent; + matrix3x4_t *pBones = &GetBoneForWrite( 0 ); + + // Store off the original hand position. + matrix3x4_t mSource = pBones[iHand]; + + + // Figure out the rotation offset from the current shoulder and elbow bone rotations + // and what the IK code's alignment code is going to produce, because we'll have to + // re-apply that offset after the IK runs. + matrix3x4_t mShoulderCorrection, mElbowCorrection; + GetCorrectionMatrices( pBones[iShoulder], pBones[iElbow], pBones[iHand], mShoulderCorrection, mElbowCorrection ); + + + // Do the IK solution. + Vector vHandTarget; + MatrixPosition( pWeapon->GetBone( iWeaponBone ), vHandTarget ); + Studio_SolveIK( iShoulder, iElbow, iHand, vHandTarget, pBones ); + + + // Now reapply the rotation correction. + matrix3x4_t mTempShoulder = pBones[iShoulder]; + matrix3x4_t mTempElbow = pBones[iElbow]; + ConcatTransforms( mTempShoulder, mShoulderCorrection, pBones[iShoulder] ); + ConcatTransforms( mTempElbow, mElbowCorrection, pBones[iElbow] ); + + + // Now apply the transformation on the hand to the fingers. + matrix3x4_t &mDest = GetBoneForWrite( iHand ); + ApplyDifferenceTransformToChildren( this, mSource, mDest, iHand ); + } + } +} + + +C_BaseAnimating * C_CSPlayer::BecomeRagdollOnClient() +{ + return NULL; +} + + +IRagdoll* C_CSPlayer::GetRepresentativeRagdoll() const +{ + if ( m_hRagdoll.Get() ) + { + C_CSRagdoll *pRagdoll = (C_CSRagdoll*)m_hRagdoll.Get(); + + return pRagdoll->GetIRagdoll(); + } + else + { + return NULL; + } +} + + +void C_CSPlayer::PlayReloadEffect() +{ + // Only play the effect for other players. + if ( this == C_CSPlayer::GetLocalCSPlayer() ) + { + Assert( false ); // We shouldn't have been sent this message. + return; + } + + // Get the view model for our current gun. + CWeaponCSBase *pWeapon = GetActiveCSWeapon(); + if ( !pWeapon ) + return; + + // The weapon needs two models, world and view, but can only cache one. Synthesize the other. + const CCSWeaponInfo &info = pWeapon->GetCSWpnData(); + const model_t *pModel = modelinfo->GetModel( modelinfo->GetModelIndex( info.szViewModel ) ); + if ( !pModel ) + return; + CStudioHdr studioHdr( modelinfo->GetStudiomodel( pModel ), mdlcache ); + if ( !studioHdr.IsValid() ) + return; + + // Find the reload animation. + for ( int iSeq=0; iSeq < studioHdr.GetNumSeq(); iSeq++ ) + { + mstudioseqdesc_t *pSeq = &studioHdr.pSeqdesc( iSeq ); + + if ( pSeq->activity == ACT_VM_RELOAD ) + { + float poseParameters[MAXSTUDIOPOSEPARAM]; + memset( poseParameters, 0, sizeof( poseParameters ) ); + float cyclesPerSecond = Studio_CPS( &studioHdr, *pSeq, iSeq, poseParameters ); + + // Now read out all the sound events with their timing + for ( int iEvent=0; iEvent < pSeq->numevents; iEvent++ ) + { + mstudioevent_t *pEvent = pSeq->pEvent( iEvent ); + + if ( pEvent->event == CL_EVENT_SOUND ) + { + CCSSoundEvent event; + event.m_SoundName = pEvent->options; + event.m_flEventTime = gpGlobals->curtime + pEvent->cycle / cyclesPerSecond; + m_SoundEvents.AddToTail( event ); + } + } + + break; + } + } +} + +void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) +{ + if ( event == PLAYERANIMEVENT_THROW_GRENADE ) + { + // Let the server handle this event. It will update m_iThrowGrenadeCounter and the client will + // pick up the event in CCSPlayerAnimState. + } + else + { + m_PlayerAnimState->DoAnimationEvent( event, nData ); + } +} + +void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) +{ + if( event == 7001 ) + { + bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER ); + + //Msg( "run event ( %d )\n", bInWater ? 1 : 0 ); + + if( bInWater ) + { + //run splash + CEffectData data; + + //trace up from foot position to the water surface + trace_t tr; + Vector vecTrace(0,0,1024); + UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr ); + if ( tr.fractionleftsolid ) + { + data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid); + } + else + { + data.m_vOrigin = origin; + } + + data.m_vNormal = Vector( 0,0,1 ); + data.m_flScale = random->RandomFloat( 4.0f, 5.0f ); + DispatchEffect( "watersplash", data ); + } + } + else if( event == 7002 ) + { + bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER ); + + //Msg( "walk event ( %d )\n", bInWater ? 1 : 0 ); + + if( bInWater ) + { + //walk ripple + CEffectData data; + + //trace up from foot position to the water surface + trace_t tr; + Vector vecTrace(0,0,1024); + UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr ); + if ( tr.fractionleftsolid ) + { + data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid); + } + else + { + data.m_vOrigin = origin; + } + + data.m_vNormal = Vector( 0,0,1 ); + data.m_flScale = random->RandomFloat( 4.0f, 7.0f ); + DispatchEffect( "waterripple", data ); + } + } + else + BaseClass::FireEvent( origin, angles, event, options ); +} + + +void C_CSPlayer::SetActivity( Activity eActivity ) +{ + m_Activity = eActivity; +} + + +Activity C_CSPlayer::GetActivity() const +{ + return m_Activity; +} + + +const Vector& C_CSPlayer::GetRenderOrigin( void ) +{ + if ( m_hRagdoll.Get() ) + { + C_CSRagdoll *pRagdoll = (C_CSRagdoll*)m_hRagdoll.Get(); + if ( pRagdoll->IsInitialized() ) + return pRagdoll->GetRenderOrigin(); + } + + return BaseClass::GetRenderOrigin(); +} + + +void C_CSPlayer::Simulate( void ) +{ + if( this != C_BasePlayer::GetLocalPlayer() ) + { + if ( IsEffectActive( EF_DIMLIGHT ) ) + { + QAngle eyeAngles = EyeAngles(); + Vector vForward; + AngleVectors( eyeAngles, &vForward ); + + int iAttachment = LookupAttachment( "muzzle_flash" ); + + if ( iAttachment < 0 ) + return; + + Vector vecOrigin; + QAngle dummy; + GetAttachment( iAttachment, vecOrigin, dummy ); + + trace_t tr; + UTIL_TraceLine( vecOrigin, vecOrigin + (vForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + if( !m_pFlashlightBeam ) + { + BeamInfo_t beamInfo; + beamInfo.m_nType = TE_BEAMPOINTS; + beamInfo.m_vecStart = tr.startpos; + beamInfo.m_vecEnd = tr.endpos; + beamInfo.m_pszModelName = "sprites/glow01.vmt"; + beamInfo.m_pszHaloName = "sprites/glow01.vmt"; + beamInfo.m_flHaloScale = 3.0; + beamInfo.m_flWidth = 8.0f; + beamInfo.m_flEndWidth = 35.0f; + beamInfo.m_flFadeLength = 300.0f; + beamInfo.m_flAmplitude = 0; + beamInfo.m_flBrightness = 60.0; + beamInfo.m_flSpeed = 0.0f; + beamInfo.m_nStartFrame = 0.0; + beamInfo.m_flFrameRate = 0.0; + beamInfo.m_flRed = 255.0; + beamInfo.m_flGreen = 255.0; + beamInfo.m_flBlue = 255.0; + beamInfo.m_nSegments = 8; + beamInfo.m_bRenderable = true; + beamInfo.m_flLife = 0.5; + beamInfo.m_nFlags = FBEAM_FOREVER | FBEAM_ONLYNOISEONCE | FBEAM_NOTILE | FBEAM_HALOBEAM; + + m_pFlashlightBeam = beams->CreateBeamPoints( beamInfo ); + } + + if( m_pFlashlightBeam ) + { + BeamInfo_t beamInfo; + beamInfo.m_vecStart = tr.startpos; + beamInfo.m_vecEnd = tr.endpos; + beamInfo.m_flRed = 255.0; + beamInfo.m_flGreen = 255.0; + beamInfo.m_flBlue = 255.0; + + beams->UpdateBeamInfo( m_pFlashlightBeam, beamInfo ); + + dlight_t *el = effects->CL_AllocDlight( 0 ); + el->origin = tr.endpos; + el->radius = 50; + el->color.r = 200; + el->color.g = 200; + el->color.b = 200; + el->die = gpGlobals->curtime + 0.1; + } + } + else if ( m_pFlashlightBeam ) + { + ReleaseFlashlight(); + } + } + + BaseClass::Simulate(); +} + +void C_CSPlayer::ReleaseFlashlight( void ) +{ + if( m_pFlashlightBeam ) + { + m_pFlashlightBeam->flags = 0; + m_pFlashlightBeam->die = gpGlobals->curtime - 1; + + m_pFlashlightBeam = NULL; + } +} + +bool C_CSPlayer::HasC4( void ) +{ + if( this == C_CSPlayer::GetLocalPlayer() ) + { + return Weapon_OwnsThisType( "weapon_c4" ); + } + else + { + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + + return pCSPR->HasC4( entindex() ); + } +} + +void C_CSPlayer::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ) +{ + static ConVar *violence_hblood = cvar->FindVar( "violence_hblood" ); + if ( violence_hblood && !violence_hblood->GetBool() ) + return; + + BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName ); +} + + +//----------------------------------------------------------------------------- +void C_CSPlayer::CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ) +{ + /** + * TODO: Fix this! + // CS:S standing eyeheight is above the collision volume, so we need to pull it + // down when we go into close quarters. + float maxEyeHeightAboveBounds = VEC_VIEW_SCALED( this ).z - VEC_HULL_MAX_SCALED( this ).z; + if ( GetObserverMode() == OBS_MODE_IN_EYE && + maxEyeHeightAboveBounds > 0.0f && + GetObserverTarget() && + GetObserverTarget()->IsPlayer() ) + { + const float eyeClearance = 12.0f; // eye pos must be this far below the ceiling + + C_CSPlayer *target = ToCSPlayer( GetObserverTarget() ); + + Vector offset = eyeOrigin - GetAbsOrigin(); + + Vector vHullMin = VEC_HULL_MIN_SCALED( this ); + vHullMin.z = 0.0f; + Vector vHullMax = VEC_HULL_MAX_SCALED( this ); + + Vector start = GetAbsOrigin(); + start.z += vHullMax.z; + Vector end = start; + end.z += eyeClearance + VEC_VIEW_SCALED( this ).z - vHullMax_SCALED( this ).z; + + vHullMax.z = 0.0f; + + Vector fudge( 1, 1, 0 ); + vHullMin += fudge; + vHullMax -= fudge; + + trace_t trace; + Ray_t ray; + ray.Init( start, end, vHullMin, vHullMax ); + UTIL_TraceRay( ray, MASK_PLAYERSOLID, target, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + if ( trace.fraction < 1.0f ) + { + float est = start.z + trace.fraction * (end.z - start.z) - GetAbsOrigin().z - eyeClearance; + if ( ( target->GetFlags() & FL_DUCKING ) == 0 && !target->GetFallVelocity() && !target->IsDucked() ) + { + offset.z = est; + } + else + { + offset.z = MIN( est, offset.z ); + } + eyeOrigin.z = GetAbsOrigin().z + offset.z; + } + } + */ + + BaseClass::CalcObserverView( eyeOrigin, eyeAngles, fov ); +} + +//============================================================================= +// HPE_BEGIN: +//============================================================================= +// [tj] checks if this player has another given player on their Steam friends list. +bool C_CSPlayer::HasPlayerAsFriend(C_CSPlayer* player) +{ + if (!steamapicontext || !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() || !player) + { + return false; + } + + player_info_t pi; + if ( !engine->GetPlayerInfo( player->entindex(), &pi ) ) + { + return false; + } + + if ( !pi.friendsID ) + { + return false; + } + + // check and see if they're on the local player's friends list + CSteamID steamID( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual ); + return steamapicontext->SteamFriends()->HasFriend( steamID, k_EFriendFlagImmediate); +} + +// [menglish] Returns whether this player is dominating or is being dominated by the specified player +bool C_CSPlayer::IsPlayerDominated( int iPlayerIndex ) +{ + return m_bPlayerDominated.Get( iPlayerIndex ); +} + +bool C_CSPlayer::IsPlayerDominatingMe( int iPlayerIndex ) +{ + return m_bPlayerDominatingMe.Get( iPlayerIndex ); +} + + +// helper interpolation functions +namespace Interpolators +{ + inline float Linear( float t ) { return t; } + + inline float SmoothStep( float t ) + { + t = 3 * t * t - 2.0f * t * t * t; + return t; + } + + inline float SmoothStep2( float t ) + { + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); + } + + inline float SmoothStepStart( float t ) + { + t = 0.5f * t; + t = 3 * t * t - 2.0f * t * t * t; + t = t* 2.0f; + return t; + } + + inline float SmoothStepEnd( float t ) + { + t = 0.5f * t + 0.5f; + t = 3 * t * t - 2.0f * t * t * t; + t = (t - 0.5f) * 2.0f; + return t; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Calculate the view for the player while he's in freeze frame observer mode +//----------------------------------------------------------------------------- +void C_CSPlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ) +{ + C_BaseEntity *pTarget = GetObserverTarget(); + + //============================================================================= + // HPE_BEGIN: + // [Forrest] Added sv_disablefreezecam check + //============================================================================= + static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" ); + if ( !pTarget || cl_disablefreezecam.GetBool() || sv_disablefreezecam.GetBool() ) + //============================================================================= + // HPE_END + //============================================================================= + { + return CalcDeathCamView( eyeOrigin, eyeAngles, fov ); + } + + // pick a zoom camera target + Vector vLookAt = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled + vLookAt += GetChaseCamViewOffset( pTarget ); + + // look over ragdoll, not through + if ( !pTarget->IsAlive() ) + vLookAt.z += pTarget->GetBaseAnimating() ? VEC_DEAD_VIEWHEIGHT_SCALED( pTarget->GetBaseAnimating() ).z : VEC_DEAD_VIEWHEIGHT.z; + + // Figure out a view position in front of the target + Vector vEyeOnPlane = eyeOrigin; + vEyeOnPlane.z = vLookAt.z; + Vector vToTarget = vLookAt - vEyeOnPlane; + VectorNormalize( vToTarget ); + + // goal position of camera is pulled away from target by m_flFreezeFrameDistance + Vector vTargetPos = vLookAt - (vToTarget * m_flFreezeFrameDistance); + + // Now trace out from the target, so that we're put in front of any walls + trace_t trace; + C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace + UTIL_TraceHull( vLookAt, vTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace ); + C_BaseEntity::PopEnableAbsRecomputations(); + if ( trace.fraction < 1.0 ) + { + // The camera's going to be really close to the target. So we don't end up + // looking at someone's chest, aim close freezecams at the target's eyes. + vTargetPos = trace.endpos; + + // To stop all close in views looking up at character's chins, move the view up. + vTargetPos.z += fabs(vLookAt.z - vTargetPos.z) * 0.85; + C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace + UTIL_TraceHull( vLookAt, vTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace ); + C_BaseEntity::PopEnableAbsRecomputations(); + vTargetPos = trace.endpos; + } + + // Look directly at the target + vToTarget = vLookAt - vTargetPos; + VectorNormalize( vToTarget ); + VectorAngles( vToTarget, eyeAngles ); + + float fCurTime = gpGlobals->curtime - m_flFreezeFrameStartTime; + float fInterpolant = clamp( fCurTime / spec_freeze_traveltime.GetFloat(), 0.0f, 1.0f ); + fInterpolant = Interpolators::SmoothStepEnd( fInterpolant ); + + // move the eye toward our killer + VectorLerp( m_vecFreezeFrameStart, vTargetPos, fInterpolant, eyeOrigin ); + + if ( fCurTime >= spec_freeze_traveltime.GetFloat() && !m_bSentFreezeFrame ) + { + IGameEvent *pEvent = gameeventmanager->CreateEvent( "freezecam_started" ); + if ( pEvent ) + { + gameeventmanager->FireEventClientSide( pEvent ); + } + + m_bSentFreezeFrame = true; + view->FreezeFrame( spec_freeze_time.GetFloat() ); + } +} + +float C_CSPlayer::GetDeathCamInterpolationTime() +{ + static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" ); + if ( cl_disablefreezecam.GetBool() || sv_disablefreezecam.GetBool() || !GetObserverTarget() ) + return spec_freeze_time.GetFloat(); + else + return CS_DEATH_ANIMATION_TIME; + +} + + +//============================================================================= +// HPE_END +//============================================================================= + diff --git a/game/client/cstrike/c_cs_player.h b/game/client/cstrike/c_cs_player.h new file mode 100644 index 0000000..0d50a20 --- /dev/null +++ b/game/client/cstrike/c_cs_player.h @@ -0,0 +1,419 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef C_CS_PLAYER_H +#define C_CS_PLAYER_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "cs_playeranimstate.h" +#include "c_baseplayer.h" +#include "cs_shareddefs.h" +#include "weapon_csbase.h" +#include "baseparticleentity.h" +#include "beamdraw.h" + + +class C_PhysicsProp; + +extern ConVar cl_disablefreezecam; + +class CAddonModel +{ +public: + CHandle<C_BaseAnimating> m_hEnt; // The model for the addon. + int m_iAddon; // One of the ADDON_ bits telling which model this is. + int m_iAttachmentPoint; // Which attachment point on the player model this guy is on. +}; + + + +class C_CSPlayer : public C_BasePlayer, public ICSPlayerAnimStateHelpers +{ +public: + DECLARE_CLASS( C_CSPlayer, C_BasePlayer ); + DECLARE_CLIENTCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_INTERPOLATION(); + + C_CSPlayer(); + ~C_CSPlayer(); + + virtual void Simulate(); + + bool HasDefuser() const; + + void GiveDefuser(); + void RemoveDefuser(); + + bool HasNightVision() const; + + static C_CSPlayer* GetLocalCSPlayer(); + CSPlayerState State_Get() const; + + virtual float GetMinFOV() const; + + // Get how much $$$ this guy has. + int GetAccount() const; + + // Returns one of the CS_CLASS_ enums. + int PlayerClass() const; + + bool IsInBuyZone(); + bool CanShowTeamMenu() const; // Returns true if we're allowed to show the team menu right now. + + // Get the amount of armor the player has. + int ArmorValue() const; + bool HasHelmet() const; + int GetCurrentAssaultSuitPrice(); + + virtual const QAngle& EyeAngles(); + virtual const QAngle& GetRenderAngles(); + virtual void CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); + + virtual void GetRenderBounds( Vector& theMins, Vector& theMaxs ); + virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType ); + virtual bool GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const; + + virtual void VPhysicsUpdate( IPhysicsObject *pPhysics ); + + // Get the ID target entity index. The ID target is the player that is behind our crosshairs, used to + // display the player's name. + int GetIDTarget() const; + + virtual void NotifyShouldTransmit( ShouldTransmitState_t state ); + virtual void ClientThink(); + + virtual void OnDataChanged( DataUpdateType_t type ); + virtual void PostDataUpdate( DataUpdateType_t updateType ); + virtual bool Interpolate( float currentTime ); + virtual void UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity ); + virtual surfacedata_t * GetFootstepSurface( const Vector &origin, const char *surfaceName ); + virtual void ValidateModelIndex( void ); + + virtual int GetMaxHealth() const; + + bool Weapon_CanSwitchTo(C_BaseCombatWeapon *pWeapon); + + virtual void UpdateClientSideAnimation(); + virtual void ProcessMuzzleFlashEvent(); + + virtual const Vector& GetRenderOrigin( void ); + + bool CreateMove( float flInputSampleTime, CUserCmd *pCmd ); + + CUtlVector< C_BaseParticleEntity* > m_SmokeGrenades; + + virtual bool ShouldDraw( void ); + virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ); + + virtual C_BaseAnimating * BecomeRagdollOnClient(); + virtual IRagdoll* GetRepresentativeRagdoll() const; + + void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ); + + // Have this player play the sounds from his view model's reload animation. + void PlayReloadEffect(); + + virtual void FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); + + bool HasC4( void ); + + virtual void CreateLightEffects( void ) {} //no dimlight effects + + // Sometimes the server wants to update the client's cycle to get the two to run in sync (for proper hit detection) + virtual void SetServerIntendedCycle( float intended ) { m_serverIntendedCycle = intended; } + virtual float GetServerIntendedCycle( void ) { return m_serverIntendedCycle; } + + virtual bool ShouldReceiveProjectedTextures( int flags ) + { + return ( this != C_BasePlayer::GetLocalPlayer() ); + } + + void ClearSoundEvents() + { + m_SoundEvents.RemoveAll(); + } + + //============================================================================= + // HPE_BEGIN: + // [menglish] Returns whether this player is dominating or is being dominated by the specified player + //============================================================================= + bool IsPlayerDominated( int iPlayerIndex ); + bool IsPlayerDominatingMe( int iPlayerIndex ); + + virtual void CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); + + virtual float GetDeathCamInterpolationTime(); + //============================================================================= + // HPE_END + //============================================================================= + + +// Called by shared code. +public: + + // ICSPlayerAnimState overrides. + virtual CWeaponCSBase* CSAnim_GetActiveWeapon(); + virtual bool CSAnim_CanMove(); + + + void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 ); + + +// Implemented in shared code. +public: + virtual float GetPlayerMaxSpeed(); + + void GetBulletTypeParameters( + int iBulletType, + float &fPenetrationPower, + float &flPenetrationDistance ); + + void FireBullet( + Vector vecSrc, + const QAngle &shootAngles, + float flDistance, + int iPenetration, + int iBulletType, + int iDamage, + float flRangeModifier, + CBaseEntity *pevAttacker, + bool bDoEffects, + float xSpread, float ySpread ); + + void KickBack( + float up_base, + float lateral_base, + float up_modifier, + float lateral_modifier, + float up_max, + float lateral_max, + int direction_change ); + + // Returns true if the player is allowed to move. + bool CanMove() const; + + void OnJump( float fImpulse ); + void OnLand( float fVelocity ); + + bool HasC4() const; // Is this player carrying a C4 bomb? + bool IsVIP() const; // Is this player the VIP? + + virtual void SetAnimation( PLAYER_ANIM playerAnim ); + + +public: + + void UpdateIDTarget( void ); + void RemoveAddonModels( void ); + void UpdateMinModels( void ); + + void SetActivity( Activity eActivity ); + Activity GetActivity( void ) const; + + ICSPlayerAnimState *GetPlayerAnimState() { return m_PlayerAnimState; } + +public: + + ICSPlayerAnimState *m_PlayerAnimState; + + // Used to control animation state. + Activity m_Activity; + + // Predicted variables. + CNetworkVar( bool, m_bResumeZoom ); + CNetworkVar( int , m_iLastZoom ); // after firing a shot, set the FOV to 90, and after showing the animation, bring the FOV back to last zoom level. + CNetworkVar( CSPlayerState, m_iPlayerState ); // SupraFiend: this gives the current state in the joining process, the states are listed above + CNetworkVar( bool, m_bIsDefusing ); // tracks whether this player is currently defusing a bomb + CNetworkVar( bool, m_bInBombZone ); + CNetworkVar( bool, m_bInBuyZone ); + CNetworkVar( int, m_iThrowGrenadeCounter ); // used to trigger grenade throw animations. + + bool IsInHostageRescueZone( void ); + + // This is a combination of the ADDON_ flags in cs_shareddefs.h. + CNetworkVar( int, m_iAddonBits ); + + // Clients don't know about holstered weapons, so we need to be told about them here + CNetworkVar( int, m_iPrimaryAddon ); + CNetworkVar( int, m_iSecondaryAddon ); + + // How long the progress bar takes to get to the end. If this is 0, then the progress bar + // should not be drawn. + CNetworkVar( int, m_iProgressBarDuration ); + + // When the progress bar should start. + CNetworkVar( float, m_flProgressBarStartTime ); + + CNetworkVar( float, m_flStamina ); + CNetworkVar( int, m_iDirection ); // The current lateral kicking direction; 1 = right, 0 = left + CNetworkVar( int, m_iShotsFired ); // number of shots fired recently + CNetworkVar( bool, m_bNightVisionOn ); + CNetworkVar( bool, m_bHasNightVision ); + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Added for fun-fact support + //============================================================================= + + //CNetworkVar( bool, m_bPickedUpDefuser ); + //CNetworkVar( bool, m_bDefusedWithPickedUpKit ); + + //============================================================================= + // HPE_END + //============================================================================= + + CNetworkVar( float, m_flVelocityModifier ); + + bool m_bDetected; + + EHANDLE m_hRagdoll; + + CWeaponCSBase* GetActiveCSWeapon() const; + CWeaponCSBase* GetCSWeapon( CSWeaponID id ) const; + + virtual ShadowType_t ShadowCastType(); + +#ifdef CS_SHIELD_ENABLED + bool HasShield( void ) { return m_bHasShield; } + bool IsShieldDrawn( void ) { return m_bShieldDrawn; } + void SetShieldDrawnState( bool bState ) { m_bShieldDrawn = bState; } +#else + bool HasShield( void ) { return false; } + bool IsShieldDrawn( void ) { return false; } + void SetShieldDrawnState( bool bState ) {} +#endif + + float m_flNightVisionAlpha; + + float m_flFlashAlpha; + float m_flFlashBangTime; + CNetworkVar( float, m_flFlashMaxAlpha ); + CNetworkVar( float, m_flFlashDuration ); + + // Having the RecvProxy in the player allows us to keep the var private + static void RecvProxy_CycleLatch( const CRecvProxyData *pData, void *pStruct, void *pOut ); + + // Bots and hostages auto-duck during jumps + bool m_duckUntilOnGround; + + Vector m_lastStandingPos; // used by the gamemovement code for finding ladders + + void SurpressLadderChecks( const Vector& pos, const Vector& normal ); + bool CanGrabLadder( const Vector& pos, const Vector& normal ); + +//============================================================================= +// HPE_BEGIN: +//============================================================================= + +// [tj] checks if this player has another given player on their Steam friends list. + bool HasPlayerAsFriend(C_CSPlayer* player); + +private: + CountdownTimer m_ladderSurpressionTimer; + Vector m_lastLadderNormal; + Vector m_lastLadderPos; + + void UpdateRadar(); + void UpdateSoundEvents(); + + void CreateAddonModel( int i ); + void UpdateAddonModels(); + + void PushawayThink(); + + int m_iAccount; + bool m_bHasHelmet; + int m_iClass; + int m_ArmorValue; + QAngle m_angEyeAngles; + bool m_bHasDefuser; + bool m_bInHostageRescueZone; + float m_fNextThinkPushAway; + + bool m_bPlayingFreezeCamSound; + +#ifdef CS_SHIELD_ENABLED + bool m_bHasShield; + bool m_bShieldDrawn; +#endif + + Vector m_vecRagdollVelocity; + + CInterpolatedVar< QAngle > m_iv_angEyeAngles; + + // ID Target + int m_iIDEntIndex; + CountdownTimer m_delayTargetIDTimer; + + // Show the ID target after the cursor leaves the entity + int m_iOldIDEntIndex; + CountdownTimer m_holdTargetIDTimer; + + void ReleaseFlashlight( void ); + Beam_t *m_pFlashlightBeam; + + class CCSSoundEvent + { + public: + string_t m_SoundName; + float m_flEventTime; // Play the event when gpGlobals->curtime goes past this. + }; + CUtlLinkedList<CCSSoundEvent,int> m_SoundEvents; + + + // This is the list of addons hanging off the guy (grenades, C4, nightvision, etc). + CUtlLinkedList<CAddonModel, int> m_AddonModels; + int m_iLastAddonBits; + int m_iLastPrimaryAddon; + int m_iLastSecondaryAddon; + + int m_cycleLatch; // server periodically updates this to fix up our anims, here it is a 4 bit fixed point + float m_serverIntendedCycle; // server periodically updates this to fix up our anims, here it is the float we want, or -1 for no override + + + + //============================================================================= + // HPE_BEGIN: + // [tj] Network variables that track who are dominating and being dominated by + //============================================================================= + + CNetworkArray( bool, m_bPlayerDominated, MAX_PLAYERS+1 ); // array of state per other player whether player is dominating other players + CNetworkArray( bool, m_bPlayerDominatingMe, MAX_PLAYERS+1 ); // array of state per other player whether other players are dominating this player + + //============================================================================= + // HPE_END + //============================================================================= + + + + C_CSPlayer( const C_CSPlayer & ); +}; + +C_CSPlayer* GetLocalOrInEyeCSPlayer( void ); + +inline C_CSPlayer *ToCSPlayer( CBaseEntity *pEntity ) +{ + if ( !pEntity || !pEntity->IsPlayer() ) + return NULL; + + return dynamic_cast<C_CSPlayer*>( pEntity ); +} + +namespace vgui +{ + class IImage; +} + +vgui::IImage* GetDefaultAvatarImage( C_BasePlayer *pPlayer ); + + + + +#endif // C_CS_PLAYER_H diff --git a/game/client/cstrike/c_cs_playerresource.cpp b/game/client/cstrike/c_cs_playerresource.cpp new file mode 100644 index 0000000..767fc2c --- /dev/null +++ b/game/client/cstrike/c_cs_playerresource.cpp @@ -0,0 +1,206 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: CS's custom C_PlayerResource +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "c_cs_playerresource.h" +#include <shareddefs.h> +#include <cs_shareddefs.h> +#include "hud.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT(C_CS_PlayerResource, DT_CSPlayerResource, CCSPlayerResource) + RecvPropInt( RECVINFO( m_iPlayerC4 ) ), + RecvPropInt( RECVINFO( m_iPlayerVIP ) ), + RecvPropVector( RECVINFO(m_vecC4) ), + RecvPropArray3( RECVINFO_ARRAY(m_bHostageAlive), RecvPropInt( RECVINFO(m_bHostageAlive[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_isHostageFollowingSomeone), RecvPropInt( RECVINFO(m_isHostageFollowingSomeone[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_iHostageEntityIDs), RecvPropInt( RECVINFO(m_iHostageEntityIDs[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_iHostageX), RecvPropInt( RECVINFO(m_iHostageX[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_iHostageY), RecvPropInt( RECVINFO(m_iHostageY[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_iHostageZ), RecvPropInt( RECVINFO(m_iHostageZ[0]))), + RecvPropVector( RECVINFO(m_bombsiteCenterA) ), + RecvPropVector( RECVINFO(m_bombsiteCenterB) ), + RecvPropArray3( RECVINFO_ARRAY(m_hostageRescueX), RecvPropInt( RECVINFO(m_hostageRescueX[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_hostageRescueY), RecvPropInt( RECVINFO(m_hostageRescueY[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_hostageRescueZ), RecvPropInt( RECVINFO(m_hostageRescueZ[0]))), + RecvPropInt( RECVINFO( m_bBombSpotted ) ), + RecvPropArray3( RECVINFO_ARRAY(m_bPlayerSpotted), RecvPropInt( RECVINFO(m_bPlayerSpotted[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_iMVPs), RecvPropInt( RECVINFO(m_iMVPs[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_bHasDefuser), RecvPropInt( RECVINFO(m_bHasDefuser[0]))), + RecvPropArray3( RECVINFO_ARRAY(m_szClan), RecvPropString( RECVINFO(m_szClan[0]))), +END_RECV_TABLE() + +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CS_PlayerResource::C_CS_PlayerResource() +{ + m_Colors[TEAM_TERRORIST] = COLOR_RED; + m_Colors[TEAM_CT] = COLOR_BLUE; + memset( m_iMVPs, 0, sizeof( m_iMVPs ) ); + memset( m_bHasDefuser, 0, sizeof( m_bHasDefuser ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CS_PlayerResource::~C_CS_PlayerResource() +{ +} + +bool C_CS_PlayerResource::IsVIP(int iIndex ) +{ + return m_iPlayerVIP == iIndex; +} + +bool C_CS_PlayerResource::HasC4(int iIndex ) +{ + return m_iPlayerC4 == iIndex; +} + +bool C_CS_PlayerResource::IsHostageAlive(int iIndex) +{ + if ( iIndex < 0 || iIndex >= MAX_HOSTAGES ) + return false; + + return m_bHostageAlive[iIndex]; +} + +bool C_CS_PlayerResource::IsHostageFollowingSomeone(int iIndex) +{ + if ( iIndex < 0 || iIndex >= MAX_HOSTAGES ) + return false; + + return m_isHostageFollowingSomeone[iIndex]; +} + +int C_CS_PlayerResource::GetHostageEntityID(int iIndex) +{ + if ( iIndex < 0 || iIndex >= MAX_HOSTAGES ) + return -1; + + return m_iHostageEntityIDs[iIndex]; +} + +const Vector C_CS_PlayerResource::GetHostagePosition( int iIndex ) +{ + if ( iIndex < 0 || iIndex >= MAX_HOSTAGES ) + return vec3_origin; + + Vector ret; + + ret.x = m_iHostageX[iIndex]; + ret.y = m_iHostageY[iIndex]; + ret.z = m_iHostageZ[iIndex]; + + return ret; +} + +const Vector C_CS_PlayerResource::GetC4Postion() +{ + if ( m_iPlayerC4 > 0 ) + { + // C4 is carried by player + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_iPlayerC4 ); + + if ( pPlayer ) + { + return pPlayer->GetAbsOrigin(); + } + } + + // C4 is lying on ground + return m_vecC4; +} + +const Vector C_CS_PlayerResource::GetBombsiteAPosition() +{ + return m_bombsiteCenterA; +} + +const Vector C_CS_PlayerResource::GetBombsiteBPosition() +{ + return m_bombsiteCenterB; +} + +const Vector C_CS_PlayerResource::GetHostageRescuePosition( int iIndex ) +{ + if ( iIndex < 0 || iIndex >= MAX_HOSTAGE_RESCUES ) + return vec3_origin; + + Vector ret; + + ret.x = m_hostageRescueX[iIndex]; + ret.y = m_hostageRescueY[iIndex]; + ret.z = m_hostageRescueZ[iIndex]; + + return ret; +} + +int C_CS_PlayerResource::GetPlayerClass( int iIndex ) +{ + if ( !IsConnected( iIndex ) ) + { + return CS_CLASS_NONE; + } + + return m_iPlayerClasses[ iIndex ]; +} + +//-------------------------------------------------------------------------------------------------------- +bool C_CS_PlayerResource::IsBombSpotted( void ) const +{ + return m_bBombSpotted; +} + + +//-------------------------------------------------------------------------------------------------------- +bool C_CS_PlayerResource::IsPlayerSpotted( int iIndex ) +{ + if ( !IsConnected( iIndex ) ) + return false; + + return m_bPlayerSpotted[iIndex]; +} + +//----------------------------------------------------------------------------- +const char *C_CS_PlayerResource::GetClanTag( int iIndex ) +{ + if ( iIndex < 1 || iIndex > MAX_PLAYERS ) + { + Assert( false ); + return ""; + } + + if ( !IsConnected( iIndex ) ) + return ""; + + return m_szClan[iIndex]; +} + +//----------------------------------------------------------------------------- +int C_CS_PlayerResource::GetNumMVPs( int iIndex ) +{ + if ( !IsConnected( iIndex ) ) + return false; + + return m_iMVPs[iIndex]; +} + +//----------------------------------------------------------------------------- +bool C_CS_PlayerResource::HasDefuser( int iIndex ) +{ + if ( !IsConnected( iIndex ) ) + return false; + + return m_bHasDefuser[iIndex]; +} diff --git a/game/client/cstrike/c_cs_playerresource.h b/game/client/cstrike/c_cs_playerresource.h new file mode 100644 index 0000000..6665bf2 --- /dev/null +++ b/game/client/cstrike/c_cs_playerresource.h @@ -0,0 +1,76 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: CS's custom C_PlayerResource +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef C_CS_PLAYERRESOURCE_H +#define C_CS_PLAYERRESOURCE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "cs_shareddefs.h" +#include "c_playerresource.h" + +class C_CS_PlayerResource : public C_PlayerResource +{ + DECLARE_CLASS( C_CS_PlayerResource, C_PlayerResource ); +public: + DECLARE_CLIENTCLASS(); + + C_CS_PlayerResource(); + virtual ~C_CS_PlayerResource(); + + bool IsVIP(int iIndex ); + bool HasC4(int iIndex ); + bool IsHostageAlive(int iIndex); + bool IsHostageFollowingSomeone(int iIndex); + const Vector GetHostagePosition( int index ); + int GetHostageEntityID(int iIndex); + const Vector GetC4Postion(); + const Vector GetBombsiteAPosition(); + const Vector GetBombsiteBPosition(); + const Vector GetHostageRescuePosition( int index ); + int GetPlayerClass( int iIndex ); + + bool IsBombSpotted( void ) const; + bool IsPlayerSpotted( int iIndex ); + + const char *GetClanTag( int index ); + + int GetNumMVPs( int iIndex ); + bool HasDefuser( int iIndex ); + +protected: + + int m_iPlayerC4; // entity index of C4 carrier or 0 + int m_iPlayerVIP; // entity index of VIP player or 0 + Vector m_vecC4; // position of C4 + Vector m_bombsiteCenterA; + Vector m_bombsiteCenterB; + + bool m_bHostageAlive[MAX_HOSTAGES]; + bool m_isHostageFollowingSomeone[MAX_HOSTAGES]; + int m_iHostageEntityIDs[MAX_HOSTAGES]; + int m_iHostageX[MAX_HOSTAGES]; + int m_iHostageY[MAX_HOSTAGES]; + int m_iHostageZ[MAX_HOSTAGES]; + + int m_hostageRescueX[MAX_HOSTAGE_RESCUES]; + int m_hostageRescueY[MAX_HOSTAGE_RESCUES]; + int m_hostageRescueZ[MAX_HOSTAGE_RESCUES]; + + bool m_bBombSpotted; + bool m_bPlayerSpotted[ MAX_PLAYERS + 1 ]; + int m_iPlayerClasses[ MAX_PLAYERS + 1 ]; + + char m_szClan[MAX_PLAYERS+1][MAX_CLAN_TAG_LENGTH]; + + int m_iMVPs[ MAX_PLAYERS + 1 ]; + bool m_bHasDefuser[ MAX_PLAYERS + 1 ]; +}; + + +#endif // C_CS_PLAYERRESOURCE_H diff --git a/game/client/cstrike/c_cs_team.cpp b/game/client/cstrike/c_cs_team.cpp new file mode 100644 index 0000000..721858a --- /dev/null +++ b/game/client/cstrike/c_cs_team.cpp @@ -0,0 +1,33 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Client side C_CSTeam class +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "engine/IEngineSound.h" +#include "hud.h" +#include "recvproxy.h" +#include "c_cs_team.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +IMPLEMENT_CLIENTCLASS_DT(C_CSTeam, DT_CSTeam, CCSTeam) +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CSTeam::C_CSTeam() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CSTeam::~C_CSTeam() +{ +} + diff --git a/game/client/cstrike/c_cs_team.h b/game/client/cstrike/c_cs_team.h new file mode 100644 index 0000000..97cfa6b --- /dev/null +++ b/game/client/cstrike/c_cs_team.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Client side CTFTeam class +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef C_CS_TEAM_H +#define C_CS_TEAM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "c_team.h" +#include "shareddefs.h" + +class C_BaseEntity; +class C_BaseObject; +class CBaseTechnology; + +//----------------------------------------------------------------------------- +// Purpose: TF's Team manager +//----------------------------------------------------------------------------- +class C_CSTeam : public C_Team +{ + DECLARE_CLASS( C_CSTeam, C_Team ); +public: + DECLARE_CLIENTCLASS(); + + C_CSTeam(); + virtual ~C_CSTeam(); +}; + + +#endif // C_CS_TEAM_H diff --git a/game/client/cstrike/c_csrootpanel.cpp b/game/client/cstrike/c_csrootpanel.cpp new file mode 100644 index 0000000..2fa0648 --- /dev/null +++ b/game/client/cstrike/c_csrootpanel.cpp @@ -0,0 +1,86 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "c_csrootpanel.h" +#include <vgui_controls/Controls.h> +#include <vgui/IVGui.h> +#include "clientmode_csnormal.h" + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *parent - +//----------------------------------------------------------------------------- +C_CSRootPanel::C_CSRootPanel( vgui::VPANEL parent ) + : BaseClass( NULL, "CounterStrike Root Panel" ) +{ + SetParent( parent ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( false ); + SetPaintBackgroundEnabled( false ); + + // This panel does post child painting + SetPostChildPaintEnabled( true ); + + int w, h; + surface()->GetScreenSize( w, h ); + + // Make it screen sized + SetBounds( 0, 0, w, h ); + + // Ask for OnTick messages + vgui::ivgui()->AddTickSignal( GetVPanel() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_CSRootPanel::~C_CSRootPanel( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_CSRootPanel::PostChildPaint() +{ + BaseClass::PostChildPaint(); + + // Draw all panel effects + RenderPanelEffects(); +} + +//----------------------------------------------------------------------------- +// Purpose: For each panel effect, check if it wants to draw and draw it on +// this panel/surface if so +//----------------------------------------------------------------------------- +void C_CSRootPanel::RenderPanelEffects( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_CSRootPanel::OnTick( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Reset effects on level load/shutdown +//----------------------------------------------------------------------------- +void C_CSRootPanel::LevelInit( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_CSRootPanel::LevelShutdown( void ) +{ +} + diff --git a/game/client/cstrike/c_csrootpanel.h b/game/client/cstrike/c_csrootpanel.h new file mode 100644 index 0000000..ca7bb45 --- /dev/null +++ b/game/client/cstrike/c_csrootpanel.h @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef C_CSRootPanel_H +#define C_CSRootPanel_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Panel.h> +#include <vgui_controls/EditablePanel.h> +#include "utlvector.h" + +class CPanelEffect; +class ITFHintItem; + +// Serial under of effect, for safe lookup +typedef unsigned int EFFECT_HANDLE; + +//----------------------------------------------------------------------------- +// Purpose: Sits between engine and client .dll panels +// Responsible for drawing screen overlays +//----------------------------------------------------------------------------- +class C_CSRootPanel : public vgui::Panel +{ + typedef vgui::Panel BaseClass; +public: + C_CSRootPanel( vgui::VPANEL parent ); + virtual ~C_CSRootPanel( void ); + + // Draw Panel effects here + virtual void PostChildPaint(); + + // Clear list of Panel Effects + virtual void LevelInit( void ); + virtual void LevelShutdown( void ); + + // Run effects and let them decide whether to remove themselves + void OnTick( void ); + +private: + + // Render all panel effects + void RenderPanelEffects( void ); + + // List of current panel effects + CUtlVector< CPanelEffect *> m_Effects; +}; + +extern C_CSRootPanel *g_pTF2RootPanel; + +#endif // C_CSRootPanel_H diff --git a/game/client/cstrike/c_plantedc4.cpp b/game/client/cstrike/c_plantedc4.cpp new file mode 100644 index 0000000..d745e32 --- /dev/null +++ b/game/client/cstrike/c_plantedc4.cpp @@ -0,0 +1,219 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "c_plantedc4.h" +#include "c_te_legacytempents.h" +#include "tempent.h" +#include "engine/IEngineSound.h" +#include "dlight.h" +#include "iefx.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include <bitbuf.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define PLANTEDC4_MSG_JUSTBLEW 1 + +ConVar cl_c4dynamiclight( "cl_c4dynamiclight", "0", 0, "Draw dynamic light when planted c4 flashes" ); + +IMPLEMENT_CLIENTCLASS_DT(C_PlantedC4, DT_PlantedC4, CPlantedC4) + RecvPropBool( RECVINFO(m_bBombTicking) ), + RecvPropFloat( RECVINFO(m_flC4Blow) ), + RecvPropFloat( RECVINFO(m_flTimerLength) ), + RecvPropFloat( RECVINFO(m_flDefuseLength) ), + RecvPropFloat( RECVINFO(m_flDefuseCountDown) ), +END_RECV_TABLE() + +CUtlVector< C_PlantedC4* > g_PlantedC4s; + +C_PlantedC4::C_PlantedC4() +{ + g_PlantedC4s.AddToTail( this ); + + m_flNextRadarFlashTime = gpGlobals->curtime; + m_bRadarFlash = true; + m_pC4Explosion = NULL; + + // Don't beep right away, leave time for the planting sound + m_flNextGlow = gpGlobals->curtime + 1.0; + m_flNextBeep = gpGlobals->curtime + 1.0; +} + + +C_PlantedC4::~C_PlantedC4() +{ + g_PlantedC4s.FindAndRemove( this ); + //============================================================================= + // HPE_BEGIN: + // [menglish] Upon the new round remove the remaining bomb explosion particle effect + //============================================================================= + + if (m_pC4Explosion) + { + m_pC4Explosion->SetRemoveFlag(); + } + + //============================================================================= + // HPE_END + //============================================================================= +} + +void C_PlantedC4::SetDormant( bool bDormant ) +{ + BaseClass::SetDormant( bDormant ); + + // Remove us from the list of planted C4s. + if ( bDormant ) + { + g_PlantedC4s.FindAndRemove( this ); + } + else + { + if ( g_PlantedC4s.Find( this ) == -1 ) + g_PlantedC4s.AddToTail( this ); + } +} + +void C_PlantedC4::Spawn( void ) +{ + BaseClass::Spawn(); + + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +void C_PlantedC4::ClientThink( void ) +{ + BaseClass::ClientThink(); + + // If it's dormant, don't beep or anything.. + if ( IsDormant() ) + return; + + if ( !m_bBombTicking ) + { + // disable C4 thinking if not armed + SetNextClientThink( CLIENT_THINK_NEVER ); + return; + } + + if( gpGlobals->curtime > m_flNextBeep ) + { + // as it gets closer to going off, increase the radius + + CLocalPlayerFilter filter; + float attenuation; + float freq; + + //the percent complete of the bomb timer + float fComplete = ( ( m_flC4Blow - gpGlobals->curtime ) / m_flTimerLength ); + + fComplete = clamp( fComplete, 0.0f, 1.0f ); + + attenuation = MIN( 0.3 + 0.6 * fComplete, 1.0 ); + + CSoundParameters params; + + if ( GetParametersForSound( "C4.PlantSound", params, NULL ) ) + { + EmitSound_t ep( params ); + ep.m_SoundLevel = ATTN_TO_SNDLVL( attenuation ); + ep.m_pOrigin = &GetAbsOrigin(); + + EmitSound( filter, SOUND_FROM_WORLD, ep ); + } + + freq = MAX( 0.1 + 0.9 * fComplete, 0.15 ); + + m_flNextBeep = gpGlobals->curtime + freq; + } + + if( gpGlobals->curtime > m_flNextGlow ) + { + int modelindex = modelinfo->GetModelIndex( "sprites/ledglow.vmt" ); + + float scale = 0.8f; + Vector vPos = GetAbsOrigin(); + const Vector offset( 0, 0, 4 ); + + // See if the c4 ended up underwater - we need to pull the flash up, or it won't get seen + if ( enginetrace->GetPointContents( vPos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) + { + C_CSPlayer *player = GetLocalOrInEyeCSPlayer(); + if ( player ) + { + const Vector& eyes = player->EyePosition(); + + if ( ( enginetrace->GetPointContents( eyes ) & (CONTENTS_WATER|CONTENTS_SLIME) ) == 0 ) + { + // trace from the player to the water + trace_t waterTrace; + UTIL_TraceLine( eyes, vPos, (CONTENTS_WATER|CONTENTS_SLIME), player, COLLISION_GROUP_NONE, &waterTrace ); + + if( waterTrace.allsolid != 1 ) + { + // now trace from the C4 to the edge of the water (in case there was something solid in the water) + trace_t solidTrace; + UTIL_TraceLine( vPos, waterTrace.endpos, MASK_SOLID, this, COLLISION_GROUP_NONE, &solidTrace ); + + if( solidTrace.allsolid != 1 ) + { + float waterDist = (solidTrace.endpos - vPos).Length(); + float remainingDist = (solidTrace.endpos - eyes).Length(); + + scale = scale * remainingDist / ( remainingDist + waterDist ); + vPos = solidTrace.endpos; + } + } + } + } + } + + vPos += offset; + + tempents->TempSprite( vPos, vec3_origin, scale, modelindex, kRenderTransAdd, 0, 1.0, 0.05, FTENT_SPRANIMATE | FTENT_SPRANIMATELOOP ); + + if( cl_c4dynamiclight.GetBool() ) + { + dlight_t *dl; + + dl = effects->CL_AllocDlight( entindex() ); + + if( dl ) + { + dl->origin = GetAbsOrigin() + offset; // can't use vPos because it might have been moved + dl->color.r = 255; + dl->color.g = 0; + dl->color.b = 0; + dl->radius = 64; + dl->die = gpGlobals->curtime + 0.01; + } + } + + float freq = 0.1 + 0.9 * ( ( m_flC4Blow - gpGlobals->curtime ) / m_flTimerLength ); + + if( freq < 0.15 ) freq = 0.15; + + m_flNextGlow = gpGlobals->curtime + freq; + } +} + + +//============================================================================= +// HPE_BEGIN: +// [menglish] Create the client side explosion particle effect for when the bomb explodes and hide the bomb +//============================================================================= +void C_PlantedC4::Explode( void ) +{ + m_pC4Explosion = ParticleProp()->Create( "bomb_explosion_huge", PATTACH_ABSORIGIN ); + AddEffects( EF_NODRAW ); + SetDormant( true ); +} +//============================================================================= +// HPE_END +//=============================================================================
\ No newline at end of file diff --git a/game/client/cstrike/c_plantedc4.h b/game/client/cstrike/c_plantedc4.h new file mode 100644 index 0000000..b5ae7c7 --- /dev/null +++ b/game/client/cstrike/c_plantedc4.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef C_PLANTEDC4_H +#define C_PLANTEDC4_H + +#include "cbase.h" +#include "in_buttons.h" +#include "decals.h" + +#include "c_cs_player.h" + +#include "utlvector.h" + +// ------------------------------------------------------------------------------------------ // +// CPlantedC4 class. +// For now to show the planted c4 on the radar - client proxy to remove the CBaseAnimating +// network vars? +// ------------------------------------------------------------------------------------------ // + +class C_PlantedC4 : public C_BaseAnimating +{ +public: + DECLARE_CLASS( C_PlantedC4, CBaseAnimating ); + DECLARE_CLIENTCLASS(); + + C_PlantedC4(); + virtual ~C_PlantedC4(); + + void Explode( void ); + void Spawn( void ); + virtual void SetDormant( bool bDormant ); + + void ClientThink( void ); + + int GetSecondsRemaining( void ) { return ceil( m_flC4Blow - gpGlobals->curtime ); } + + inline bool IsBombActive( void ) { return m_bBombTicking; } + CNetworkVar( bool, m_bBombTicking ); + + float m_flNextGlow; + float m_flNextBeep; + + float m_flC4Blow; + float m_flTimerLength; + + CNetworkVar( float, m_flDefuseLength ); + CNetworkVar( float, m_flDefuseCountDown ); + + float GetDefuseProgress( void ) + { + float flProgress = 1.0f; + + if( m_flDefuseLength > 0.0 ) + { + flProgress = ( ( m_flDefuseCountDown - gpGlobals->curtime ) / m_flDefuseLength ); + } + + return flProgress; + } + + float m_flNextRadarFlashTime; // next time to change flash state + bool m_bRadarFlash; // is the flash on or off + CNewParticleEffect *m_pC4Explosion; // client side explosion particle effect for the bomb +}; + +extern CUtlVector< C_PlantedC4* > g_PlantedC4s; + +#endif //C_PLANTEDC4_H
\ No newline at end of file diff --git a/game/client/cstrike/c_te_radioicon.cpp b/game/client/cstrike/c_te_radioicon.cpp new file mode 100644 index 0000000..c49c11b --- /dev/null +++ b/game/client/cstrike/c_te_radioicon.cpp @@ -0,0 +1,74 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "c_basetempentity.h" +#include "c_cs_player.h" +#include "hud_radar.h" +#include "radio_status.h" + + + +//----------------------------------------------------------------------------- +// Purpose: Kills Player Attachments +//----------------------------------------------------------------------------- +class C_TERadioIcon : public C_BaseTempEntity +{ +public: + DECLARE_CLASS( C_TERadioIcon, C_BaseTempEntity ); + DECLARE_CLIENTCLASS(); + + C_TERadioIcon( void ); + virtual ~C_TERadioIcon( void ); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + +public: + int m_iAttachToClient; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_TERadioIcon::C_TERadioIcon( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_TERadioIcon::~C_TERadioIcon( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bool - +//----------------------------------------------------------------------------- +void C_TERadioIcon::PostDataUpdate( DataUpdateType_t updateType ) +{ + //Flash them on the radar + //this could be in a better place. + C_CSPlayer *pPlayer = static_cast<C_CSPlayer*>( cl_entitylist->GetEnt(m_iAttachToClient) ); + + if ( pPlayer && !pPlayer->IsDormant() ) + { + // Create the flashy above player's head + RadioManager()->UpdateRadioStatus( m_iAttachToClient, 1.5f ); + } + + Radar_FlashPlayer( m_iAttachToClient ); +} + +IMPLEMENT_CLIENTCLASS_EVENT_DT(C_TERadioIcon, DT_TERadioIcon, CTERadioIcon) + RecvPropInt( RECVINFO(m_iAttachToClient)), +END_RECV_TABLE() diff --git a/game/client/cstrike/c_te_shotgun_shot.cpp b/game/client/cstrike/c_te_shotgun_shot.cpp new file mode 100644 index 0000000..e468763 --- /dev/null +++ b/game/client/cstrike/c_te_shotgun_shot.cpp @@ -0,0 +1,100 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_cs_shared.h" +#include "c_cs_player.h" +#include "c_basetempentity.h" +#include <cliententitylist.h> + + +class C_TEFireBullets : public C_BaseTempEntity +{ +public: + DECLARE_CLASS( C_TEFireBullets, C_BaseTempEntity ); + DECLARE_CLIENTCLASS(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + +public: + int m_iPlayer; + Vector m_vecOrigin; + QAngle m_vecAngles; + int m_iWeaponID; + int m_iMode; + int m_iSeed; + float m_fInaccuracy; + float m_fSpread; +}; + + +void C_TEFireBullets::PostDataUpdate( DataUpdateType_t updateType ) +{ + // Create the effect. + + m_vecAngles.z = 0; + + FX_FireBullets( + m_iPlayer+1, + m_vecOrigin, + m_vecAngles, + m_iWeaponID, + m_iMode, + m_iSeed, + m_fInaccuracy, + m_fSpread + ); +} + + +IMPLEMENT_CLIENTCLASS_EVENT( C_TEFireBullets, DT_TEFireBullets, CTEFireBullets ); + + +BEGIN_RECV_TABLE_NOBASE(C_TEFireBullets, DT_TEFireBullets) + RecvPropVector( RECVINFO( m_vecOrigin ) ), + RecvPropFloat( RECVINFO( m_vecAngles[0] ) ), + RecvPropFloat( RECVINFO( m_vecAngles[1] ) ), + RecvPropInt( RECVINFO( m_iWeaponID ) ), + RecvPropInt( RECVINFO( m_iMode ) ), + RecvPropInt( RECVINFO( m_iSeed ) ), + RecvPropInt( RECVINFO( m_iPlayer ) ), + RecvPropFloat( RECVINFO( m_fInaccuracy ) ), + RecvPropFloat( RECVINFO( m_fSpread ) ), +END_RECV_TABLE() + + +class C_TEPlantBomb : public C_BaseTempEntity +{ +public: + DECLARE_CLASS( C_TEPlantBomb, C_BaseTempEntity ); + DECLARE_CLIENTCLASS(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + +public: + int m_iPlayer; + Vector m_vecOrigin; + PlantBombOption_t m_option; +}; + + +void C_TEPlantBomb::PostDataUpdate( DataUpdateType_t updateType ) +{ + // Create the effect. + FX_PlantBomb( m_iPlayer+1, m_vecOrigin, m_option ); +} + + +IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlantBomb, DT_TEPlantBomb, CTEPlantBomb ); + + +BEGIN_RECV_TABLE_NOBASE(C_TEPlantBomb, DT_TEPlantBomb) + RecvPropVector( RECVINFO( m_vecOrigin ) ), + RecvPropInt( RECVINFO( m_iPlayer ) ), + RecvPropInt( RECVINFO( m_option ) ), +END_RECV_TABLE() + + diff --git a/game/client/cstrike/clientmode_csnormal.cpp b/game/client/cstrike/clientmode_csnormal.cpp new file mode 100644 index 0000000..87b5cc5 --- /dev/null +++ b/game/client/cstrike/clientmode_csnormal.cpp @@ -0,0 +1,1108 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "cbase.h" +#include "hud.h" +#include "clientmode_csnormal.h" +#include "cdll_client_int.h" +#include "iinput.h" +#include "vgui/ISurface.h" +#include "vgui/IPanel.h" +#include <vgui_controls/AnimationController.h> +#include "ivmodemanager.h" +#include "buymenu.h" +#include "filesystem.h" +#include "vgui/IVGui.h" +#include "hud_basechat.h" +#include "view_shared.h" +#include "view.h" +#include "ivrenderview.h" +#include "cstrikeclassmenu.h" +#include "model_types.h" +#include "iefx.h" +#include "dlight.h" +#include <imapoverview.h> +#include "c_playerresource.h" +#include "c_soundscape.h" +#include <KeyValues.h> +#include "text_message.h" +#include "panelmetaclassmgr.h" +#include "vguicenterprint.h" +#include "physpropclientside.h" +#include "c_weapon__stubs.h" +#include <engine/IEngineSound.h> +#include "c_cs_hostage.h" +#include "buy_presets/buy_presets.h" +#include "bitbuf.h" +#include "usermessages.h" +#include "prediction.h" +#include "datacache/imdlcache.h" +//============================================================================= +// HPE_BEGIN: +// [tj] Needed to retrieve achievement text +// [menglish] Need access to message macros +//============================================================================= + +#include "achievementmgr.h" +#include "hud_macros.h" +#include "c_plantedc4.h" +#include "tier1/fmtstr.h" +#include "history_resource.h" +#include "cs_client_gamestats.h" + +// [tj] We need to forward declare this, since the definition is all inside the implementation file +class CHudHintDisplay; + +//============================================================================= +// HPE_END +//============================================================================= + + +void __MsgFunc_MatchEndConditions( bf_read &msg ); + +class CHudChat; + +ConVar default_fov( "default_fov", "90", FCVAR_CHEAT ); + +IClientMode *g_pClientMode = NULL; + +// This is a temporary entity used to render the player's model while drawing the class selection menu. +CHandle<C_BaseAnimatingOverlay> g_ClassImagePlayer; // player +CHandle<C_BaseAnimating> g_ClassImageWeapon; // weapon + +STUB_WEAPON_CLASS( cycler_weapon, WeaponCycler, C_BaseCombatWeapon ); +STUB_WEAPON_CLASS( weapon_cubemap, WeaponCubemap, C_BaseCombatWeapon ); + +//----------------------------------------------------------------------------- +// HACK: the detail sway convars are archive, and default to 0. Existing CS:S players thus have no detail +// prop sway. We'll force them to DoD's default values for now. What we really need in the long run is +// a system to apply changes to archived convars' defaults to existing players. +extern ConVar cl_detail_max_sway; +extern ConVar cl_detail_avoid_radius; +extern ConVar cl_detail_avoid_force; +extern ConVar cl_detail_avoid_recover_speed; + +//----------------------------------------------------------------------------- +ConVar cl_autobuy( + "cl_autobuy", + "", + FCVAR_USERINFO, + "The order in which autobuy will attempt to purchase items" ); + +//----------------------------------------------------------------------------- +ConVar cl_rebuy( + "cl_rebuy", + "", + FCVAR_USERINFO, + "The order in which rebuy will attempt to repurchase items" ); + +//----------------------------------------------------------------------------- +void SetBuyData( const ConVar &buyVar, const char *filename ) +{ + // if we already have autobuy data, don't bother re-parsing the text file + if ( *buyVar.GetString() ) + return; + + // First, look for a mapcycle file in the cfg directory, which is preferred + char szRecommendedName[ 256 ]; + char szResolvedName[ 256 ]; + V_sprintf_safe( szRecommendedName, "cfg/%s", filename ); + V_strcpy_safe( szResolvedName, szRecommendedName ); + if ( filesystem->FileExists( szResolvedName, "GAME" ) ) + { + Msg( "Loading '%s'.\n", szResolvedName ); + } + else + { + // Check the root + V_strcpy_safe( szResolvedName, filename ); + if ( filesystem->FileExists( szResolvedName, "GAME" ) ) + { + Msg( "Loading '%s' ('%s' was not found.)\n", szResolvedName, szRecommendedName ); + } + else + { + + // Try cfg/xxx_default.txt + V_strcpy_safe( szResolvedName, szRecommendedName ); + char *dotTxt = V_stristr( szResolvedName, ".txt" ); + Assert( dotTxt ); + if ( dotTxt ) + { + V_strcpy( dotTxt, "_default.txt" ); + } + if ( !filesystem->FileExists( szResolvedName, "GAME" ) ) + { + Warning( "Not loading buy data. Neither '%s' nor %s were found.\n", szResolvedName, szRecommendedName ); + return; + } + Msg( "Loading '%s'\n", szResolvedName ); + } + } + + CUtlBuffer buf; + if ( !filesystem->ReadFile( szResolvedName, "GAME", buf ) ) + { + // WAT + Warning( "Failed to load '%s'.\n", szResolvedName ); + return; + } + buf.PutChar('\0'); + + char token[256]; + char buystring[256]; + V_sprintf_safe( buystring, "setinfo %s \"", buyVar.GetName() ); + + const char *pfile = engine->ParseFile( (const char *)buf.Base(), token, sizeof(token) ); + + bool first = true; + + while (pfile != NULL) + { + if (first) + { + first = false; + } + else + { + Q_strncat(buystring, " ", sizeof(buystring), COPY_ALL_CHARACTERS); + } + + Q_strncat(buystring, token, sizeof(buystring), COPY_ALL_CHARACTERS); + + pfile = engine->ParseFile( pfile, token, sizeof(token) ); + } + + Q_strncat(buystring, "\"", sizeof(buystring), COPY_ALL_CHARACTERS); + + engine->ClientCmd(buystring); +} + +void MsgFunc_KillCam(bf_read &msg) +{ + C_CSPlayer *pPlayer = ToCSPlayer( C_BasePlayer::GetLocalPlayer() ); + + if ( !pPlayer ) + return; + + int newMode = msg.ReadByte(); + + if ( newMode != g_nKillCamMode ) + { +#if !defined( NO_ENTITY_PREDICTION ) + if ( g_nKillCamMode == OBS_MODE_NONE ) + { + // kill cam is switch on, turn off prediction + g_bForceCLPredictOff = true; + } + else if ( newMode == OBS_MODE_NONE ) + { + // kill cam is switched off, restore old prediction setting is we switch back to normal mode + g_bForceCLPredictOff = false; + } +#endif + g_nKillCamMode = newMode; + } + + g_nKillCamTarget1 = msg.ReadByte(); + g_nKillCamTarget2 = msg.ReadByte(); +} + +// --------------------------------------------------------------------------------- // +// CCSModeManager. +// --------------------------------------------------------------------------------- // + +class CCSModeManager : public IVModeManager +{ +public: + virtual void Init(); + virtual void SwitchMode( bool commander, bool force ) {} + virtual void LevelInit( const char *newmap ); + virtual void LevelShutdown( void ); + virtual void ActivateMouse( bool isactive ) {} +}; + +static CCSModeManager g_ModeManager; +IVModeManager *modemanager = ( IVModeManager * )&g_ModeManager; + +// --------------------------------------------------------------------------------- // +// CCSModeManager implementation. +// --------------------------------------------------------------------------------- // + +#define SCREEN_FILE "scripts/vgui_screens.txt" + +void CCSModeManager::Init() +{ + g_pClientMode = GetClientModeNormal(); + + PanelMetaClassMgr()->LoadMetaClassDefinitionFile( SCREEN_FILE ); +} + +void CCSModeManager::LevelInit( const char *newmap ) +{ + g_pClientMode->LevelInit( newmap ); + + SetBuyData( cl_autobuy, "autobuy.txt" ); + SetBuyData( cl_rebuy, "rebuy.txt" ); + +#if !defined( NO_ENTITY_PREDICTION ) + if ( g_nKillCamMode > OBS_MODE_NONE ) + { + g_bForceCLPredictOff = false; + } +#endif + + g_nKillCamMode = OBS_MODE_NONE; + g_nKillCamTarget1 = 0; + g_nKillCamTarget2 = 0; + + // HACK: the detail sway convars are archive, and default to 0. Existing CS:S players thus have no detail + // prop sway. We'll force them to DoD's default values for now. + if ( !cl_detail_max_sway.GetFloat() && + !cl_detail_avoid_radius.GetFloat() && + !cl_detail_avoid_force.GetFloat() && + !cl_detail_avoid_recover_speed.GetFloat() ) + { + cl_detail_max_sway.SetValue( "5" ); + cl_detail_avoid_radius.SetValue( "64" ); + cl_detail_avoid_force.SetValue( "0.4" ); + cl_detail_avoid_recover_speed.SetValue( "0.25" ); + } +} + +void CCSModeManager::LevelShutdown( void ) +{ + g_pClientMode->LevelShutdown(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ClientModeCSNormal::ClientModeCSNormal() +{ + HOOK_MESSAGE( MatchEndConditions ); +} + +void ClientModeCSNormal::Init() +{ + BaseClass::Init(); + + ListenForGameEvent( "round_end" ); + ListenForGameEvent( "round_start" ); + ListenForGameEvent( "player_team" ); + ListenForGameEvent( "player_death" ); + ListenForGameEvent( "bomb_planted" ); + ListenForGameEvent( "bomb_exploded" ); + ListenForGameEvent( "bomb_defused" ); + ListenForGameEvent( "hostage_killed" ); + ListenForGameEvent( "hostage_hurt" ); + + usermessages->HookMessage( "KillCam", MsgFunc_KillCam ); + + //============================================================================= + // HPE_BEGIN: + // [tj] Add the shared HUD elements to the render groups responsible for hiding + // conflicting UI + //============================================================================= + CHudElement* hintBox = (CHudElement*)GET_HUDELEMENT( CHudHintDisplay ); + if (hintBox) + { + hintBox->RegisterForRenderGroup("hide_for_scoreboard"); + hintBox->RegisterForRenderGroup("hide_for_round_panel"); + } + + + CHudElement* historyResource = (CHudElement*)GET_HUDELEMENT( CHudHistoryResource ); + if (historyResource) + { + historyResource->RegisterForRenderGroup("hide_for_scoreboard"); + } + //============================================================================= + // HPE_END + //============================================================================= +} + +void ClientModeCSNormal::InitViewport() +{ + BaseClass::InitViewport(); + + m_pViewport = new CounterStrikeViewport(); + m_pViewport->Start( gameuifuncs, gameeventmanager ); +} + + +void ClientModeCSNormal::Update() +{ + BaseClass::Update(); + + // Override the hud's visibility if this is a logo (like E3 demo) map. + if ( CSGameRules() && CSGameRules()->IsLogoMap() ) + m_pViewport->SetVisible( false ); +} + + +/* +void ClientModeCSNormal::UpdateSpectatorMode( void ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer ) + return; + + IMapOverview * overviewmap = m_pViewport->GetMapOverviewInterface(); + + if ( !overviewmap ) + return; + + overviewmap->SetTime( gpGlobals->curtime ); + + int obs_mode = pPlayer->GetObserverMode(); + + if ( obs_mode < OBS_MODE_IN_EYE ) + return; + + Vector worldpos = pPlayer->GetLocalOrigin(); + QAngle angles; engine->GetViewAngles( angles ); + + C_BaseEntity *target = pPlayer->GetObserverTarget(); + + if ( target && (obs_mode == OBS_MODE_IN_EYE || obs_mode == OBS_MODE_CHASE) ) + { + worldpos = target->GetAbsOrigin(); + + if ( obs_mode == OBS_MODE_IN_EYE ) + { + angles = target->GetAbsAngles(); + } + } + + Vector2D mappos = overviewmap->WorldToMap( worldpos ); + + overviewmap->SetCenter( mappos ); + overviewmap->SetAngle( angles.y ); + + for ( int i = 1; i<= MAX_PLAYERS; i++) + { + C_BaseEntity *ent = ClientEntityList().GetEnt( i ); + + if ( !ent || !ent->IsPlayer() ) + continue; + + C_BasePlayer *p = ToBasePlayer( ent ); + + // update position of active players in our PVS + Vector position = p->GetAbsOrigin(); + QAngle angle = p->GetAbsAngles(); + + if ( p->IsDormant() ) + { + // if player is not in PVS, use PlayerResources data + position = g_PR->GetPosition( i ); + angles[1] = g_PR->GetViewAngle( i ); + } + + overviewmap->SetPlayerPositions( i-1, position, angles ); + } +} */ + +//----------------------------------------------------------------------------- +// Purpose: We've received a keypress from the engine. Return 1 if the engine is allowed to handle it. +//----------------------------------------------------------------------------- +int ClientModeCSNormal::KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) +{ + // don't process input in LogoMaps + if( CSGameRules() && CSGameRules()->IsLogoMap() ) + return 1; + + return BaseClass::KeyInput( down, keynum, pszCurrentBinding ); +} + + + +IClientMode *GetClientModeNormal() +{ + static ClientModeCSNormal g_ClientModeNormal; + return &g_ClientModeNormal; +} + + +ClientModeCSNormal* GetClientModeCSNormal() +{ + Assert( dynamic_cast< ClientModeCSNormal* >( GetClientModeNormal() ) ); + + return static_cast< ClientModeCSNormal* >( GetClientModeNormal() ); +} + +float ClientModeCSNormal::GetViewModelFOV( void ) +{ + return 74.0f; +} + +int ClientModeCSNormal::GetDeathMessageStartHeight( void ) +{ + return m_pViewport->GetDeathMessageStartHeight(); +} + +void ClientModeCSNormal::FireGameEvent( IGameEvent *event ) +{ + CBaseHudChat *pHudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + CLocalPlayerFilter filter; + + if ( !pLocalPlayer || !pHudChat ) + return; + + const char *eventname = event->GetName(); + + if ( !eventname || !eventname[0] ) + return; + + if ( Q_strcmp( "round_start", eventname ) == 0 ) + { + // recreate all client side physics props + C_PhysPropClientside::RecreateAll(); + + // remove hostage ragdolls + for ( int i=0; i<g_HostageRagdolls.Count(); ++i ) + { + // double-check that the EHANDLE is still valid + if ( g_HostageRagdolls[i] ) + { + g_HostageRagdolls[i]->Release(); + } + } + g_HostageRagdolls.RemoveAll(); + + // Just tell engine to clear decals + engine->ClientCmd( "r_cleardecals\n" ); + + //stop any looping sounds + enginesound->StopAllSounds( true ); + + Soundscape_OnStopAllSounds(); // Tell the soundscape system. + } + + + else if ( Q_strcmp( "round_end", eventname ) == 0 ) + { + int winningTeam = event->GetInt("winner"); + int reason = event->GetInt("reason"); + + // play endround announcer sound + if ( winningTeam == TEAM_CT ) + { + if ( reason == Bomb_Defused ) + { + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.BombDefused"); + } + else + { + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.CTWin"); + } + } + else if ( winningTeam == TEAM_TERRORIST ) + { + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.TERWin"); + } + else + { + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.RoundDraw"); + } + + //============================================================================= + // HPE_BEGIN: + // [pfreese] Only show centerprint message for game commencing; the rest of + // these messages are handled by the end-of-round panel. + // [Forrest] Show all centerprint messages if the end-of-round panel is disabled. + //============================================================================= + static ConVarRef sv_nowinpanel( "sv_nowinpanel" ); + static ConVarRef cl_nowinpanel( "cl_nowinpanel" ); + if ( reason == Game_Commencing || sv_nowinpanel.GetBool() || cl_nowinpanel.GetBool() ) + { + internalCenterPrint->Print( hudtextmessage->LookupString( event->GetString("message") ) ); + + // we are starting a new round; clear the current match stats + g_CSClientGameStats.ResetMatchStats(); + } + + //============================================================================= + // HPE_END + //============================================================================= + } + + else if ( Q_strcmp( "player_team", eventname ) == 0 ) + { + CBaseHudChat *pHudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); + C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") ); + + if ( !pPlayer ) + return; + + bool bDisconnected = event->GetBool("disconnect"); + + if ( bDisconnected ) + return; + + int iTeam = event->GetInt("team"); + + if ( pPlayer->IsLocalPlayer() ) + { + // that's me + pPlayer->TeamChange( iTeam ); + } + + if ( iTeam == TEAM_SPECTATOR ) + pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_spectators" ), pPlayer->GetPlayerName() ); + else if ( iTeam == TEAM_TERRORIST ) + pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_terrorist" ), pPlayer->GetPlayerName() ); + else if ( iTeam == TEAM_CT ) + pHudChat->Printf( CHAT_FILTER_NONE, hudtextmessage->LookupString( "#Game_join_ct" ), pPlayer->GetPlayerName() ); + } + + else if ( Q_strcmp( "bomb_planted", eventname ) == 0 ) + { + //C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") ); + + // show centerprint message + internalCenterPrint->Print( "#Cstrike_TitlesTXT_Bomb_Planted" ); + + // play sound + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.BombPlanted") ; + } + + else if ( Q_strcmp( "bomb_defused", eventname ) == 0 ) + { + // C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") ); + } + //============================================================================= + // HPE_BEGIN: + // [menglish] Tell the client side bomb that the bomb has exploding here creating the explosion particle effect + //============================================================================= + + else if ( Q_strcmp( "bomb_exploded", eventname ) == 0 ) + { + if ( g_PlantedC4s.Count() > 0 ) + { + // bomb is planted + C_PlantedC4 *pC4 = g_PlantedC4s[0]; + pC4->Explode(); + } + } + + //============================================================================= + // HPE_END + //============================================================================= + + else if ( Q_strcmp( "hostage_killed", eventname ) == 0 ) + { + // play sound for spectators and CTs + if ( pLocalPlayer->IsObserver() || (pLocalPlayer->GetTeamNumber() == TEAM_CT) ) + { + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Event.HostageKilled") ; + } + + // Show warning to killer + if ( pLocalPlayer->GetUserID() == event->GetInt("userid") ) + { + internalCenterPrint->Print( "#Cstrike_TitlesTXT_Killed_Hostage" ); + } + } + + else if ( Q_strcmp( "hostage_hurt", eventname ) == 0 ) + { + // Let the loacl player know he harmed a hostage + if ( pLocalPlayer->GetUserID() == event->GetInt("userid") ) + { + internalCenterPrint->Print( "#Cstrike_TitlesTXT_Injured_Hostage" ); + } + } + + else if ( Q_strcmp( "player_death", eventname ) == 0 ) + { + C_BasePlayer *pPlayer = USERID2PLAYER( event->GetInt("userid") ); + + C_CSPlayer* csPlayer = ToCSPlayer(pPlayer); + if (csPlayer) + { + csPlayer->ClearSoundEvents(); + } + + if ( pPlayer == C_BasePlayer::GetLocalPlayer() ) + { + // we just died, hide any buy panels + gViewPortInterface->ShowPanel( PANEL_BUY, false ); + gViewPortInterface->ShowPanel( PANEL_BUY_CT, false ); + gViewPortInterface->ShowPanel( PANEL_BUY_TER, false ); + gViewPortInterface->ShowPanel( PANEL_BUY_EQUIP_CT, false ); + gViewPortInterface->ShowPanel( PANEL_BUY_EQUIP_TER, false ); + } + } + + else if ( Q_strcmp( "player_changename", eventname ) == 0 ) + { + return; // server sends a colorized text string for this + } + + //============================================================================= + // HPE_BEGIN: + // [tj] We handle this here instead of in the base class + // The reason is that we don't use string tables to localize. + // Instead, we use the steam localization mechanism. + // + // [dwenger] Remove dependency on stats system for name of achievement. + //============================================================================= + + else if ( Q_strcmp( "achievement_earned", eventname ) == 0 ) + { + CBaseHudChat *hudChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); + int iPlayerIndex = event->GetInt( "player" ); + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex ); + int iAchievement = event->GetInt( "achievement" ); + + if ( !hudChat || !pPlayer ) + return; + + + CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr *>( engine->GetAchievementMgr() ); + if ( !pAchievementMgr ) + return; + + IAchievement *pAchievement = pAchievementMgr->GetAchievementByID( iAchievement ); + if ( pAchievement ) + { + if ( !pPlayer->IsDormant() && pPlayer->ShouldAnnounceAchievement() ) + { + pPlayer->SetNextAchievementAnnounceTime( gpGlobals->curtime + ACHIEVEMENT_ANNOUNCEMENT_MIN_TIME ); + + //Do something for the player - Actually we should probably do this client-side when the achievement is first earned. + if (pPlayer->IsLocalPlayer()) + { + } + pPlayer->OnAchievementAchieved( iAchievement ); + } + + if ( g_PR ) + { + wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; + g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iPlayerIndex ), wszPlayerName, sizeof( wszPlayerName ) ); + + wchar_t achievementName[1024]; + const wchar_t* constAchievementName = &achievementName[0]; + + constAchievementName = ACHIEVEMENT_LOCALIZED_NAME( pAchievement ); + + if (constAchievementName) + { + wchar_t wszLocalizedString[128]; + g_pVGuiLocalize->ConstructString( wszLocalizedString, sizeof( wszLocalizedString ), g_pVGuiLocalize->Find( "#Achievement_Earned" ), 2, wszPlayerName, constAchievementName/*wszAchievementString*/ ); + + char szLocalized[128]; + g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedString, szLocalized, sizeof( szLocalized ) ); + + hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_ACHIEVEMENT, "%s", szLocalized ); + + /* + if (pPlayer->IsLocalPlayer()) + { + char achievementDescription[1024]; + const char* constAchievementDescription = &achievementDescription[0]; + + constAchievementDescription = pUserStats->GetAchievementDisplayAttribute( pAchievement->GetName(), "desc" ); + hudChat->ChatPrintf( iPlayerIndex, CHAT_FILTER_ACHIEVEMENT, "(%s)", constAchievementDescription ); + } + */ + } + } + } + } + + //============================================================================= + // HPE_END + //============================================================================= + + + else + { + BaseClass::FireGameEvent( event ); + } +} + + +void RemoveClassImageEntity() +{ + C_BaseAnimating *pEnt = g_ClassImagePlayer.Get(); + if ( pEnt ) + { + pEnt->Remove(); + g_ClassImagePlayer = NULL; + } + + pEnt = g_ClassImageWeapon.Get(); + if ( pEnt ) + { + pEnt->Remove(); + g_ClassImagePlayer = NULL; + } +} + + +bool ShouldRecreateClassImageEntity( C_BaseAnimating *pEnt, const char *pNewModelName ) +{ + if ( !pNewModelName || !pNewModelName[0] ) + return false; + + if ( !pEnt ) + return true; + + const model_t *pModel = pEnt->GetModel(); + + if ( !pModel ) + return true; + + const char *pName = modelinfo->GetModelName( pModel ); + if ( !pName ) + return true; + + // reload only if names are different + const char *pNameNoPath = V_UnqualifiedFileName( pName ); + const char *pNewModelNameNoPath = V_UnqualifiedFileName( pNewModelName ); + return( Q_stricmp( pNameNoPath, pNewModelNameNoPath ) != 0 ); +} + + +void UpdateClassImageEntity( + const char *pModelName, + int x, int y, int width, int height ) +{ + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pLocalPlayer ) + return; + + MDLCACHE_CRITICAL_SECTION(); + + const char *pWeaponName = "models/weapons/w_rif_ak47.mdl"; + const char *pWeaponSequence = "Walk_Upper_AK"; + + int i; + for ( i=0; i<CTPlayerModels.Count(); ++i ) + { + if ( Q_strcasecmp( pModelName, CTPlayerModels[i] ) == 0 ) + { + // give CTs a M4 + pWeaponName = "models/weapons/w_rif_m4a1.mdl"; + pWeaponSequence = "Walk_Upper_M4"; + break; + } + } + + if ( pLocalPlayer->IsAlive() && pLocalPlayer->GetActiveWeapon() ) + { + C_WeaponCSBase *weapon = dynamic_cast< C_WeaponCSBase * >(pLocalPlayer->GetActiveWeapon()); + if ( weapon ) + { + pWeaponName = weapon->GetWorldModel(); + pWeaponSequence = VarArgs("Walk_Upper_%s", weapon->GetCSWpnData().m_szAnimExtension); + } + } + + C_BaseAnimatingOverlay *pPlayerModel = g_ClassImagePlayer.Get(); + + // Does the entity even exist yet? + bool recreatePlayer = ShouldRecreateClassImageEntity( pPlayerModel, pModelName ); + if ( recreatePlayer ) + { + if ( pPlayerModel ) + pPlayerModel->Remove(); + + pPlayerModel = new C_BaseAnimatingOverlay; + pPlayerModel->InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY ); + pPlayerModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally + + // let player walk ahead + pPlayerModel->SetSequence( pPlayerModel->LookupSequence( "walk_lower" ) ); + pPlayerModel->SetPoseParameter( "move_yaw", 0.0f ); // move_yaw + pPlayerModel->SetPoseParameter( "body_pitch", 10.0f ); // body_pitch, look down a bit + pPlayerModel->SetPoseParameter( "body_yaw", 0.0f ); // body_yaw + pPlayerModel->SetPoseParameter( "move_y", 0.0f ); // move_y + pPlayerModel->SetPoseParameter( "move_x", 1.0f ); // move_x, walk forward + pPlayerModel->m_flAnimTime = gpGlobals->curtime; + + g_ClassImagePlayer = pPlayerModel; + } + + C_BaseAnimating *pWeaponModel = g_ClassImageWeapon.Get(); + + // Does the entity even exist yet? + if ( recreatePlayer || ShouldRecreateClassImageEntity( pWeaponModel, pWeaponName ) ) + { + if ( pWeaponModel ) + pWeaponModel->Remove(); + + pWeaponModel = new C_BaseAnimating; + pWeaponModel->InitializeAsClientEntity( pWeaponName, RENDER_GROUP_OPAQUE_ENTITY ); + pWeaponModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally + pWeaponModel->FollowEntity( pPlayerModel ); // attach to player model + pWeaponModel->m_flAnimTime = gpGlobals->curtime; + g_ClassImageWeapon = pWeaponModel; + } + + Vector origin = pLocalPlayer->EyePosition(); + Vector lightOrigin = origin; + + // find a spot inside the world for the dlight's origin, or it won't illuminate the model + Vector testPos( origin.x - 100, origin.y, origin.z + 100 ); + trace_t tr; + UTIL_TraceLine( origin, testPos, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction == 1.0f ) + { + lightOrigin = tr.endpos; + } + else + { + // Now move the model away so we get the correct illumination + lightOrigin = tr.endpos + Vector( 1, 0, -1 ); // pull out from the solid + Vector start = lightOrigin; + Vector end = lightOrigin + Vector( 100, 0, -100 ); + UTIL_TraceLine( start, end, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr ); + origin = tr.endpos; + } + + // move player model in front of our view + pPlayerModel->SetAbsOrigin( origin ); + pPlayerModel->SetAbsAngles( QAngle( 0, 210, 0 ) ); + + // wacky hacky, set upper body animation + pPlayerModel->m_SequenceTransitioner.CheckForSequenceChange( + pPlayerModel->GetModelPtr(), + pPlayerModel->LookupSequence( "walk_lower" ), + false, + true + ); + pPlayerModel->m_SequenceTransitioner.UpdateCurrent( + pPlayerModel->GetModelPtr(), + pPlayerModel->LookupSequence( "walk_lower" ), + pPlayerModel->GetCycle(), + pPlayerModel->GetPlaybackRate(), + gpGlobals->realtime + ); + + // Now, blend the lower and upper (aim) anims together + pPlayerModel->SetNumAnimOverlays( 2 ); + int numOverlays = pPlayerModel->GetNumAnimOverlays(); + for ( i=0; i < numOverlays; ++i ) + { + C_AnimationLayer *layer = pPlayerModel->GetAnimOverlay( i ); + + layer->m_flCycle = pPlayerModel->GetCycle(); + if ( i ) + layer->m_nSequence = pPlayerModel->LookupSequence( pWeaponSequence ); + else + layer->m_nSequence = pPlayerModel->LookupSequence( "walk_lower" ); + + layer->m_flPlaybackRate = 1.0; + layer->m_flWeight = 1.0f; + layer->SetOrder( i ); + } + + pPlayerModel->FrameAdvance( gpGlobals->frametime ); + + // Now draw it. + CViewSetup view; + view.x = x; + view.y = y; + view.width = width; + view.height = height; + + view.m_bOrtho = false; + view.fov = 54; + + view.origin = origin + Vector( -110, -5, -5 ); + + Vector vMins, vMaxs; + pPlayerModel->C_BaseAnimating::GetRenderBounds( vMins, vMaxs ); + view.origin.z += ( vMins.z + vMaxs.z ) * 0.55f; + + view.angles.Init(); + view.zNear = VIEW_NEARZ; + view.zFar = 1000; + + Frustum dummyFrustum; + render->Push3DView( view, 0, NULL, dummyFrustum ); + + //============================================================================= + // HPE_BEGIN: + // [mhansen] We don't want to light the model in the world. We want it to + // always be lit normal like even if you are standing in a dark (or green) area + // in the world. + //============================================================================= + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->SetLightingOrigin( vec3_origin ); + pRenderContext->SetAmbientLight( 0.4, 0.4, 0.4 ); + + static Vector white[6] = + { + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + }; + + g_pStudioRender->SetAmbientLightColors( white ); + g_pStudioRender->SetLocalLights( 0, NULL ); + + modelrender->SuppressEngineLighting( true ); + float color[3] = { 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( color ); + render->SetBlend( 1.0f ); + pPlayerModel->DrawModel( STUDIO_RENDER ); + + if ( pWeaponModel ) + { + pWeaponModel->DrawModel( STUDIO_RENDER ); + } + + modelrender->SuppressEngineLighting( false ); + //============================================================================= + // HPE_END + //============================================================================= + + render->PopView( dummyFrustum ); +} + + +bool WillPanelBeVisible( vgui::VPANEL hPanel ) +{ + while ( hPanel ) + { + if ( !vgui::ipanel()->IsVisible( hPanel ) ) + return false; + + hPanel = vgui::ipanel()->GetParent( hPanel ); + } + return true; +} + + + +void ClientModeCSNormal::PostRenderVGui() +{ + // If the team menu is up, then we will render the model of the character that is currently selected. + for ( int i=0; i < g_ClassImagePanels.Count(); i++ ) + { + CCSClassImagePanel *pPanel = g_ClassImagePanels[i]; + if ( WillPanelBeVisible( pPanel->GetVPanel() ) ) + { + // Ok, we have a visible class image panel. + int x, y, w, h; + pPanel->GetBounds( x, y, w, h ); + pPanel->LocalToScreen( x, y ); + + // Allow for the border. + x += 3; + y += 5; + w -= 2; + h -= 10; + + UpdateClassImageEntity( g_ClassImagePanels[i]->m_ModelName, x, y, w, h ); + return; + } + } +} + +bool ClientModeCSNormal::ShouldDrawViewModel( void ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if( pPlayer && pPlayer->GetFOV() != CSGameRules()->DefaultFOV() ) + { + CWeaponCSBase *pWpn = pPlayer->GetActiveCSWeapon(); + + if( pWpn && pWpn->HideViewModelWhenZoomed() ) + { + return false; + } + } + + return BaseClass::ShouldDrawViewModel(); +} + + +bool ClientModeCSNormal::CanRecordDemo( char *errorMsg, int length ) const +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + if ( !player ) + { + return true; + } + + if ( !player->IsAlive() ) + { + return true; + } + + // don't start recording while flashed, as it would remove the flash + if ( player->m_flFlashBangTime > gpGlobals->curtime ) + { + Q_strncpy( errorMsg, "Cannot record demos while blind.", length ); + return false; + } + + // don't start recording while smoke grenades are spewing smoke, as the existing smoke would be destroyed + C_BaseEntityIterator it; + C_BaseEntity *ent; + while ( (ent = it.Next()) != NULL ) + { + if ( Q_strcmp( ent->GetClassname(), "class C_ParticleSmokeGrenade" ) == 0 ) + { + Q_strncpy( errorMsg, "Cannot record demos while a smoke grenade is active.", length ); + return false; + } + } + + return true; +} + +//============================================================================= +// HPE_BEGIN: +// [menglish] Save server information shown to the client in a persistent place +//============================================================================= + +void ClientModeCSNormal::SetServerName(wchar_t* name) +{ + V_wcsncpy(m_pServerName, name, sizeof( m_pServerName ) ); +} + +void ClientModeCSNormal::SetMapName(wchar_t* name) +{ + V_wcsncpy(m_pMapName, name, sizeof( m_pMapName ) ); +} + +//============================================================================= +// HPE_END +//============================================================================= + +// Receive the PlayerIgnited user message and send out a clientside event for achievements to hook. +void __MsgFunc_MatchEndConditions( bf_read &msg ) +{ + int iFragLimit = (int) msg.ReadLong(); + int iMaxRounds = (int) msg.ReadLong(); + int iWinRounds = (int) msg.ReadLong(); + int iTimeLimit = (int) msg.ReadLong(); + + IGameEvent *event = gameeventmanager->CreateEvent( "match_end_conditions" ); + if ( event ) + { + event->SetInt( "frags", iFragLimit ); + event->SetInt( "max_rounds", iMaxRounds ); + event->SetInt( "win_rounds", iWinRounds ); + event->SetInt( "time", iTimeLimit ); + gameeventmanager->FireEventClientSide( event ); + } +} diff --git a/game/client/cstrike/clientmode_csnormal.h b/game/client/cstrike/clientmode_csnormal.h new file mode 100644 index 0000000..38e6058 --- /dev/null +++ b/game/client/cstrike/clientmode_csnormal.h @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CS_CLIENTMODE_H +#define CS_CLIENTMODE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "clientmode_shared.h" +#include "counterstrikeviewport.h" + +class ClientModeCSNormal : public ClientModeShared +{ +DECLARE_CLASS( ClientModeCSNormal, ClientModeShared ); + +private: + +// IClientMode overrides. +public: + + ClientModeCSNormal(); + + virtual void Init(); + virtual void InitViewport(); + virtual void Update(); + + virtual int KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ); + + virtual float GetViewModelFOV( void ); + + int GetDeathMessageStartHeight( void ); + + virtual void FireGameEvent( IGameEvent *event ); + virtual void PostRenderVGui(); + + virtual bool ShouldDrawViewModel( void ); + + virtual bool CanRecordDemo( char *errorMsg, int length ) const; + + //============================================================================= + // HPE_BEGIN: + // [menglish] Save server information shown to the client in a persistent place + //============================================================================= + + virtual wchar_t* GetServerName() { return m_pServerName; } + virtual void SetServerName(wchar_t* name); + virtual wchar_t* GetMapName() { return m_pMapName; } + virtual void SetMapName(wchar_t* name); + +private: + wchar_t m_pServerName[256]; + wchar_t m_pMapName[256]; + + //============================================================================= + // HPE_END + //============================================================================= +}; + + +extern IClientMode *GetClientModeNormal(); +extern ClientModeCSNormal* GetClientModeCSNormal(); + + +#endif // CS_CLIENTMODE_H diff --git a/game/client/cstrike/cs_client_gamestats.cpp b/game/client/cstrike/cs_client_gamestats.cpp new file mode 100644 index 0000000..8a8ea54 --- /dev/null +++ b/game/client/cstrike/cs_client_gamestats.cpp @@ -0,0 +1,789 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +//------------------------------------------------------------- +// File: cs_client_gamestats.cpp +// Desc: Manages client side stat storage, accumulation, and access +// Author: Peter Freese <[email protected]> +// Date: 2009/09/11 +// Copyright: � 2009 Hidden Path Entertainment +// +// Keywords: +//------------------------------------------------------------- + +#include "cbase.h" +#include "cs_client_gamestats.h" +#include "achievementmgr.h" +#include "engine/imatchmaking.h" +#include "ipresence.h" +#include "usermessages.h" +#include "c_cs_player.h" +#include "achievements_cs.h" +#include "vgui/ILocalize.h" +#include "c_team.h" +#include "../shared/steamworks_gamestats.h" + +CCSClientGameStats g_CSClientGameStats; + +void MsgFunc_PlayerStatsUpdate( bf_read &msg ) +{ + g_CSClientGameStats.MsgFunc_PlayerStatsUpdate(msg); +} + +void MsgFunc_MatchStatsUpdate( bf_read &msg ) +{ + g_CSClientGameStats.MsgFunc_MatchStatsUpdate(msg); +} + +CCSClientGameStats::StatContainerList_t* CCSClientGameStats::s_StatLists = new CCSClientGameStats::StatContainerList_t(); +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSClientGameStats::CCSClientGameStats() +{ + m_bSteamStatsDownload = false; +} + +//----------------------------------------------------------------------------- +// Purpose: called at init time after all systems are init'd. We have to +// do this in PostInit because the Steam app ID is not available earlier +//----------------------------------------------------------------------------- +void CCSClientGameStats::PostInit() +{ + // listen for events + ListenForGameEvent( "player_stats_updated" ); + ListenForGameEvent( "user_data_downloaded" ); + ListenForGameEvent( "round_end" ); + ListenForGameEvent( "round_start" ); + + + usermessages->HookMessage( "PlayerStatsUpdate", ::MsgFunc_PlayerStatsUpdate ); + usermessages->HookMessage( "MatchStatsUpdate", ::MsgFunc_MatchStatsUpdate ); + + GetSteamWorksSGameStatsUploader().StartSession(); + + m_RoundEndReason = Invalid_Round_End_Reason; +} + +//----------------------------------------------------------------------------- +// Purpose: called at level shutdown +//----------------------------------------------------------------------------- +void CCSClientGameStats::LevelShutdownPreEntity() +{ + // This is a good opportunity to update our last match stats + UpdateLastMatchStats(); + + // upload user stats to Steam on every map change + UpdateSteamStats(); +} + +//----------------------------------------------------------------------------- +// Purpose: called at app shutdown +//----------------------------------------------------------------------------- +void CCSClientGameStats::Shutdown() +{ + GetSteamWorksSGameStatsUploader().EndSession(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSClientGameStats::LevelShutdownPreClearSteamAPIContext( void ) +{ + UploadRoundData(); +} + +//----------------------------------------------------------------------------- +// Purpose: called when the stats have changed in-game +//----------------------------------------------------------------------------- +void CCSClientGameStats::FireGameEvent( IGameEvent *event ) +{ + const char *pEventName = event->GetName(); + if ( 0 == Q_strcmp( pEventName, "player_stats_updated" ) ) + { + UpdateSteamStats(); + } + else if ( 0 == Q_strcmp( pEventName, "user_data_downloaded" ) ) + { + RetrieveSteamStats(); + } + else if ( Q_strcmp( pEventName, "round_end" ) == 0 ) + { + // Store off the reason the round ended. Used with the OGS data. + m_RoundEndReason = event->GetInt( "reason", Invalid_Round_End_Reason ); + + // update player count for last match stats + int iCurrentPlayerCount = 0; + if ( GetGlobalTeam(TEAM_CT) != NULL ) + iCurrentPlayerCount += GetGlobalTeam(TEAM_CT)->Get_Number_Players(); + if ( GetGlobalTeam(TEAM_TERRORIST) != NULL ) + iCurrentPlayerCount += GetGlobalTeam(TEAM_TERRORIST)->Get_Number_Players(); + + m_matchMaxPlayerCount = MAX(m_matchMaxPlayerCount, iCurrentPlayerCount); + } + + // The user stats for a round get sent piece meal, so we'll wait until a new round starts + // before sending the previous round stats. + else if ( Q_strcmp( pEventName, "round_start" ) == 0 && m_roundStats[CSSTAT_PLAYTIME] > 0 ) + { + SRoundData *pRoundStatData = new SRoundData( &m_roundStats); + C_CSPlayer *pPlayer = ToCSPlayer( C_BasePlayer::GetLocalPlayer() ); + if ( pPlayer ) + { + // Our current money + what we spent is what we started with at the beginning of round + pRoundStatData->nStartingMoney = pPlayer->GetAccount() + m_roundStats[CSSTAT_MONEY_SPENT]; + } + m_RoundStatData.AddToTail( pRoundStatData ); + UploadRoundData(); + m_roundStats.Reset(); + } +} + +void CCSClientGameStats::RetrieveSteamStats() +{ + Assert( steamapicontext->SteamUserStats() ); + if ( !steamapicontext->SteamUserStats() ) + return; + + // we shouldn't be downloading stats more than once + Assert(m_bSteamStatsDownload == false); + if (m_bSteamStatsDownload) + return; + + int nStatFailCount = 0; + for ( int i = 0; i < CSSTAT_MAX; ++i ) + { + if ( CSStatProperty_Table[i].szSteamName == NULL ) + continue; + + int iData; + if ( steamapicontext->SteamUserStats()->GetStat( CSStatProperty_Table[i].szSteamName, &iData ) ) + { + m_lifetimeStats[CSStatProperty_Table[i].statId] = iData; + } + else + { + ++nStatFailCount; + } + } + + if ( nStatFailCount > 0 ) + { + Msg("RetrieveSteamStats: failed to get %i stats\n", nStatFailCount); + return; + } + + IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" ); + if ( event ) + { + gameeventmanager->FireEventClientSide( event ); + } + + m_bSteamStatsDownload = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Uploads stats for current Steam user to Steam +//----------------------------------------------------------------------------- +void CCSClientGameStats::UpdateSteamStats() +{ + // only upload if Steam is running + if ( !steamapicontext->SteamUserStats() ) + return; + + CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr *>( engine->GetAchievementMgr() ); + Assert(pAchievementMgr != NULL); + if (!pAchievementMgr) + return; + + // don't upload any stats if we haven't successfully download stats yet + if ( !m_bSteamStatsDownload ) + { + // try to periodically download stats from Steam if we haven't gotten them yet + static float fLastStatsRetrieveTime = 0.0f; + const float kRetrieveInterval = 30.0f; + if ( gpGlobals->curtime > fLastStatsRetrieveTime + kRetrieveInterval ) + { + pAchievementMgr->DownloadUserData(); + fLastStatsRetrieveTime = gpGlobals->curtime; + } + + return; + } + + for ( int i = 0; i < CSSTAT_MAX; ++i ) + { + if ( CSStatProperty_Table[i].szSteamName == NULL ) + continue; + + // set the stats locally in Steam client + steamapicontext->SteamUserStats()->SetStat( CSStatProperty_Table[i].szSteamName, m_lifetimeStats[CSStatProperty_Table[i].statId]); + } + + // let the achievement manager know the stats have changed + pAchievementMgr->SetDirty(true); +} + +CON_COMMAND_F( stats_reset, "Resets all player stats", FCVAR_CLIENTDLL ) +{ + g_CSClientGameStats.ResetAllStats(); +} + +CON_COMMAND_F( stats_dump, "Dumps all player stats", FCVAR_DEVELOPMENTONLY ) +{ + Msg( "Accumulated stats on Steam\n"); + + const StatsCollection_t& accumulatedStats = g_CSClientGameStats.GetLifetimeStats(); + + for ( int i = 0; i < CSSTAT_MAX; ++i ) + { + if ( CSStatProperty_Table[i].szSteamName == NULL ) + continue; + + Msg( "%42s: %i\n", CSStatProperty_Table[i].szSteamName, accumulatedStats[CSStatProperty_Table[i].statId]); + } +} + +#if defined(_DEBUG) +CON_COMMAND_F( stats_preload, "Load stats with data ripe for getting achievmenets", FCVAR_DEVELOPMENTONLY ) +{ + struct DataSet + { + CSStatType_t statId; + int value; + }; + + DataSet statData[] = + { + { CSSTAT_KILLS, 9999}, + { CSSTAT_ROUNDS_WON, 4999}, + { CSSTAT_PISTOLROUNDS_WON, 249}, + { CSSTAT_MONEY_EARNED, 49999999}, + { CSSTAT_DAMAGE, 999999}, + { CSSTAT_KILLS_DEAGLE, 199}, + { CSSTAT_KILLS_USP, 199}, + { CSSTAT_KILLS_GLOCK, 199}, + { CSSTAT_KILLS_P228, 199}, + { CSSTAT_KILLS_ELITE, 99}, + { CSSTAT_KILLS_FIVESEVEN, 99}, + { CSSTAT_KILLS_AWP, 999}, + { CSSTAT_KILLS_AK47, 999}, + { CSSTAT_KILLS_M4A1, 999}, + { CSSTAT_KILLS_AUG, 499}, + { CSSTAT_KILLS_SG552, 499}, + { CSSTAT_KILLS_SG550, 499}, + { CSSTAT_KILLS_GALIL, 499}, + { CSSTAT_KILLS_FAMAS, 499}, + { CSSTAT_KILLS_SCOUT, 999}, + { CSSTAT_KILLS_G3SG1, 499}, + { CSSTAT_KILLS_P90, 999}, + { CSSTAT_KILLS_MP5NAVY, 999}, + { CSSTAT_KILLS_TMP, 499}, + { CSSTAT_KILLS_MAC10, 499}, + { CSSTAT_KILLS_UMP45, 999}, + { CSSTAT_KILLS_M3, 199}, + { CSSTAT_KILLS_XM1014, 199}, + { CSSTAT_KILLS_M249, 499}, + { CSSTAT_KILLS_KNIFE, 99}, + { CSSTAT_KILLS_HEGRENADE, 499}, + { CSSTAT_KILLS_HEADSHOT, 249}, + { CSSTAT_KILLS_ENEMY_WEAPON, 99}, + { CSSTAT_KILLS_ENEMY_BLINDED, 24}, + { CSSTAT_NUM_BOMBS_DEFUSED, 99}, + { CSSTAT_NUM_BOMBS_PLANTED, 99}, + { CSSTAT_NUM_HOSTAGES_RESCUED, 499}, + { CSSTAT_KILLS_KNIFE_FIGHT, 99}, + { CSSTAT_DECAL_SPRAYS, 99}, + { CSSTAT_NIGHTVISION_DAMAGE, 4999}, + { CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, 99}, + { CSSTAT_MAP_WINS_CS_ASSAULT, 99}, + { CSSTAT_MAP_WINS_CS_COMPOUND, 99}, + { CSSTAT_MAP_WINS_CS_HAVANA, 99}, + { CSSTAT_MAP_WINS_CS_ITALY, 99}, + { CSSTAT_MAP_WINS_CS_MILITIA, 99}, + { CSSTAT_MAP_WINS_CS_OFFICE, 99}, + { CSSTAT_MAP_WINS_DE_AZTEC, 99}, + { CSSTAT_MAP_WINS_DE_CBBLE, 99}, + { CSSTAT_MAP_WINS_DE_CHATEAU, 99}, + { CSSTAT_MAP_WINS_DE_DUST2, 99}, + { CSSTAT_MAP_WINS_DE_DUST, 99}, + { CSSTAT_MAP_WINS_DE_INFERNO, 99}, + { CSSTAT_MAP_WINS_DE_NUKE, 99}, + { CSSTAT_MAP_WINS_DE_PIRANESI, 99}, + { CSSTAT_MAP_WINS_DE_PORT, 99}, + { CSSTAT_MAP_WINS_DE_PRODIGY, 99}, + { CSSTAT_MAP_WINS_DE_TIDES, 99}, + { CSSTAT_MAP_WINS_DE_TRAIN, 99}, + { CSSTAT_WEAPONS_DONATED, 99}, + { CSSTAT_DOMINATIONS, 9}, + { CSSTAT_DOMINATION_OVERKILLS, 99}, + { CSSTAT_REVENGES, 19}, + }; + + StatsCollection_t& lifetimeStats = const_cast<StatsCollection_t&>(g_CSClientGameStats.GetLifetimeStats()); + + for ( int i = 0; i < ARRAYSIZE(statData); ++i ) + { + CSStatType_t statId = statData[i].statId; + lifetimeStats[statId] = statData[i].value; + } + + IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" ); + if ( event ) + { + gameeventmanager->FireEventClientSide( event ); + } +} +#endif + +#if defined(_DEBUG) +CON_COMMAND_F( stats_corrupt, "Load stats with corrupt values", FCVAR_DEVELOPMENTONLY ) +{ + struct DataSet + { + CSStatType_t statId; + int value; + }; + + DataSet badData[] = + { + { CSSTAT_SHOTS_HIT, 0x40000089 }, + { CSSTAT_SHOTS_FIRED, 0x400002BE }, + { CSSTAT_KILLS, 0x40000021 }, + { CSSTAT_DEATHS, 0x00000056 }, + { CSSTAT_DAMAGE, 0x00000FE3 }, + { CSSTAT_NUM_BOMBS_PLANTED, 0x00000004 }, + { CSSTAT_NUM_BOMBS_DEFUSED, 0x00000000 }, + { CSSTAT_PLAYTIME, 0x40000F46 }, + { CSSTAT_ROUNDS_WON, 0x40000028 }, + { CSSTAT_ROUNDS_PLAYED, 0x40001019 }, + { CSSTAT_PISTOLROUNDS_WON, 0x00000001 }, + { CSSTAT_MONEY_EARNED, 0x00021E94 }, + { CSSTAT_KILLS_DEAGLE, 0x00000009 }, + { CSSTAT_KILLS_USP, 0x00000000 }, + { CSSTAT_KILLS_GLOCK, 0x00000002 }, + { CSSTAT_KILLS_P228, 0x00000000 }, + { CSSTAT_KILLS_ELITE, 0x00000000 }, + { CSSTAT_KILLS_FIVESEVEN, 0x00000000 }, + { CSSTAT_KILLS_AWP, 0x00000000 }, + { CSSTAT_KILLS_AK47, 0x00000001 }, + { CSSTAT_KILLS_M4A1, 0x00000000 }, + { CSSTAT_KILLS_AUG, 0x00000000 }, + { CSSTAT_KILLS_SG552, 0x00000000 }, + { CSSTAT_KILLS_SG550, 0x00000000 }, + { CSSTAT_KILLS_GALIL, 0x00000000 }, + { CSSTAT_KILLS_FAMAS, 0x00000001 }, + { CSSTAT_KILLS_SCOUT, 0x00000000 }, + { CSSTAT_KILLS_G3SG1, 0x00000000 }, + { CSSTAT_KILLS_P90, 0x00000001 }, + { CSSTAT_KILLS_MP5NAVY, 0x00000000 }, + { CSSTAT_KILLS_TMP, 0x00000002 }, + { CSSTAT_KILLS_MAC10, 0x00000000 }, + { CSSTAT_KILLS_UMP45, 0x00000001 }, + { CSSTAT_KILLS_M3, 0x00000000 }, + { CSSTAT_KILLS_XM1014, 0x0000000A }, + { CSSTAT_KILLS_M249, 0x00000000 }, + { CSSTAT_KILLS_KNIFE, 0x00000000 }, + { CSSTAT_KILLS_HEGRENADE, 0x00000000 }, + { CSSTAT_SHOTS_DEAGLE, 0x0000004C }, + { CSSTAT_SHOTS_USP, 0x00000001 }, + { CSSTAT_SHOTS_GLOCK, 0x00000017 }, + { CSSTAT_SHOTS_P228, 0x00000000 }, + { CSSTAT_SHOTS_ELITE, 0x00000000 }, + { CSSTAT_SHOTS_FIVESEVEN, 0x00000000 }, + { CSSTAT_SHOTS_AWP, 0x00000000 }, + { CSSTAT_SHOTS_AK47, 0x0000000E }, + { CSSTAT_SHOTS_M4A1, 0x00000000 }, + { CSSTAT_SHOTS_AUG, 0x00000000 }, + { CSSTAT_SHOTS_SG552, 0x00000000 }, + { CSSTAT_SHOTS_SG550, 0x00000008 }, + { CSSTAT_SHOTS_GALIL, 0x00000000 }, + { CSSTAT_SHOTS_FAMAS, 0x00000010 }, + { CSSTAT_SHOTS_SCOUT, 0x00000000 }, + { CSSTAT_SHOTS_G3SG1, 0x00000000 }, + { CSSTAT_SHOTS_P90, 0x0000007F }, + { CSSTAT_SHOTS_MP5NAVY, 0x00000000 }, + { CSSTAT_SHOTS_TMP, 0x00000010 }, + { CSSTAT_SHOTS_MAC10, 0x00000000 }, + { CSSTAT_SHOTS_UMP45, 0x00000015 }, + { CSSTAT_SHOTS_M3, 0x00000009 }, + { CSSTAT_SHOTS_XM1014, 0x0000024C }, + { CSSTAT_SHOTS_M249, 0x00000000 }, + { CSSTAT_HITS_DEAGLE, 0x00000019 }, + { CSSTAT_HITS_USP, 0x00000000 }, + { CSSTAT_HITS_GLOCK, 0x0000000A }, + { CSSTAT_HITS_P228, 0x00000000 }, + { CSSTAT_HITS_ELITE, 0x00000000 }, + { CSSTAT_HITS_FIVESEVEN, 0x00000000 }, + { CSSTAT_HITS_AWP, 0x00000000 }, + { CSSTAT_HITS_AK47, 0x00000003 }, + { CSSTAT_HITS_M4A1, 0x00000000 }, + { CSSTAT_HITS_AUG, 0x00000000 }, + { CSSTAT_HITS_SG552, 0x00000000 }, + { CSSTAT_HITS_SG550, 0x00000001 }, + { CSSTAT_HITS_GALIL, 0x00000000 }, + { CSSTAT_HITS_FAMAS, 0x00000007 }, + { CSSTAT_HITS_SCOUT, 0x00000000 }, + { CSSTAT_HITS_G3SG1, 0x00000000 }, + { CSSTAT_HITS_P90, 0x0000000D }, + { CSSTAT_HITS_MP5NAVY, 0x00000000 }, + { CSSTAT_HITS_TMP, 0x00000006 }, + { CSSTAT_HITS_MAC10, 0x00000000 }, + { CSSTAT_HITS_UMP45, 0x00000006 }, + { CSSTAT_HITS_M3, 0x00000000 }, + { CSSTAT_HITS_XM1014, 0x0000004C }, + { CSSTAT_HITS_M249, 0x00000000 }, + { CSSTAT_KILLS_HEADSHOT, 0x00000013 }, + { CSSTAT_KILLS_ENEMY_BLINDED, 0x00000002 }, + { CSSTAT_KILLS_ENEMY_WEAPON, 0x00000002 }, + { CSSTAT_KILLS_KNIFE_FIGHT, 0x00000000 }, + { CSSTAT_DECAL_SPRAYS, 0x00000000 }, + { CSSTAT_NIGHTVISION_DAMAGE, 0x00000000 }, + { CSSTAT_NUM_HOSTAGES_RESCUED, 0x00000000 }, + { CSSTAT_NUM_BROKEN_WINDOWS, 0x00000000 }, + { CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, 0x00000000 }, + { CSSTAT_WEAPONS_DONATED, 0x00000000 }, + { CSSTAT_DOMINATIONS, 0x00000001 }, + { CSSTAT_DOMINATION_OVERKILLS, 0x00000000 }, + { CSSTAT_REVENGES, 0x00000000 }, + { CSSTAT_MVPS, 0x00000005 }, + { CSSTAT_MAP_WINS_CS_ASSAULT, 0x00000000 }, + { CSSTAT_MAP_WINS_CS_COMPOUND, 0x00000000 }, + { CSSTAT_MAP_WINS_CS_HAVANA, 0x00000000 }, + { CSSTAT_MAP_WINS_CS_ITALY, 0x40000002 }, + { CSSTAT_MAP_WINS_CS_MILITIA, 0x00000000 }, + { CSSTAT_MAP_WINS_CS_OFFICE, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_AZTEC, 0x0000000A }, + { CSSTAT_MAP_WINS_DE_CBBLE, 0x40000000 }, + { CSSTAT_MAP_WINS_DE_CHATEAU, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_DUST2, 0x0000000B }, + { CSSTAT_MAP_WINS_DE_DUST, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_INFERNO, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_NUKE, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_PIRANESI, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_PORT, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_PRODIGY, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_TIDES, 0x00000000 }, + { CSSTAT_MAP_WINS_DE_TRAIN, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_CS_ASSAULT, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_CS_COMPOUND, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_CS_HAVANA, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_CS_ITALY, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_CS_MILITIA, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_CS_OFFICE, 0x00000003 }, + { CSSTAT_MAP_ROUNDS_DE_AZTEC, 0x00000019 }, + { CSSTAT_MAP_ROUNDS_DE_CBBLE, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_CHATEAU, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_DUST2, 0x00000014 }, + { CSSTAT_MAP_ROUNDS_DE_DUST, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_INFERNO, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_NUKE, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_PIRANESI, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_PORT, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_PRODIGY, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_TIDES, 0x00000000 }, + { CSSTAT_MAP_ROUNDS_DE_TRAIN, 0x00000000 }, + { CSSTAT_LASTMATCH_T_ROUNDS_WON, 0x00000000 }, + { CSSTAT_LASTMATCH_CT_ROUNDS_WON, 0x00000000 }, + { CSSTAT_LASTMATCH_ROUNDS_WON, 0x40000000 }, + { CSSTAT_LASTMATCH_KILLS, 0x00000000 }, + { CSSTAT_LASTMATCH_DEATHS, 0x00000000 }, + { CSSTAT_LASTMATCH_MVPS, 0x00000000 }, + { CSSTAT_LASTMATCH_DAMAGE, 0x00000000 }, + { CSSTAT_LASTMATCH_MONEYSPENT, 0x00000000 }, + { CSSTAT_LASTMATCH_DOMINATIONS, 0x00000000 }, + { CSSTAT_LASTMATCH_REVENGES, 0x00000000 }, + { CSSTAT_LASTMATCH_MAX_PLAYERS, 0x0000001B }, + { CSSTAT_LASTMATCH_FAVWEAPON_ID, 0x00000000 }, + { CSSTAT_LASTMATCH_FAVWEAPON_SHOTS, 0x00000000 }, + { CSSTAT_LASTMATCH_FAVWEAPON_HITS, 0x00000000 }, + { CSSTAT_LASTMATCH_FAVWEAPON_KILLS, 0x00000000 }, + }; + + StatsCollection_t& lifetimeStats = const_cast<StatsCollection_t&>(g_CSClientGameStats.GetLifetimeStats()); + for ( int i = 0; i < ARRAYSIZE(badData); ++i ) + { + CSStatType_t statId = badData[i].statId; + lifetimeStats[statId] = badData[i].value; + } + + IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" ); + if ( event ) + { + gameeventmanager->FireEventClientSide( event ); + } +} +#endif + +int CCSClientGameStats::GetStatCount() +{ + return CSSTAT_MAX; +} + +PlayerStatData_t CCSClientGameStats::GetStatByIndex( int index ) +{ + PlayerStatData_t statData; + + statData.iStatId = CSStatProperty_Table[index].statId; + statData.iStatValue = m_lifetimeStats[statData.iStatId]; + + // we can make this more efficient by caching the localized names + statData.pStatDisplayName = g_pVGuiLocalize->Find( CSStatProperty_Table[index].szLocalizationToken ); + + return statData; +} + +PlayerStatData_t CCSClientGameStats::GetStatById( int id ) +{ + Assert(id >= 0 && id < CSSTAT_MAX); + if ( id >= 0 && id < CSSTAT_MAX) + { + return GetStatByIndex(id); + } + else + { + PlayerStatData_t dummy; + dummy.pStatDisplayName = NULL; + dummy.iStatId = CSSTAT_UNDEFINED; + dummy.iStatValue = 0; + return dummy; + } +} + +void CCSClientGameStats::UpdateStats( const StatsCollection_t &stats ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return; + + // don't count stats if cheats on, commentary mode, etc + if ( !g_AchievementMgrCS.CheckAchievementsEnabled() ) + return; + + // Update the accumulated stats + m_lifetimeStats.Aggregate(stats); + m_matchStats.Aggregate(stats); + m_roundStats.Aggregate(stats); + + IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" ); + if ( event ) + { + gameeventmanager->FireEventClientSide( event ); + } +} + +void CCSClientGameStats::ResetAllStats( void ) +{ + m_lifetimeStats.Reset(); + m_matchStats.Reset(); + m_roundStats.Reset(); + + // reset the stats on Steam + if (steamapicontext && steamapicontext->SteamUserStats()) + { + steamapicontext->SteamUserStats()->ResetAllStats(false); + } + + IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" ); + if ( event ) + { + gameeventmanager->FireEventClientSide( event ); + } +} + +void CCSClientGameStats::MsgFunc_MatchStatsUpdate( bf_read &msg ) +{ + int firstStat = msg.ReadShort(); + + for (int iStat = firstStat; iStat < CSSTAT_MAX && msg.GetNumBytesLeft() > 0; iStat++ ) + { + m_directTStatAverages.m_fStat[iStat] = msg.ReadFloat(); + m_directCTStatAverages.m_fStat[iStat] = msg.ReadFloat(); + m_directPlayerStatAverages.m_fStat[iStat] = msg.ReadFloat(); + } + + // sanity check: the message should contain exactly the # of bytes we expect based on the bit field + Assert( !msg.IsOverflowed() ); + Assert( 0 == msg.GetNumBytesLeft() ); +} + +void CCSClientGameStats::MsgFunc_PlayerStatsUpdate( bf_read &msg ) +{ + // Note: if any check fails while decoding this message, bail out and disregard this data to avoid + // potentially polluting player stats + + StatsCollection_t deltaStats; + + CRC32_t crc; + CRC32_Init( &crc ); + + const uint32 key = 0x82DA9F4C; // this key should match the key in cs_gamestats.cpp + CRC32_ProcessBuffer( &crc, &key, sizeof(key)); + + const byte version = 0x01; + CRC32_ProcessBuffer( &crc, &version, sizeof(version)); + + if (msg.ReadByte() != version) + { + Warning("PlayerStatsUpdate message: ignoring unsupported version\n"); + return; + } + + byte iStatsToRead = msg.ReadByte(); + CRC32_ProcessBuffer( &crc, &iStatsToRead, sizeof(iStatsToRead)); + + for ( int i = 0; i < iStatsToRead; ++i) + { + byte iStat = msg.ReadByte(); + CRC32_ProcessBuffer( &crc, &iStat, sizeof(iStat)); + + if (iStat >= CSSTAT_MAX) + { + Warning("PlayerStatsUpdate: invalid statId encountered; ignoring stats update\n"); + return; + } + short delta = msg.ReadShort(); + deltaStats[iStat] = delta; + CRC32_ProcessBuffer( &crc, &delta, sizeof(delta)); + } + + CRC32_Final( &crc ); + CRC32_t readCRC = msg.ReadLong(); + + if (readCRC != crc || msg.IsOverflowed() || ( 0 != msg.GetNumBytesLeft() ) ) + { + Warning("PlayerStatsUpdate message from server is corrupt; ignoring\n"); + return; + } + + // do one additional pass for out of band values + for ( int iStat = CSSTAT_FIRST; iStat < CSSTAT_MAX; ++iStat ) + { + if (deltaStats[iStat] < 0 || deltaStats[iStat] >= 0x4000) + { + Warning("PlayerStatsUpdate message from server has out of band values; ignoring\n"); + return; + } + } + + // everything looks okay at this point; add these stats for the player's round, match, and lifetime stats + UpdateStats(deltaStats); +} + +void CCSClientGameStats::UploadRoundData() +{ + // If there's nothing to send, don't bother + if ( !m_RoundStatData.Count() ) + return; + + // Temporary ConVar to disable stats + if ( sv_noroundstats.GetBool() ) + { + m_RoundStatData.PurgeAndDeleteElements(); + return; + } + + // Send off all OGS stats at level shutdown + KeyValues *pKV = new KeyValues( "basedata" ); + if ( !pKV ) + return; + + pKV->SetString( "MapID", MapName() ); + + // Add all the vector based stats + for ( int k=0 ; k < m_RoundStatData.Count() ; ++k ) + { + m_RoundStatData[ k ] ->nRoundEndReason = m_RoundEndReason; + SubmitStat( m_RoundStatData[ k ] ); + } + + // Perform the actual submission + SubmitGameStats( pKV ); + + // Clear out the per map stats + m_RoundStatData.Purge(); + pKV->deleteThis(); + + // Reset the last round's ending status. + m_RoundEndReason = Invalid_Round_End_Reason; +} + +void CCSClientGameStats::ResetMatchStats() +{ + m_roundStats.Reset(); + m_matchStats.Reset(); + m_matchMaxPlayerCount = 0; +} + +void CCSClientGameStats::UpdateLastMatchStats() +{ + // only update that last match if we actually have valid data + if ( m_matchStats[CSSTAT_ROUNDS_PLAYED] == 0 ) + return; + + // check to see if the player materially participate; they could have been spectating or joined just in time for the ending. + int s = 0; + s += m_matchStats[CSSTAT_ROUNDS_WON]; + s += m_matchStats[CSSTAT_KILLS]; + s += m_matchStats[CSSTAT_DEATHS]; + s += m_matchStats[CSSTAT_MVPS]; + s += m_matchStats[CSSTAT_DAMAGE]; + s += m_matchStats[CSSTAT_MONEY_SPENT]; + + if ( s == 0 ) + return; + + m_lifetimeStats[CSSTAT_LASTMATCH_T_ROUNDS_WON] = m_matchStats[CSSTAT_T_ROUNDS_WON]; + m_lifetimeStats[CSSTAT_LASTMATCH_CT_ROUNDS_WON] = m_matchStats[CSSTAT_CT_ROUNDS_WON]; + m_lifetimeStats[CSSTAT_LASTMATCH_ROUNDS_WON] = m_matchStats[CSSTAT_ROUNDS_WON]; + m_lifetimeStats[CSSTAT_LASTMATCH_KILLS] = m_matchStats[CSSTAT_KILLS]; + m_lifetimeStats[CSSTAT_LASTMATCH_DEATHS] = m_matchStats[CSSTAT_DEATHS]; + m_lifetimeStats[CSSTAT_LASTMATCH_MVPS] = m_matchStats[CSSTAT_MVPS]; + m_lifetimeStats[CSSTAT_LASTMATCH_DAMAGE] = m_matchStats[CSSTAT_DAMAGE]; + m_lifetimeStats[CSSTAT_LASTMATCH_MONEYSPENT] = m_matchStats[CSSTAT_MONEY_SPENT]; + m_lifetimeStats[CSSTAT_LASTMATCH_DOMINATIONS] = m_matchStats[CSSTAT_DOMINATIONS]; + m_lifetimeStats[CSSTAT_LASTMATCH_REVENGES] = m_matchStats[CSSTAT_REVENGES]; + m_lifetimeStats[CSSTAT_LASTMATCH_MAX_PLAYERS] = m_matchMaxPlayerCount; + + CalculateMatchFavoriteWeapons(); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate and store the match favorite weapon for each player as only deltaStats for that weapon are stored on Steam +//----------------------------------------------------------------------------- +void CCSClientGameStats::CalculateMatchFavoriteWeapons() +{ + int maxKills = 0, maxKillId = -1; + + for( int j = CSSTAT_KILLS_DEAGLE; j <= CSSTAT_KILLS_M249; ++j ) + { + if ( m_matchStats[j] > maxKills ) + { + maxKills = m_matchStats[j]; + maxKillId = j; + } + } + if ( maxKillId == -1 ) + { + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_ID] = WEAPON_NONE; + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_SHOTS] = 0; + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_HITS] = 0; + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_KILLS] = 0; + } + else + { + int statTableID = -1; + for (int j = 0; WeaponName_StatId_Table[j].killStatId != CSSTAT_UNDEFINED; ++j) + { + if ( WeaponName_StatId_Table[j].killStatId == maxKillId ) + { + statTableID = j; + break; + } + } + Assert( statTableID != -1 ); + + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_ID] = WeaponName_StatId_Table[statTableID].weaponId; + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_SHOTS] = m_matchStats[WeaponName_StatId_Table[statTableID].shotStatId]; + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_HITS] = m_matchStats[WeaponName_StatId_Table[statTableID].hitStatId]; + m_lifetimeStats[CSSTAT_LASTMATCH_FAVWEAPON_KILLS] = m_matchStats[WeaponName_StatId_Table[statTableID].killStatId]; + } +} diff --git a/game/client/cstrike/cs_client_gamestats.h b/game/client/cstrike/cs_client_gamestats.h new file mode 100644 index 0000000..76a2e1d --- /dev/null +++ b/game/client/cstrike/cs_client_gamestats.h @@ -0,0 +1,163 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CS_STEAMSTATS_H +#define CS_STEAMSTATS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "steam/steam_api.h" +#include "GameEventListener.h" +#include "../game/shared/cstrike/cs_gamestats_shared.h" + +struct SRoundData : public BaseStatData +{ + SRoundData( const StatsCollection_t *pRoundData ) + { + nRoundTime = (*pRoundData)[CSSTAT_PLAYTIME]; + nWasKilled = (*pRoundData)[CSSTAT_DEATHS]; + nIsMVP = (*pRoundData)[CSSTAT_MVPS]; + nMoneySpent = (*pRoundData)[CSSTAT_MONEY_SPENT]; + nStartingMoney = -1;// We'll get this data separately + nRoundEndReason = Invalid_Round_End_Reason;// We'll get this data separately + nRevenges = (*pRoundData)[CSSTAT_REVENGES]; + nDamageDealt = (*pRoundData)[CSSTAT_DAMAGE]; + + if ( (*pRoundData)[CSSTAT_T_ROUNDS_WON] ) + { + nWinningTeamID = TEAM_TERRORIST; + if ( (*pRoundData)[CSSTAT_ROUNDS_WON] ) + { + nTeamID = TEAM_TERRORIST; + } + else + { + nTeamID = TEAM_CT; + } + } + else + { + nWinningTeamID = TEAM_CT; + if ( (*pRoundData)[CSSTAT_ROUNDS_WON] ) + { + nTeamID = TEAM_CT; + } + else + { + nTeamID = TEAM_TERRORIST; + } + } + } + + uint32 nRoundTime; + int nTeamID; + int nWinningTeamID; + int nWasKilled; + int nIsMVP; + int nDamageDealt; + int nMoneySpent; + int nStartingMoney; + int nRevenges; + int nRoundEndReason; + + BEGIN_STAT_TABLE( "CSSRoundData" ) + REGISTER_STAT_NAMED( nRoundTime, "RoundTime" ) + REGISTER_STAT_NAMED( nTeamID, "TeamID" ) + REGISTER_STAT_NAMED( nWinningTeamID, "WinningTeamID" ) + REGISTER_STAT_NAMED( nWasKilled, "WasKilled" ) + REGISTER_STAT_NAMED( nIsMVP, "IsMvp" ) + REGISTER_STAT_NAMED( nDamageDealt, "DamageDealt" ) + REGISTER_STAT_NAMED( nMoneySpent, "MoneySpent" ) + REGISTER_STAT_NAMED( nStartingMoney, "StartingMoney" ) + REGISTER_STAT_NAMED( nRevenges, "Revenges" ) + REGISTER_STAT_NAMED( nRoundEndReason, "RoundEndReason" ) + END_STAT_TABLE() +}; +typedef CUtlVector< SRoundData* > VectorRoundData; + +struct PlayerStatData_t +{ + wchar_t* pStatDisplayName; // Localized display name of the stat + int iStatId; // CSStatType_t enum value of stat + int32 iStatValue; // Value of the stat +}; + +class CCSClientGameStats : public CAutoGameSystem, public CGameEventListener, public IGameStatTracker +{ +public: + CCSClientGameStats(); + virtual void PostInit(); + virtual void LevelShutdownPreEntity(); + virtual void Shutdown(); + virtual void LevelShutdownPreClearSteamAPIContext(); + + int GetStatCount(); + PlayerStatData_t GetStatByIndex(int index); + PlayerStatData_t GetStatById(int id); + + void ResetAllStats( void ); + void ResetMatchStats(); + void UploadRoundData( void ); + + const StatsCollection_t& GetLifetimeStats() { return m_lifetimeStats; } + const StatsCollection_t& GetMatchStats() { return m_matchStats; } + + RoundStatsDirectAverage_t* GetDirectCTStatsAverages() { return &m_directCTStatAverages; } + RoundStatsDirectAverage_t* GetDirectTStatsAverages() { return &m_directTStatAverages; } + RoundStatsDirectAverage_t* GetDirectPlayerStatsAverages() { return &m_directPlayerStatAverages; } + + void MsgFunc_MatchStatsUpdate( bf_read &msg ); + void MsgFunc_PlayerStatsUpdate( bf_read &msg ); + + // Steamworks Gamestats + virtual void SubmitGameStats( KeyValues *pKV ) + { + int listCount = s_StatLists->Count(); + for( int i=0; i < listCount; ++i ) + { + // Create a master key value that has stats everybody should share (map name, session ID, etc) + (*s_StatLists)[i]->SendData(pKV); + (*s_StatLists)[i]->Clear(); + } + } + + virtual StatContainerList_t* GetStatContainerList( void ) + { + return s_StatLists; + } + +protected: + void FireGameEvent( IGameEvent *event ); + + void UpdateSteamStats(); + void RetrieveSteamStats(); + void UpdateStats( const StatsCollection_t &stats ); + void CalculateMatchFavoriteWeapons(); + void UpdateLastMatchStats(); + +private: + StatsCollection_t m_lifetimeStats; + StatsCollection_t m_matchStats; + StatsCollection_t m_roundStats; + + RoundStatsDirectAverage_t m_directCTStatAverages; + RoundStatsDirectAverage_t m_directTStatAverages; + RoundStatsDirectAverage_t m_directPlayerStatAverages; + int m_matchMaxPlayerCount; + bool m_bSteamStatsDownload; + VectorRoundData m_RoundStatData; + + int m_RoundEndReason; + + // A static list of all the stat containers, one for each data structure being tracked + static StatContainerList_t * s_StatLists; +}; + +extern CCSClientGameStats g_CSClientGameStats; + +#endif //CS_STEAMSTATS_H diff --git a/game/client/cstrike/cs_hud_achievement_announce.cpp b/game/client/cstrike/cs_hud_achievement_announce.cpp new file mode 100644 index 0000000..7739c78 --- /dev/null +++ b/game/client/cstrike/cs_hud_achievement_announce.cpp @@ -0,0 +1,415 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cs_hud_achievement_announce.h" +#include <vgui/IVGui.h> +#include "vgui_controls/AnimationController.h" +#include "iclientmode.h" +#include "c_cs_player.h" +#include "c_cs_playerresource.h" +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include "fmtstr.h" +#include "cs_gamerules.h" +#include "view.h" +#include "ivieweffects.h" +#include "viewrender.h" +#include "usermessages.h" +#include "hud_macros.h" +#include "c_baseanimating.h" +#include "achievementmgr.h" +#include "filesystem.h" + + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DECLARE_HUDELEMENT_DEPTH( CCSAchievementAnnouncePanel, 1 ); + +#define CALLOUT_WIDE (XRES(100)) +#define CALLOUT_TALL (XRES(50)) + + +namespace Interpolators + +{ + inline float Linear( float t ) { return t; } + + inline float SmoothStep( float t ) + { + t = 3 * t * t - 2.0f * t * t * t; + return t; + } + + inline float SmoothStep2( float t ) + { + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); + } + + inline float SmoothStepStart( float t ) + { + t = 0.5f * t; + t = 3 * t * t - 2.0f * t * t * t; + t = t* 2.0f; + return t; + } + + inline float SmoothStepEnd( float t ) + { + t = 0.5f * t + 0.5f; + t = 3 * t * t - 2.0f * t * t * t; + t = (t - 0.5f) * 2.0f; + return t; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSAchievementAnnouncePanel::CCSAchievementAnnouncePanel( const char *pElementName ) +: EditablePanel( NULL, "AchievementAnnouncePanel" ), CHudElement( pElementName ) +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + m_bShouldBeVisible = false; + SetScheme( "ClientScheme" ); + m_currentDisplayedAchievement = CSInvalidAchievement; + m_pGlowPanel = NULL; + + RegisterForRenderGroup( "hide_for_scoreboard" ); + + //Create the grey popup that has the name, text and icon. + m_pAchievementInfoPanel = new CCSAchivementInfoPanel(this, "InfoPanel"); +} + +CCSAchievementAnnouncePanel::~CCSAchievementAnnouncePanel() +{ + if (m_pAchievementInfoPanel) + { + delete m_pAchievementInfoPanel; + } +} + + +void CCSAchievementAnnouncePanel::Reset() +{ + //We don't want to hide the UI here since we want the achievement popup to show through death +} + +void CCSAchievementAnnouncePanel::Init() +{ + ListenForGameEvent( "achievement_earned_local" ); + + Hide(); + + CHudElement::Init(); +} + +void CCSAchievementAnnouncePanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "Resource/UI/Achievement_Glow.res" ); + + //This is the glowy flash that comes up under the popup + m_pGlowPanel = dynamic_cast<EditablePanel *>( FindChildByName("GlowPanel") ); + + Hide(); +} + +ConVar cl_show_achievement_popups( "cl_show_achievement_popups", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + + +void CCSAchievementAnnouncePanel::FireGameEvent( IGameEvent * event ) +{ + const char *pEventName = event->GetName(); + + if ( cl_show_achievement_popups.GetBool() ) + { + if ( Q_strcmp( "achievement_earned_local", pEventName ) == 0 ) + { + //Add achievement to queue and show the UI (since the UI doesn't "Think()" until it is shown + Show(); + m_achievementQueue.Insert((eCSAchievementType)event->GetInt("achievement")); + } + } +} + +void CCSAchievementAnnouncePanel::Show() +{ + m_bShouldBeVisible = true; +} + +void CCSAchievementAnnouncePanel::Hide() +{ + m_bShouldBeVisible = false; +} + +bool CCSAchievementAnnouncePanel::ShouldDraw( void ) +{ + return (m_bShouldBeVisible && CHudElement::ShouldDraw()); +} + +void CCSAchievementAnnouncePanel::Paint( void ) +{ +} + +void CCSAchievementAnnouncePanel::OnThink( void ) +{ + BaseClass::OnThink(); + + if (!m_pAchievementInfoPanel) + { + return; + } + + //if we have an achievement, update it + if (m_currentDisplayedAchievement != CSInvalidAchievement) + { + //If the match restarts, we need to move the start time for the achievement back. + if (m_displayStartTime > gpGlobals->curtime) + { + m_displayStartTime = gpGlobals->curtime; + } + float timeSinceDisplayStart = gpGlobals->curtime - m_displayStartTime; + float glowAlpha; + float achievementPanelAlpha; + + //Update the flash + if (m_pGlowPanel) + { + GetGlowAlpha(timeSinceDisplayStart, glowAlpha); + m_pGlowPanel->SetAlpha(glowAlpha); + } + + if (GetAchievementPanelAlpha(timeSinceDisplayStart, achievementPanelAlpha)) + { + m_pAchievementInfoPanel->SetAlpha(achievementPanelAlpha); + } + else + { + //achievement is faded, so we are done + m_pAchievementInfoPanel->SetAlpha(0); + m_currentDisplayedAchievement = CSInvalidAchievement; + + if (m_achievementQueue.Count() == 0) + { + Hide(); + } + } + } + else + { + //Check if we need to start the next announcement in the queue. We won't update it this frame, but no time has elapsed anyway. + if (m_achievementQueue.Count() > 0) + { + m_currentDisplayedAchievement = m_achievementQueue.RemoveAtHead(); + m_displayStartTime = gpGlobals->curtime; + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Play the achievement earned sound effect + //============================================================================= + + vgui::surface()->PlaySound( "UI/achievement_earned.wav" ); + + //============================================================================= + // HPE_END + //============================================================================= + + //Here we get the achievement to be displayed and set that in the popup windows + CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr *>( engine->GetAchievementMgr() ); + if ( !pAchievementMgr ) + return; + + IAchievement *pAchievement = pAchievementMgr->GetAchievementByID( m_currentDisplayedAchievement ); + if ( pAchievement ) + { + m_pAchievementInfoPanel->SetAchievement(pAchievement); + } + } + } +} + +bool CCSAchievementAnnouncePanel::GetAlphaFromTime(float elapsedTime, float delay, float fadeInTime, float holdTime, float fadeOutTime, float&alpha) +{ + //We just pass through each phase, subtracting off the full time if we are already done with that phase + //We return whether the control should still be active + + //I. Delay before fading in + if (elapsedTime > delay) + { + elapsedTime -= delay; + } + else + { + alpha = 0; + return true; + } + + + //II. Fade in time + if (elapsedTime > fadeInTime) + { + elapsedTime -= fadeInTime; + } + else + { + alpha = 255.0f * elapsedTime / fadeInTime; + return true; + } + + + //III. Hold at full alpha time + if (elapsedTime > holdTime) + { + elapsedTime -= holdTime; + } + else + { + alpha = 255.0f; + return true; + } + + //IV. Fade out time + if (elapsedTime > fadeOutTime) + { + alpha = 0; + return false; + } + else + { + alpha = 1.0f - (elapsedTime / fadeOutTime); + alpha = Interpolators::SmoothStepStart(alpha); + alpha *= 255.0f; + + if (m_achievementQueue.Count() > 0 && alpha < 128.0f) + { + return false; + } + return true; + } +} + +ConVar glow_delay( "glow_delay", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); +ConVar glow_fadeInTime( "glow_fadeInTime", "0.1", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); +ConVar glow_holdTime( "glow_holdTime", "0.1", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); +ConVar glow_fadeOutTime( "glow_fadeOutTime", "2.5", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); + +bool CCSAchievementAnnouncePanel::GetGlowAlpha (float time, float& alpha) +{ + + + /* + float delay = 0.0f; + float fadeInTime = 0.3f; + float holdTime = 0.1f; + float fadeOutTime = 0.4f; + */ + + //return GetAlphaFromTime(time, delay, fadeInTime, holdTime, fadeOutTime, alpha); + + return GetAlphaFromTime(time, glow_delay.GetFloat(), glow_fadeInTime.GetFloat(), glow_holdTime.GetFloat(), glow_fadeOutTime.GetFloat(), alpha); +} + + +ConVar popup_delay( "popup_delay", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); +ConVar popup_fadeInTime( "popup_fadeInTime", "0.3", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); +ConVar popup_holdTime( "popup_holdTime", "3.5", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); +ConVar popup_fadeOutTime( "popup_fadeOutTime", "3.0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY); + +bool CCSAchievementAnnouncePanel::GetAchievementPanelAlpha (float time, float& alpha) +{ + /* + float delay = 0.2f; + float fadeInTime = 0.3f; + float holdTime = 3.0f; + float fadeOutTime = 2.0f; + */ + + //return GetAlphaFromTime(time, delay, fadeInTime, holdTime, fadeOutTime, alpha); + + return GetAlphaFromTime(time, popup_delay.GetFloat(), popup_fadeInTime.GetFloat(), popup_holdTime.GetFloat(), popup_fadeOutTime.GetFloat(), alpha); + +} + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CCSAchivementInfoPanel::CCSAchivementInfoPanel( vgui::Panel *parent, const char* name ) : BaseClass( parent, name ) +{ + //Create the main components of the display and attach them to the panel + m_pAchievementIcon = SETUP_PANEL(new vgui::ScalableImagePanel( this, "Icon" )); + m_pAchievementNameLabel = new vgui::Label( this, "Name", "name" ); + m_pAchievementDescLabel = new vgui::Label( this, "Description", "desc" ); +} + +CCSAchivementInfoPanel::~CCSAchivementInfoPanel() +{ + if (m_pAchievementIcon) + { + delete m_pAchievementIcon; + } + + if (m_pAchievementNameLabel) + { + delete m_pAchievementNameLabel; + } + + if (m_pAchievementDescLabel) + { + delete m_pAchievementDescLabel; + } +} + +void CCSAchivementInfoPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + LoadControlSettings( "Resource/UI/Achievement_Popup.res" ); + + BaseClass::ApplySchemeSettings( pScheme ); + + //Override the automatic scheme setting that happen above + SetBgColor( pScheme->GetColor( "AchievementsLightGrey", Color(255, 0, 0, 255) ) ); + m_pAchievementNameLabel->SetFgColor(pScheme->GetColor("SteamLightGreen", Color(255, 255, 255, 255))); + m_pAchievementDescLabel->SetFgColor(pScheme->GetColor("White", Color(255, 255, 255, 255))); +} + +void CCSAchivementInfoPanel::SetAchievement(IAchievement* pAchievement) +{ + //============================================================================= + // HPE_BEGIN: + // [dwenger] Get achievement name and description text from the localized file + //============================================================================= + + // Set name and description + m_pAchievementNameLabel->SetText( ACHIEVEMENT_LOCALIZED_NAME( pAchievement ) ); + m_pAchievementDescLabel->SetText( ACHIEVEMENT_LOCALIZED_DESC( pAchievement ) ); + + //Find, load and show the achievement icon + char imagePath[_MAX_PATH]; + 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 ); + + 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_pAchievementIcon->SetImage( imagePath ); + m_pAchievementIcon->SetVisible( true ); + + //============================================================================= + // HPE_END + //============================================================================= +} diff --git a/game/client/cstrike/cs_hud_achievement_announce.h b/game/client/cstrike/cs_hud_achievement_announce.h new file mode 100644 index 0000000..a03522a --- /dev/null +++ b/game/client/cstrike/cs_hud_achievement_announce.h @@ -0,0 +1,92 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef CS_HUD_ACHIVEMENT_ANNOUNCE_H +#define CS_HUD_ACHIVEMENT_ANNOUNCE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui_controls/AnimationController.h> +#include <vgui_controls/EditablePanel.h> +#include <vgui_controls/ScalableImagePanel.h> +#include "vgui/ILocalize.h" +#include "vgui_avatarimage.h" +#include "hud.h" +#include "hudelement.h" +#include "cs_hud_playerhealth.h" + +#include "cs_shareddefs.h" + +using namespace vgui; + +class IAchievement; + + +class CCSAchivementInfoPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CCSAchivementInfoPanel, vgui::EditablePanel ); + +public: + CCSAchivementInfoPanel( vgui::Panel *parent, const char* name); + ~CCSAchivementInfoPanel(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + void SetAchievement(IAchievement* pAchievement); + +private: + vgui::Label *m_pAchievementNameLabel; + vgui::Label *m_pAchievementDescLabel; + vgui::ScalableImagePanel *m_pAchievementIcon; +}; + + + +class CCSAchievementAnnouncePanel: public EditablePanel, public CHudElement +{ +private: + DECLARE_CLASS_SIMPLE( CCSAchievementAnnouncePanel, EditablePanel ); + +public: + CCSAchievementAnnouncePanel( const char *pElementName ); + ~CCSAchievementAnnouncePanel(); + + virtual void Reset(); + virtual void Init(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void FireGameEvent( IGameEvent * event ); + + void Show(); + void Hide(); + + virtual bool ShouldDraw( void ); + virtual void Paint( void ); + void OnThink( void ); + bool GetAlphaFromTime(float elapsedTime, float delay, float fadeInTime, float holdTime, float fadeOutTime, float&alpha); + + int HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ); + +protected: + bool GetGlowAlpha (float time, float& alpha); + bool GetAchievementPanelAlpha (float time, float& alpha); + +private: + + CUtlQueue<eCSAchievementType> m_achievementQueue; + eCSAchievementType m_currentDisplayedAchievement; + float m_displayStartTime; + + + vgui::EditablePanel *m_pGlowPanel; + CCSAchivementInfoPanel *m_pAchievementInfoPanel; + + bool m_bShouldBeVisible; +}; + +#endif //CS_HUD_ACHIVEMENT_ANNOUNCE_H
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_achievement_tracker.cpp b/game/client/cstrike/cs_hud_achievement_tracker.cpp new file mode 100644 index 0000000..ce8958a --- /dev/null +++ b/game/client/cstrike/cs_hud_achievement_tracker.cpp @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#include "cbase.h" +#include "hud_baseachievement_tracker.h" +#include "c_cs_player.h" +#include "iachievementmgr.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// The number of counter-strike HUD achievements to display +const int cMaxCSHUDAchievments = 4; + + +using namespace vgui; + +class CHudAchievementTracker : public CHudBaseAchievementTracker +{ + DECLARE_CLASS_SIMPLE( CHudAchievementTracker, CHudBaseAchievementTracker ); + +public: + CHudAchievementTracker( const char *pElementName ); + virtual void OnThink(); + virtual void PerformLayout(); + virtual int GetMaxAchievementsShown(); + virtual bool ShouldShowAchievement( IAchievement *pAchievement ); + +private: + CPanelAnimationVarAliasType( int, m_iNormalY, "NormalY", "5", "proportional_int" ); +}; + +DECLARE_HUDELEMENT( CHudAchievementTracker ); + + +CHudAchievementTracker::CHudAchievementTracker( const char *pElementName ) : BaseClass( pElementName ) +{ + RegisterForRenderGroup( "hide_for_scoreboard" ); +} + +void CHudAchievementTracker::OnThink() +{ + BaseClass::OnThink(); +} + +int CHudAchievementTracker::GetMaxAchievementsShown() +{ + return MIN( BaseClass::GetMaxAchievementsShown(), cMaxCSHUDAchievments ); +} + +void CHudAchievementTracker::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y; + GetPos( x, y ); + SetPos( x, m_iNormalY ); +} + +bool CHudAchievementTracker::ShouldShowAchievement( IAchievement *pAchievement ) +{ + if ( !BaseClass::ShouldShowAchievement(pAchievement) ) + return false; + + C_CSPlayer *pPlayer = CCSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return false; + + return true; +}
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_ammo.cpp b/game/client/cstrike/cs_hud_ammo.cpp new file mode 100644 index 0000000..f952948 --- /dev/null +++ b/game/client/cstrike/cs_hud_ammo.cpp @@ -0,0 +1,292 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hud.h" +#include "hudelement.h" +#include "hud_macros.h" +#include "hud_numericdisplay.h" +#include "iclientmode.h" +#include "ihudlcd.h" +#include "vgui/ILocalize.h" + +#include <vgui/ISurface.h> +#include <vgui_controls/AnimationController.h> + +//----------------------------------------------------------------------------- +// Purpose: Displays current ammunition level +//----------------------------------------------------------------------------- +class CHudAmmo : public CHudNumericDisplay, public CHudElement +{ + DECLARE_CLASS_SIMPLE( CHudAmmo, CHudNumericDisplay ); + +public: + CHudAmmo( const char *pElementName ); + void Init( void ); + void VidInit( void ); + + void SetAmmo(int ammo, bool playAnimation); + void SetAmmo2(int ammo2, bool playAnimation); + +protected: + virtual void OnThink(); + virtual void Paint( void ); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + +private: + CHandle< C_BaseCombatWeapon > m_hCurrentActiveWeapon; + int m_iAmmo; + int m_iAmmo2; + + bool m_bUsesClips; + + int m_iAdditiveWhiteID; + + CPanelAnimationVarAliasType( float, digit_xpos, "digit_xpos", "50", "proportional_float" ); + CPanelAnimationVarAliasType( float, digit_ypos, "digit_ypos", "2", "proportional_float" ); + CPanelAnimationVarAliasType( float, digit2_xpos, "digit2_xpos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, digit2_ypos, "digit2_ypos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, bar_xpos, "bar_xpos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, bar_ypos, "bar_ypos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, bar_width, "bar_width", "2", "proportional_float" ); + CPanelAnimationVarAliasType( float, bar_height, "bar_height", "2", "proportional_float" ); + CPanelAnimationVarAliasType( float, icon_xpos, "icon_xpos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, icon_ypos, "icon_ypos", "0", "proportional_float" ); + + CPanelAnimationVar( Color, m_TextColor, "TextColor", "FgColor" ); + CPanelAnimationVar( vgui::HFont, m_hNumberFont, "NumberFont", "HudNumbers" ); +}; + +DECLARE_HUDELEMENT( CHudAmmo ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CHudAmmo::CHudAmmo( const char *pElementName ) : BaseClass(NULL, "HudAmmo"), CHudElement( pElementName ) +{ + m_iAdditiveWhiteID = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( m_iAdditiveWhiteID, "vgui/white_additive" , true, false); + + SetHiddenBits( HIDEHUD_HEALTH | HIDEHUD_PLAYERDEAD | HIDEHUD_WEAPONSELECTION ); + + hudlcd->SetGlobalStat( "(ammo_primary)", "0" ); + hudlcd->SetGlobalStat( "(ammo_secondary)", "0" ); + hudlcd->SetGlobalStat( "(weapon_print_name)", "" ); + hudlcd->SetGlobalStat( "(weapon_name)", "" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudAmmo::Init( void ) +{ + m_iAmmo = -1; + m_iAmmo2 = -1; +} + +void CHudAmmo::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudAmmo::VidInit( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: called every frame to get ammo info from the weapon +//----------------------------------------------------------------------------- +void CHudAmmo::OnThink() +{ + C_BaseCombatWeapon *wpn = GetActiveWeapon(); + + hudlcd->SetGlobalStat( "(weapon_print_name)", wpn ? wpn->GetPrintName() : " " ); + hudlcd->SetGlobalStat( "(weapon_name)", wpn ? wpn->GetName() : " " ); + + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if (!wpn || !player || !wpn->UsesPrimaryAmmo()) + { + hudlcd->SetGlobalStat( "(ammo_primary)", "n/a" ); + hudlcd->SetGlobalStat( "(ammo_secondary)", "n/a" ); + + SetPaintEnabled(false); + SetPaintBackgroundEnabled(false); + return; + } + else + { + SetPaintEnabled(true); + SetPaintBackgroundEnabled(true); + } + + // get the ammo in our clip + int ammo1 = wpn->Clip1(); + int ammo2; + if (ammo1 < 0) + { + // we don't use clip ammo, just use the total ammo count + ammo1 = player->GetAmmoCount(wpn->GetPrimaryAmmoType()); + ammo2 = 0; + } + else + { + // we use clip ammo, so the second ammo is the total ammo + ammo2 = player->GetAmmoCount(wpn->GetPrimaryAmmoType()); + } + + hudlcd->SetGlobalStat( "(ammo_primary)", VarArgs( "%d", ammo1 ) ); + hudlcd->SetGlobalStat( "(ammo_secondary)", VarArgs( "%d", ammo2 ) ); + + if (wpn == m_hCurrentActiveWeapon) + { + // same weapon, just update counts + SetAmmo(ammo1, true); + SetAmmo2(ammo2, true); + } + else + { + // diferent weapon, change without triggering + SetAmmo(ammo1, false); + SetAmmo2(ammo2, false); + + // update whether or not we show the total ammo display + if (wpn->UsesClipsForAmmo1()) + { + m_bUsesClips = true; + + } + else + { + m_bUsesClips = false; + } + + m_hCurrentActiveWeapon = wpn; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates ammo display +//----------------------------------------------------------------------------- +void CHudAmmo::SetAmmo(int ammo, bool playAnimation) +{ + if (ammo != m_iAmmo) + { + if (ammo == 0) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("PrimaryAmmoEmpty"); + } + else if (ammo < m_iAmmo) + { + // ammo has decreased + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("PrimaryAmmoDecrement"); + } + else + { + // ammunition has increased + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("PrimaryAmmoIncrement"); + } + + m_iAmmo = ammo; + } + + SetDisplayValue(ammo); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates 2nd ammo display +//----------------------------------------------------------------------------- +void CHudAmmo::SetAmmo2(int ammo2, bool playAnimation) +{ + if (ammo2 != m_iAmmo2) + { + if (ammo2 == 0) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SecondaryAmmoEmpty"); + } + else if (ammo2 < m_iAmmo2) + { + // ammo has decreased + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SecondaryAmmoDecrement"); + } + else + { + // ammunition has increased + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("SecondaryAmmoIncrement"); + } + + m_iAmmo2 = ammo2; + } +} + +void CHudAmmo::Paint( void ) +{ + float alpha = 1.0f; + Color fgColor = GetFgColor(); + fgColor[3] *= alpha; + SetFgColor( fgColor ); + + int x, y; + + if( m_bUsesClips ) + { + x = digit_xpos; + y = digit_ypos; + } + else + { + x = digit2_xpos; + y = digit2_ypos; + } + + // Assume constant width font + int charWidth = vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' ); + + int digits = clamp( log10((double)m_iAmmo)+1, 1, 3 ); + + x += ( 3 - digits ) * charWidth; + + // draw primary ammo + vgui::surface()->DrawSetTextColor(GetFgColor()); + PaintNumbers( m_hNumberFont, x, y, m_iAmmo ); + + //draw reserve ammo + if( m_bUsesClips ) + { + //draw the divider + Color c = GetFgColor(); + vgui::surface()->DrawSetColor(c); + vgui::surface()->DrawSetTexture( m_iAdditiveWhiteID ); + vgui::surface()->DrawTexturedRect( bar_xpos, bar_ypos, bar_xpos + bar_width, bar_ypos + bar_height ); + + digits = clamp( log10((double)m_iAmmo2)+1, 1, 3 ); + x = digit2_xpos + ( 3 - digits ) * charWidth; + + // draw secondary ammo + vgui::surface()->DrawSetTextColor(GetFgColor()); + PaintNumbers( m_hNumberFont, x, digit2_ypos, m_iAmmo2 ); + } + + //draw the icon + C_BaseCombatWeapon *wpn = GetActiveWeapon(); + if( wpn ) + { + int ammoType = wpn->GetPrimaryAmmoType(); + + CHudTexture *icon = gWR.GetAmmoIconFromWeapon( ammoType ); + + if( icon ) + { + float icon_tall = GetTall() - YRES(2); + float scale = icon_tall / (float)icon->Height(); + float icon_wide = ( scale ) * (float)icon->Width(); + + icon->DrawSelf( icon_xpos, icon_ypos, icon_wide, icon_tall, GetFgColor() ); + } + } +} diff --git a/game/client/cstrike/cs_hud_chat.cpp b/game/client/cstrike/cs_hud_chat.cpp new file mode 100644 index 0000000..51e95e7 --- /dev/null +++ b/game/client/cstrike/cs_hud_chat.cpp @@ -0,0 +1,355 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hud_radar.h" +#include "cs_hud_chat.h" +#include "c_cs_player.h" +#include "c_cs_playerresource.h" +#include "hud_macros.h" +#include "text_message.h" +#include "vguicenterprint.h" +#include "vgui/ILocalize.h" +#include "engine/IEngineSound.h" +#include "radio_status.h" +#include "cstrike/bot/shared_util.h" +#include "ihudlcd.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DECLARE_HUDELEMENT( CHudChat ); + +DECLARE_HUD_MESSAGE( CHudChat, RadioText ); +DECLARE_HUD_MESSAGE( CHudChat, SayText ); +DECLARE_HUD_MESSAGE( CHudChat, SayText2 ); +DECLARE_HUD_MESSAGE( CHudChat, TextMsg ); +DECLARE_HUD_MESSAGE( CHudChat, RawAudio ); + + +//===================== +//CHudChatLine +//===================== + +CHudChatLine::CHudChatLine( vgui::Panel *parent, const char *panelName ) : CBaseHudChatLine( parent, panelName ) +{ + m_text = NULL; +} + +void CHudChatLine::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); +} +//===================== +//CHudChatInputLine +//===================== + +void CHudChatInputLine::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + vgui::HFont hFont = pScheme->GetFont( "ChatFont" ); + + m_pPrompt->SetFont( hFont ); + m_pInput->SetFont( hFont ); + + m_pInput->SetFgColor( pScheme->GetColor( "Chat.TypingText", pScheme->GetColor( "Panel.FgColor", Color( 255, 255, 255, 255 ) ) ) ); +} + + + +//===================== +//CHudChat +//===================== + +CHudChat::CHudChat( const char *pElementName ) : BaseClass( pElementName ) +{ + //============================================================================= + // HPE_BEGIN: + // [tj] Add this to the render group that disappears when the scoreboard is up + // + // [pmf] Removed from render group so that chat still works during intermission + // (when the scoreboard is forced to be up). The downside is that chat shows + // over the scoreboard during regular play, but this might be less of an issue + // if we reduce the need to display it constantly by adding HUD support for + // live player counts. + //============================================================================= +// RegisterForRenderGroup("hide_for_scoreboard"); + //============================================================================= + // HPE_END + //============================================================================= +} + +void CHudChat::CreateChatInputLine( void ) +{ + m_pChatInput = new CHudChatInputLine( this, "ChatInputLine" ); + m_pChatInput->SetVisible( false ); +} + +void CHudChat::CreateChatLines( void ) +{ +#ifndef _XBOX + m_ChatLine = new CHudChatLine( this, "ChatLine1" ); + m_ChatLine->SetVisible( false ); + +#endif +} + +void CHudChat::Init( void ) +{ + BaseClass::Init(); + + HOOK_HUD_MESSAGE( CHudChat, RadioText ); + HOOK_HUD_MESSAGE( CHudChat, SayText ); + HOOK_HUD_MESSAGE( CHudChat, SayText2 ); + HOOK_HUD_MESSAGE( CHudChat, TextMsg ); + HOOK_HUD_MESSAGE( CHudChat, RawAudio ); +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides base reset to not cancel chat at round restart +//----------------------------------------------------------------------------- +void CHudChat::Reset( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Reads in a player's Radio text from the server +//----------------------------------------------------------------------------- +void CHudChat::MsgFunc_RadioText( bf_read &msg ) +{ + int msg_dest = msg.ReadByte(); + NOTE_UNUSED( msg_dest ); + int client = msg.ReadByte(); + + wchar_t szBuf[6][128]; + wchar_t *msg_text = ReadLocalizedString( msg, szBuf[0], sizeof( szBuf[0] ), false ); + + // keep reading strings and using C format strings for subsituting the strings into the localised text string + ReadChatTextString ( msg, szBuf[1], sizeof( szBuf[1] ) ); // player name + ReadLocalizedString( msg, szBuf[2], sizeof( szBuf[2] ), true ); // location + ReadLocalizedString( msg, szBuf[3], sizeof( szBuf[3] ), true ); // radio text + ReadLocalizedString( msg, szBuf[4], sizeof( szBuf[4] ), true ); // unused :( + + g_pVGuiLocalize->ConstructString( szBuf[5], sizeof( szBuf[5] ), msg_text, 4, szBuf[1], szBuf[2], szBuf[3], szBuf[4] ); + + char ansiString[512]; + g_pVGuiLocalize->ConvertUnicodeToANSI( ConvertCRtoNL( szBuf[5] ), ansiString, sizeof( ansiString ) ); + ChatPrintf( client, CHAT_FILTER_TEAMCHANGE, "%s", ansiString ); + + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "HudChat.Message" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Reads in a player's Chat text from the server +//----------------------------------------------------------------------------- +void CHudChat::MsgFunc_SayText2( bf_read &msg ) +{ + // Got message during connection + if ( !g_PR ) + return; + + int client = msg.ReadByte(); + bool bWantsToChat = msg.ReadByte(); + + wchar_t szBuf[6][256]; + char untranslated_msg_text[256]; + wchar_t *msg_text = ReadLocalizedString( msg, szBuf[0], sizeof( szBuf[0] ), false, untranslated_msg_text, sizeof( untranslated_msg_text ) ); + + // keep reading strings and using C format strings for subsituting the strings into the localised text string + ReadChatTextString ( msg, szBuf[1], sizeof( szBuf[1] ) ); // player name + ReadChatTextString ( msg, szBuf[2], sizeof( szBuf[2] ) ); // chat text + ReadLocalizedString( msg, szBuf[3], sizeof( szBuf[3] ), true ); // location + ReadLocalizedString( msg, szBuf[4], sizeof( szBuf[4] ), true ); // unused :( + + g_pVGuiLocalize->ConstructString( szBuf[5], sizeof( szBuf[5] ), msg_text, 4, szBuf[1], szBuf[2], szBuf[3], szBuf[4] ); + + char ansiString[512]; + g_pVGuiLocalize->ConvertUnicodeToANSI( ConvertCRtoNL( szBuf[5] ), ansiString, sizeof( ansiString ) ); + + // flash speaking player dot + if ( client > 0 ) + Radar_FlashPlayer( client ); + + if ( bWantsToChat ) + { + int iFilter = CHAT_FILTER_NONE; + bool playChatSound = true; + + if ( client > 0 && (g_PR->GetTeam( client ) != g_PR->GetTeam( GetLocalPlayerIndex() )) ) + { + iFilter = CHAT_FILTER_PUBLICCHAT; + if ( !( iFilter & GetFilterFlags() ) ) + { + playChatSound = false; + } + } + + // print raw chat text + ChatPrintf( client, iFilter, "%s", ansiString ); + + Msg( "%s\n", RemoveColorMarkup(ansiString) ); + + if ( playChatSound ) + { + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "HudChat.Message" ); + } + } + else + { + // print raw chat text + ChatPrintf( client, GetFilterForString( untranslated_msg_text), "%s", ansiString ); + } +} +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CHudChat::GetChatInputOffset( void ) +{ + if ( m_pChatInput->IsVisible() ) + { + return m_iFontHeight; + } + else + { + return 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Reads in an Audio message from the server (wav file to be played +// via the player's voice, i.e. for bot chatter) +//----------------------------------------------------------------------------- +void CHudChat::MsgFunc_RawAudio( bf_read &msg ) +{ + char szString[2048]; + int pitch = msg.ReadByte(); + int playerIndex = msg.ReadByte(); + float feedbackDuration = msg.ReadFloat(); + msg.ReadString( szString, sizeof(szString) ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = szString; + ep.m_flVolume = 1.0f; + ep.m_SoundLevel = SNDLVL_NORM; + ep.m_nPitch = pitch; + + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, ep ); + + if ( feedbackDuration > 0.0f ) + { + //Flash them on the radar + C_CSPlayer *pPlayer = static_cast<C_CSPlayer*>( cl_entitylist->GetEnt(playerIndex) ); + + if ( pPlayer ) + { + // Create the flashy above player's head + RadioManager()->UpdateVoiceStatus( playerIndex, feedbackDuration ); + } + } +} + + +//----------------------------------------------------------------------------- +Color CHudChat::GetClientColor( int clientIndex ) +{ + if ( clientIndex == 0 ) // console msg + { + return g_ColorGreen; + } + else if( g_PR ) + { + switch ( g_PR->GetTeam( clientIndex ) ) + { + case 2 : return g_ColorRed; + case 3 : return g_ColorBlue; + default : return g_ColorGrey; + } + } + + return g_ColorYellow; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color CHudChat::GetTextColorForClient( TextColor colorNum, int clientIndex ) +{ + Color c; + switch ( colorNum ) + { + case COLOR_PLAYERNAME: + c = GetClientColor( clientIndex ); + break; + + case COLOR_LOCATION: + c = g_ColorDarkGreen; + break; + + //============================================================================= + // HPE_BEGIN: + // [tj] Adding support for achievement coloring. + // Just doing what all the other games do + //============================================================================= + + case COLOR_ACHIEVEMENT: + { + vgui::IScheme *pSourceScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "SourceScheme" ) ); + if ( pSourceScheme ) + { + c = pSourceScheme->GetColor( "SteamLightGreen", GetBgColor() ); + } + else + { + c = GetDefaultTextColor(); + } + } + break; + + //============================================================================= + // HPE_END + //============================================================================= + + + default: + c = g_ColorYellow; + } + + return Color( c[0], c[1], c[2], 255 ); +} + +int CHudChat::GetFilterForString( const char *pString ) +{ + int iFilter = BaseClass::GetFilterForString( pString ); + + if ( iFilter == CHAT_FILTER_NONE ) + { + if ( !Q_stricmp( pString, "#CStrike_Name_Change" ) ) + { + return CHAT_FILTER_NAMECHANGE; + } + } + + return iFilter; +} + +void CHudChat::StartMessageMode( int iMessageModeType ) +{ + BaseClass::StartMessageMode(iMessageModeType); + + vgui::ipanel()->SetTopmostPopup(GetVPanel(), true); +} + +void CHudChat::StopMessageMode( void ) +{ + vgui::ipanel()->SetTopmostPopup(GetVPanel(), false); + + BaseClass::StopMessageMode(); +}
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_chat.h b/game/client/cstrike/cs_hud_chat.h new file mode 100644 index 0000000..8c94ace --- /dev/null +++ b/game/client/cstrike/cs_hud_chat.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CS_HUD_CHAT_H +#define CS_HUD_CHAT_H +#ifdef _WIN32 +#pragma once +#endif + +#include <hud_basechat.h> + +//-------------------------------------------------------------------------------------------------------------- +class CHudChatLine : public CBaseHudChatLine +{ + DECLARE_CLASS_SIMPLE( CHudChatLine, CBaseHudChatLine ); + +public: + CHudChatLine( vgui::Panel *parent, const char *panelName ); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + +private: + CHudChatLine( const CHudChatLine & ); // not defined, not accessible + +}; + +//----------------------------------------------------------------------------- +// Purpose: The prompt and text entry area for chat messages +//----------------------------------------------------------------------------- +class CHudChatInputLine : public CBaseHudChatInputLine +{ + DECLARE_CLASS_SIMPLE( CHudChatInputLine, CBaseHudChatInputLine ); + +public: + CHudChatInputLine( CBaseHudChat *parent, char const *panelName ) : CBaseHudChatInputLine( parent, panelName ) {} + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); +}; + +class CHudChat : public CBaseHudChat +{ + DECLARE_CLASS_SIMPLE( CHudChat, CBaseHudChat ); + +public: + CHudChat( const char *pElementName ); + + virtual void CreateChatInputLine( void ); + virtual void CreateChatLines( void ); + + virtual void Init( void ); + virtual void Reset( void ); + + virtual void StartMessageMode( int iMessageModeType ); + virtual void StopMessageMode( void ); + + virtual void MsgFunc_SayText2( bf_read &msg ); + virtual void MsgFunc_RadioText( bf_read &msg ); + void MsgFunc_RawAudio( bf_read &msg ); + + int GetChatInputOffset( void ); + + + virtual Color GetTextColorForClient( TextColor colorNum, int clientIndex ); + virtual Color GetClientColor( int clientIndex ); + + virtual int GetFilterForString( const char *pString ); +}; + +#endif //CS_HUD_CHAT_H diff --git a/game/client/cstrike/cs_hud_damageindicator.cpp b/game/client/cstrike/cs_hud_damageindicator.cpp new file mode 100644 index 0000000..60ce009 --- /dev/null +++ b/game/client/cstrike/cs_hud_damageindicator.cpp @@ -0,0 +1,362 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hud.h" +#include "text_message.h" +#include "hud_macros.h" +#include "iclientmode.h" +#include "view.h" +#include <KeyValues.h> +#include <vgui/ISurface.h> +#include <vgui_controls/Panel.h> +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterialvar.h" +#include "IEffects.h" +#include "hudelement.h" +// NVNT damage +#include "haptics/haptic_utils.h" +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: HDU Damage indication +//----------------------------------------------------------------------------- +class CHudDamageIndicator : public CHudElement, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CHudDamageIndicator, vgui::Panel ); + +public: + CHudDamageIndicator( const char *pElementName ); + + void Init( void ); + void Reset( void ); + bool ShouldDraw( void ); + + // Handler for our message + void MsgFunc_Damage( bf_read &msg ); + +private: + void Paint(); + void ApplySchemeSettings(vgui::IScheme *pScheme); + +private: + void CalcDamageDirection( const Vector &vecFrom ); + void DrawDamageIndicatorFront( float flFade ); + void DrawDamageIndicatorRear( float flFade ); + void DrawDamageIndicatorLeft( float flFade ); + void DrawDamageIndicatorRight( float flFade ); + +private: + float m_flAttackFront; + float m_flAttackRear; + float m_flAttackLeft; + float m_flAttackRight; + + Color m_clrIndicator; + + CHudTexture *icon_up; + CHudTexture *icon_down; + CHudTexture *icon_left; + CHudTexture *icon_right; + + float m_flFadeCompleteTime; //don't draw past this time +}; + +DECLARE_HUDELEMENT( CHudDamageIndicator ); +DECLARE_HUD_MESSAGE( CHudDamageIndicator, Damage ); + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CHudDamageIndicator::CHudDamageIndicator( const char *pElementName ) : CHudElement( pElementName ), BaseClass(NULL, "HudDamageIndicator") +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetHiddenBits( HIDEHUD_HEALTH ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudDamageIndicator::Reset( void ) +{ + m_flAttackFront = 0.0; + m_flAttackRear = 0.0; + m_flAttackRight = 0.0; + m_flAttackLeft = 0.0; + + m_flFadeCompleteTime = 0.0; + + m_clrIndicator.SetColor( 250, 0, 0, 255 ); +} + +void CHudDamageIndicator::Init( void ) +{ + HOOK_HUD_MESSAGE( CHudDamageIndicator, Damage ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHudDamageIndicator::ShouldDraw( void ) +{ + if ( !CHudElement::ShouldDraw() ) + return false; + + if ( ( m_flAttackFront <= 0.0 ) && ( m_flAttackRear <= 0.0 ) && ( m_flAttackLeft <= 0.0 ) && ( m_flAttackRight <= 0.0 ) ) + return false; + + return true; +} + +void CHudDamageIndicator::DrawDamageIndicatorFront( float flFade ) +{ + if ( m_flAttackFront > 0.4 ) + { + if ( !icon_up ) + { + icon_up = gHUD.GetIcon( "pain_up" ); + } + + if ( !icon_up ) + { + return; + } + + int x = ( ScreenWidth() / 2 ) - icon_up->Width() / 2; + int y = ( ScreenHeight() / 2 ) - icon_up->Height() * 3; + icon_up->DrawSelf( x, y, m_clrIndicator ); + + m_flAttackFront = MAX( 0.0, m_flAttackFront - flFade ); + } + else + { + m_flAttackFront = 0.0; + } +} + +void CHudDamageIndicator::DrawDamageIndicatorRear( float flFade ) +{ + if ( m_flAttackRear > 0.4 ) + { + if ( !icon_down ) + { + icon_down = gHUD.GetIcon( "pain_down" ); + } + + if ( !icon_down ) + { + return; + } + + int x = ( ScreenWidth() / 2 ) - icon_down->Width() / 2; + int y = ( ScreenHeight() / 2 ) + icon_down->Height() * 2; + icon_down->DrawSelf( x, y, m_clrIndicator ); + + m_flAttackRear = MAX( 0.0, m_flAttackRear - flFade ); + } + else + { + m_flAttackRear = 0.0; + } +} + + +void CHudDamageIndicator::DrawDamageIndicatorLeft( float flFade ) +{ + if ( m_flAttackLeft > 0.4 ) + { + if ( !icon_left ) + { + icon_left = gHUD.GetIcon( "pain_left" ); + } + + if ( !icon_left ) + { + return; + } + + int x = ( ScreenWidth() / 2 ) - icon_left->Width() * 3; + int y = ( ScreenHeight() / 2 ) - icon_left->Height() / 2; + icon_left->DrawSelf( x, y, m_clrIndicator ); + + m_flAttackLeft = MAX( 0.0, m_flAttackLeft - flFade ); + } + else + { + m_flAttackLeft = 0.0; + } +} + + +void CHudDamageIndicator::DrawDamageIndicatorRight( float flFade ) +{ + if ( m_flAttackRight > 0.4 ) + { + if ( !icon_right ) + { + icon_right = gHUD.GetIcon( "pain_right" ); + } + + if ( !icon_right ) + { + return; + } + + int x = ( ScreenWidth() / 2 ) + icon_right->Width() * 2; + int y = ( ScreenHeight() / 2 ) - icon_right->Height() / 2; + icon_right->DrawSelf( x, y, m_clrIndicator ); + + m_flAttackRight = MAX( 0.0, m_flAttackRight - flFade ); + } + else + { + m_flAttackRight = 0.0; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Paints the damage display +//----------------------------------------------------------------------------- +void CHudDamageIndicator::Paint() +{ + if( m_flFadeCompleteTime > gpGlobals->curtime ) + { + float flFade = gpGlobals->frametime * 2; + // draw damage indicators + DrawDamageIndicatorFront( flFade ); + DrawDamageIndicatorRear( flFade ); + DrawDamageIndicatorLeft( flFade ); + DrawDamageIndicatorRight( flFade ); + } +} +// NVNT static to pass damage +static float hap_damage_amount = 0; + +//----------------------------------------------------------------------------- +// Purpose: Message handler for Damage message +//----------------------------------------------------------------------------- +void CHudDamageIndicator::MsgFunc_Damage( bf_read &msg ) +{ + int damageTaken = msg.ReadByte(); + + Vector vecFrom; + msg.ReadBitVec3Coord( vecFrom ); + + // NVNT pass damage to static holder + hap_damage_amount = damageTaken; + + if ( damageTaken > 0 ) + { + m_flFadeCompleteTime = gpGlobals->curtime + 1.0; + CalcDamageDirection( vecFrom ); + } +//============================================================================= +// HPE_BEGIN: +// [menglish] Added reads for the added location based parameters to this message +//============================================================================= + msg.ReadLong(); + msg.ReadBitVec3Coord( vecFrom ); +//============================================================================= +// HPE_END +//============================================================================= +} + +void CHudDamageIndicator::CalcDamageDirection( const Vector &vecFrom ) +{ + if ( vecFrom == vec3_origin ) + { + m_flAttackFront = 0.0; + m_flAttackRear = 0.0; + m_flAttackRight = 0.0; + m_flAttackLeft = 0.0; + + return; + } + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pLocalPlayer ) + { + return; + } + + Vector vecDelta = ( vecFrom - pLocalPlayer->GetRenderOrigin() ); + + if ( vecDelta.Length() <= 50 ) + { + m_flAttackFront = 1.0; + m_flAttackRear = 1.0; + m_flAttackRight = 1.0; + m_flAttackLeft = 1.0; + + return; + } + + VectorNormalize( vecDelta ); + + Vector forward; + Vector right; + Vector up; + AngleVectors( MainViewAngles(), &forward, &right, &up ); + + + float flFront = DotProduct( vecDelta, forward ); + float flSide = DotProduct( vecDelta, right ); + float flUp = DotProduct( vecDelta, up); + + if ( flFront > 0 ) + { + if ( flFront > 0.3 ) + m_flAttackFront = MAX( m_flAttackFront, flFront ); + } + else + { + float f = fabs( flFront ); + if ( f > 0.3 ) + m_flAttackRear = MAX( m_flAttackRear, f ); + } + + if ( flSide > 0 ) + { + if ( flSide > 0.3 ) + m_flAttackRight = MAX( m_flAttackRight, flSide ); + } + else + { + float f = fabs( flSide ); + if ( f > 0.3 ) + m_flAttackLeft = MAX( m_flAttackLeft, f ); + } + + // NVNT pass damage. (use hap_damage amount to apply) + // do rotation + Vector hapDir(-flSide,-flUp,flFront); + if ( haptics ) + haptics->ApplyDamageEffect(hap_damage_amount, 0, hapDir); +} + + +//----------------------------------------------------------------------------- +// Purpose: hud scheme settings +//----------------------------------------------------------------------------- +void CHudDamageIndicator::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + SetPaintBackgroundEnabled(false); + + int wide, tall; + GetHudSize(wide, tall); + SetSize(wide, tall); +} diff --git a/game/client/cstrike/cs_hud_freezepanel.cpp b/game/client/cstrike/cs_hud_freezepanel.cpp new file mode 100644 index 0000000..866be24 --- /dev/null +++ b/game/client/cstrike/cs_hud_freezepanel.cpp @@ -0,0 +1,347 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cs_hud_freezepanel.h" +#include <vgui/IVGui.h> +#include "vgui_controls/AnimationController.h" +#include "iclientmode.h" +#include "c_cs_player.h" +#include "c_cs_playerresource.h" +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include "VGUI/bordered_panel.h" +#include "fmtstr.h" +#include "cs_gamerules.h" +#include "view.h" +#include "ivieweffects.h" +#include "viewrender.h" +#include "usermessages.h" +#include "hud_macros.h" +#include "c_baseanimating.h" +#include "backgroundpanel.h" // rounded border support + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DECLARE_HUDELEMENT_DEPTH( CCSFreezePanel, 1 ); +// DECLARE_HUD_MESSAGE( CCSFreezePanel, Damage ); +// DECLARE_HUD_MESSAGE( CCSFreezePanel, DroppedEquipment ); + +#define CALLOUT_WIDE (XRES(100)) +#define CALLOUT_TALL (XRES(50)) + + +ConVar cl_disablefreezecam( + "cl_disablefreezecam", + "0", + FCVAR_ARCHIVE, + "Turn on/off freezecam on client" + ); + + +Color LerpColors( Color cStart, Color cEnd, float flPercent ) +{ + float r = (float)((float)(cStart.r()) + (float)(cEnd.r() - cStart.r()) * flPercent); + float g = (float)((float)(cStart.g()) + (float)(cEnd.g() - cStart.g()) * flPercent); + float b = (float)((float)(cStart.b()) + (float)(cEnd.b() - cStart.b()) * flPercent); + float a = (float)((float)(cStart.a()) + (float)(cEnd.a() - cStart.a()) * flPercent); + return Color( r, g, b, a ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clips the health image to the appropriate percentage +//----------------------------------------------------------------------------- +class HorizontalGauge : public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( HorizontalGauge, vgui::Panel ); + + HorizontalGauge( Panel *parent, const char *name ) : + vgui::Panel( parent, name ), + m_fPercent(0.0f) + { + } + +/* + void ApplySettings(KeyValues *inResourceData) + { + BaseClass::ApplySettings(inResourceData); + + Color color0 = inResourceData->GetColor( "color0"); + Color color1 = inResourceData->GetColor( "color1"); + } +*/ + + void PaintBackground() + { + int wide, tall; + GetSize(wide, tall); + + surface()->DrawSetColor( Color(0, 0, 0, 128) ); + surface()->DrawFilledRect(0, 0, wide, tall); + + // do the border explicitly here + surface()->DrawSetColor( Color(0,0,0,255)); + surface()->DrawOutlinedRect(0, 0, wide, tall); + } + + virtual void Paint() + { + int wide, tall; + GetSize(wide, tall); + + Color lowHealth(192, 32, 32, 255); + Color highHealth(32, 255, 32, 255); + + surface()->DrawSetColor( LerpColors(lowHealth, highHealth, m_fPercent) ); + surface()->DrawFilledRect(1, 1, (int)((wide - 1) * m_fPercent), tall - 1); + } + + void SetPercent( float fPercent ) { m_fPercent = fPercent; } + +private: + float m_fPercent; +}; + +DECLARE_BUILD_FACTORY( HorizontalGauge ); + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSFreezePanel::CCSFreezePanel( const char *pElementName ) : + EditablePanel( NULL, "FreezePanel" ), + CHudElement( pElementName ), + m_pBackgroundPanel(NULL), + m_pKillerHealth(NULL), + m_pAvatar(NULL), + m_pDominationIcon(NULL) +{ + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + SetParent(g_pClientMode->GetViewport()); + m_bShouldBeVisible = false; + SetScheme( "ClientScheme" ); + RegisterForRenderGroup( "hide_for_scoreboard" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSFreezePanel::Reset() +{ + Hide(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSFreezePanel::Init() +{ + CHudElement::Init(); + + // listen for events + ListenForGameEvent( "show_freezepanel" ); + ListenForGameEvent( "hide_freezepanel" ); + ListenForGameEvent( "freezecam_started" ); + ListenForGameEvent( "player_death" ); + + Hide(); + + InitLayout(); + +} + +void CCSFreezePanel::InitLayout() +{ + LoadControlSettings( "resource/UI/FreezePanel_Basic.res" ); + + m_pBackgroundPanel = dynamic_cast<BorderedPanel*>( FindChildByName("FreezePanelBG")); + m_pAvatar = dynamic_cast<CAvatarImagePanel*>( m_pBackgroundPanel->FindChildByName("AvatarImage")); + m_pKillerHealth = dynamic_cast<HorizontalGauge*>( m_pBackgroundPanel->FindChildByName("KillerHealth")); + m_pDominationIcon = dynamic_cast<ImagePanel*>( m_pBackgroundPanel->FindChildByName("DominationIcon")); + + m_pAvatar->SetDefaultAvatar(scheme()->GetImage( CSTRIKE_DEFAULT_AVATAR, true )); + m_pAvatar->SetShouldScaleImage(true); + m_pAvatar->SetShouldDrawFriendIcon(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Applies scheme settings +//----------------------------------------------------------------------------- +void CCSFreezePanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSFreezePanel::FireGameEvent( IGameEvent * event ) +{ + const char *pEventName = event->GetName(); + + if ( Q_strcmp( "player_death", pEventName ) == 0 ) + { + // see if the local player died + int iPlayerIndexVictim = engine->GetPlayerForUserID( event->GetInt( "userid" ) ); + int iPlayerIndexKiller = engine->GetPlayerForUserID( event->GetInt( "attacker" ) ); + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + CCSPlayer* pKiller = ToCSPlayer(ClientEntityList().GetBaseEntity(iPlayerIndexKiller)); + + if ( pLocalPlayer && iPlayerIndexVictim == pLocalPlayer->entindex() ) + { + // the local player is dead, see if this is a new nemesis or a revenge + if ( event->GetInt( "dominated" ) > 0) + { + m_pDominationIcon->SetImage("../hud/freeze_nemesis"); + m_pDominationIcon->SetVisible(true); + + m_pBackgroundPanel->SetDialogVariable( "InfoLabel1", g_pVGuiLocalize->Find("#FreezePanel_NewNemesis1")); + m_pBackgroundPanel->SetDialogVariable( "InfoLabel2", g_pVGuiLocalize->Find("#FreezePanel_NewNemesis2")); + } + // was the killer your pre-existing nemesis? + else if ( pKiller != NULL && pKiller->IsPlayerDominated(iPlayerIndexVictim) ) + { + m_pDominationIcon->SetImage("../hud/freeze_nemesis"); + m_pDominationIcon->SetVisible(true); + + m_pBackgroundPanel->SetDialogVariable( "InfoLabel1", g_pVGuiLocalize->Find("#FreezePanel_OldNemesis1")); + m_pBackgroundPanel->SetDialogVariable( "InfoLabel2", g_pVGuiLocalize->Find("#FreezePanel_OldNemesis2")); + } + else if ( event->GetInt( "revenge" ) > 0 ) + { + m_pDominationIcon->SetImage("../hud/freeze_revenge"); + m_pDominationIcon->SetVisible(true); + + m_pBackgroundPanel->SetDialogVariable( "InfoLabel1", g_pVGuiLocalize->Find("#FreezePanel_Revenge1")); + m_pBackgroundPanel->SetDialogVariable( "InfoLabel2", g_pVGuiLocalize->Find("#FreezePanel_Revenge2")); + } + else + { + m_pDominationIcon->SetVisible(false); + m_pBackgroundPanel->SetDialogVariable( "InfoLabel1", g_pVGuiLocalize->Find("#FreezePanel_Killer1")); + m_pBackgroundPanel->SetDialogVariable( "InfoLabel2", g_pVGuiLocalize->Find("#FreezePanel_Killer2")); + } + } + } + else if ( Q_strcmp( "hide_freezepanel", pEventName ) == 0 ) + { + Hide(); + } + else if ( Q_strcmp( "freezecam_started", pEventName ) == 0 ) + { + } + else if ( Q_strcmp( "show_freezepanel", pEventName ) == 0 ) + { + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + if ( !cs_PR ) + return; + + Show(); + + // Get the entity who killed us + int iKillerIndex = event->GetInt( "killer" ); + CCSPlayer* pKiller = ToCSPlayer(ClientEntityList().GetBaseEntity(iKillerIndex)); + m_pAvatar->ClearAvatar(); + + if ( pKiller ) + { + int iMaxHealth = pKiller->GetMaxHealth(); + int iKillerHealth = pKiller->GetHealth(); + if ( !pKiller->IsAlive() ) + { + iKillerHealth = 0; + } + + m_pKillerHealth->SetPercent( (float)iKillerHealth / iMaxHealth ); + + char killerName[128]; + V_snprintf( killerName, sizeof(killerName), "%s", g_PR->GetPlayerName(iKillerIndex) ); +// V_strupr( killerName ); + + m_pBackgroundPanel->SetDialogVariable( "killername", killerName); + + int iKillerIndex = pKiller->entindex(); + player_info_t pi; + + m_pAvatar->SetDefaultAvatar( GetDefaultAvatarImage( pKiller ) ); + + if ( engine->GetPlayerInfo(iKillerIndex, &pi) ) + { + m_pAvatar->SetPlayer( (C_BasePlayer*)pKiller, k_EAvatarSize64x64); + m_pAvatar->SetVisible(true); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCSFreezePanel::ShouldDraw( void ) +{ + //============================================================================= + // HPE_BEGIN: + // [Forrest] Added sv_disablefreezecam check + //============================================================================= + static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" ); + return ( m_bShouldBeVisible && !cl_disablefreezecam.GetBool() && !sv_disablefreezecam.GetBool() && CHudElement::ShouldDraw() ); + //============================================================================= + // HPE_END + //============================================================================= +} + +void CCSFreezePanel::OnScreenSizeChanged( int nOldWide, int nOldTall ) +{ + BaseClass::OnScreenSizeChanged(nOldWide, nOldTall); + + InitLayout(); +} + +void CCSFreezePanel::SetActive( bool bActive ) +{ + CHudElement::SetActive( bActive ); + + if ( bActive ) + { + // Setup replay key binding in UI + const char *pKey = engine->Key_LookupBinding( "save_replay" ); + if ( pKey == NULL || FStrEq( pKey, "(null)" ) ) + { + pKey = "<NOT BOUND>"; + } + + char szKey[16]; + Q_snprintf( szKey, sizeof(szKey), "%s", pKey ); + wchar_t wKey[16]; + wchar_t wLabel[256]; + + g_pVGuiLocalize->ConvertANSIToUnicode( szKey, wKey, sizeof( wKey ) ); + g_pVGuiLocalize->ConstructString( wLabel, sizeof( wLabel ), g_pVGuiLocalize->Find("#FreezePanel_SaveReplay" ), 1, wKey ); + + m_pBackgroundPanel->SetDialogVariable( "savereplay", wLabel ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSFreezePanel::Show() +{ + m_bShouldBeVisible = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSFreezePanel::Hide() +{ + m_bShouldBeVisible = false; +} diff --git a/game/client/cstrike/cs_hud_freezepanel.h b/game/client/cstrike/cs_hud_freezepanel.h new file mode 100644 index 0000000..12fed71 --- /dev/null +++ b/game/client/cstrike/cs_hud_freezepanel.h @@ -0,0 +1,70 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef CS_HUD_FREEZEPANEL_H +#define CS_HUD_FREEZEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui_controls/AnimationController.h> +#include <vgui_controls/EditablePanel.h> +#include "vgui/ILocalize.h" +#include "vgui_avatarimage.h" +#include "hud.h" +#include "hudelement.h" +#include "cs_hud_playerhealth.h" + +#include "cs_shareddefs.h" + +using namespace vgui; + +class HorizontalGauge; +class BorderedPanel; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CCSFreezePanel : public EditablePanel, public CHudElement +{ +private: + DECLARE_CLASS_SIMPLE( CCSFreezePanel, EditablePanel ); + +public: + CCSFreezePanel( const char *pElementName ); + + virtual void Reset(); + virtual void Init(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void FireGameEvent( IGameEvent * event ); + virtual bool ShouldDraw(); + virtual void OnScreenSizeChanged(int nOldWide, int nOldTall); + + virtual void SetActive( bool bActive ); + + void InitLayout(); + void Show(); + void Hide(); + + int HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ); + +protected: + +private: + BorderedPanel* m_pBackgroundPanel; + HorizontalGauge* m_pKillerHealth; + CAvatarImagePanel* m_pAvatar; + ImagePanel* m_pDominationIcon; + + bool m_bShouldBeVisible; +}; + +#endif //CS_HUD_FREEZEPANEL_H
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_health.cpp b/game/client/cstrike/cs_hud_health.cpp new file mode 100644 index 0000000..26faee2 --- /dev/null +++ b/game/client/cstrike/cs_hud_health.cpp @@ -0,0 +1,175 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// +// Health.cpp +// +// implementation of CHudHealth class +// +#include "cbase.h" +#include "hud.h" +#include "hud_macros.h" +#include "view.h" + +#include "iclientmode.h" + +#define PAIN_NAME "sprites/%d_pain.vmt" + +#include <KeyValues.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui_controls/AnimationController.h> + +using namespace vgui; + +#include "hudelement.h" +#include "hud_numericdisplay.h" +#include "cs_gamerules.h" + +#include "convar.h" + +//----------------------------------------------------------------------------- +// Purpose: Health panel +//----------------------------------------------------------------------------- +class CHudHealth : public CHudElement, public CHudNumericDisplay +{ + DECLARE_CLASS_SIMPLE( CHudHealth, CHudNumericDisplay ); + +public: + CHudHealth( const char *pElementName ); + virtual void Init( void ); + virtual void VidInit( void ); + virtual void Reset( void ); + virtual void OnThink(); + + virtual void Paint( void ); + virtual void ApplySchemeSettings( IScheme *scheme ); + +private: + // old variables + int m_iHealth; + + int m_bitsDamage; + + CHudTexture *m_pHealthIcon; + + CPanelAnimationVarAliasType( float, icon_xpos, "icon_xpos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, icon_ypos, "icon_ypos", "0", "proportional_float" ); + +// CPanelAnimationVar( Color, m_LowHealthColor, "LowHealthColor", "255 0 0 255" ); + + float icon_tall; + float icon_wide; + +}; + +DECLARE_HUDELEMENT( CHudHealth ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CHudHealth::CHudHealth( const char *pElementName ) : CHudElement( pElementName ), CHudNumericDisplay(NULL, "HudHealth") +{ + SetHiddenBits( HIDEHUD_HEALTH | HIDEHUD_PLAYERDEAD ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudHealth::Init() +{ + m_iHealth = 100; + m_bitsDamage = 0; + icon_tall = 0; + icon_wide = 0; + SetIndent(true); + SetDisplayValue(m_iHealth); +} + +void CHudHealth::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + if( !m_pHealthIcon ) + { + m_pHealthIcon = gHUD.GetIcon( "health_icon" ); + } + + if( m_pHealthIcon ) + { + + icon_tall = GetTall() - YRES(2); + float scale = icon_tall / (float)m_pHealthIcon->Height(); + icon_wide = ( scale ) * (float)m_pHealthIcon->Width(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: reset health to normal color at round restart +//----------------------------------------------------------------------------- +void CHudHealth::Reset() +{ + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("HealthRestored"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudHealth::VidInit() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudHealth::OnThink() +{ + int realHealth = 0; + C_BasePlayer *local = C_BasePlayer::GetLocalPlayer(); + if ( local ) + { + // Never below zero + realHealth = MAX( local->GetHealth(), 0 ); + } + + // Only update the fade if we've changed health + if ( realHealth == m_iHealth ) + { + return; + } + + if( realHealth > m_iHealth) + { + // round restarted, we have 100 again + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("HealthRestored"); + } + else if ( realHealth <= 25 ) + { + // we are badly injured + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("HealthLow"); + } + else if( realHealth < m_iHealth ) + { + // took a hit + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("HealthTookDamage"); + } + + m_iHealth = realHealth; + + SetDisplayValue(m_iHealth); +} + +void CHudHealth::Paint( void ) +{ + if( m_pHealthIcon ) + { + m_pHealthIcon->DrawSelf( icon_xpos, icon_ypos, icon_wide, icon_tall, GetFgColor() ); + } + + //draw the health icon + BaseClass::Paint(); +}
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_playerhealth.cpp b/game/client/cstrike/cs_hud_playerhealth.cpp new file mode 100644 index 0000000..f0f2160 --- /dev/null +++ b/game/client/cstrike/cs_hud_playerhealth.cpp @@ -0,0 +1,172 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: To display the player's health with the use of one graphic over another. A cross in this case +// Currently this is only used on the freeze cam panel +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cs_hud_freezepanel.h" +#include "vgui_controls/AnimationController.h" +#include "iclientmode.h" +#include "c_cs_player.h" +#include "c_cs_playerresource.h" +#include <vgui_controls/Label.h> +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include "cs_gamerules.h" + +DECLARE_BUILD_FACTORY( CCSHudPlayerHealth ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCSHudPlayerHealth::CCSHudPlayerHealth( Panel *parent, const char *name ) : EditablePanel( parent, name ) +{ + m_pHealthImage = new CCSHealthPanel( this, "PlayerStatusHealthImage" ); + m_pHealthImageBG = new ImagePanel( this, "PlayerStatusHealthImageBG" ); + + m_flNextThink = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSHudPlayerHealth::Reset() +{ + //m_flNextThink = gpGlobals->curtime + 0.05f; + m_nHealth = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSHudPlayerHealth::ApplySchemeSettings( IScheme *pScheme ) +{ + // load control settings... + LoadControlSettings( GetResFilename() ); + + m_flNextThink = 0.0f; + + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSHudPlayerHealth::SetHealth( int iNewHealth, int iMaxHealth, int iMaxBuffedHealth ) +{ + int nPrevHealth = m_nHealth; + + // set our health + m_nHealth = iNewHealth; + m_nMaxHealth = iMaxHealth; + m_pHealthImage->SetHealth( (float)(m_nHealth) / (float)(m_nMaxHealth) ); + + if ( m_pHealthImage ) + { + m_pHealthImage->SetFgColor( Color( 255, 255, 255, 255 ) ); + } + + if ( m_nHealth <= 0 ) + { + if ( m_pHealthImageBG->IsVisible() ) + { + m_pHealthImageBG->SetVisible( false ); + } + } + else + { + if ( !m_pHealthImageBG->IsVisible() ) + { + m_pHealthImageBG->SetVisible( true ); + } + } + + // set our health display value + if ( nPrevHealth != m_nHealth ) + { + if ( m_nHealth > 0 ) + { + SetDialogVariable( "Health", m_nHealth ); + } + else + { + SetDialogVariable( "Health", "" ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCSHealthPanel::CCSHealthPanel( Panel *parent, const char *name ) : vgui::Panel( parent, name ) +{ + m_flHealth = 1.0f; + + m_iMaterialIndex = surface()->DrawGetTextureId( "hud/health_color" ); + if ( m_iMaterialIndex == -1 ) // we didn't find it, so create a new one + { + m_iMaterialIndex = surface()->CreateNewTextureID(); + } + + surface()->DrawSetTextureFile( m_iMaterialIndex, "hud/health_color", true, false ); + + m_iDeadMaterialIndex = surface()->DrawGetTextureId( "hud/health_dead" ); + if ( m_iDeadMaterialIndex == -1 ) // we didn't find it, so create a new one + { + m_iDeadMaterialIndex = surface()->CreateNewTextureID(); + } + surface()->DrawSetTextureFile( m_iDeadMaterialIndex, "hud/health_dead", true, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSHealthPanel::Paint() +{ + BaseClass::Paint(); + + int x, y, w, h; + GetBounds( x, y, w, h ); + + Vertex_t vert[4]; + float uv1 = 0.0f; + float uv2 = 1.0f; + int xpos = 0, ypos = 0; + + if ( m_flHealth <= 0 ) + { + // Draw the dead material + surface()->DrawSetTexture( m_iDeadMaterialIndex ); + + vert[0].Init( Vector2D( xpos, ypos ), Vector2D( uv1, uv1 ) ); + vert[1].Init( Vector2D( xpos + w, ypos ), Vector2D( uv2, uv1 ) ); + vert[2].Init( Vector2D( xpos + w, ypos + h ), Vector2D( uv2, uv2 ) ); + vert[3].Init( Vector2D( xpos, ypos + h ), Vector2D( uv1, uv2 ) ); + + surface()->DrawSetColor( Color(255,255,255,255) ); + } + else + { + float flDamageY = h * ( 1.0f - m_flHealth ); + + // blend in the red "damage" part + surface()->DrawSetTexture( m_iMaterialIndex ); + + Vector2D uv11( uv1, uv2 - m_flHealth ); + Vector2D uv21( uv2, uv2 - m_flHealth ); + Vector2D uv22( uv2, uv2 ); + Vector2D uv12( uv1, uv2 ); + + vert[0].Init( Vector2D( xpos, flDamageY ), uv11 ); + vert[1].Init( Vector2D( xpos + w, flDamageY ), uv21 ); + vert[2].Init( Vector2D( xpos + w, ypos + h ), uv22 ); + vert[3].Init( Vector2D( xpos, ypos + h ), uv12 ); + + surface()->DrawSetColor( GetFgColor() ); + } + + surface()->DrawTexturedPolygon( 4, vert ); +}
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_playerhealth.h b/game/client/cstrike/cs_hud_playerhealth.h new file mode 100644 index 0000000..6009dc9 --- /dev/null +++ b/game/client/cstrike/cs_hud_playerhealth.h @@ -0,0 +1,77 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: To display the player's health with the use of one graphic over another. A cross in this case +// Currently this is only used on the freeze cam panel +// +// $NoKeywords: $ +//=============================================================================// + + +#ifndef CS_HUD_HEALTHPANEL_H +#define CS_HUD_HEALTHPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui_controls/AnimationController.h> +#include <vgui_controls/EditablePanel.h> +#include "vgui/ILocalize.h" +#include "hud.h" +#include "hudelement.h" + +//----------------------------------------------------------------------------- +// Purpose: Clips the health image to the appropriate percentage +//----------------------------------------------------------------------------- +class CCSHealthPanel : public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CCSHealthPanel, vgui::Panel ); + + CCSHealthPanel( vgui::Panel *parent, const char *name ); + virtual void Paint(); + void SetHealth( float flHealth ){ m_flHealth = ( flHealth <= 1.0 ) ? flHealth : 1.0f; } + +private: + + float m_flHealth; // percentage from 0.0 -> 1.0 + int m_iMaterialIndex; + int m_iDeadMaterialIndex; +}; + +//----------------------------------------------------------------------------- +// Purpose: Displays player health data +//----------------------------------------------------------------------------- +class CCSHudPlayerHealth : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CCSHudPlayerHealth, EditablePanel ); + +public: + + CCSHudPlayerHealth( Panel *parent, const char *name ); + + virtual const char *GetResFilename( void ) { return "resource/UI/FreezePanelKillerHealth.res"; } + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void Reset(); + + void SetHealth( int iNewHealth, int iMaxHealth, int iMaxBuffedHealth ); + void HideHealthBonusImage( void ); + +protected: + //virtual void OnThink(); + +protected: + float m_flNextThink; + +private: + CCSHealthPanel *m_pHealthImage; + vgui::ImagePanel *m_pHealthImageBG; + + int m_nHealth; + int m_nMaxHealth; +}; + +#endif
\ No newline at end of file diff --git a/game/client/cstrike/cs_hud_scope.cpp b/game/client/cstrike/cs_hud_scope.cpp new file mode 100644 index 0000000..13f51ac --- /dev/null +++ b/game/client/cstrike/cs_hud_scope.cpp @@ -0,0 +1,203 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hud.h" +#include "hudelement.h" +#include "hud_macros.h" +#include "hud_numericdisplay.h" +#include "iclientmode.h" +#include "c_cs_player.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterialvar.h" + +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <KeyValues.h> +#include <vgui_controls/AnimationController.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Draws the zoom screen +//----------------------------------------------------------------------------- +class CHudScope : public vgui::Panel, public CHudElement +{ + DECLARE_CLASS_SIMPLE( CHudScope, vgui::Panel ); + +public: + CHudScope( const char *pElementName ); + + void Init( void ); + void LevelInit( void ); + +protected: + virtual void ApplySchemeSettings(vgui::IScheme *scheme); + virtual void Paint( void ); + +private: + CMaterialReference m_ScopeMaterial; + CMaterialReference m_DustOverlayMaterial; + + int m_iScopeArcTexture; + int m_iScopeDustTexture; +}; + +DECLARE_HUDELEMENT_DEPTH( CHudScope, 70 ); + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudScope") +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); +} + +//----------------------------------------------------------------------------- +// Purpose: standard hud element init function +//----------------------------------------------------------------------------- +void CHudScope::Init( void ) +{ + m_iScopeArcTexture = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile(m_iScopeArcTexture, "sprites/scope_arc", true, false); + + m_iScopeDustTexture = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile(m_iScopeDustTexture, "overlays/scope_lens", true, false); +} + +//----------------------------------------------------------------------------- +// Purpose: standard hud element init function +//----------------------------------------------------------------------------- +void CHudScope::LevelInit( void ) +{ + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets scheme colors +//----------------------------------------------------------------------------- +void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings(scheme); + + SetPaintBackgroundEnabled(false); + SetPaintBorderEnabled(false); + + int screenWide, screenTall; + GetHudSize(screenWide, screenTall); + SetBounds(0, 0, screenWide, screenTall); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the zoom effect +//----------------------------------------------------------------------------- +void CHudScope::Paint( void ) +{ + C_CSPlayer *pPlayer = dynamic_cast<C_CSPlayer *>(C_BasePlayer::GetLocalPlayer()); + + if ( pPlayer == NULL ) + return; + + CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon(); + + if( !pWeapon ) + return; + + Assert( m_iScopeArcTexture ); + Assert( m_iScopeDustTexture ); + + // see if we're zoomed with a sniper rifle + if( pPlayer->GetFOV() != pPlayer->GetDefaultFOV() && + pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE ) + { + int screenWide, screenTall; + GetHudSize(screenWide, screenTall); + + // calculate the bounds in which we should draw the scope + int inset = screenTall / 16; + int y1 = inset; + int x1 = (screenWide - screenTall) / 2 + inset; + int y2 = screenTall - inset; + int x2 = screenWide - x1; + + int x = screenWide / 2; + int y = screenTall / 2; + + float uv1 = 0.5f / 256.0f, uv2 = 1.0f - uv1; + + vgui::Vertex_t vert[4]; + + Vector2D uv11( uv1, uv1 ); + Vector2D uv12( uv1, uv2 ); + Vector2D uv21( uv2, uv1 ); + Vector2D uv22( uv2, uv2 ); + + int xMod = ( screenWide / 2 ); + int yMod = ( screenTall / 2 ); + + int iMiddleX = (screenWide / 2 ); + int iMiddleY = (screenTall / 2 ); + + vgui::surface()->DrawSetTexture( m_iScopeDustTexture ); + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + + vert[0].Init( Vector2D( iMiddleX + xMod, iMiddleY + yMod ), uv21 ); + vert[1].Init( Vector2D( iMiddleX - xMod, iMiddleY + yMod ), uv11 ); + vert[2].Init( Vector2D( iMiddleX - xMod, iMiddleY - yMod ), uv12 ); + vert[3].Init( Vector2D( iMiddleX + xMod, iMiddleY - yMod ), uv22 ); + vgui::surface()->DrawTexturedPolygon( 4, vert ); + + vgui::surface()->DrawSetColor(0,0,0,255); + + //Draw the reticle with primitives + vgui::surface()->DrawLine( 0, y, screenWide, y ); + vgui::surface()->DrawLine( x, 0, x, screenTall ); + + //Draw the outline + vgui::surface()->DrawSetTexture(m_iScopeArcTexture); + + // bottom right + vert[0].Init( Vector2D( x, y ), uv11 ); + vert[1].Init( Vector2D( x2, y ), uv21 ); + vert[2].Init( Vector2D( x2, y2 ), uv22 ); + vert[3].Init( Vector2D( x, y2 ), uv12 ); + vgui::surface()->DrawTexturedPolygon( 4, vert ); + + // top right + vert[0].Init( Vector2D( x - 1, y1 ), uv12 ); + vert[1].Init( Vector2D ( x2, y1 ), uv22 ); + vert[2].Init( Vector2D( x2, y + 1 ), uv21 ); + vert[3].Init( Vector2D( x - 1, y + 1 ), uv11 ); + vgui::surface()->DrawTexturedPolygon(4, vert); + + // bottom left + vert[0].Init( Vector2D( x1, y ), uv21 ); + vert[1].Init( Vector2D( x, y ), uv11 ); + vert[2].Init( Vector2D( x, y2 ), uv12 ); + vert[3].Init( Vector2D( x1, y2), uv22 ); + vgui::surface()->DrawTexturedPolygon(4, vert); + + // top left + vert[0].Init( Vector2D( x1, y1 ), uv22 ); + vert[1].Init( Vector2D( x, y1 ), uv12 ); + vert[2].Init( Vector2D( x, y ), uv11 ); + vert[3].Init( Vector2D( x1, y ), uv21 ); + vgui::surface()->DrawTexturedPolygon(4, vert); + + vgui::surface()->DrawFilledRect(0, 0, screenWide, y1); // top + vgui::surface()->DrawFilledRect(0, y2, screenWide, screenTall); // bottom + vgui::surface()->DrawFilledRect(0, y1, x1, screenTall); // left + vgui::surface()->DrawFilledRect(x2, y1, screenWide, screenTall); // right + } +} diff --git a/game/client/cstrike/cs_hud_target_id.cpp b/game/client/cstrike/cs_hud_target_id.cpp new file mode 100644 index 0000000..8c8c2d8 --- /dev/null +++ b/game/client/cstrike/cs_hud_target_id.cpp @@ -0,0 +1,379 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: HUD Target ID element +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "hud.h" +#include "hudelement.h" +#include "c_cs_player.h" +#include "c_playerresource.h" +#include "c_cs_playerresource.h" +#include "vgui_entitypanel.h" +#include "iclientmode.h" +#include "vgui/ILocalize.h" + +#include "c_cs_hostage.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define PLAYER_HINT_DISTANCE 150 +#define PLAYER_HINT_DISTANCE_SQ (PLAYER_HINT_DISTANCE*PLAYER_HINT_DISTANCE) + +extern CUtlVector< C_CHostage* > g_Hostages; + +static ConVar hud_showtargetpos( "hud_showtargetpos", "0", FCVAR_ARCHIVE, "0: center, 1: upper left, 2 upper right, 3: lower left, 4: lower right" ); +static ConVar hud_showtargetid( "hud_showtargetid", "1", FCVAR_ARCHIVE, "Enables display of target names" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTargetID : public CHudElement, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CTargetID, vgui::Panel ); + +public: + CTargetID( const char *pElementName ); + void Init( void ); + virtual void ApplySchemeSettings( vgui::IScheme *scheme ); + virtual void Paint( void ); + void VidInit( void ); + +private: + Color GetColorForTargetTeam( int iTeamNumber ); + + vgui::HFont m_hFont; + int m_iLastEntIndex; + float m_flLastChangeTime; + + Color m_cCTColor; + Color m_cTerroristColor; + Color m_cHostageColor; +}; + +DECLARE_HUDELEMENT( CTargetID ); + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTargetID::CTargetID( const char *pElementName ) : + CHudElement( pElementName ), BaseClass( NULL, "TargetID" ) +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + m_hFont = g_hFontTrebuchet24; + m_flLastChangeTime = 0; + m_iLastEntIndex = 0; + + SetHiddenBits( HIDEHUD_MISCSTATUS ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup +//----------------------------------------------------------------------------- +void CTargetID::Init( void ) +{ +}; + +void CTargetID::ApplySchemeSettings( vgui::IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_cTerroristColor = scheme->GetColor( "T_Red", Color( 255, 64, 64, 255 ) ); + m_cCTColor = scheme->GetColor( "CT_Blue", Color( 255, 64, 64, 255 ) ); + m_cHostageColor = scheme->GetColor( "Hostage_yellow", Color( 255, 160, 0, 255 ) ); + m_hFont = scheme->GetFont( "TargetID", true ); + + SetPaintBackgroundEnabled( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: clear out string etc between levels +//----------------------------------------------------------------------------- +void CTargetID::VidInit() +{ + CHudElement::VidInit(); + + // set our size to the current viewport size + SetSize(g_pClientMode->GetViewport()->GetWide(), g_pClientMode->GetViewport()->GetTall()); + + m_flLastChangeTime = 0; + m_iLastEntIndex = 0; +} + +Color CTargetID::GetColorForTargetTeam( int iTeamNumber ) +{ + switch( iTeamNumber ) + { + case TEAM_CT: + return m_cCTColor; + break; + + case TEAM_TERRORIST: + return m_cTerroristColor; + break; + + default: + return m_cHostageColor; + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw function for the element +//----------------------------------------------------------------------------- +void CTargetID::Paint() +{ + if ( hud_showtargetid.GetBool() == false ) + return; + +#define MAX_ID_STRING 256 + wchar_t sIDString[ MAX_ID_STRING ]; + sIDString[0] = 0; + + Color c = m_cHostageColor; + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !pPlayer ) + return; + + // don't show target IDs when flashed + if ( pPlayer->m_flFlashBangTime > (gpGlobals->curtime+0.5) ) + return; + + //============================================================================= + // HPE_BEGIN: + // [menglish] Don't show target ID's when in freezecam mode + //============================================================================= + + if ( pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM ) + return; + + //============================================================================= + // HPE_END + //============================================================================= + + // Get our target's ent index + int iEntIndex = pPlayer->GetIDTarget(); + // Didn't find one? + if ( !iEntIndex ) + { + // Check to see if we should clear our ID + if ( m_flLastChangeTime && (gpGlobals->curtime > (m_flLastChangeTime + 0.5)) ) + { + m_flLastChangeTime = 0; + sIDString[0] = 0; + m_iLastEntIndex = 0; + } + else + { + // Keep re-using the old one + iEntIndex = m_iLastEntIndex; + } + } + else + { + m_flLastChangeTime = gpGlobals->curtime; + } + + // Is this an entindex sent by the server? + if ( iEntIndex ) + { + C_BasePlayer *pPlayer = static_cast<C_BasePlayer*>(cl_entitylist->GetEnt( iEntIndex )); + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + const char *printFormatString = NULL; + wchar_t wszClanTag[ MAX_PLAYER_NAME_LENGTH ]; + wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ]; + wchar_t wszHealthText[ 10 ]; + bool bShowHealth = false; + bool bShowPlayerName = false; + + // Some entities we always want to check, cause the text may change + // even while we're looking at it + // Is it a player? + if ( IsPlayerIndex( iEntIndex ) ) + { + if ( !pPlayer ) + { + // This can happen because the object was destroyed + sIDString[0] = 0; + m_iLastEntIndex = 0; + } + else + { + c = GetColorForTargetTeam( pPlayer->GetTeamNumber() ); + + bShowPlayerName = true; + g_pVGuiLocalize->ConvertANSIToUnicode( pPlayer->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) ); + + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + + char szClan[MAX_PLAYER_NAME_LENGTH]; + if ( cs_PR && Q_strlen( cs_PR->GetClanTag( iEntIndex ) ) > 1 ) + { + Q_snprintf( szClan, sizeof( szClan ), "%s ", cs_PR->GetClanTag( iEntIndex ) ); + } + else + { + szClan[ 0 ] = 0; + } + g_pVGuiLocalize->ConvertANSIToUnicode( szClan, wszClanTag, sizeof( wszClanTag ) ); + + if ( pPlayer->InSameTeam(pLocalPlayer) ) + { + printFormatString = "#Cstrike_playerid_sameteam"; + bShowHealth = true; + } + else if ( pLocalPlayer->GetTeamNumber() != TEAM_CT && pLocalPlayer->GetTeamNumber() != TEAM_TERRORIST ) + { + printFormatString = "#Cstrike_playerid_noteam"; + bShowHealth = true; + } + else + { + printFormatString = "#Cstrike_playerid_diffteam"; + } + + if ( bShowHealth ) + { + _snwprintf( wszHealthText, ARRAYSIZE(wszHealthText) - 1, L"%.0f%%", ((float)pPlayer->GetHealth() / (float)pPlayer->GetMaxHealth() ) * 100 ); + wszHealthText[ ARRAYSIZE(wszHealthText)-1 ] = '\0'; + } + } + } + else + { + C_BaseEntity *pEnt = cl_entitylist->GetEnt( iEntIndex ); + + //Hostages! + + //"Hostage : Health 100%" + + /* + if( long range ) + { + m_flDisplayHistory |= DHF_HOSTAGE_SEEN_FAR; + switch ( pLocalPlayer->GetTeamNumber() ) + { + case TERRORIST: + HintMessage( "#Hint_prevent_hostage_rescue", TRUE ); + break; + + case CT: + HintMessage( "#Hint_rescue_the_hostages", TRUE ); + break; + } + } + else + { + m_flDisplayHistory |= DHF_HOSTAGE_SEEN_NEAR; + m_flDisplayHistory |= DHF_HOSTAGE_SEEN_FAR; // Don't want the other msg to appear now + HintMessage( "#Hint_press_use_so_hostage_will_follow", FALSE ); + } + */ + + C_CHostage *pHostage = NULL; + + for( int i=0;i<g_Hostages.Count();i++ ) + { + // compare entity pointers + if( g_Hostages[i] == pEnt ) + { + pHostage = g_Hostages[i]; + break; + } + } + + if( pHostage != NULL ) + { + c = m_cHostageColor; + printFormatString = "#Cstrike_playerid_hostage"; + _snwprintf( wszHealthText, ARRAYSIZE(wszHealthText) - 1, L"%.0f%%", ((float)pHostage->GetHealth() / (float)pHostage->GetMaxHealth() ) * 100 ); + wszHealthText[ ARRAYSIZE(wszHealthText)-1 ] = '\0'; + bShowHealth = true; + } + else if ( !pEnt || !pEnt->InSameTeam(pLocalPlayer) ) + { + // This can happen because the object was destroyed + sIDString[0] = 0; + m_iLastEntIndex = 0; + } + else + { + // Don't check validity if it's sent by the server + c = m_cHostageColor; + g_pVGuiLocalize->ConvertANSIToUnicode( pEnt->GetIDString(), sIDString, sizeof(sIDString) ); + m_iLastEntIndex = iEntIndex; + } + } + + if ( printFormatString ) + { + if ( bShowPlayerName && bShowHealth ) + { + g_pVGuiLocalize->ConstructString( sIDString, sizeof(sIDString), g_pVGuiLocalize->Find(printFormatString), 3, wszClanTag, wszPlayerName, wszHealthText ); + } + else if ( bShowPlayerName ) + { + g_pVGuiLocalize->ConstructString( sIDString, sizeof(sIDString), g_pVGuiLocalize->Find(printFormatString), 2, wszClanTag, wszPlayerName ); + } + else if ( bShowHealth ) + { + g_pVGuiLocalize->ConstructString( sIDString, sizeof(sIDString), g_pVGuiLocalize->Find(printFormatString), 1, wszHealthText ); + } + else + { + g_pVGuiLocalize->ConstructString( sIDString, sizeof(sIDString), g_pVGuiLocalize->Find(printFormatString), 0 ); + } + } + + if ( sIDString[0] ) + { + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + bool bObserverMode = pPlayer && pPlayer->IsObserver(); + + int wide, tall; + vgui::surface()->GetTextSize( m_hFont, sIDString, wide, tall ); + + int ypos; + int xpos; + + switch ( hud_showtargetpos.GetInt() ) + { + case 0: // center + default: + xpos = (ScreenWidth() - wide) / 2; + ypos = YRES(260) - tall / 2; + break; + case 1: // upper left + xpos = XRES(10); + ypos = bObserverMode ? YRES(55) : YRES(5); + break; + case 2: // upper right + xpos = XRES(630) - wide; + ypos = bObserverMode ? YRES(55) : YRES(5); + break; + case 3: // lower left + xpos = XRES(10); + ypos = bObserverMode ? YRES(415) : YRES(445) - tall; + break; + case 4: // lower right + xpos = XRES(630) - wide; + ypos = bObserverMode ? YRES(415) : YRES(410) - tall; + break; + } + + vgui::surface()->DrawSetTextFont( m_hFont ); + vgui::surface()->DrawSetTextPos( xpos, ypos ); + vgui::surface()->DrawSetTextColor( c ); + vgui::surface()->DrawPrintText( sIDString, wcslen(sIDString) ); + } + } +} diff --git a/game/client/cstrike/cs_hud_weaponselection.cpp b/game/client/cstrike/cs_hud_weaponselection.cpp new file mode 100644 index 0000000..bf8f006 --- /dev/null +++ b/game/client/cstrike/cs_hud_weaponselection.cpp @@ -0,0 +1,845 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_selection.h" +#include "iclientmode.h" +#include "history_resource.h" +#include "iinput.h" +#include "cs_gamerules.h" + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui_controls/AnimationController.h> +#include <vgui_controls/Panel.h> + +#include "vgui/ILocalize.h" + +#include <string.h> + +//----------------------------------------------------------------------------- +// Purpose: cstrike weapon selection hud element +//----------------------------------------------------------------------------- +class CHudWeaponSelection : public CBaseHudWeaponSelection, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CHudWeaponSelection, vgui::Panel ); + +public: + CHudWeaponSelection(const char *pElementName ); + + virtual bool ShouldDraw(); + virtual void OnWeaponPickup( C_BaseCombatWeapon *pWeapon ); + + virtual void CycleToNextWeapon( void ); + virtual void CycleToPrevWeapon( void ); + virtual void SwitchToLastWeapon( void ); + + virtual C_BaseCombatWeapon *GetWeaponInSlot( int iSlot, int iSlotPos ); + virtual void SelectWeaponSlot( int iSlot ); + virtual void SelectWeapon( void ); + + virtual C_BaseCombatWeapon *GetSelectedWeapon( void ) + { + return m_hSelectedWeapon; + } + + virtual void OpenSelection( void ); + virtual void HideSelection( void ); + + virtual void CancelWeaponSelection( void ); + + virtual void LevelInit(); + +protected: + virtual void OnThink(); + virtual void Paint(); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + + virtual bool IsWeaponSelectable() + { + if (IsInSelectionMode()) + return true; + + return false; + } + + virtual bool IsHudMenuTakingInput(); + virtual bool IsHudMenuPreventingWeaponSelection(); + +private: + C_BaseCombatWeapon *FindNextWeaponInWeaponSelection(int iCurrentSlot, int iCurrentPosition); + C_BaseCombatWeapon *FindPrevWeaponInWeaponSelection(int iCurrentSlot, int iCurrentPosition); + + virtual void SetSelectedWeapon( C_BaseCombatWeapon *pWeapon ) + { + m_hSelectedWeapon = pWeapon; + } + + void DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, int number); + + CPanelAnimationVar( vgui::HFont, m_hNumberFont, "NumberFont", "HudSelectionNumbers" ); + CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudSelectionText" ); + + CPanelAnimationVarAliasType( float, m_flSmallBoxSize, "SmallBoxSize", "32", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flLargeBoxWide, "LargeBoxWide", "108", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flLargeBoxTall, "LargeBoxTall", "72", "proportional_float" ); + + CPanelAnimationVarAliasType( float, m_flBoxGap, "BoxGap", "12", "proportional_float" ); + + CPanelAnimationVarAliasType( float, m_flSelectionNumberXPos, "SelectionNumberXPos", "4", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flSelectionNumberYPos, "SelectionNumberYPos", "4", "proportional_float" ); + + CPanelAnimationVarAliasType( float, m_flIconXPos, "IconXPos", "16", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flIconYPos, "IconYPos", "8", "proportional_float" ); + + CPanelAnimationVarAliasType( float, m_flTextYPos, "TextYPos", "54", "proportional_float" ); + + CPanelAnimationVar( float, m_flAlphaOverride, "Alpha", "255" ); + CPanelAnimationVar( float, m_flSelectionAlphaOverride, "SelectionAlpha", "255" ); + + + CPanelAnimationVar( Color, m_TextColor, "TextColor", "SelectionTextFg" ); + CPanelAnimationVar( Color, m_NumberColor, "NumberColor", "SelectionNumberFg" ); + CPanelAnimationVar( Color, m_EmptyBoxColor, "EmptyBoxColor", "SelectionEmptyBoxBg" ); + CPanelAnimationVar( Color, m_BoxColor, "BoxColor", "SelectionBoxBg" ); + CPanelAnimationVar( Color, m_SelectedBoxColor, "SelectedBoxClor", "SelectionSelectedBoxBg" ); + + CPanelAnimationVar( float, m_flWeaponPickupGrowTime, "SelectionGrowTime", "0.1" ); + + CPanelAnimationVar( float, m_flTextScan, "TextScan", "1.0" ); + + CPanelAnimationVar( int, m_iMaxSlots, "MaxSlots", "6" ); + CPanelAnimationVar( bool, m_bPlaySelectionSounds, "PlaySelectSounds", "1" ); +}; + +DECLARE_HUDELEMENT( CHudWeaponSelection ); + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CHudWeaponSelection::CHudWeaponSelection( const char *pElementName ) : CBaseHudWeaponSelection(pElementName), BaseClass(NULL, "HudWeaponSelection") +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetHiddenBits( HIDEHUD_WEAPONSELECTION | HIDEHUD_PLAYERDEAD ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up display for showing weapon pickup +//----------------------------------------------------------------------------- +void CHudWeaponSelection::OnWeaponPickup( C_BaseCombatWeapon *pWeapon ) +{ + // add to pickup history + CHudHistoryResource *pHudHR = GET_HUDELEMENT( CHudHistoryResource ); + if ( pHudHR ) + { + pHudHR->AddToHistory( pWeapon ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: updates animation status +//----------------------------------------------------------------------------- +void CHudWeaponSelection::OnThink() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the CHudMenu should take slot1, etc commands +//----------------------------------------------------------------------------- +bool CHudWeaponSelection::IsHudMenuTakingInput() +{ + return CBaseHudWeaponSelection::IsHudMenuTakingInput(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the weapon selection hud should be hidden because +// the CHudMenu is open +//----------------------------------------------------------------------------- +bool CHudWeaponSelection::IsHudMenuPreventingWeaponSelection() +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the panel should draw +//----------------------------------------------------------------------------- +bool CHudWeaponSelection::ShouldDraw() +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + { + if ( IsInSelectionMode() ) + { + HideSelection(); + } + return false; + } + + bool bret = CBaseHudWeaponSelection::ShouldDraw(); + if ( !bret ) + return false; + + return ( m_bSelectionVisible ) ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudWeaponSelection::LevelInit() +{ + CHudElement::LevelInit(); + + m_iMaxSlots = clamp( m_iMaxSlots, 0, MAX_WEAPON_SLOTS ); +} + +//------------------------------------------------------------------------- +// Purpose: draws the selection area +//------------------------------------------------------------------------- +void CHudWeaponSelection::Paint() +{ + if (!ShouldDraw()) + return; + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return; + + // find and display our current selection + C_BaseCombatWeapon *pSelectedWeapon = GetSelectedWeapon(); + if ( !pSelectedWeapon ) + return; + + int iActiveSlot = (pSelectedWeapon ? pSelectedWeapon->GetSlot() : -1); + + // interpolate the selected box size between the small box size and the large box size + // interpolation has been removed since there is no weapon pickup animation anymore, so it's all at the largest size + float percentageDone = 1.0f; //min(1.0f, (gpGlobals->curtime - m_flPickupStartTime) / m_flWeaponPickupGrowTime); + int largeBoxWide = m_flSmallBoxSize + ((m_flLargeBoxWide - m_flSmallBoxSize) * percentageDone); + int largeBoxTall = m_flSmallBoxSize + ((m_flLargeBoxTall - m_flSmallBoxSize) * percentageDone); + Color selectedColor; + {for (int i = 0; i < 4; i++) + { + selectedColor[i] = m_BoxColor[i] + ((m_SelectedBoxColor[i] - m_BoxColor[i]) * percentageDone); + }} + + // calculate where to start drawing + int width = (m_iMaxSlots - 1) * (m_flSmallBoxSize + m_flBoxGap) + largeBoxWide; + int xpos = (GetWide() - width) / 2; + int ypos = 0; + + // iterate over all the weapon slots + for ( int i = 0; i < m_iMaxSlots; i++ ) + { + if ( i == iActiveSlot ) + { + bool bFirstItem = true; + for (int slotpos = 0; slotpos < MAX_WEAPON_POSITIONS; slotpos++) + { + C_BaseCombatWeapon *pWeapon = GetWeaponInSlot(i, slotpos); + if ( !pWeapon ) + continue; + + // draw selected weapon + DrawBox(xpos, ypos, largeBoxWide, largeBoxTall, selectedColor, m_flSelectionAlphaOverride, bFirstItem ? i + 1 : -1); + + // draw icon + Color col = GetFgColor(); + // icons use old system, drawing in screen space + if ( pWeapon->GetSpriteActive() ) + { + if (!pWeapon->CanBeSelected()) + { + // unselectable weapon, display as such + col = Color(255, 0, 0, col[3]); + } + else if (pWeapon == pSelectedWeapon) + { + // currently selected weapon, display brighter + col[3] = m_flSelectionAlphaOverride; + } + pWeapon->GetSpriteActive()->DrawSelf( xpos + m_flIconXPos, ypos + m_flIconYPos, col ); + } + + // draw text + col = m_TextColor; + const FileWeaponInfo_t &weaponInfo = pWeapon->GetWpnData(); + + if (pWeapon == pSelectedWeapon) + { + wchar_t text[128]; + wchar_t *tempString = g_pVGuiLocalize->Find(weaponInfo.szPrintName); + + // setup our localized string + if ( tempString ) + { +#ifdef WIN32 + _snwprintf(text, sizeof(text)/sizeof(wchar_t) - 1, L"%s", tempString); +#else + _snwprintf(text, sizeof(text)/sizeof(wchar_t) - 1, L"%S", tempString); +#endif + text[sizeof(text)/sizeof(wchar_t) - 1] = 0; + } + else + { + // string wasn't found by g_pVGuiLocalize->Find() + g_pVGuiLocalize->ConvertANSIToUnicode(weaponInfo.szPrintName, text, sizeof(text)); + } + + surface()->DrawSetTextColor( col ); + surface()->DrawSetTextFont( m_hTextFont ); + + // count the position + int slen = 0, charCount = 0, maxslen = 0; + { + for (wchar_t *pch = text; *pch != 0; pch++) + { + if (*pch == '\n') + { + // newline character, drop to the next line + if (slen > maxslen) + { + maxslen = slen; + } + slen = 0; + } + else if (*pch == '\r') + { + // do nothing + } + else + { + slen += surface()->GetCharacterWidth( m_hTextFont, *pch ); + charCount++; + } + } + } + if (slen > maxslen) + { + maxslen = slen; + } + + int tx = xpos + ((largeBoxWide - maxslen) / 2); + int ty = ypos + (int)m_flTextYPos; + surface()->DrawSetTextPos( tx, ty ); + // adjust the charCount by the scan amount + charCount *= m_flTextScan; + for (wchar_t *pch = text; charCount > 0; pch++) + { + if (*pch == '\n') + { + // newline character, move to the next line + surface()->DrawSetTextPos( xpos + ((largeBoxWide - slen) / 2), ty + (surface()->GetFontTall(m_hTextFont) * 1.1f)); + } + else if (*pch == '\r') + { + // do nothing + } + else + { + surface()->DrawUnicodeChar(*pch); + charCount--; + } + } + } + + ypos += (largeBoxTall + m_flBoxGap); + bFirstItem = false; + } + + xpos += largeBoxWide; + } + else + { + // check to see if there is a weapons in this bucket + if ( GetFirstPos( i ) ) + { + // draw has weapon in slot + DrawBox(xpos, ypos, m_flSmallBoxSize, m_flSmallBoxSize, m_BoxColor, m_flAlphaOverride, i + 1); + } + else + { + // draw empty slot + DrawBox(xpos, ypos, m_flSmallBoxSize, m_flSmallBoxSize, m_EmptyBoxColor, m_flAlphaOverride, -1); + } + + xpos += m_flSmallBoxSize; + } + + // reset position + ypos = 0; + xpos += m_flBoxGap; + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws a selection box +//----------------------------------------------------------------------------- +void CHudWeaponSelection::DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, int number) +{ + BaseClass::DrawBox( x, y, wide, tall, color, normalizedAlpha / 255.0f ); + + // draw the number + if (number >= 0) + { + Color numberColor = m_NumberColor; + numberColor[3] *= normalizedAlpha / 255.0f; + surface()->DrawSetTextColor(numberColor); + surface()->DrawSetTextFont(m_hNumberFont); + wchar_t wch = '0' + number; + surface()->DrawSetTextPos(x + m_flSelectionNumberXPos, y + m_flSelectionNumberYPos); + surface()->DrawUnicodeChar(wch); + } +} + +//----------------------------------------------------------------------------- +// Purpose: hud scheme settings +//----------------------------------------------------------------------------- +void CHudWeaponSelection::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + SetPaintBackgroundEnabled(false); + + // set our size + int screenWide, screenTall; + int x, y; + GetPos(x, y); + GetHudSize(screenWide, screenTall); + SetBounds(0, y, screenWide, screenTall - y); +} + +//----------------------------------------------------------------------------- +// Purpose: Opens weapon selection control +//----------------------------------------------------------------------------- +void CHudWeaponSelection::OpenSelection( void ) +{ + Assert(!IsInSelectionMode()); + + CBaseHudWeaponSelection::OpenSelection(); + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("OpenWeaponSelectionMenu"); +} + +//----------------------------------------------------------------------------- +// Purpose: Closes weapon selection control +//----------------------------------------------------------------------------- +void CHudWeaponSelection::HideSelection( void ) +{ + CBaseHudWeaponSelection::HideSelection(); + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("CloseWeaponSelectionMenu"); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the next available weapon item in the weapon selection +//----------------------------------------------------------------------------- +C_BaseCombatWeapon *CHudWeaponSelection::FindNextWeaponInWeaponSelection(int iCurrentSlot, int iCurrentPosition) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return NULL; + + C_BaseCombatWeapon *pNextWeapon = NULL; + + // search all the weapons looking for the closest next + int iLowestNextSlot = MAX_WEAPON_SLOTS; + int iLowestNextPosition = MAX_WEAPON_POSITIONS; + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + C_BaseCombatWeapon *pWeapon = pPlayer->GetWeapon(i); + if ( !pWeapon ) + continue; + + if ( pWeapon->CanBeSelected() ) + { + int weaponSlot = pWeapon->GetSlot(), weaponPosition = pWeapon->GetPosition(); + + // see if this weapon is further ahead in the selection list + if ( weaponSlot > iCurrentSlot || (weaponSlot == iCurrentSlot && weaponPosition > iCurrentPosition) ) + { + // see if this weapon is closer than the current lowest + if ( weaponSlot < iLowestNextSlot || (weaponSlot == iLowestNextSlot && weaponPosition < iLowestNextPosition) ) + { + iLowestNextSlot = weaponSlot; + iLowestNextPosition = weaponPosition; + pNextWeapon = pWeapon; + } + } + } + } + + return pNextWeapon; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the prior available weapon item in the weapon selection +//----------------------------------------------------------------------------- +C_BaseCombatWeapon *CHudWeaponSelection::FindPrevWeaponInWeaponSelection(int iCurrentSlot, int iCurrentPosition) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return NULL; + + C_BaseCombatWeapon *pPrevWeapon = NULL; + + // search all the weapons looking for the closest next + int iLowestPrevSlot = -1; + int iLowestPrevPosition = -1; + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + C_BaseCombatWeapon *pWeapon = pPlayer->GetWeapon(i); + if ( !pWeapon ) + continue; + + if ( pWeapon->CanBeSelected() ) + { + int weaponSlot = pWeapon->GetSlot(), weaponPosition = pWeapon->GetPosition(); + + // see if this weapon is further ahead in the selection list + if ( weaponSlot < iCurrentSlot || (weaponSlot == iCurrentSlot && weaponPosition < iCurrentPosition) ) + { + // see if this weapon is closer than the current lowest + if ( weaponSlot > iLowestPrevSlot || (weaponSlot == iLowestPrevSlot && weaponPosition > iLowestPrevPosition) ) + { + iLowestPrevSlot = weaponSlot; + iLowestPrevPosition = weaponPosition; + pPrevWeapon = pWeapon; + } + } + } + } + + return pPrevWeapon; +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the selection to the next item in the menu +//----------------------------------------------------------------------------- +void CHudWeaponSelection::CycleToNextWeapon( void ) +{ + // Get the local player. + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return; + + C_BaseCombatWeapon *pNextWeapon = NULL; + if ( IsInSelectionMode() ) + { + // find the next selection spot + C_BaseCombatWeapon *pWeapon = GetSelectedWeapon(); + if ( !pWeapon ) + return; + + pNextWeapon = FindNextWeaponInWeaponSelection( pWeapon->GetSlot(), pWeapon->GetPosition() ); + } + else + { + // open selection at the current place + pNextWeapon = pPlayer->GetActiveWeapon(); + if ( pNextWeapon ) + { + pNextWeapon = FindNextWeaponInWeaponSelection( pNextWeapon->GetSlot(), pNextWeapon->GetPosition() ); + } + } + + if ( !pNextWeapon ) + { + // wrap around back to start + pNextWeapon = FindNextWeaponInWeaponSelection(-1, -1); + } + + if ( pNextWeapon ) + { + SetSelectedWeapon( pNextWeapon ); + + if( hud_fastswitch.GetInt() > 0 ) + { + SelectWeapon(); + } + else if ( !IsInSelectionMode() ) + { + OpenSelection(); + } + + // Play the "cycle to next weapon" sound + if( m_bPlaySelectionSounds ) + pPlayer->EmitSound( "Player.WeaponSelectionMoveSlot" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the selection to the previous item in the menu +//----------------------------------------------------------------------------- +void CHudWeaponSelection::CycleToPrevWeapon( void ) +{ + // Get the local player. + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return; + + if ( pPlayer->IsPlayerDead() ) + return; + + C_BaseCombatWeapon *pNextWeapon = NULL; + if ( IsInSelectionMode() ) + { + // find the next selection spot + C_BaseCombatWeapon *pWeapon = GetSelectedWeapon(); + if ( !pWeapon ) + return; + + pNextWeapon = FindPrevWeaponInWeaponSelection( pWeapon->GetSlot(), pWeapon->GetPosition() ); + } + else + { + // open selection at the current place + pNextWeapon = pPlayer->GetActiveWeapon(); + if ( pNextWeapon ) + { + pNextWeapon = FindPrevWeaponInWeaponSelection( pNextWeapon->GetSlot(), pNextWeapon->GetPosition() ); + } + } + + if ( !pNextWeapon ) + { + // wrap around back to end of weapon list + pNextWeapon = FindPrevWeaponInWeaponSelection(MAX_WEAPON_SLOTS, MAX_WEAPON_POSITIONS); + } + + if ( pNextWeapon ) + { + SetSelectedWeapon( pNextWeapon ); + + if( hud_fastswitch.GetInt() > 0 ) + { + SelectWeapon(); + } + else if ( !IsInSelectionMode() ) + { + OpenSelection(); + } + + // Play the "cycle to next weapon" sound + if( m_bPlaySelectionSounds ) + pPlayer->EmitSound( "Player.WeaponSelectionMoveSlot" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Switches the last weapon the player was using +//----------------------------------------------------------------------------- +void CHudWeaponSelection::SwitchToLastWeapon( void ) +{ + // Get the player's last weapon + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if ( !player ) + return; + + if ( player->IsPlayerDead() ) + return; + + C_BaseCombatWeapon *lastWeapon = player->GetLastWeapon(); + C_BaseCombatWeapon *activeWeapon = player->GetActiveWeapon(); + + if ( lastWeapon == activeWeapon ) + lastWeapon = NULL; + + // make sure our last weapon is still with us and valid (has ammo etc) + if ( lastWeapon ) + { + int i; + for ( i = 0; i < MAX_WEAPONS; i++ ) + { + C_BaseCombatWeapon *weapon = player->GetWeapon(i); + + if ( !weapon || !weapon->CanBeSelected() ) + continue; + + if (weapon == lastWeapon ) + break; + } + + if ( i == MAX_WEAPONS ) + lastWeapon = NULL; // weapon not found/valid + } + + // if we don't have a 'last weapon' choose best weapon + if ( !lastWeapon ) + { + lastWeapon = GameRules()->GetNextBestWeapon( player, activeWeapon ); + } + + ::input->MakeWeaponSelection( lastWeapon ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the weapon in the specified slot +//----------------------------------------------------------------------------- +C_BaseCombatWeapon *CHudWeaponSelection::GetWeaponInSlot( int iSlot, int iSlotPos ) +{ + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if ( !player ) + return NULL; + + if ( player->IsPlayerDead() ) + return NULL; + + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + C_BaseCombatWeapon *pWeapon = player->GetWeapon(i); + + if ( pWeapon == NULL ) + continue; + + if ( pWeapon->GetSlot() == iSlot && pWeapon->GetPosition() == iSlotPos ) + return pWeapon; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Player has chosen to draw the currently selected weapon +//----------------------------------------------------------------------------- +void CHudWeaponSelection::SelectWeapon( void ) +{ + if ( !GetSelectedWeapon() ) + { + engine->ClientCmd( "cancelselect\n" ); + return; + } + + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if ( !player ) + return; + + C_BaseCombatWeapon *activeWeapon = player->GetActiveWeapon(); + + // Don't allow selections of weapons that can't be selected (out of ammo, etc) + if ( !GetSelectedWeapon()->CanBeSelected() ) + { + player->EmitSound( "Player.DenyWeaponSelection" ); + } + else + { + // Only play the "weapon selected" sound if they are selecting + // a weapon different than the one that is already active. + if (GetSelectedWeapon() != activeWeapon) + { + // Play the "weapon selected" sound + player->EmitSound( "Player.WeaponSelected" ); + } + + SetWeaponSelected(); + + m_hSelectedWeapon = NULL; + + engine->ClientCmd( "cancelselect\n" ); + + } +} + +//----------------------------------------------------------------------------- +// Purpose: Abort selecting a weapon +//----------------------------------------------------------------------------- +void CHudWeaponSelection::CancelWeaponSelection() +{ + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if ( !player ) + return; + + // Fastswitches happen in a single frame, so the Weapon Selection HUD Element isn't visible + // yet, but it's going to be next frame. We need to ask it if it thinks it's going to draw, + // instead of checking it's IsActive flag. + if ( ShouldDraw() ) + { + HideSelection(); + + m_hSelectedWeapon = NULL; + } + else + { + engine->ClientCmd("escape"); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Moves selection to the specified slot +//----------------------------------------------------------------------------- +void CHudWeaponSelection::SelectWeaponSlot( int iSlot ) +{ + // iSlot is one higher than it should be, since it's the number key, not the 0-based index into the weapons + --iSlot; + + // Get the local player. + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return; + + // Don't try and read past our possible number of slots + if ( iSlot > MAX_WEAPON_SLOTS ) + return; + + // Make sure the player's allowed to switch weapons + if ( pPlayer->IsAllowedToSwitchWeapons() == false ) + return; + + int slotPos = 0; + C_BaseCombatWeapon *pActiveWeapon = GetSelectedWeapon(); + + // start later in the list + if ( IsInSelectionMode() && pActiveWeapon && pActiveWeapon->GetSlot() == iSlot ) + { + slotPos = pActiveWeapon->GetPosition() + 1; + } + + // find the weapon in this slot + pActiveWeapon = GetNextActivePos( iSlot, slotPos ); + if ( !pActiveWeapon ) + { + pActiveWeapon = GetNextActivePos( iSlot, 0 ); + } + + if ( pActiveWeapon != NULL ) + { + // Mark the change + SetSelectedWeapon( pActiveWeapon ); + + bool bMultipleWeaponsInSlot = false; + + for( int i=0;i<MAX_WEAPON_POSITIONS;i++ ) + { + C_BaseCombatWeapon *pSlotWpn = GetWeaponInSlot( pActiveWeapon->GetSlot(), i ); + + if( pSlotWpn != NULL && pSlotWpn != pActiveWeapon ) + { + bMultipleWeaponsInSlot = true; + break; + } + } + + // if fast weapon switch is on, then weapons can be selected in a single keypress + // but only if there is only one item in the bucket + if( hud_fastswitch.GetInt() > 0 && bMultipleWeaponsInSlot == false ) + { + // only one active item in bucket, so change directly to weapon + SelectWeapon(); + } + else if ( !IsInSelectionMode() ) + { + // open the weapon selection + OpenSelection(); + } + } + + if( m_bPlaySelectionSounds ) + pPlayer->EmitSound( "Player.WeaponSelectionMoveSlot" ); +} diff --git a/game/client/cstrike/cs_in_main.cpp b/game/client/cstrike/cs_in_main.cpp new file mode 100644 index 0000000..29a2b70 --- /dev/null +++ b/game/client/cstrike/cs_in_main.cpp @@ -0,0 +1,23 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: TF2 specific input handling +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "kbutton.h" +#include "input.h" + +//----------------------------------------------------------------------------- +// Purpose: TF Input interface +//----------------------------------------------------------------------------- +class CCSInput : public CInput +{ +public: +}; + +static CCSInput g_Input; + +// Expose this interface +IInput *input = ( IInput * )&g_Input; + diff --git a/game/client/cstrike/cs_prediction.cpp b/game/client/cstrike/cs_prediction.cpp new file mode 100644 index 0000000..32cf88c --- /dev/null +++ b/game/client/cstrike/cs_prediction.cpp @@ -0,0 +1,55 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "prediction.h" +#include "c_cs_player.h" +#include "igamemovement.h" + + +static CMoveData g_MoveData; +CMoveData *g_pMoveData = &g_MoveData; + + +class CCSPrediction : public CPrediction +{ +DECLARE_CLASS( CCSPrediction, CPrediction ); + +public: + virtual void SetupMove( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move ); + virtual void FinishMove( C_BasePlayer *player, CUserCmd *ucmd, CMoveData *move ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSPrediction::SetupMove( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, + CMoveData *move ) +{ + player->AvoidPhysicsProps( ucmd ); + + // Call the default SetupMove code. + BaseClass::SetupMove( player, ucmd, pHelper, move ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSPrediction::FinishMove( C_BasePlayer *player, CUserCmd *ucmd, CMoveData *move ) +{ + // Call the default FinishMove code. + BaseClass::FinishMove( player, ucmd, move ); +} + + +// Expose interface to engine +// Expose interface to engine +static CCSPrediction g_Prediction; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCSPrediction, IPrediction, VCLIENT_PREDICTION_INTERFACE_VERSION, g_Prediction ); + +CPrediction *prediction = &g_Prediction; + diff --git a/game/client/cstrike/cs_replay.cpp b/game/client/cstrike/cs_replay.cpp new file mode 100644 index 0000000..f977e73 --- /dev/null +++ b/game/client/cstrike/cs_replay.cpp @@ -0,0 +1,227 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#include "cbase.h" + +#if defined( REPLAY_ENABLED ) + +#include "cs_replay.h" +#include "c_cs_player.h" +#include "cs_gamestats_shared.h" +#include "cs_client_gamestats.h" +#include "clientmode_shared.h" +#include "replay/ireplaymoviemanager.h" +#include "replay/ireplayfactory.h" +#include "replay/ireplayscreenshotmanager.h" +#include "replay/screenshot.h" +#include <time.h> + +//---------------------------------------------------------------------------------------- + +extern IReplayScreenshotManager *g_pReplayScreenshotManager; + +//---------------------------------------------------------------------------------------- + +CCSReplay::CCSReplay() +{ +} + +CCSReplay::~CCSReplay() +{ +} + +void CCSReplay::OnBeginRecording() +{ + BaseClass::OnBeginRecording(); + + // Setup the newly created replay + C_CSPlayer* pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + SetPlayerClass( pPlayer->PlayerClass() ); + SetPlayerTeam( pPlayer->GetTeamNumber() ); + } +} + +void CCSReplay::OnEndRecording() +{ + if ( gameeventmanager ) + { + gameeventmanager->RemoveListener( this ); + } + + BaseClass::OnEndRecording(); +} + +void CCSReplay::OnComplete() +{ + BaseClass::OnComplete(); +} + +void CCSReplay::Update() +{ + BaseClass::Update(); +} + +float CCSReplay::GetSentryKillScreenshotDelay() +{ + ConVarRef replay_screenshotsentrykilldelay( "replay_screenshotsentrykilldelay" ); + return replay_screenshotsentrykilldelay.IsValid() ? replay_screenshotsentrykilldelay.GetFloat() : 0.5f; +} + +void CCSReplay::FireGameEvent( IGameEvent *pEvent ) +{ + C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pLocalPlayer ) + return; + + CaptureScreenshotParams_t params; + V_memset( ¶ms, 0, sizeof( params ) ); + + if ( FStrEq( pEvent->GetName(), "player_death" ) ) + { + ConVarRef replay_debug( "replay_debug" ); + if ( replay_debug.IsValid() && replay_debug.GetBool() ) + { + DevMsg( "%i: CCSReplay::FireGameEvent(): player_death\n", gpGlobals->tickcount ); + } + + int nKillerID = pEvent->GetInt( "attacker" ); + int nVictimID = pEvent->GetInt( "userid" ); + int nDeathFlags = pEvent->GetInt( "death_flags" ); + + const char *pWeaponName = pEvent->GetString( "weapon" ); + + // Suicide? + bool bSuicide = nKillerID == nVictimID; + + // Try to get killer + C_CSPlayer *pKiller = ToCSPlayer( USERID2PLAYER( nKillerID ) ); + + // Try to get victim + C_CSPlayer *pVictim = ToCSPlayer( USERID2PLAYER( nVictimID ) ); + + // Inflictor was a sentry gun? + bool bSentry = V_strnicmp( pWeaponName, "obj_sentrygun", 13 ) == 0; + + // Is the killer the local player? + if ( nKillerID == pLocalPlayer->GetUserID() && + !bSuicide && + !bSentry ) + { + // Domination? + if ( nDeathFlags & REPLAY_DEATH_DOMINATION ) + { + AddDomination( nVictimID ); + } + + // Revenge? + if ( pEvent->GetInt( "death_flags" ) & REPLAY_DEATH_REVENGE ) + { + AddRevenge( nVictimID ); + } + + // Add victim info to kill list + if ( pVictim ) + { + AddKill( pVictim->GetPlayerName(), pVictim->PlayerClass() ); + } + + // Take a quick screenshot with some delay + ConVarRef replay_screenshotkilldelay( "replay_screenshotkilldelay" ); + if ( replay_screenshotkilldelay.IsValid() ) + { + params.m_flDelay = GetKillScreenshotDelay(); + g_pReplayScreenshotManager->CaptureScreenshot( params ); + } + } + + // Player death? + else if ( pKiller && + nVictimID == pLocalPlayer->GetUserID() ) + { + // Record who killed the player if not a suicide + if ( !bSuicide ) + { + RecordPlayerDeath( pKiller->GetPlayerName(), pKiller->PlayerClass() ); + } + + // Take screenshot - taking a screenshot during feign death is cool, too. + ConVarRef replay_deathcammaxverticaloffset( "replay_deathcammaxverticaloffset" ); + ConVarRef replay_playerdeathscreenshotdelay( "replay_playerdeathscreenshotdelay" ); + params.m_flDelay = replay_playerdeathscreenshotdelay.IsValid() ? replay_playerdeathscreenshotdelay.GetFloat() : 0.0f; + params.m_nEntity = pLocalPlayer->entindex(); + params.m_posCamera.Init( 0,0, replay_deathcammaxverticaloffset.IsValid() ? replay_deathcammaxverticaloffset.GetFloat() : 150 ); + params.m_angCamera.Init( 90, 0, 0 ); // Look straight down + params.m_bUseCameraAngles = true; + params.m_bIgnoreMinTimeBetweenScreenshots = true; + g_pReplayScreenshotManager->CaptureScreenshot( params ); + } + } +} + +bool CCSReplay::IsValidClass( int iClass ) const +{ + return ( iClass >= CS_CLASS_NONE && iClass < CS_NUM_CLASSES ); +} + +bool CCSReplay::IsValidTeam( int iTeam ) const +{ + return ( iTeam == TEAM_TERRORIST || iTeam == TEAM_CT ); +} + +bool CCSReplay::GetCurrentStats( RoundStats_t &out ) +{ + out = g_CSClientGameStats.GetLifetimeStats(); + return true; +} + +const char *CCSReplay::GetStatString( int iStat ) const +{ + return CSStatProperty_Table[ iStat ].szSteamName; +} + +const char *CCSReplay::GetPlayerClass( int iClass ) const +{ + Assert( iClass >= CS_CLASS_NONE && iClass < CS_NUM_CLASSES ); + return GetCSClassInfo( iClass )->m_pClassName; +} + +bool CCSReplay::Read( KeyValues *pIn ) +{ + return BaseClass::Read( pIn ); +} + +void CCSReplay::Write( KeyValues *pOut ) +{ + BaseClass::Write( pOut ); +} + +const char *CCSReplay::GetMaterialFriendlyPlayerClass() const +{ + return BaseClass::GetMaterialFriendlyPlayerClass(); +} + +void CCSReplay::DumpGameSpecificData() const +{ + BaseClass::DumpGameSpecificData(); +} + +//---------------------------------------------------------------------------------------- + +class CCSReplayFactory : public IReplayFactory +{ +public: + virtual CReplay *Create() + { + return new CCSReplay(); + } +}; + +static CCSReplayFactory s_ReplayManager; +IReplayFactory *g_pReplayFactory = &s_ReplayManager; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCSReplayFactory, IReplayFactory, INTERFACE_VERSION_REPLAY_FACTORY, s_ReplayManager ); + +#endif
\ No newline at end of file diff --git a/game/client/cstrike/cs_replay.h b/game/client/cstrike/cs_replay.h new file mode 100644 index 0000000..12916b7 --- /dev/null +++ b/game/client/cstrike/cs_replay.h @@ -0,0 +1,71 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +//=======================================================================================// + +#if defined( REPLAY_ENABLED ) + +#ifndef CS_REPLAY_H +#define CS_REPLAY_H +#ifdef _WIN32 +#pragma once +#endif + +//---------------------------------------------------------------------------------------- + +#include "replay/genericclassbased_replay.h" + +//---------------------------------------------------------------------------------------- + +class CCSReplay : public CGenericClassBasedReplay +{ + typedef CGenericClassBasedReplay BaseClass; +public: + CCSReplay(); + ~CCSReplay(); + + virtual void OnBeginRecording(); + virtual void OnEndRecording(); + virtual void OnComplete(); + virtual void FireGameEvent( IGameEvent *pEvent ); + + virtual bool Read( KeyValues *pIn ); + virtual void Write( KeyValues *pOut ); + + virtual void DumpGameSpecificData() const; + + virtual const char *GetPlayerClass() const { return GetCSClassInfo( m_nPlayerClass )->m_pClassName; } + virtual const char *GetPlayerTeam() const { return m_nPlayerTeam == TEAM_TERRORIST ? "terrorists" : "counterterrorists"; } + virtual const char *GetMaterialFriendlyPlayerClass() const; + +private: + virtual void Update(); + float GetSentryKillScreenshotDelay(); + + virtual bool IsValidClass( int nClass ) const; + virtual bool IsValidTeam( int iTeam ) const; + virtual bool GetCurrentStats( RoundStats_t &out ); + virtual const char *GetStatString( int iStat ) const; + virtual const char *GetPlayerClass( int iClass ) const; +}; + +//---------------------------------------------------------------------------------------- + +inline CCSReplay *ToCSReplay( CReplay *pClientReplay ) +{ + return static_cast< CCSReplay * >( pClientReplay ); +} + +inline const CCSReplay *ToCSReplay( const CReplay *pClientReplay ) +{ + return static_cast< const CCSReplay * >( pClientReplay ); +} + +inline CCSReplay *GetCSReplay( ReplayHandle_t hReplay ) +{ + return ToCSReplay( g_pClientReplayContext->GetReplay( hReplay ) ); +} + +//---------------------------------------------------------------------------------------- + +#endif // CS_REPLAY_H + +#endif
\ No newline at end of file diff --git a/game/client/cstrike/cs_view_scene.cpp b/game/client/cstrike/cs_view_scene.cpp new file mode 100644 index 0000000..9b02442 --- /dev/null +++ b/game/client/cstrike/cs_view_scene.cpp @@ -0,0 +1,325 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Responsible for drawing the scene +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "view.h" +#include "iviewrender.h" +#include "view_shared.h" +#include "ivieweffects.h" +#include "iinput.h" +#include "model_types.h" +#include "clientsideeffects.h" +#include "particlemgr.h" +#include "viewrender.h" +#include "iclientmode.h" +#include "voice_status.h" +#include "radio_status.h" +#include "glow_overlay.h" +#include "materialsystem/imesh.h" +#include "materialsystem/itexture.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "detailobjectsystem.h" +#include "tier0/vprof.h" +#include "engine/IEngineTrace.h" +#include "engine/ivmodelinfo.h" +#include "view_scene.h" +#include "particles_ez.h" +#include "engine/IStaticPropMgr.h" +#include "engine/ivdebugoverlay.h" +#include "cs_view_scene.h" +#include "c_cs_player.h" +#include "cs_gamerules.h" +#include "shake.h" +#include "clienteffectprecachesystem.h" +#include <vgui/ISurface.h> + +CLIENTEFFECT_REGISTER_BEGIN( PrecacheCSViewScene ) +CLIENTEFFECT_MATERIAL( "effects/flashbang" ) +CLIENTEFFECT_MATERIAL( "effects/flashbang_white" ) +CLIENTEFFECT_MATERIAL( "effects/nightvision" ) +CLIENTEFFECT_REGISTER_END() + +static CCSViewRender g_ViewRender; + +CCSViewRender::CCSViewRender() +{ + view = ( IViewRender * )&g_ViewRender; + m_pFlashTexture = NULL; +} + +struct ConVarFlags +{ + const char *name; + int flags; +}; + +ConVarFlags s_flaggedConVars[] = +{ + { "r_screenfademinsize", FCVAR_CHEAT }, + { "r_screenfademaxsize", FCVAR_CHEAT }, + { "developer", FCVAR_CHEAT }, +}; + +void CCSViewRender::Init( void ) +{ + for ( int i=0; i<ARRAYSIZE( s_flaggedConVars ); ++i ) + { + ConVar *flaggedConVar = cvar->FindVar( s_flaggedConVars[i].name ); + if ( flaggedConVar ) + { + flaggedConVar->AddFlags( s_flaggedConVars[i].flags ); + } + } + + CViewRender::Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the min/max fade distances +//----------------------------------------------------------------------------- +void CCSViewRender::GetScreenFadeDistances( float *min, float *max ) +{ + if ( min ) + { + *min = 0.0f; + } + + if ( max ) + { + *max = 0.0f; + } +} + + +void CCSViewRender::PerformNightVisionEffect( const CViewSetup &view ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !pPlayer ) + return; + + if (pPlayer->GetObserverMode() == OBS_MODE_IN_EYE) + { + CBaseEntity *target = pPlayer->GetObserverTarget(); + if (target && target->IsPlayer()) + { + pPlayer = (C_CSPlayer *)target; + } + } + + if ( pPlayer && pPlayer->m_flNightVisionAlpha > 0 ) + { + IMaterial *pMaterial = materials->FindMaterial( "effects/nightvision", TEXTURE_GROUP_CLIENT_EFFECTS, true ); + + if ( pMaterial ) + { + int iMaxValue = 255; + byte overlaycolor[4] = { 0, 255, 0, 255 }; + + if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 ) + { + UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height ); + } + else + { + // In DX7, use the values CS:goldsrc uses. + iMaxValue = 225; + overlaycolor[0] = overlaycolor[2] = 50 / 2; + overlaycolor[1] = 225 / 2; + } + + if ( pPlayer->m_bNightVisionOn ) + { + pPlayer->m_flNightVisionAlpha += 15; + + pPlayer->m_flNightVisionAlpha = MIN( pPlayer->m_flNightVisionAlpha, iMaxValue ); + } + else + { + pPlayer->m_flNightVisionAlpha -= 40; + + pPlayer->m_flNightVisionAlpha = MAX( pPlayer->m_flNightVisionAlpha, 0 ); + + } + + overlaycolor[3] = pPlayer->m_flNightVisionAlpha; + + render->ViewDrawFade( overlaycolor, pMaterial ); + + // Only one pass in DX7. + if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 ) + { + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->DrawScreenSpaceQuad( pMaterial ); + render->ViewDrawFade( overlaycolor, pMaterial ); + pRenderContext->DrawScreenSpaceQuad( pMaterial ); + } + } + } +} + + +//Adrian - Super Nifty Flashbang Effect(tm) +// this does the burn in for the flashbang effect. +void CCSViewRender::PerformFlashbangEffect( const CViewSetup &view ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( pPlayer == NULL ) + return; + + if ( pPlayer->m_flFlashBangTime < gpGlobals->curtime ) + return; + + IMaterial *pMaterial = materials->FindMaterial( "effects/flashbang", TEXTURE_GROUP_CLIENT_EFFECTS, true ); + + if ( !pMaterial ) + return; + + byte overlaycolor[4] = { 255, 255, 255, 255 }; + + CMatRenderContextPtr pRenderContext( materials ); + + if ( pPlayer->m_flFlashAlpha < pPlayer->m_flFlashMaxAlpha ) + { + pPlayer->m_flFlashAlpha += 45; + + pPlayer->m_flFlashAlpha = MIN( pPlayer->m_flFlashAlpha, pPlayer->m_flFlashMaxAlpha ); + + overlaycolor[0] = overlaycolor[1] = overlaycolor[2] = pPlayer->m_flFlashAlpha; + + m_pFlashTexture = GetFullFrameFrameBufferTexture( 1 ); + + bool foundVar; + + IMaterialVar* m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false ); + + Rect_t srcRect; + srcRect.x = view.x; + srcRect.y = view.y; + srcRect.width = view.width; + srcRect.height = view.height; + m_BaseTextureVar->SetTextureValue( m_pFlashTexture ); + pRenderContext->CopyRenderTargetToTextureEx( m_pFlashTexture, 0, &srcRect, NULL ); + pRenderContext->SetFrameBufferCopyTexture( m_pFlashTexture ); + + render->ViewDrawFade( overlaycolor, pMaterial ); + + // just do one pass for dxlevel < 80. + if (g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80) + { + pRenderContext->DrawScreenSpaceRectangle( pMaterial, view.x, view.y, view.width, view.height, + 0, 0, m_pFlashTexture->GetActualWidth()-1, m_pFlashTexture->GetActualHeight()-1, + m_pFlashTexture->GetActualWidth(), m_pFlashTexture->GetActualHeight() ); + render->ViewDrawFade( overlaycolor, pMaterial ); + pRenderContext->DrawScreenSpaceRectangle( pMaterial, view.x, view.y, view.width, view.height, + 0, 0, m_pFlashTexture->GetActualWidth()-1, m_pFlashTexture->GetActualHeight()-1, + m_pFlashTexture->GetActualWidth(), m_pFlashTexture->GetActualHeight() ); + } + } + else if ( m_pFlashTexture ) + { + float flAlpha = pPlayer->m_flFlashMaxAlpha * (pPlayer->m_flFlashBangTime - gpGlobals->curtime) / pPlayer->m_flFlashDuration; + + flAlpha = clamp( flAlpha, 0, pPlayer->m_flFlashMaxAlpha ); + + overlaycolor[0] = overlaycolor[1] = overlaycolor[2] = flAlpha; + + render->ViewDrawFade( overlaycolor, pMaterial ); + + // just do one pass for dxlevel < 80. + if (g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80) + { + pRenderContext->DrawScreenSpaceRectangle( pMaterial, view.x, view.y, view.width, view.height, + 0, 0, m_pFlashTexture->GetActualWidth()-1, m_pFlashTexture->GetActualHeight()-1, + m_pFlashTexture->GetActualWidth(), m_pFlashTexture->GetActualHeight() ); + render->ViewDrawFade( overlaycolor, pMaterial ); + pRenderContext->DrawScreenSpaceRectangle( pMaterial, view.x, view.y, view.width, view.height, + 0, 0, m_pFlashTexture->GetActualWidth()-1, m_pFlashTexture->GetActualHeight()-1, + m_pFlashTexture->GetActualWidth(), m_pFlashTexture->GetActualHeight() ); + } + } + + // this does the pure white overlay part of the flashbang effect. + pMaterial = materials->FindMaterial( "effects/flashbang_white", TEXTURE_GROUP_CLIENT_EFFECTS, true ); + + if ( !pMaterial ) + return; + + float flAlpha = 255; + + if ( pPlayer->m_flFlashAlpha < pPlayer->m_flFlashMaxAlpha ) + { + flAlpha = pPlayer->m_flFlashAlpha; + } + else + { + float flFlashTimeLeft = pPlayer->m_flFlashBangTime - gpGlobals->curtime; + float flAlphaPercentage = 1.0; + const float certainBlindnessTimeThresh = 3.0; // yes this is a magic number, necessary to match CS/CZ flashbang effectiveness cause the rendering system is completely different. + + if (flFlashTimeLeft > certainBlindnessTimeThresh) + { + // if we still have enough time of blindness left, make sure the player can't see anything yet. + flAlphaPercentage = 1.0; + } + else + { + // blindness effects shorter than 'certainBlindnessTimeThresh' will start off at less than 255 alpha. + flAlphaPercentage = flFlashTimeLeft / certainBlindnessTimeThresh; + + if (g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80) + { + // reduce alpha level quicker with dx 8 support and higher to compensate + // for having the burn-in effect. + flAlphaPercentage *= flAlphaPercentage; + } + } + + flAlpha = flAlphaPercentage *= pPlayer->m_flFlashMaxAlpha; // scale a [0..1) value to a [0..MaxAlpha] value for the alpha. + + // make sure the alpha is in the range of [0..MaxAlpha] + flAlpha = MAX ( flAlpha, 0 ); + flAlpha = MIN ( flAlpha, pPlayer->m_flFlashMaxAlpha); + } + + overlaycolor[0] = overlaycolor[1] = overlaycolor[2] = flAlpha; + render->ViewDrawFade( overlaycolor, pMaterial ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Renders extra 2D effects in derived classes while the 2D view is on the stack +//----------------------------------------------------------------------------- +void CCSViewRender::Render2DEffectsPreHUD( const CViewSetup &view ) +{ + PerformNightVisionEffect( view ); // this needs to come before the HUD is drawn, or it will wash the HUD out +} + + +//----------------------------------------------------------------------------- +// Purpose: Renders extra 2D effects in derived classes while the 2D view is on the stack +//----------------------------------------------------------------------------- +void CCSViewRender::Render2DEffectsPostHUD( const CViewSetup &view ) +{ + PerformFlashbangEffect( view ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Renders voice feedback and other sprites attached to players +// Input : none +//----------------------------------------------------------------------------- +void CCSViewRender::RenderPlayerSprites() +{ + GetClientVoiceMgr()->SetHeadLabelOffset( 40 ); + + CViewRender::RenderPlayerSprites(); + RadioManager()->DrawHeadLabels(); +} + diff --git a/game/client/cstrike/cs_view_scene.h b/game/client/cstrike/cs_view_scene.h new file mode 100644 index 0000000..7c8c538 --- /dev/null +++ b/game/client/cstrike/cs_view_scene.h @@ -0,0 +1,40 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CS_VIEW_SCENE_H +#define CS_VIEW_SCENE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "viewrender.h" + +//----------------------------------------------------------------------------- +// Purpose: Implements the interview to view rendering for the client .dll +//----------------------------------------------------------------------------- +class CCSViewRender : public CViewRender +{ +public: + CCSViewRender(); + + virtual void Init( void ); + + virtual void GetScreenFadeDistances( float *min, float *max ); + + virtual void Render2DEffectsPreHUD( const CViewSetup &view ); + virtual void Render2DEffectsPostHUD( const CViewSetup &view ); + virtual void RenderPlayerSprites( void ); + +private: + + void PerformFlashbangEffect( const CViewSetup &view ); + void PerformNightVisionEffect( const CViewSetup &view ); + + ITexture *m_pFlashTexture; +}; + +#endif //CS_VIEW_SCENE_H
\ No newline at end of file diff --git a/game/client/cstrike/fx_cs_blood.cpp b/game/client/cstrike/fx_cs_blood.cpp new file mode 100644 index 0000000..faf3c4b --- /dev/null +++ b/game/client/cstrike/fx_cs_blood.cpp @@ -0,0 +1,482 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: A blood spray effect to expose successful hits. +// +//=============================================================================// + +#include "cbase.h" +#include "clienteffectprecachesystem.h" +#include "fx_sparks.h" +#include "iefx.h" +#include "c_te_effect_dispatch.h" +#include "particles_ez.h" +#include "decals.h" +#include "engine/IEngineSound.h" +#include "fx_quad.h" +#include "engine/ivdebugoverlay.h" +#include "shareddefs.h" +#include "fx_blood.h" +#include "view.h" +#include "c_cs_player.h" + +CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectCSBloodSpray ) +CLIENTEFFECT_MATERIAL( "effects/blood_gore" ) +CLIENTEFFECT_MATERIAL( "effects/blood_drop" ) +CLIENTEFFECT_MATERIAL( "effects/blood_puff" ) +CLIENTEFFECT_REGISTER_END() + + +class CHitEffectRamp +{ +public: + float m_flDamageAmount; + + float m_flMinAlpha; + float m_flMaxAlpha; + + float m_flMinSize; + float m_flMaxSize; + + float m_flMinVelocity; + float m_flMaxVelocity; +}; + + +void InterpolateRamp( + const CHitEffectRamp &a, + const CHitEffectRamp &b, + CHitEffectRamp &out, + int iDamage ) +{ + float t = RemapVal( iDamage, a.m_flDamageAmount, b.m_flDamageAmount, 0, 1 ); + + out.m_flMinAlpha = FLerp( a.m_flMinAlpha, b.m_flMinAlpha, t ); + out.m_flMaxAlpha = FLerp( a.m_flMaxAlpha, b.m_flMaxAlpha, t ); + out.m_flMinAlpha = clamp( out.m_flMinAlpha, 0, 255 ); + out.m_flMaxAlpha = clamp( out.m_flMaxAlpha, 0, 255 ); + + out.m_flMinSize = FLerp( a.m_flMinSize, b.m_flMinSize, t ); + out.m_flMaxSize = FLerp( a.m_flMaxSize, b.m_flMaxSize, t ); + + out.m_flMinVelocity = FLerp( a.m_flMinVelocity, b.m_flMinVelocity, t ); + out.m_flMaxVelocity = FLerp( a.m_flMaxVelocity, b.m_flMaxVelocity, t ); +} + + +void FX_HitEffectSmoke( + CSmartPtr<CBloodSprayEmitter> pEmitter, + int iDamage, + const Vector &vEntryPoint, + const Vector &vDirection, + float flScale) +{ + SimpleParticle newParticle; + PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_gore" ); + + // These parameters create a ramp based on how much damage the shot did. + CHitEffectRamp ramps[2] = + { + { + 0, + 30, // min/max alpha + 70, + 0.5, // min/max size + 1, + 0, // min/max velocity (not used here) + 0 + }, + + { + 50, + 30, // min/max alpha + 70, + 1, // min/max size + 2, + 0, // min/max velocity (not used here) + 0 + } + }; + + CHitEffectRamp interpolatedRamp; + InterpolateRamp( + ramps[0], + ramps[1], + interpolatedRamp, + iDamage ); + + for ( int i=0; i < 2; i++ ) + { + SimpleParticle &newParticle = *pEmitter->AddSimpleParticle( hMaterial, vEntryPoint, 0, 0 ); + + newParticle.m_flLifetime = 0.0f; + newParticle.m_flDieTime = 3.0f; + + newParticle.m_uchStartSize = random->RandomInt( + interpolatedRamp.m_flMinSize, + interpolatedRamp.m_flMaxSize ) * flScale; + newParticle.m_uchEndSize = newParticle.m_uchStartSize * 4; + + newParticle.m_vecVelocity = Vector( 0, 0, 5 ) + RandomVector( -2, 2 ); + newParticle.m_uchStartAlpha = random->RandomInt( + interpolatedRamp.m_flMinSize, + interpolatedRamp.m_flMaxSize ); + newParticle.m_uchEndAlpha = 0; + + newParticle.m_flRoll = random->RandomFloat( 0, 360 ); + newParticle.m_flRollDelta = random->RandomFloat( -1, 1 ); + + newParticle.m_iFlags = SIMPLE_PARTICLE_FLAG_NO_VEL_DECAY; + + float colorRamp = random->RandomFloat( 0.5f, 1.25f ); + + newParticle.m_uchColor[0] = MIN( 1.0f, colorRamp ) * 255.0f; + newParticle.m_uchColor[1] = MIN( 1.0f, colorRamp ) * 255.0f; + newParticle.m_uchColor[2] = MIN( 1.0f, colorRamp ) * 255.0f; + } +} + + +void FX_HitEffectBloodSpray( + CSmartPtr<CBloodSprayEmitter> pEmitter, + int iDamage, + const Vector &vEntryPoint, + const Vector &vSprayNormal, + const char *pMaterialName, + float flLODDistance, + float flDistanceScale, + float flScale, + float flSpeed ) +{ + PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( pMaterialName ); + SimpleParticle *pParticle; + + float color[3] = { 1.0, 0, 0 }; + + Vector up( 0, 0, 1 ); + Vector right = up.Cross( vSprayNormal ); + VectorNormalize( right ); + + // These parameters create a ramp based on how much damage the shot did. + CHitEffectRamp ramps[2] = + { + { + 0, + 80, // min/max alpha + 128, + flScale/2,// min/max size + flScale, + 10, // min/max velocity + 20 + }, + + { + 50, + 80, // min/max alpha + 128, + flScale/2,// min/max size + flScale, + 30, // min/max velocity + 60 + } + }; + + CHitEffectRamp interpolatedRamp; + InterpolateRamp( + ramps[0], + ramps[1], + interpolatedRamp, + iDamage ); + + for ( int i = 0; i < 6; i++ ) + { + // Originate from within a circle '2 * scale' inches in diameter. + Vector offset = vEntryPoint + ( flScale * vSprayNormal * 0.5 ); + offset += right * random->RandomFloat( -1, 1 ) * flScale; + offset += up * random->RandomFloat( -1, 1 ) * flScale; + + pParticle = pEmitter->AddSimpleParticle( hMaterial, offset, 0, 0 ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = random->RandomFloat( 0.7f, 1.3f); + + // All the particles are between red and white. The whiter the particle is, the slower it goes. + float whiteness = random->RandomFloat( 0.1, 0.7 ); + float speedFactor = 1 - whiteness; + + float spread = 0.5f; + pParticle->m_vecVelocity.Random( -spread, spread ); + pParticle->m_vecVelocity += vSprayNormal * random->RandomInt( interpolatedRamp.m_flMinVelocity, interpolatedRamp.m_flMaxVelocity ) * flSpeed * speedFactor; + + float colorRamp = random->RandomFloat( 0.5f, 0.75f ) + flLODDistance; + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, whiteness * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, whiteness * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = random->RandomFloat( interpolatedRamp.m_flMinSize, interpolatedRamp.m_flMaxSize ) * flDistanceScale; + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 4 * flDistanceScale; + + pParticle->m_uchStartAlpha = random->RandomInt( interpolatedRamp.m_flMinAlpha, interpolatedRamp.m_flMaxAlpha ); + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f ); + } + } +} + + +void FX_HitEffectBloodSplatter( + CSmartPtr<CBloodSprayEmitter> pTrailEmitter, + int iDamage, + const Vector &vExitPoint, + const Vector &vSplatterNormal, + float flLODDistance ) +{ + float flScale = 4; + + pTrailEmitter->SetSortOrigin( vExitPoint ); + PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_drop" ); + + Vector up( 0, 0, 1 ); + Vector right = up.Cross( vSplatterNormal ); + VectorNormalize( right ); + + // These parameters create a ramp based on how much damage the shot did. + CHitEffectRamp ramps[2] = + { + { + 0, + 0, // min/max alpha + 75, + 1.5f, // min/max size + 2.0f, + 25.0f * flScale, // min/max velocity + 35.0f * flScale + }, + + { + 50, + 0, // min/max alpha + 140, + 1.5f,// min/max size + 2.0f, + 65.0f * flScale, // min/max velocity + 75.0f * flScale + } + }; + + CHitEffectRamp interpolatedRamp; + InterpolateRamp( + ramps[0], + ramps[1], + interpolatedRamp, + iDamage ); + + + for ( int i = 0; i < 20; i++ ) + { + // Originate from within a circle 'scale' inches in diameter. + Vector offset = vExitPoint; + offset += right * random->RandomFloat( -0.15f, 0.15f ) * flScale; + offset += up * random->RandomFloat( -0.15f, 0.15f ) * flScale; + + SimpleParticle *tParticle = (SimpleParticle*)pTrailEmitter->AddSimpleParticle( + hMaterial, + vExitPoint, + random->RandomFloat( 0.225f, 0.35f ), + random->RandomFloat( interpolatedRamp.m_flMinSize, interpolatedRamp.m_flMaxSize ) + ); + + if ( tParticle == NULL ) + break; + + Vector offDir = vSplatterNormal + RandomVector( -0.05f, 0.05f ); + + tParticle->m_vecVelocity = offDir * random->RandomFloat( interpolatedRamp.m_flMinVelocity, interpolatedRamp.m_flMaxVelocity ); + + tParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_NO_VEL_DECAY; + tParticle->m_uchColor[0] = 150; + tParticle->m_uchColor[1] = 0; + tParticle->m_uchColor[2] = 0; + tParticle->m_uchStartAlpha = interpolatedRamp.m_flMaxAlpha / 2; + tParticle->m_uchEndAlpha = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : origin - +// normal - +// scale - +//----------------------------------------------------------------------------- +void FX_CS_BloodSpray( const Vector &origin, const Vector &normal, float flDamage ) +{ + if ( UTIL_IsLowViolence() ) + return; + + static ConVar *violence_hblood = cvar->FindVar( "violence_hblood" ); + if ( violence_hblood && !violence_hblood->GetBool() ) + return; + + Vector offset; + int i; + + float r = 64; + float g = 0; + float b = 4; + + float scale = 0.5 + clamp( flDamage/50.f, 0.0, 1.0 ) ; + + //Find area ambient light color and use it to tint smoke + Vector worldLight = WorldGetLightForPoint( origin, true ); + Vector color = Vector( (float)(worldLight[0] * r) / 255.0f, (float)(worldLight[1] * g) / 255.0f, (float)(worldLight[2] * b) / 255.0f ); + float colorRamp; + + Vector offDir; + + CSmartPtr<CBloodSprayEmitter> pSimple = CBloodSprayEmitter::Create( "bloodgore" ); + if ( !pSimple ) + return; + + pSimple->SetSortOrigin( origin ); + pSimple->SetGravity( 0 ); + + // Blood impact + PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_core" ); + + SimpleParticle *pParticle; + + Vector dir = normal * RandomVector( -0.5f, 0.5f ); + + offset = origin + ( 2.0f * normal ); + + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = 0.75f; + + pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f ); + pParticle->m_vecVelocity[2] -= random->RandomFloat( 8.0f, 16.0f ); + + colorRamp = random->RandomFloat( 0.75f, 2.0f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = 8; + pParticle->m_uchEndSize = 32; + + pParticle->m_uchStartAlpha = 255; + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = 0; + } + + hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_gore" ); + + for ( i = 0; i < 4; i++ ) + { + offset = origin + ( 2.0f * normal ); + + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = random->RandomFloat( 0.75f, 1.0f); + + pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f )*(i+1); + pParticle->m_vecVelocity[2] -= random->RandomFloat( 16.0f, 32.0f )*(i+1); + + colorRamp = random->RandomFloat( 0.75f, 2.0f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = scale * random->RandomInt( 4, 8 ); + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 4; + + pParticle->m_uchStartAlpha = 255; + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = 0; + } + } + + // + // Dump out drops + // + TrailParticle *tParticle; + + CSmartPtr<CTrailParticles> pTrailEmitter = CTrailParticles::Create( "blooddrops" ); + if ( !pTrailEmitter ) + return; + + pTrailEmitter->SetSortOrigin( origin ); + + // Partial gravity on blood drops + pTrailEmitter->SetGravity( 400.0 ); + + // Enable simple collisions with nearby surfaces + pTrailEmitter->Setup(origin, &normal, 1, 10, 100, 400, 0.2, 0 ); + + hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_drop" ); + + // + // Shorter droplets + // + for ( i = 0; i < 32; i++ ) + { + // Originate from within a circle 'scale' inches in diameter + offset = origin; + + tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset ); + + if ( tParticle == NULL ) + break; + + tParticle->m_flLifetime = 0.0f; + + offDir = RandomVector( -1.0f, 1.0f ); + + tParticle->m_vecVelocity = offDir * random->RandomFloat( 32.0f, 128.0f ); + + tParticle->m_flWidth = scale * random->RandomFloat( 1.0f, 3.0f ); + tParticle->m_flLength = random->RandomFloat( 0.1f, 0.15f ); + tParticle->m_flDieTime = random->RandomFloat( 0.5f, 1.0f ); + + FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bloodtype - +// r - +// g - +// b - +//----------------------------------------------------------------------------- +void GetBloodColorForTeam( int iTeam, unsigned char &r, unsigned char &g, unsigned char &b ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: Intercepts the blood spray message. +//----------------------------------------------------------------------------- +void CSBloodSprayCallback( const CEffectData &data ) +{ + FX_CS_BloodSpray( data.m_vOrigin, data.m_vNormal, data.m_flMagnitude ); +} + +DECLARE_CLIENT_EFFECT( "csblood", CSBloodSprayCallback ); diff --git a/game/client/cstrike/fx_cs_blood.h b/game/client/cstrike/fx_cs_blood.h new file mode 100644 index 0000000..5f496d6 --- /dev/null +++ b/game/client/cstrike/fx_cs_blood.h @@ -0,0 +1,20 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FX_CS_BLOOD_H +#define FX_CS_BLOOD_H +#ifdef _WIN32 +#pragma once +#endif + + +void FX_CS_BloodSpray( + const Vector &origin, + const Vector &normal, + float flDamage ); + + +#endif // FX_CS_BLOOD_H diff --git a/game/client/cstrike/fx_cs_impacts.cpp b/game/client/cstrike/fx_cs_impacts.cpp new file mode 100644 index 0000000..c1ae37f --- /dev/null +++ b/game/client/cstrike/fx_cs_impacts.cpp @@ -0,0 +1,44 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Game-specific impact effect hooks +// +//=============================================================================// +#include "cbase.h" +#include "fx_impact.h" +#include "engine/IEngineSound.h" + + + +//----------------------------------------------------------------------------- +// Purpose: Handle weapon impacts +//----------------------------------------------------------------------------- +void ImpactCallback( const CEffectData &data ) +{ + trace_t tr; + Vector vecOrigin, vecStart, vecShotDir; + int iMaterial, iDamageType, iHitbox; + short nSurfaceProp; + + C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); + + if ( !pEntity ) + return; + + // If we hit, perform our custom effects and play the sound + if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) ) + { + // Check for custom effects based on the Decal index + PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 1.0 ); + + //Play a ricochet sound some of the time + if( random->RandomInt(1,10) <= 3 && (iDamageType == DMG_BULLET) ) + { + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, "Bounce.Shrapnel", &vecOrigin ); + } + } + + PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); +} + +DECLARE_CLIENT_EFFECT( "Impact", ImpactCallback ); diff --git a/game/client/cstrike/fx_cs_knifeslash.cpp b/game/client/cstrike/fx_cs_knifeslash.cpp new file mode 100644 index 0000000..107ee36 --- /dev/null +++ b/game/client/cstrike/fx_cs_knifeslash.cpp @@ -0,0 +1,72 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Game-specific impact effect hooks +// +//=============================================================================// +#include "cbase.h" +#include "decals.h" +#include "iefx.h" +#include "fx_impact.h" +#include "tempent.h" +#include "c_te_effect_dispatch.h" +#include "c_te_legacytempents.h" + +void KnifeSlash( const CEffectData &data ) +{ + trace_t tr; + Vector vecOrigin, vecStart, vecShotDir; + int iMaterial, iDamageType, iHitbox; + short nSurfaceProp; + + C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); + + if( pEntity == NULL ) + return; + + int decalNumber = decalsystem->GetDecalIndexForName( GetImpactDecal( pEntity, iMaterial, iDamageType ) ); + if ( decalNumber == -1 ) + return; + + // vector perpendicular to the slash direction + // so we can align the slash decal to that + Vector vecPerp; + AngleVectors( data.m_vAngles, NULL, &vecPerp, NULL ); + + const ConVar *decals =cvar->FindVar( "r_decals" ); + + if ( decals && decals->GetInt() ) + { + if ( (pEntity->entindex() == 0) && (iHitbox != 0) ) + { + // Setup our shot information + Vector shotDir = vecOrigin - vecStart; + float flLength = VectorNormalize( shotDir ); + Vector traceExt; + VectorMA( vecStart, flLength + 8.0f, shotDir, traceExt ); + + // Special case for world entity with hitbox (that's a static prop): + // In this case, we've hit a static prop. Decal it! + staticpropmgr->AddDecalToStaticProp( vecStart, traceExt, iHitbox - 1, decalNumber, true, tr ); + } + else + { + effects->DecalShoot( decalNumber, + pEntity->entindex(), + pEntity->GetModel(), + pEntity->GetAbsOrigin(), + pEntity->GetAbsAngles(), + vecOrigin, + &vecPerp, + 0 ); + } + + } + + if( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr, data.m_fFlags ) ) + { + // Check for custom effects based on the Decal index + PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 1.0 ); + } +} + +DECLARE_CLIENT_EFFECT( "KnifeSlash", KnifeSlash ); diff --git a/game/client/cstrike/fx_cs_muzzleflash.cpp b/game/client/cstrike/fx_cs_muzzleflash.cpp new file mode 100644 index 0000000..9f3223c --- /dev/null +++ b/game/client/cstrike/fx_cs_muzzleflash.cpp @@ -0,0 +1,139 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "particles_simple.h" +#include "particles_localspace.h" +#include "c_te_effect_dispatch.h" +#include "clienteffectprecachesystem.h" + +// Precache our effects +CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffect_CS_MuzzleFlash ) +CLIENTEFFECT_MATERIAL( "effects/muzzleflashX" ) //.vmt +CLIENTEFFECT_MATERIAL( "sprites/muzzleflash4" ) //.vmt +CLIENTEFFECT_REGISTER_END() + +void TE_DynamicLight( IRecipientFilter& filter, float delay, + const Vector* org, int r, int g, int b, int exponent, float radius, float time, float decay, int nLightIndex = LIGHT_INDEX_TE_DYNAMIC ); + +void CS_MuzzleFlashCallback( const CEffectData &data ) +{ + CSmartPtr<CLocalSpaceEmitter> pEmitter = + CLocalSpaceEmitter::Create( "CS_MuzzleFlash", data.m_hEntity, data.m_nAttachmentIndex, 0 ); + + if ( !pEmitter ) + return; + + // SetBBox() manually on the particle system so it doesn't have to be recalculated more than once. + Vector vCenter( 0.0f, 0.0f, 0.0f ); + C_BaseEntity *pEnt = data.GetEntity(); + if ( pEnt ) + { + vCenter = pEnt->WorldSpaceCenter(); + } + else + { + IClientRenderable *pRenderable = data.GetRenderable( ); + if ( pRenderable ) + { + Vector vecMins, vecMaxs; + pRenderable->GetRenderBoundsWorldspace( vecMins, vecMaxs ); + VectorAdd( vecMins, vecMaxs, vCenter ); + vCenter *= 0.5f; + } + } + + Assert( pEmitter ); + pEmitter->GetBinding().SetBBox( vCenter - Vector( 3, 3, 3 ), vCenter + Vector( 3, 3, 3 ) ); + + // haxors - make the clip much shorter so the alpha is not + // changed based on large clip distances + pEmitter->SetNearClip( 0, 5 ); + + PMaterialHandle hFlashMaterial = pEmitter->GetPMaterial( "sprites/muzzleflash4" ); + + for( int i=0;i<3;i++ ) + { + SimpleParticle *pParticle = (SimpleParticle *)pEmitter->AddParticle( sizeof( SimpleParticle ), + hFlashMaterial, + vec3_origin ); + if( pParticle ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = 0.08f; + + pParticle->m_vecVelocity = vec3_origin; + + pParticle->m_uchColor[0] = 255; + pParticle->m_uchColor[1] = 255; + pParticle->m_uchColor[2] = 255; + + pParticle->m_uchStartAlpha = 80; + pParticle->m_uchEndAlpha = 30; + + pParticle->m_uchStartSize = ( 3.0 + 3.0*i ) * data.m_flScale; + + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 0.8; + pParticle->m_flRoll = random->RandomInt( 0, 3 ); + + pParticle->m_flRollDelta = 0.0f; + } + } + + // dynamic light temporary entity for the muzzle flash + CPVSFilter filter(pEmitter->GetSortOrigin()); + TE_DynamicLight( filter, 0.0, &(pEmitter->GetSortOrigin()), 255, 192, 64, 5, 70, 0.05, 768 ); +} + +DECLARE_CLIENT_EFFECT( "CS_MuzzleFlash", CS_MuzzleFlashCallback ); + + +// 'X' shaped muzzleflash used by certain weapons +void CS_MuzzleFlashXCallback( const CEffectData &data ) +{ + CSmartPtr<CLocalSpaceEmitter> pEmitter = + CLocalSpaceEmitter::Create( "CS_MuzzleFlashX", data.m_hEntity, data.m_nAttachmentIndex, 0 ); + + Assert( pEmitter ); + + // haxors - make the clip much shorter so the alpha is not + // changed based on large clip distances + pEmitter->SetNearClip( 0, 5 ); + + PMaterialHandle hFlashMaterial = pEmitter->GetPMaterial( "effects/muzzleflashX" ); + + SimpleParticle *pParticle = (SimpleParticle *)pEmitter->AddParticle( sizeof( SimpleParticle ), + hFlashMaterial, + vec3_origin ); + if( pParticle ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = 0.08f; + + pParticle->m_vecVelocity = vec3_origin; + + pParticle->m_uchColor[0] = 255; + pParticle->m_uchColor[1] = 255; + pParticle->m_uchColor[2] = 255; + + pParticle->m_uchStartAlpha = 130; + pParticle->m_uchEndAlpha = 80; + + pParticle->m_uchStartSize = 6.0f * data.m_flScale * random->RandomFloat( 0.9, 1.1 ); + + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 0.8; + + pParticle->m_flRoll = random->RandomFloat( -0.25, 0.25 ); + + pParticle->m_flRollDelta = 0.0f; + } + + // dynamic light temporary entity for the muzzle flash + CPVSFilter filter(pEmitter->GetSortOrigin()); + TE_DynamicLight( filter, 0.0, &(pEmitter->GetSortOrigin()), 255, 192, 64, 5, 70, 0.05, 768 ); +} + +DECLARE_CLIENT_EFFECT( "CS_MuzzleFlash_X", CS_MuzzleFlashXCallback ); diff --git a/game/client/cstrike/fx_cs_weaponfx.cpp b/game/client/cstrike/fx_cs_weaponfx.cpp new file mode 100644 index 0000000..4364d64 --- /dev/null +++ b/game/client/cstrike/fx_cs_weaponfx.cpp @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Game-specific impact effect hooks +// +//=============================================================================// +#include "cbase.h" +#include "fx_impact.h" +#include "tempent.h" +#include "c_te_effect_dispatch.h" +#include "c_te_legacytempents.h" + + +//----------------------------------------------------------------------------- +// Purpose: Handle weapon effect callbacks +//----------------------------------------------------------------------------- +void CStrike_EjectBrass( int shell, const CEffectData &data ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if( !pPlayer ) + return; + + tempents->CSEjectBrass( data.m_vOrigin, data.m_vAngles, data.m_fFlags, shell, pPlayer ); +} + +void CStrike_FX_EjectBrass_9mm_Callback( const CEffectData &data ) +{ + CStrike_EjectBrass( CS_SHELL_9MM, data ); +} + +void CStrike_FX_EjectBrass_57_Callback( const CEffectData &data ) +{ + CStrike_EjectBrass( CS_SHELL_57, data ); +} + +void CStrike_FX_EjectBrass_12Gauge_Callback( const CEffectData &data ) +{ + CStrike_EjectBrass( CS_SHELL_12GAUGE, data ); +} + +void CStrike_FX_EjectBrass_556_Callback( const CEffectData &data ) +{ + CStrike_EjectBrass( CS_SHELL_556, data ); +} + +void CStrike_FX_EjectBrass_762Nato_Callback( const CEffectData &data ) +{ + CStrike_EjectBrass( CS_SHELL_762NATO, data ); +} + +void CStrike_FX_EjectBrass_338Mag_Callback( const CEffectData &data ) +{ + CStrike_EjectBrass( CS_SHELL_338MAG, data ); +} + +DECLARE_CLIENT_EFFECT( "EjectBrass_9mm", CStrike_FX_EjectBrass_9mm_Callback ); +DECLARE_CLIENT_EFFECT( "EjectBrass_12Gauge", CStrike_FX_EjectBrass_12Gauge_Callback ); +DECLARE_CLIENT_EFFECT( "EjectBrass_57", CStrike_FX_EjectBrass_57_Callback ); +DECLARE_CLIENT_EFFECT( "EjectBrass_556", CStrike_FX_EjectBrass_556_Callback ); +DECLARE_CLIENT_EFFECT( "EjectBrass_762Nato", CStrike_FX_EjectBrass_762Nato_Callback ); +DECLARE_CLIENT_EFFECT( "EjectBrass_338Mag", CStrike_FX_EjectBrass_338Mag_Callback ); diff --git a/game/client/cstrike/hud_account.cpp b/game/client/cstrike/hud_account.cpp new file mode 100644 index 0000000..abd9f57 --- /dev/null +++ b/game/client/cstrike/hud_account.cpp @@ -0,0 +1,66 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hud_base_account.h" +#include "c_cs_player.h" +#include "clientmode_csnormal.h" + +using namespace vgui; + +class CHudAccount : public CHudBaseAccount +{ +public: + DECLARE_CLASS_SIMPLE( CHudAccount, CHudBaseAccount ); + + CHudAccount( const char *name ); + + virtual bool ShouldDraw(); + virtual int GetPlayerAccount( void ); + virtual vgui::AnimationController *GetAnimationController( void ); +}; + +DECLARE_HUDELEMENT( CHudAccount ); + +CHudAccount::CHudAccount( const char *pName ) : +CHudBaseAccount( "HudAccount" ) +{ + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + SetIndent( false ); // don't indent small numbers in the drawing code - we're doing it manually +} + +bool CHudAccount::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + return !pPlayer->IsObserver(); + } + else + { + return false; + } +} + +// How much money does the player have +int CHudAccount::GetPlayerAccount( void ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if( !pPlayer ) + return 0; + + return (int)pPlayer->GetAccount(); +} + +vgui::AnimationController *CHudAccount::GetAnimationController( void ) +{ + vgui::AnimationController *pController = g_pClientMode->GetViewportAnimationController(); + + Assert( pController ); + + return pController; +}
\ No newline at end of file diff --git a/game/client/cstrike/hud_armor.cpp b/game/client/cstrike/hud_armor.cpp new file mode 100644 index 0000000..cbdb1de --- /dev/null +++ b/game/client/cstrike/hud_armor.cpp @@ -0,0 +1,124 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + //====== Copyright � 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "cs_gamerules.h" +#include "hud_numericdisplay.h" + + +class CHudArmor : public CHudElement, public CHudNumericDisplay +{ +public: + DECLARE_CLASS_SIMPLE( CHudArmor, CHudNumericDisplay ); + + CHudArmor( const char *name ); + + virtual bool ShouldDraw(); + virtual void Paint(); + virtual void Init(); + virtual void ApplySchemeSettings( IScheme *scheme ); + +private: + CHudTexture *m_pArmorIcon; + CHudTexture *m_pArmor_HelmetIcon; + + CPanelAnimationVarAliasType( float, icon_xpos, "icon_xpos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, icon_ypos, "icon_ypos", "2", "proportional_float" ); + + float icon_wide; + float icon_tall; +}; + + +DECLARE_HUDELEMENT( CHudArmor ); + + +CHudArmor::CHudArmor( const char *pName ) : CHudNumericDisplay( NULL, "HudArmor" ), CHudElement( pName ) +{ + SetHiddenBits( HIDEHUD_HEALTH | HIDEHUD_PLAYERDEAD ); +} + + +void CHudArmor::Init() +{ + SetIndent(true); +} + +void CHudArmor::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + if( !m_pArmorIcon ) + { + m_pArmorIcon = gHUD.GetIcon( "shield_bright" ); + } + + if( !m_pArmor_HelmetIcon ) + { + m_pArmor_HelmetIcon = gHUD.GetIcon( "shield_kevlar_bright" ); + } + + if( m_pArmorIcon ) + { + icon_tall = GetTall() - YRES(2); + float scale = icon_tall / (float)m_pArmorIcon->Height(); + icon_wide = ( scale ) * (float)m_pArmorIcon->Width(); + } +} + + +bool CHudArmor::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + return !pPlayer->IsObserver(); + } + else + { + return false; + } +} + + +void CHudArmor::Paint() +{ + // Update the time. + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + if( pPlayer->HasHelmet() && (int)pPlayer->ArmorValue() > 0 ) + { + if( m_pArmor_HelmetIcon ) + { + m_pArmor_HelmetIcon->DrawSelf( icon_xpos, icon_ypos, icon_wide, icon_tall, GetFgColor() ); + } + } + else + { + if( m_pArmorIcon ) + { + m_pArmorIcon->DrawSelf( icon_xpos, icon_ypos, icon_wide, icon_tall, GetFgColor() ); + } + } + + SetDisplayValue( (int)pPlayer->ArmorValue() ); + SetShouldDisplayValue( true ); + BaseClass::Paint(); + } +} + diff --git a/game/client/cstrike/hud_c4.cpp b/game/client/cstrike/hud_c4.cpp new file mode 100644 index 0000000..0c39e55 --- /dev/null +++ b/game/client/cstrike/hud_c4.cpp @@ -0,0 +1,128 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "cs_gamerules.h" +#include "hud_numericdisplay.h" + + +class CHudC4 : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudC4, vgui::Panel ); + + CHudC4( const char *name ); + + virtual bool ShouldDraw(); + virtual void Paint(); + virtual void Init(); + + +private: + CPanelAnimationVar( Color, m_clrIcon, "IconColor", "IconColor" ); + CPanelAnimationVar( Color, m_clrFlash, "FlashColor", "FlashColor" ); + + CHudTexture *m_pIcon; + + float m_flNextFlashTime; + bool m_bFlash; +}; + + +DECLARE_HUDELEMENT( CHudC4 ); + + +CHudC4::CHudC4( const char *pName ) : + vgui::Panel( NULL, "HudC4" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + m_pIcon = NULL; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + //============================================================================= + // HPE_BEGIN: + // [tj] Add this to the render group that disappears when the scoreboard is up + //============================================================================= + RegisterForRenderGroup( "hide_for_scoreboard" ); + //============================================================================= + // HPE_END + //============================================================================= +} + + +void CHudC4::Init() +{ + m_flNextFlashTime = 0; + m_bFlash = false; +} + + +bool CHudC4::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + // if we are spectating another player first person, check this player + if ( pPlayer && (pPlayer->GetObserverMode() == OBS_MODE_IN_EYE) ) + { + pPlayer = ToCSPlayer( pPlayer->GetObserverTarget() ); + } + + //============================================================================= + // HPE_BEGIN: + // [tj] Added base class call + //============================================================================= + return pPlayer && pPlayer->HasC4() && CHudElement::ShouldDraw(); + //============================================================================= + // HPE_END + //============================================================================= +} + + +void CHudC4::Paint() +{ + if ( !m_pIcon ) + { + m_pIcon = gHUD.GetIcon( "c4" ); + } + + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !pPlayer ) + return; + + if( pPlayer->m_bInBombZone ) + { + if( m_flNextFlashTime < gpGlobals->curtime ) + { + m_bFlash = !m_bFlash; + m_flNextFlashTime = gpGlobals->curtime + 0.15; + } + } + else + { + m_bFlash = false; + } + + int x, y, w, h; + GetBounds( x, y, w, h ); + + if ( m_pIcon ) + { + if( m_bFlash ) + { + m_pIcon->DrawSelf( 0, 0, w, h, m_clrFlash ); + } + else + { + m_pIcon->DrawSelf( 0, 0, w, h, m_clrIcon ); + } + } +} + diff --git a/game/client/cstrike/hud_deathnotice.cpp b/game/client/cstrike/hud_deathnotice.cpp new file mode 100644 index 0000000..3ca5d4a --- /dev/null +++ b/game/client/cstrike/hud_deathnotice.cpp @@ -0,0 +1,485 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Draws CSPort's death notices +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "hudelement.h" +#include "hud_macros.h" +#include "c_playerresource.h" +#include "iclientmode.h" +#include <vgui_controls/Controls.h> +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include <KeyValues.h> +#include "c_baseplayer.h" +#include "c_team.h" + +#include "cs_shareddefs.h" +#include "clientmode_csnormal.h" +#include "c_cs_player.h" +#include "c_cs_playerresource.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +const int DOMINATION_DRAW_HEIGHT = 20; +const int DOMINATION_DRAW_WIDTH = 20; + +static ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 ); + +// Player entries in a death notice +struct DeathNoticePlayer +{ + char szName[MAX_PLAYER_NAME_LENGTH]; + char szClan[MAX_CLAN_TAG_LENGTH]; + int iEntIndex; + Color color; +}; + +// Contents of each entry in our list of death notices +struct DeathNoticeItem +{ + DeathNoticePlayer Killer; + DeathNoticePlayer Victim; + CHudTexture *iconDeath; + int iSuicide; + float flDisplayTime; + bool bHeadshot; + int iDominationImageId; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CHudDeathNotice : public CHudElement, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CHudDeathNotice, vgui::Panel ); +public: + CHudDeathNotice( const char *pElementName ); + + void Init( void ); + void VidInit( void ); + virtual bool ShouldDraw( void ); + virtual void Paint( void ); + virtual void ApplySchemeSettings( vgui::IScheme *scheme ); + + void RetireExpiredDeathNotices( void ); + + void FireGameEvent( IGameEvent *event ); + +protected: + int SetupHudImageId( const char* fname ); + +private: + + CPanelAnimationVarAliasType( float, m_flLineHeight, "LineHeight", "15", "proportional_float" ); + + CPanelAnimationVar( float, m_flMaxDeathNotices, "MaxDeathNotices", "4" ); + + CPanelAnimationVar( bool, m_bRightJustify, "RightJustify", "1" ); + + CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudNumbersTimer" ); + + CPanelAnimationVar( Color, m_clrCTText, "CTTextColor", "CTTextColor" ); + CPanelAnimationVar( Color, m_clrTerroristText, "TerroristTextColor", "TerroristTextColor" ); + + // Texture for skull symbol + CHudTexture *m_iconD_skull; + CHudTexture *m_iconD_headshot; + + int m_iNemesisImageId; + int m_iDominatedImageId; + int m_iRevengeImageId; + + Color m_teamColors[TEAM_MAXCOUNT]; + + CUtlVector<DeathNoticeItem> m_DeathNotices; +}; + +using namespace vgui; + +DECLARE_HUDELEMENT( CHudDeathNotice ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudDeathNotice::CHudDeathNotice( const char *pElementName ) : + CHudElement( pElementName ), BaseClass( NULL, "HudDeathNotice" ) +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + m_iconD_headshot = NULL; + m_iconD_skull = NULL; + + SetHiddenBits( HIDEHUD_MISCSTATUS ); + + m_iNemesisImageId = SetupHudImageId("hud/freeze_nemesis"); + m_iDominatedImageId = SetupHudImageId("hud/freeze_dominated"); + m_iRevengeImageId = SetupHudImageId("hud/freeze_revenge"); +} + + +/** + * Helper function to get an image id and set + */ +int CHudDeathNotice::SetupHudImageId( const char* fname ) +{ + int imageId = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( imageId, fname, true, false ); + return imageId; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudDeathNotice::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + SetPaintBackgroundEnabled( false ); + + // make team color lookups easier + memset(m_teamColors, 0, sizeof(m_teamColors)); + m_teamColors[TEAM_CT] = m_clrCTText; + m_teamColors[TEAM_TERRORIST] = m_clrTerroristText; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudDeathNotice::Init( void ) +{ + ListenForGameEvent( "player_death" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudDeathNotice::VidInit( void ) +{ + m_iconD_skull = gHUD.GetIcon( "d_skull_cs" ); + m_iconD_headshot = gHUD.GetIcon( "d_headshot" ); + m_DeathNotices.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw if we've got at least one death notice in the queue +//----------------------------------------------------------------------------- +bool CHudDeathNotice::ShouldDraw( void ) +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !pPlayer ) + return false; + + // don't show death notices when flashed + if ( pPlayer->IsAlive() && pPlayer->m_flFlashBangTime >= gpGlobals->curtime ) + { + float flAlpha = pPlayer->m_flFlashMaxAlpha * (pPlayer->m_flFlashBangTime - gpGlobals->curtime) / pPlayer->m_flFlashDuration; + if ( flAlpha > 75.0f ) // 0..255 + { + return false; + } + } + + return ( CHudElement::ShouldDraw() && ( m_DeathNotices.Count() ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudDeathNotice::Paint() +{ + if ( !m_iconD_headshot || !m_iconD_skull ) + return; + + int yStart = GetClientModeCSNormal()->GetDeathMessageStartHeight(); + + surface()->DrawSetTextFont( m_hTextFont ); + surface()->DrawSetTextColor( m_clrCTText ); + + int dominationDrawWidth = scheme()->GetProportionalScaledValueEx( GetScheme(), DOMINATION_DRAW_WIDTH ); + int dominationDrawHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DOMINATION_DRAW_HEIGHT ); + + int iconHeadshotWide; + int iconHeadshotTall; + + if( m_iconD_headshot->bRenderUsingFont ) + { + iconHeadshotWide = surface()->GetCharacterWidth( m_iconD_headshot->hFont, m_iconD_headshot->cCharacterInFont ); + iconHeadshotTall = surface()->GetFontTall( m_iconD_headshot->hFont ); + } + else + { + float scale = ( (float)ScreenHeight() / 480.0f ); //scale based on 640x480 + iconHeadshotWide = (int)( scale * (float)m_iconD_headshot->Width() ); + iconHeadshotTall = (int)( scale * (float)m_iconD_headshot->Height() ); + } + + int iCount = m_DeathNotices.Count(); + for ( int i = 0; i < iCount; i++ ) + { + CHudTexture *icon = m_DeathNotices[i].iconDeath; + if ( !icon ) + continue; + + wchar_t victim[ 256 ]; + wchar_t killer[ 256 ]; + wchar_t victimclan[ 256 ]; + wchar_t killerclan[ 256 ]; + + g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Victim.szName, victim, sizeof( victim ) ); + g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Killer.szName, killer, sizeof( killer ) ); + g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Victim.szClan, victimclan, sizeof( victimclan ) ); + g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Killer.szClan, killerclan, sizeof( killerclan ) ); + + // Get the local position for this notice + int victimNameLen = UTIL_ComputeStringWidth( m_hTextFont, victim ); + int victimClanLen = UTIL_ComputeStringWidth( m_hTextFont, victimclan ); + int y = yStart + (m_flLineHeight * i); + + int iconWide; + int iconTall; + + if( icon->bRenderUsingFont ) + { + iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont ); + iconTall = surface()->GetFontTall( icon->hFont ); + } + else + { + float scale = ( (float)ScreenHeight() / 480.0f ); //scale based on 640x480 + iconWide = (int)( scale * (float)icon->Width() ); + iconTall = (int)( scale * (float)icon->Height() ); + } + + int x = 0; + if ( m_bRightJustify ) + { + x = GetWide(); + x -= victimNameLen; + x -= victimClanLen; + x -= iconWide; + + if ( m_DeathNotices[i].bHeadshot ) + x -= iconHeadshotWide; + + if ( !m_DeathNotices[i].iSuicide ) + { + x -= UTIL_ComputeStringWidth( m_hTextFont, killer ); + x -= UTIL_ComputeStringWidth( m_hTextFont, killerclan ); + } + + if (m_DeathNotices[i].iDominationImageId >= 0) + { + x -= dominationDrawWidth; + } + } + + if (m_DeathNotices[i].iDominationImageId >= 0) + { + surface()->DrawSetTexture(m_DeathNotices[i].iDominationImageId); + surface()->DrawSetColor(m_DeathNotices[i].Killer.color); + surface()->DrawTexturedRect( x, y, x + dominationDrawWidth, y + dominationDrawHeight ); + x += dominationDrawWidth; + } + + // Only draw killers name if it wasn't a suicide + if ( !m_DeathNotices[i].iSuicide ) + { + // Draw killer's clan + surface()->DrawSetTextColor( m_DeathNotices[i].Killer.color ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawSetTextFont( m_hTextFont ); + surface()->DrawUnicodeString( killerclan ); + surface()->DrawGetTextPos( x, y ); + + // Draw killer's name + surface()->DrawSetTextColor( m_DeathNotices[i].Killer.color ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawSetTextFont( m_hTextFont ); + surface()->DrawUnicodeString( killer ); + surface()->DrawGetTextPos( x, y ); + } + + + // Draw death weapon + //If we're using a font char, this will ignore iconTall and iconWide + Color iconColor( 255, 80, 0, 255 ); + icon->DrawSelf( x, y, iconWide, iconTall, iconColor ); + x += iconWide; + + if( m_DeathNotices[i].bHeadshot ) + { + m_iconD_headshot->DrawSelf( x, y, iconHeadshotWide, iconHeadshotTall, iconColor ); + x += iconHeadshotWide; + } + + // Draw victims clan + surface()->DrawSetTextColor( m_DeathNotices[i].Victim.color ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it + surface()->DrawUnicodeString( victimclan ); + surface()->DrawGetTextPos( x, y ); + + // Draw victims name + surface()->DrawSetTextColor( m_DeathNotices[i].Victim.color ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it + surface()->DrawUnicodeString( victim ); + } + + // Now retire any death notices that have expired + RetireExpiredDeathNotices(); +} + +//----------------------------------------------------------------------------- +// Purpose: This message handler may be better off elsewhere +//----------------------------------------------------------------------------- +void CHudDeathNotice::RetireExpiredDeathNotices( void ) +{ + // Loop backwards because we might remove one + int iSize = m_DeathNotices.Size(); + for ( int i = iSize-1; i >= 0; i-- ) + { + if ( m_DeathNotices[i].flDisplayTime < gpGlobals->curtime ) + { + m_DeathNotices.Remove(i); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Server's told us that someone's died +//----------------------------------------------------------------------------- +void CHudDeathNotice::FireGameEvent( IGameEvent *event ) +{ + if (!g_PR) + return; + + C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR ); + if ( !cs_PR ) + return; + + if ( hud_deathnotice_time.GetFloat() == 0 ) + return; + + // the event should be "player_death" + + int iKiller = engine->GetPlayerForUserID( event->GetInt("attacker") ); + int iVictim = engine->GetPlayerForUserID( event->GetInt("userid") ); + const char *killedwith = event->GetString( "weapon" ); + bool headshot = event->GetInt( "headshot" ) > 0; + + char fullkilledwith[128]; + if ( killedwith && *killedwith ) + { + Q_snprintf( fullkilledwith, sizeof(fullkilledwith), "d_%s", killedwith ); + } + else + { + fullkilledwith[0] = 0; + } + + // Do we have too many death messages in the queue? + if ( m_DeathNotices.Count() > 0 && + m_DeathNotices.Count() >= (int)m_flMaxDeathNotices ) + { + // Remove the oldest one in the queue, which will always be the first + m_DeathNotices.Remove(0); + } + + // Get the names of the players + const char *killer_name = iKiller > 0 ? g_PR->GetPlayerName( iKiller ) : NULL; + const char *victim_name = iVictim > 0 ? g_PR->GetPlayerName( iVictim ) : NULL; + + if ( !killer_name ) + killer_name = ""; + if ( !victim_name ) + victim_name = ""; + + // Get the clan tags of the players + const char *killer_clan = iKiller > 0 ? cs_PR->GetClanTag( iKiller ) : NULL; + const char *victim_clan = iVictim > 0 ? cs_PR->GetClanTag( iVictim ) : NULL; + + if ( !killer_clan ) + killer_clan = ""; + if ( !victim_clan ) + victim_clan = ""; + + // Make a new death notice + DeathNoticeItem deathMsg; + deathMsg.Killer.iEntIndex = iKiller; + deathMsg.Victim.iEntIndex = iVictim; + deathMsg.Killer.color = iKiller > 0 ? m_teamColors[g_PR->GetTeam(iKiller)] : COLOR_WHITE; + deathMsg.Victim.color = iVictim > 0 ? m_teamColors[g_PR->GetTeam(iVictim)] : COLOR_WHITE; + Q_snprintf( deathMsg.Killer.szClan, sizeof( deathMsg.Killer.szClan ), "%s ", killer_clan ); + Q_snprintf( deathMsg.Victim.szClan, sizeof( deathMsg.Victim.szClan ), "%s ", victim_clan ); + Q_strncpy( deathMsg.Killer.szName, killer_name, MAX_PLAYER_NAME_LENGTH ); + Q_strncpy( deathMsg.Victim.szName, victim_name, MAX_PLAYER_NAME_LENGTH ); + deathMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat(); + deathMsg.iSuicide = ( !iKiller || iKiller == iVictim ); + deathMsg.bHeadshot = headshot; + deathMsg.iDominationImageId = -1; + + CCSPlayer* pKiller = ToCSPlayer(ClientEntityList().GetBaseEntity(iKiller)); + + // the local player is dead, see if this is a new nemesis or a revenge + if ( event->GetInt( "dominated" ) > 0 || (pKiller != NULL && pKiller->IsPlayerDominated(iVictim)) ) + { + deathMsg.iDominationImageId = m_iDominatedImageId; + } + else if ( event->GetInt( "revenge" ) > 0 ) + { + deathMsg.iDominationImageId = m_iRevengeImageId; + } + + // Try and find the death identifier in the icon list + deathMsg.iconDeath = gHUD.GetIcon( fullkilledwith ); + + if ( !deathMsg.iconDeath ) + { + // Can't find it, so use the default skull & crossbones icon + deathMsg.iconDeath = m_iconD_skull; + } + + // Add it to our list of death notices + m_DeathNotices.AddToTail( deathMsg ); + + char sDeathMsg[512]; + + // Record the death notice in the console + if ( deathMsg.iSuicide ) + { + if ( !strcmp( fullkilledwith, "d_planted_c4" ) ) + { + Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s was a bit too close to the c4.\n", deathMsg.Victim.szName ); + } + else if ( !strcmp( fullkilledwith, "d_worldspawn" ) ) + { + Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s died.\n", deathMsg.Victim.szName ); + } + else //d_world + { + Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s suicided.\n", deathMsg.Victim.szName ); + } + } + else + { + Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s killed %s", deathMsg.Killer.szName, deathMsg.Victim.szName ); + + if ( fullkilledwith && *fullkilledwith && (*fullkilledwith > 13 ) ) + { + Q_strncat( sDeathMsg, VarArgs( " with %s.\n", fullkilledwith+2 ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS ); + } + } + + Msg( "%s", sDeathMsg ); +} + + + diff --git a/game/client/cstrike/hud_defuser.cpp b/game/client/cstrike/hud_defuser.cpp new file mode 100644 index 0000000..6245a57 --- /dev/null +++ b/game/client/cstrike/hud_defuser.cpp @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "c_cs_player.h" + +class CHudDefuser : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudDefuser, vgui::Panel ); + + CHudDefuser( const char *name ); + + virtual bool ShouldDraw(); + virtual void Paint(); + +private: + CPanelAnimationVar( Color, m_clrIcon, "IconColor", "IconColor" ); + + CHudTexture *m_pIcon; +}; + + +DECLARE_HUDELEMENT( CHudDefuser ); + + +CHudDefuser::CHudDefuser( const char *pName ) : + vgui::Panel( NULL, "HudDefuser" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + m_pIcon = NULL; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + //============================================================================= + // HPE_BEGIN: + // [tj] Add this to the render group that disappears when the scoreboard is up + //============================================================================= + RegisterForRenderGroup( "hide_for_scoreboard" ); + //============================================================================= + // HPE_END + //============================================================================= +} + +bool CHudDefuser::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + //============================================================================= + // HPE_BEGIN: + // [tj] Added base class call + //============================================================================= + return pPlayer && pPlayer->HasDefuser() && CHudElement::ShouldDraw(); + //============================================================================= + // HPE_END + //============================================================================= +} + +void CHudDefuser::Paint() +{ + if ( !m_pIcon ) + { + m_pIcon = gHUD.GetIcon( "defuser" ); + } + + if ( m_pIcon ) + { + int x, y, w, h; + GetBounds( x, y, w, h ); + + m_pIcon->DrawSelf( 0, 0, w, h, m_clrIcon ); + } +} + diff --git a/game/client/cstrike/hud_flashbang.cpp b/game/client/cstrike/hud_flashbang.cpp new file mode 100644 index 0000000..3ac3e3c --- /dev/null +++ b/game/client/cstrike/hud_flashbang.cpp @@ -0,0 +1,57 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "c_cs_player.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" + +class CHudFlashbang : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudFlashbang, vgui::Panel ); + + virtual bool ShouldDraw(); + virtual void Paint(); + + CHudFlashbang( const char *name ); + +private: + + int m_iAdditiveWhiteID; +}; + + +DECLARE_HUDELEMENT( CHudFlashbang ); + + +CHudFlashbang::CHudFlashbang( const char *pName ) : + vgui::Panel( NULL, "HudFlashbang" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + + m_iAdditiveWhiteID = 0; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); +} + +// the flashbang effect cannot be drawn in the HUD, because this lets the user skip its effect +// by hitting Escape, or by setting "cl_drawhud 0". +bool CHudFlashbang::ShouldDraw() +{ + return true; +} + +// the flashbang effect cannot be drawn in the HUD, because this lets the user skip its effect +// by hitting Escape, or by setting "cl_drawhud 0". +void CHudFlashbang::Paint() +{ + return; +} + diff --git a/game/client/cstrike/hud_hostagerescue.cpp b/game/client/cstrike/hud_hostagerescue.cpp new file mode 100644 index 0000000..64556ec --- /dev/null +++ b/game/client/cstrike/hud_hostagerescue.cpp @@ -0,0 +1,80 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "c_cs_player.h" + +class CHudHostageRescueZone : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudHostageRescueZone, vgui::Panel ); + + CHudHostageRescueZone( const char *name ); + + virtual bool ShouldDraw(); + virtual void Paint(); + +private: + CPanelAnimationVar( Color, m_clrIcon, "IconColor", "IconColor" ); + + CHudTexture *m_pIcon; +}; + + +DECLARE_HUDELEMENT( CHudHostageRescueZone ); + + +CHudHostageRescueZone::CHudHostageRescueZone( const char *pName ) : + vgui::Panel( NULL, "HudHostageRescueZone" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + m_pIcon = NULL; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + + //============================================================================= + // HPE_BEGIN: + // [tj] Add this to the render group that disappears when the scoreboard is up + //============================================================================= + RegisterForRenderGroup( "hide_for_scoreboard" ); + //============================================================================= + // HPE_END + //============================================================================= +} + +bool CHudHostageRescueZone::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + //============================================================================= + // HPE_BEGIN: + // [tj] Added base class call + //============================================================================= + return ( pPlayer && pPlayer->IsInHostageRescueZone() && CHudElement::ShouldDraw()); + //============================================================================= + // HPE_END + //============================================================================= +} + +void CHudHostageRescueZone::Paint() +{ + if ( !m_pIcon ) + { + m_pIcon = gHUD.GetIcon( "hostage_rescue" ); + } + + if ( m_pIcon ) + { + int x, y, w, h; + GetBounds( x, y, w, h ); + + m_pIcon->DrawSelf( 0, 0, w, h, m_clrIcon ); + } +} + diff --git a/game/client/cstrike/hud_progressbar.cpp b/game/client/cstrike/hud_progressbar.cpp new file mode 100644 index 0000000..b407aea --- /dev/null +++ b/game/client/cstrike/hud_progressbar.cpp @@ -0,0 +1,124 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "c_cs_player.h" +#include "clientmode_csnormal.h" +#include "weapon_c4.h" + +ConVar cl_c4progressbar( "cl_c4progressbar", "1", 0, "Draw progress bar when defusing the C4" ); + +class CHudProgressBar : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudProgressBar, vgui::Panel ); + + CHudProgressBar( const char *name ); + + // vgui overrides + virtual void Paint(); + virtual bool ShouldDraw(); + + CPanelAnimationVar( Color, m_clrProgress, "ProgressBarFg", "ProgressBar.FgColor" ); +}; + + +DECLARE_HUDELEMENT( CHudProgressBar ); + + +CHudProgressBar::CHudProgressBar( const char *name ) : + vgui::Panel( NULL, "HudProgressBar" ), CHudElement( name ) +{ + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetPaintBorderEnabled( false ); + SetPaintBackgroundEnabled( false ); + + SetHiddenBits( HIDEHUD_PLAYERDEAD | HIDEHUD_WEAPONSELECTION ); +} + +void CHudProgressBar::Paint() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + C_BaseEntity *pTarget = pPlayer->GetObserverTarget(); + + if( pTarget && pTarget->IsPlayer() ) + { + pPlayer = ToCSPlayer( pTarget ); + + if( !pPlayer->IsAlive() ) + return; + } + else + return; + } + + if ( !pPlayer ) + return; + + int x, y, wide, tall; + GetBounds( x, y, wide, tall ); + + tall = 10; + + int xOffset=0; + int yOffset=0; + + Color clr = m_clrProgress; + + clr[3] = 160; + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset+wide, yOffset+tall ); + + if( pPlayer->m_iProgressBarDuration > 0 ) + { + // ProgressBarStartTime is now with respect to m_flSimulationTime rather than local time + float percent = (pPlayer->m_flSimulationTime - pPlayer->m_flProgressBarStartTime) / (float)pPlayer->m_iProgressBarDuration; + percent = clamp( percent, 0, 1 ); + + clr[3] = 240; + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawFilledRect( xOffset+2, yOffset+2, xOffset+(int)(percent*wide)-2, yOffset+tall-2 ); + } +} + + +bool CHudProgressBar::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + C_BaseEntity *pTarget = pPlayer->GetObserverTarget(); + + if( pTarget && pTarget->IsPlayer() ) + { + pPlayer = ToCSPlayer( pTarget ); + + if( !pPlayer->IsAlive() ) + return false; + } + else + return false; + } + + if( !pPlayer || pPlayer->m_iProgressBarDuration == 0 || pPlayer->m_lifeState == LIFE_DEAD ) + { + return false; + } + + return cl_c4progressbar.GetBool(); +} + + + diff --git a/game/client/cstrike/hud_radar.cpp b/game/client/cstrike/hud_radar.cpp new file mode 100644 index 0000000..f3368f5 --- /dev/null +++ b/game/client/cstrike/hud_radar.cpp @@ -0,0 +1,522 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "cs_gamerules.h" +#include "hud_numericdisplay.h" +#include "voice_status.h" +#include "c_plantedc4.h" +#include "weapon_c4.h" +#include "c_cs_hostage.h" +#include "c_cs_playerresource.h" +#include <coordsize.h> +#include "hud_macros.h" +#include "vgui/IVGui.h" +#include "vgui/ILocalize.h" +#include "mapoverview.h" +#include "cstrikespectatorgui.h" +#include "hud_radar.h" + +#define RADAR_DOT_NORMAL 0 +#define RADAR_DOT_BOMB (1<<0) +#define RADAR_DOT_HOSTAGE (1<<1) +#define RADAR_DOT_BOMBCARRIER (1<<2) +#define RADAR_DOT_VIP (1<<3) +#define RADAR_DOT_LARGE_FLASH (1<<4) +#define RADAR_DOT_BOMB_PLANTED (1<<5) +#define RADAR_IGNORE_Z (1<<6) //always draw this item as if it was at the same Z as the player + +extern CUtlVector< CC4* > g_C4s; + +ConVar cl_radartype( "cl_radartype", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); +ConVar cl_radaralpha( "cl_radaralpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, NULL, true, 0, true, 255 ); +ConVar cl_locationalpha( "cl_locationalpha", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, NULL, true, 0, true, 255 ); + +DECLARE_HUDELEMENT( CHudRadar ); +DECLARE_HUD_MESSAGE( CHudRadar, UpdateRadar ); + +static CHudRadar *s_Radar = NULL; +CUtlVector<CPlayerRadarFlash> g_RadarFlashes; + + +CHudRadar::CHudRadar( const char *pName ) : vgui::Panel( NULL, "HudRadar" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + + m_pBackground = NULL; + m_pBackgroundTrans = NULL; + + m_flNextBombFlashTime = 0.0; + m_bBombFlash = true; + + m_flNextHostageFlashTime = 0.0; + m_bHostageFlash = true; + m_bHideRadar = false; + + s_Radar = this; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + +} + + +CHudRadar::~CHudRadar() +{ + s_Radar = NULL; +} + + +void CHudRadar::Init() +{ + HOOK_HUD_MESSAGE( CHudRadar, UpdateRadar ); +} + +void CHudRadar::LevelInit() +{ + m_flNextBombFlashTime = 0.0; + m_bBombFlash = true; + + m_flNextHostageFlashTime = 0.0; + m_bHostageFlash = true; + + g_RadarFlashes.RemoveAll(); + + // Map Overview handles radar duties now. + if( g_pMapOverview ) + g_pMapOverview->SetMode(CCSMapOverview::MAP_MODE_RADAR); +} + +void CHudRadar::Reset() +{ + CHudElement::Reset(); + + if( g_pMapOverview ) + g_pMapOverview->SetMode(CCSMapOverview::MAP_MODE_RADAR); +} + +void CHudRadar::MsgFunc_UpdateRadar(bf_read &msg ) +{ + int iPlayerEntity = msg.ReadByte(); + + //Draw objects on the radar + //============================= + C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); + + if ( !pLocalPlayer ) + return; + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + + if ( !pCSPR ) + return; + + //Players + for(int i=1;i<=MAX_PLAYERS;i++) + { + if( i == pLocalPlayer->entindex() ) + continue; + + C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(i) ); + + if ( pPlayer ) + { + pPlayer->m_bDetected = false; + } + } + + + while ( iPlayerEntity > 0 ) + { + int x = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4; + int y = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4; + int z = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4; + int a = msg.ReadSBitLong( 9 ); + + C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(iPlayerEntity) ); + + Vector origin( x, y, z ); + QAngle angles( 0, a, 0 ); + + if ( g_pMapOverview ) + { + g_pMapOverview->SetPlayerPositions( iPlayerEntity-1, origin, angles ); + } + + iPlayerEntity = msg.ReadByte(); // read index for next player + + if ( !pPlayer ) + continue; + + bool bOppositeTeams = (pLocalPlayer->GetTeamNumber() != TEAM_UNASSIGNED && pCSPR->GetTeam( pPlayer->entindex() ) != pLocalPlayer->GetTeamNumber()); + + //============================================================================= + // HPE_BEGIN: + // [tj] This used to do slightly different logic that caused other players + // to twitch while you were observing. + //============================================================================= + // Don't update players if they are in PVS. + if (!pPlayer->IsDormant()) + { + continue; + } + + //Don't update players if you are sill alive and they are an enemy. + if (bOppositeTeams && !pLocalPlayer->IsObserver()) + { + continue; + } + //============================================================================= + // HPE_END + //============================================================================= + // update origin and angle for players out of my PVS + origin = pPlayer->GetAbsOrigin(); + angles = pPlayer->GetAbsAngles(); + + origin.x = x; + origin.y = y; + angles.y = a; + + pPlayer->SetAbsOrigin( origin ); + pPlayer->SetAbsAngles( angles ); + pPlayer->m_bDetected = true; + } +} + + +bool CHudRadar::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + //============================================================================= + // HPE_BEGIN: + // [tj] Added base class call + //============================================================================= + return pPlayer && pPlayer->IsAlive() && !m_bHideRadar && CHudElement::ShouldDraw(); + //============================================================================= + // HPE_END + //============================================================================= +} + +void CHudRadar::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + if( g_pMapOverview && g_pMapOverview->GetMode() == CCSMapOverview::MAP_MODE_RADAR ) + { + // We are the hud element still, but he is in charge of the new style now. + g_pMapOverview->SetVisible( state ); + } +} + +void CHudRadar::Paint() +{ + // We are the hud element still, but Overview is in charge of the new style now. + return; +} + +void CHudRadar::WorldToRadar( const Vector location, const Vector origin, const QAngle angles, float &x, float &y, float &z_delta ) +{ + float x_diff = location.x - origin.x; + float y_diff = location.y - origin.y; + + int iRadarRadius = GetWide(); //width of the panel, it resizes now! + float fRange = 16 * iRadarRadius; // radar's range + + float flOffset = atan(y_diff/x_diff); + flOffset *= 180; + flOffset /= M_PI; + + if ((x_diff < 0) && (y_diff >= 0)) + flOffset = 180 + flOffset; + else if ((x_diff < 0) && (y_diff < 0)) + flOffset = 180 + flOffset; + else if ((x_diff >= 0) && (y_diff < 0)) + flOffset = 360 + flOffset; + + y_diff = -1*(sqrt((x_diff)*(x_diff) + (y_diff)*(y_diff))); + x_diff = 0; + + flOffset = angles.y - flOffset; + + flOffset *= M_PI; + flOffset /= 180; // now theta is in radians + + float xnew_diff = x_diff * cos(flOffset) - y_diff * sin(flOffset); + float ynew_diff = x_diff * sin(flOffset) + y_diff * cos(flOffset); + + // The dot is out of the radar's range.. Scale it back so that it appears on the border + if ( (-1 * y_diff) > fRange ) + { + float flScale; + + flScale = ( -1 * y_diff) / fRange; + + xnew_diff /= flScale; + ynew_diff /= flScale; + } + xnew_diff /= 32; + ynew_diff /= 32; + + //output + x = (iRadarRadius/2) + (int)xnew_diff; + y = (iRadarRadius/2) + (int)ynew_diff; + z_delta = location.z - origin.z; +} + +void CHudRadar::DrawPlayerOnRadar( int iPlayer, C_CSPlayer *pLocalPlayer ) +{ + float x, y, z_delta; + int iBaseDotSize = ScreenWidth() / 256; + int r, g, b, a = 235; + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + + if ( !pCSPR ) + return; + + C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayer ) ); + + if ( !pPlayer ) + return; + + bool bOppositeTeams = (pLocalPlayer->GetTeamNumber() != TEAM_UNASSIGNED && pCSPR->GetTeam( iPlayer ) != pLocalPlayer->GetTeamNumber()); + + if ( bOppositeTeams && pPlayer->m_bDetected == false ) + return; + + + WorldToRadar( pPlayer->GetAbsOrigin(), pLocalPlayer->GetAbsOrigin(), pLocalPlayer->LocalEyeAngles(), x, y, z_delta ); + + if( pCSPR->HasC4( iPlayer ) || pCSPR->IsVIP( iPlayer ) || bOppositeTeams ) + { + r = 250; g = 0; b = 0; + } + else if ( 0 /*m_bTrackArray[i-1] == true */ ) // Tracked players (friends we want to keep track of on the radar) + { + iBaseDotSize *= 2; + r = 185; g = 20; b = 20; + } + else + { + r = 75; g = 75; b = 250; + } + + // Handle the radio flashes + bool bRadarFlash = false; + if ( g_RadarFlashes.Count() > iPlayer ) + bRadarFlash = g_RadarFlashes[iPlayer].m_bRadarFlash && g_RadarFlashes[iPlayer].m_iNumRadarFlashes > 0; + + if ( bRadarFlash || GetClientVoiceMgr()->IsPlayerSpeaking( iPlayer ) ) + { + r = 230; g = 110; b = 25; a = 245; + + DrawRadarDot( x, y, z_delta, iBaseDotSize, RADAR_DOT_LARGE_FLASH, r, g, b, a ); + } + else + { + DrawRadarDot( x, y, z_delta, iBaseDotSize, RADAR_DOT_NORMAL, r, g, b, a ); + } +} + + +void CHudRadar::DrawEntityOnRadar( CBaseEntity *pEnt, C_CSPlayer *pLocalPlayer, int flags, int r, int g, int b, int a ) +{ + float x, y, z_delta; + int iBaseDotSize = 4; + + WorldToRadar( pEnt->GetAbsOrigin(), pLocalPlayer->GetAbsOrigin(), pLocalPlayer->LocalEyeAngles(), x, y, z_delta ); + + if( flags & RADAR_IGNORE_Z ) + z_delta = 0; + + DrawRadarDot( x, y, z_delta, iBaseDotSize, flags, r, g, b, a ); +} + +void CHudRadar::FillRect( int x, int y, int w, int h ) +{ + int panel_x, panel_y, panel_w, panel_h; + GetBounds( panel_x, panel_y, panel_w, panel_h ); + vgui::surface()->DrawFilledRect( x, y, x+w, y+h ); +} + +void CHudRadar::DrawRadarDot( int x, int y, float z_diff, int iBaseDotSize, int flags, int r, int g, int b, int a ) +{ + vgui::surface()->DrawSetColor( r, g, b, a ); + + if ( flags & RADAR_DOT_LARGE_FLASH ) + { + FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 ); + } + else if ( z_diff < -128 ) // below the player + { + z_diff *= -1; + + if ( z_diff > 3096 ) + { + z_diff = 3096; + } + + int iBar = (int)( z_diff / 400 ) + 2; + + // Draw an upside-down T shape to symbolize the dot is below the player. + + iBaseDotSize /= 2; + + //horiz + FillRect( x-(2*iBaseDotSize), y, 5*iBaseDotSize, iBaseDotSize ); + + //vert + FillRect( x, y - iBar*iBaseDotSize, iBaseDotSize, iBar*iBaseDotSize ); + } + else if ( z_diff > 128 ) // above the player + { + if ( z_diff > 3096 ) + { + z_diff = 3096; + } + + int iBar = (int)( z_diff / 400 ) + 2; + + iBaseDotSize /= 2; + + // Draw a T shape to symbolize the dot is above the player. + + //horiz + FillRect( x-(2*iBaseDotSize), y, 5*iBaseDotSize, iBaseDotSize ); + + //vert + FillRect( x, y, iBaseDotSize, iBar*iBaseDotSize ); + } + else + { + if ( flags & RADAR_DOT_HOSTAGE ) + { + FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 ); + } + else if ( flags & RADAR_DOT_BOMB ) + { + if ( flags & RADAR_DOT_BOMB_PLANTED ) + { + iBaseDotSize = 2; + // draw an X for the planted bomb + FillRect( x, y, iBaseDotSize, iBaseDotSize ); + FillRect( x-2, y-2, iBaseDotSize, iBaseDotSize ); + FillRect( x-2, y+2, iBaseDotSize, iBaseDotSize ); + FillRect( x+2, y-2, iBaseDotSize, iBaseDotSize ); + FillRect( x+2, y+2, iBaseDotSize, iBaseDotSize ); + } + else + { + FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 ); + } + } + else + { + FillRect( x, y, iBaseDotSize, iBaseDotSize ); + } + } +} + + +void Radar_FlashPlayer( int iPlayer ) +{ + if ( g_RadarFlashes.Count() <= iPlayer ) + { + g_RadarFlashes.AddMultipleToTail( iPlayer - g_RadarFlashes.Count() + 1 ); + } + + CPlayerRadarFlash *pFlash = &g_RadarFlashes[iPlayer]; + pFlash->m_flNextRadarFlashTime = gpGlobals->curtime; + pFlash->m_iNumRadarFlashes = 16; + pFlash->m_bRadarFlash = false; + + g_pMapOverview->FlashEntity(iPlayer); +} + +CON_COMMAND( drawradar, "Draws HUD radar" ) +{ + (GET_HUDELEMENT( CHudRadar ))->DrawRadar(); +} + +CON_COMMAND( hideradar, "Hides HUD radar" ) +{ + (GET_HUDELEMENT( CHudRadar ))->HideRadar(); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +// Location text under radar + +DECLARE_HUDELEMENT( CHudLocation ); + +CHudLocation::CHudLocation( const char *pName ) : vgui::Label( NULL, "HudLocation", "" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); +} + +void CHudLocation::Init() +{ + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); +} + +void CHudLocation::LevelInit() +{ +} + +bool CHudLocation::ShouldDraw() +{ + CCSMapOverview *pCSMapOverview = (CCSMapOverview *)GET_HUDELEMENT( CCSMapOverview ); + + if( g_pMapOverview && g_pMapOverview->GetMode() == CMapOverview::MAP_MODE_RADAR && pCSMapOverview && pCSMapOverview->ShouldDraw() == true ) + return true; + else if( g_pMapOverview && g_pMapOverview->GetMode() == CMapOverview::MAP_MODE_INSET ) + return true; + + return false; +} + +void CHudLocation::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_fgColor = Color( 64, 255, 64, 255 ); + SetFont( pScheme->GetFont( "ChatFont" ) ); + SetBorder( NULL ); + SetBgColor( Color( 0, 0, 0, 0 ) ); + SetFgColor( m_fgColor ); +} + +void CHudLocation::OnTick() +{ + m_fgColor[3] = cl_locationalpha.GetInt(); + SetFgColor( m_fgColor ); + + const char *pszLocation = ""; + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + pszLocation = pPlayer->GetLastKnownPlaceName(); + } + SetText( g_pVGuiLocalize->Find( pszLocation ) ); + + // We have two different locations based on the Overview mode. + // So we just position ourselves below, and center our text in their width. + if( g_pMapOverview ) + { + int x = 0, y = 0; + int width = 0, height = 0; + g_pMapOverview->GetAsPanel()->GetPos( x, y ); + g_pMapOverview->GetAsPanel()->GetSize( width, height ); + y += g_pMapOverview->GetAsPanel()->GetTall(); + SetPos( x, y ); + SetWide( width ); + } +} diff --git a/game/client/cstrike/hud_radar.h b/game/client/cstrike/hud_radar.h new file mode 100644 index 0000000..95f7db3 --- /dev/null +++ b/game/client/cstrike/hud_radar.h @@ -0,0 +1,93 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef HUD_RADAR_H +#define HUD_RADAR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui_controls/Label.h> + +class C_CSPlayer; +void Radar_FlashPlayer( int iPlayer ); + +class CPlayerRadarFlash +{ +public: + CPlayerRadarFlash() + { + m_flNextRadarFlashTime = 0.0f; + m_iNumRadarFlashes = 0; + m_bRadarFlash = false; + } + float m_flNextRadarFlashTime; // when to next toggle the flash on the radar + int m_iNumRadarFlashes; // how many flashes more to do + bool m_bRadarFlash; // flash or do not, there is no try +}; + + +class CHudRadar : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudRadar, vgui::Panel ); + + CHudRadar( const char *name ); + ~CHudRadar(); + + virtual void Paint(); + virtual void Init(); + virtual void LevelInit(); + virtual bool ShouldDraw(); + virtual void SetVisible(bool state); + virtual void Reset(); + + void DrawRadar(void) { m_bHideRadar = false; } + void HideRadar(void) { m_bHideRadar = true; } + void MsgFunc_UpdateRadar(bf_read &msg ); + +private: + + void WorldToRadar( const Vector location, const Vector origin, const QAngle angles, float &x, float &y, float &z_delta ); + + void DrawPlayerOnRadar( int iPlayer, C_CSPlayer *pLocalPlayer ); + void DrawEntityOnRadar( CBaseEntity *pEnt, C_CSPlayer *pLocalPlayer, int flags, int r, int g, int b, int a ); + + void FillRect( int x, int y, int w, int h ); + void DrawRadarDot( int x, int y, float z_diff, int iBaseDotSize, int flags, int r, int g, int b, int a ); + + CHudTexture *m_pBackground; + CHudTexture *m_pBackgroundTrans; + + float m_flNextBombFlashTime; + bool m_bBombFlash; + + float m_flNextHostageFlashTime; + bool m_bHostageFlash; + bool m_bHideRadar; +}; + +class CHudLocation : public CHudElement, public vgui::Label +{ +public: + DECLARE_CLASS_SIMPLE( CHudLocation, vgui::Panel ); + + CHudLocation( const char *name ); + + virtual void Init(); + virtual void LevelInit(); + virtual bool ShouldDraw(); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void OnTick( void ); + +private: + Color m_fgColor; +}; + +#endif // HUD_RADAR_H diff --git a/game/client/cstrike/hud_roundtimer.cpp b/game/client/cstrike/hud_roundtimer.cpp new file mode 100644 index 0000000..cdcc564 --- /dev/null +++ b/game/client/cstrike/hud_roundtimer.cpp @@ -0,0 +1,233 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "cs_gamerules.h" +#include "hud_basetimer.h" +#include "hud_bitmapnumericdisplay.h" +#include "c_plantedc4.h" + +#include <vgui_controls/AnimationController.h> + +class CHudRoundTimer : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudRoundTimer, vgui::Panel ); + + CHudRoundTimer( const char *name ); + +protected: + virtual void Paint(); + virtual void Think(); + virtual bool ShouldDraw(); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + + void PaintTime(HFont font, int xpos, int ypos, int mins, int secs); + +private: + float m_flToggleTime; + float m_flNextToggle; + CHudTexture *m_pTimerIcon; + bool m_bFlash; + + int m_iAdditiveWhiteID; + + CPanelAnimationVar( Color, m_FlashColor, "FlashColor", "Panel.FgColor" ); + + CPanelAnimationVarAliasType( float, icon_xpos, "icon_xpos", "0", "proportional_float" ); + CPanelAnimationVarAliasType( float, icon_ypos, "icon_ypos", "0", "proportional_float" ); + + CPanelAnimationVar( Color, m_TextColor, "TextColor", "Panel.FgColor" ); + CPanelAnimationVar( vgui::HFont, m_hNumberFont, "NumberFont", "HudNumbers" ); + CPanelAnimationVarAliasType( float, digit_xpos, "digit_xpos", "50", "proportional_float" ); + CPanelAnimationVarAliasType( float, digit_ypos, "digit_ypos", "2", "proportional_float" ); + + float icon_tall; + float icon_wide; +}; + + +DECLARE_HUDELEMENT( CHudRoundTimer ); + + +CHudRoundTimer::CHudRoundTimer( const char *pName ) : + BaseClass( NULL, "HudRoundTimer" ), CHudElement( pName ) +{ + m_iAdditiveWhiteID = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( m_iAdditiveWhiteID, "vgui/white_additive" , true, false); + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); +} + +void CHudRoundTimer::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + m_pTimerIcon = gHUD.GetIcon( "timer_icon" ); + + if( m_pTimerIcon ) + { + icon_tall = GetTall() - YRES(2); + float scale = icon_tall / (float)m_pTimerIcon->Height(); + icon_wide = ( scale ) * (float)m_pTimerIcon->Width(); + } + + SetFgColor( m_TextColor ); + + BaseClass::ApplySchemeSettings( pScheme ); +} + +bool CHudRoundTimer::ShouldDraw() +{ + //necessary? + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( pPlayer ) + { + return !pPlayer->IsObserver(); + } + + return false; +} + +void CHudRoundTimer::Think() +{ + C_CSGameRules *pRules = CSGameRules(); + if ( !pRules ) + return; + + int timer = (int)ceil( pRules->GetRoundRemainingTime() ); + + if ( pRules->IsFreezePeriod() ) + { + // in freeze period countdown to round start time + timer = (int)ceil(pRules->GetRoundStartTime()-gpGlobals->curtime); + } + + //If the bomb is planted don't draw -- the timer is irrelevant + SetVisible(g_PlantedC4s.Count() == 0); + + if(timer > 30) + { + SetFgColor(m_TextColor); + return; + } + + if(timer <= 0) + { + timer = 0; + SetFgColor(m_FlashColor); + return; + } + + if(gpGlobals->curtime > m_flNextToggle) + { + if( timer <= 0) + { + m_bFlash = true; + } + else if( timer <= 2) + { + m_flToggleTime = gpGlobals->curtime; + m_flNextToggle = gpGlobals->curtime + 0.05; + m_bFlash = !m_bFlash; + } + else if( timer <= 5) + { + m_flToggleTime = gpGlobals->curtime; + m_flNextToggle = gpGlobals->curtime + 0.1; + m_bFlash = !m_bFlash; + } + else if( timer <= 10) + { + m_flToggleTime = gpGlobals->curtime; + m_flNextToggle = gpGlobals->curtime + 0.2; + m_bFlash = !m_bFlash; + } + else if( timer <= 20) + { + m_flToggleTime = gpGlobals->curtime; + m_flNextToggle = gpGlobals->curtime + 0.4; + m_bFlash = !m_bFlash; + } + else if( timer <= 30) + { + m_flToggleTime = gpGlobals->curtime; + m_flNextToggle = gpGlobals->curtime + 0.8; + m_bFlash = !m_bFlash; + } + else + m_bFlash = false; + } + + Color startValue, endValue; + Color interp_color; + + if( m_bFlash ) + { + startValue = m_FlashColor; + endValue = m_TextColor; + } + else + { + startValue = m_TextColor; + endValue = m_FlashColor; + } + + float pos = (gpGlobals->curtime - m_flToggleTime) / (m_flNextToggle - m_flToggleTime); + pos = clamp( SimpleSpline( pos ), 0, 1 ); + + interp_color[0] = ((endValue[0] - startValue[0]) * pos) + startValue[0]; + interp_color[1] = ((endValue[1] - startValue[1]) * pos) + startValue[1]; + interp_color[2] = ((endValue[2] - startValue[2]) * pos) + startValue[2]; + interp_color[3] = ((endValue[3] - startValue[3]) * pos) + startValue[3]; + + SetFgColor(interp_color); +} + +void CHudRoundTimer::Paint() +{ + // Update the time. + C_CSGameRules *pRules = CSGameRules(); + if ( !pRules ) + return; + + int timer = (int)ceil( pRules->GetRoundRemainingTime() ); + + if ( pRules->IsFreezePeriod() ) + { + // in freeze period countdown to round start time + timer = (int)ceil(pRules->GetRoundStartTime()-gpGlobals->curtime); + } + + if(timer < 0) + timer = 0; + + int minutes = timer / 60; + int seconds = timer % 60; + + //Draw Timer icon + if( m_pTimerIcon ) + { + m_pTimerIcon->DrawSelf( icon_xpos, icon_ypos, icon_wide, icon_tall, GetFgColor() ); + } + + PaintTime( m_hNumberFont, digit_xpos, digit_ypos, minutes, seconds ); +} + +void CHudRoundTimer::PaintTime(HFont font, int xpos, int ypos, int mins, int secs) +{ + surface()->DrawSetTextFont(font); + wchar_t unicode[6]; + V_snwprintf(unicode, ARRAYSIZE(unicode), L"%d:%.2d", mins, secs); + + surface()->DrawSetTextPos(xpos, ypos); + surface()->DrawUnicodeString( unicode ); +} diff --git a/game/client/cstrike/hud_scenarioicon.cpp b/game/client/cstrike/hud_scenarioicon.cpp new file mode 100644 index 0000000..379a1f7 --- /dev/null +++ b/game/client/cstrike/hud_scenarioicon.cpp @@ -0,0 +1,109 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "c_cs_player.h" +#include "cs_gamerules.h" + +#include "c_cs_hostage.h" +#include "c_plantedc4.h" + +class CHudScenarioIcon : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudScenarioIcon, vgui::Panel ); + + CHudScenarioIcon( const char *name ); + + virtual bool ShouldDraw(); + virtual void Paint(); + +private: + CPanelAnimationVar( Color, m_clrIcon, "IconColor", "IconColor" ); + + CHudTexture *m_pC4Icon; + CHudTexture *m_pHostageIcon; +}; + + +DECLARE_HUDELEMENT( CHudScenarioIcon ); + + +CHudScenarioIcon::CHudScenarioIcon( const char *pName ) : + vgui::Panel( NULL, "HudScenarioIcon" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + m_pC4Icon = NULL; + m_pHostageIcon = NULL; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); +} + +bool CHudScenarioIcon::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + return pPlayer && pPlayer->IsAlive(); +} + +void CHudScenarioIcon::Paint() +{ + // If there is a bomb planted, draw that + if( g_PlantedC4s.Count() > 0 ) + { + if ( !m_pC4Icon ) + { + m_pC4Icon = gHUD.GetIcon( "scenario_c4" ); + } + + if ( m_pC4Icon ) + { + int x, y, w, h; + GetBounds( x, y, w, h ); + + C_PlantedC4 *pC4 = g_PlantedC4s[0]; + + Color c = m_clrIcon; + + c[3] = 80; + + if( pC4->m_flNextGlow - gpGlobals->curtime < 0.1 ) + { + c[3] = 255; + } + + if( pC4->IsBombActive() ) + m_pC4Icon->DrawSelf( 0, 0, h, h, c ); //draw it square! + } + } + + CCSGameRules *pRules = CSGameRules(); + + // If there are hostages, draw how many there are + if( pRules && pRules->GetNumHostagesRemaining() ) + { + if ( !m_pHostageIcon ) + { + m_pHostageIcon = gHUD.GetIcon( "scenario_hostage" ); + } + + if( m_pHostageIcon ) + { + int xpos = 0; + int iconWidth = m_pHostageIcon->Width(); + + for(int i=0;i<pRules->GetNumHostagesRemaining();i++) + { + m_pHostageIcon->DrawSelf( xpos, 0, m_clrIcon ); + xpos += iconWidth; + } + } + } +} + diff --git a/game/client/cstrike/hud_shopping_cart.cpp b/game/client/cstrike/hud_shopping_cart.cpp new file mode 100644 index 0000000..f35fd12 --- /dev/null +++ b/game/client/cstrike/hud_shopping_cart.cpp @@ -0,0 +1,94 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "hudelement.h" +#include <vgui_controls/Panel.h> +#include <vgui/ISurface.h> +#include "clientmode_csnormal.h" +#include "cs_gamerules.h" +#include "hud_numericdisplay.h" +#include <vgui_controls/AnimationController.h> + + +class CHudShoppingCart : public CHudElement, public vgui::Panel +{ +public: + DECLARE_CLASS_SIMPLE( CHudShoppingCart, vgui::Panel ); + + CHudShoppingCart( const char *name ); + + virtual bool ShouldDraw(); + virtual void Paint(); + virtual void Init(); + + +private: + CPanelAnimationVar( Color, m_clrIcon, "IconColor", "IconColor" ); + + CHudTexture *m_pCartIcon; + bool m_bPrevState; +}; + + +DECLARE_HUDELEMENT( CHudShoppingCart ); + + +CHudShoppingCart::CHudShoppingCart( const char *pName ) : + vgui::Panel( NULL, "HudShoppingCart" ), CHudElement( pName ) +{ + SetParent( g_pClientMode->GetViewport() ); + m_pCartIcon = NULL; + + SetHiddenBits( HIDEHUD_PLAYERDEAD ); + + //============================================================================= + // HPE_BEGIN: + // [tj] Add this to the render group that disappears when the scoreboard is up + //============================================================================= + RegisterForRenderGroup( "hide_for_scoreboard" ); + //============================================================================= + // HPE_END + //============================================================================= +} + + +void CHudShoppingCart::Init() +{ +} + + +bool CHudShoppingCart::ShouldDraw() +{ + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + + //============================================================================= + // HPE_BEGIN: + // [tj] Added base class call + //============================================================================= + return ( pPlayer && pPlayer->IsInBuyZone() && !CSGameRules()->IsBuyTimeElapsed() && CHudElement::ShouldDraw() ); + //============================================================================= + // HPE_END + //============================================================================= +} + + +void CHudShoppingCart::Paint() +{ + if ( !m_pCartIcon ) + { + m_pCartIcon = gHUD.GetIcon( "shopping_cart" ); + } + + if ( m_pCartIcon ) + { + int x, y, w, h; + GetBounds( x, y, w, h ); + + m_pCartIcon->DrawSelf( 0, 0, w, h, m_clrIcon ); + } +} + diff --git a/game/client/cstrike/radio_status.cpp b/game/client/cstrike/radio_status.cpp new file mode 100644 index 0000000..6c50788 --- /dev/null +++ b/game/client/cstrike/radio_status.cpp @@ -0,0 +1,421 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include <string.h> +#include <stdio.h> +#include "voice_status.h" +#include "radio_status.h" +#include "c_playerresource.h" +#include "cliententitylist.h" +#include "c_baseplayer.h" +#include "materialsystem/imesh.h" +#include "view.h" +#include "materialsystem/imaterial.h" +#include "tier0/dbg.h" +#include "cdll_int.h" +#include "c_cs_player.h" +#include "menu.h" // for CHudMenu defs + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +// ---------------------------------------------------------------------- // +// The radio feedback manager for the client. +// ---------------------------------------------------------------------- // +static CRadioStatus s_RadioStatus; + +// +//----------------------------------------------------- +// + +// Stuff for the Radio Menus +static void radio1_f( void ); +static void radio2_f( void ); +static void radio3_f( void ); + +static ConCommand radio1( "radio1", radio1_f, "Opens a radio menu" ); +static ConCommand radio2( "radio2", radio2_f, "Opens a radio menu" ); +static ConCommand radio3( "radio3", radio3_f, "Opens a radio menu" ); +static int g_whichMenu = 0; + +// +//-------------------------------------------------------------- +// +// These methods will bring up the radio menus from the client side. +// They mimic the old server commands of the same name, which used +// to require a round-trip causing latency and unreliability in +// menu responses. Only 1 message is sent to the server now which +// includes both the menu name and the selected item. The server +// is never informed that the menu has been displayed. +// +//-------------------------------------------------------------- +// +void OpenRadioMenu( int index ) +{ + // do not show the menu if the player is dead or is an observer + C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); + if ( !pPlayer ) + return; + + if ( !pPlayer->IsAlive() || pPlayer->IsObserver() ) + return; + + CHudMenu *pMenu = (CHudMenu *) gHUD.FindElement( "CHudMenu" ); + if ( !pMenu ) + return; + + g_whichMenu = index; + + // + // the 0x23f and 0x3ff are masks that describes the keys that + // are valid. This will have to be changed if the menus change. + // + switch ( index ) + { + case 1: + pMenu->ShowMenu( "#RadioA", 0x23f ); + break; + case 2: + pMenu->ShowMenu( "#RadioB", 0x23f ); + break; + case 3: + pMenu->ShowMenu( "#RadioC", 0x3ff ); + break; + default: + g_whichMenu = 0; + } +} + +static void radio1_f( void ) +{ + OpenRadioMenu( 1 ); +} + +static void radio2_f( void ) +{ + OpenRadioMenu( 2 ); +} + +static void radio3_f( void ) +{ + OpenRadioMenu( 3 ); +} + +CON_COMMAND_F( menuselect, "menuselect", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if ( args.ArgC() < 2 ) + return; + + if( g_whichMenu == 0 ) + { + // if we didn't have a menu open, maybe a plugin did. send it on to the server. + const char *cmd = VarArgs( "menuselect %s", args[1] ); + engine->ServerCmd( cmd ); + return; + } + + int whichEntry = atoi( args[ 1 ] ); + + switch( g_whichMenu ) + { + case 1: //RadioA + { + switch( whichEntry ) + { + case 1: // coverme + engine->ClientCmd( "coverme" ); + break; + case 2: // takepoint + engine->ClientCmd( "takepoint" ); + break; + case 3: // holdpos + engine->ClientCmd( "holdpos" ); + break; + case 4: // regroup + engine->ClientCmd( "regroup" ); + break; + case 5: // followme + engine->ClientCmd( "followme" ); + break; + case 6: // takingfire + engine->ClientCmd( "takingfire" ); + break; + } + } + break; + + case 2: //RadioB + { + switch( whichEntry ) + { + case 1: // go + engine->ClientCmd( "go" ); + break; + case 2: // fallback + engine->ClientCmd( "fallback" ); + break; + case 3: // sticktog + engine->ClientCmd( "sticktog" ); + break; + case 4: // getinpos + engine->ClientCmd( "getinpos" ); + break; + case 5: // stormfront + engine->ClientCmd( "stormfront" ); + break; + case 6: // report + engine->ClientCmd( "report" ); + break; + } + } + break; + + case 3: //RadioC + { + switch( whichEntry ) + { + case 1: // roger + engine->ClientCmd( "roger" ); + break; + case 2: // enemyspot + engine->ClientCmd( "enemyspot" ); + break; + case 3: // needbackup + engine->ClientCmd( "needbackup" ); + break; + case 4: // sectorclear + engine->ClientCmd( "sectorclear" ); + break; + case 5: // inposition + engine->ClientCmd( "inposition" ); + break; + case 6: // reportingin + engine->ClientCmd( "reportingin" ); + break; + case 7: // getout + engine->ClientCmd( "getout" ); + break; + case 8: // negative + engine->ClientCmd( "negative" ); + break; + case 9: // enemydown + engine->ClientCmd( "enemydown" ); + break; + } + } + break; + + default: + // if we didn't have a menu open, maybe a plugin did. send it on to the server. + const char *cmd = VarArgs( "menuselect %d", whichEntry ); + engine->ServerCmd( cmd ); + break; + } + + // reset menu + g_whichMenu = 0; +} + +// +//----------------------------------------------------- +// + +CRadioStatus* RadioManager() +{ + return &s_RadioStatus; +} + + +// ---------------------------------------------------------------------- // +// CRadioStatus. +// ---------------------------------------------------------------------- // + +CRadioStatus::CRadioStatus() +{ + m_pHeadLabelMaterial = NULL; + Q_memset(m_radioUntil, 0, sizeof(m_radioUntil)); + Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil)); +} + +bool CRadioStatus::Init() +{ + if ( !m_pHeadLabelMaterial ) + { + m_pHeadLabelMaterial = materials->FindMaterial( "sprites/radio", TEXTURE_GROUP_VGUI ); + } + + if ( IsErrorMaterial( m_pHeadLabelMaterial ) ) + return false; + + m_pHeadLabelMaterial->IncrementReferenceCount(); + + return true; +} + +void CRadioStatus::Shutdown() +{ + if ( m_pHeadLabelMaterial ) + m_pHeadLabelMaterial->DecrementReferenceCount(); + + m_pHeadLabelMaterial = NULL; +} + +void CRadioStatus::LevelInitPostEntity() +{ + ExpireBotVoice( true ); + Q_memset(m_radioUntil, 0, sizeof(m_radioUntil)); + Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil)); +} + +void CRadioStatus::LevelShutdownPreEntity() +{ + ExpireBotVoice( true ); + Q_memset(m_radioUntil, 0, sizeof(m_radioUntil)); + Q_memset(m_voiceUntil, 0, sizeof(m_voiceUntil)); +} + +extern float g_flHeadIconSize; + +static float s_flHeadOffset = 3; +static float s_flHeadIconSize = 7; + +void CRadioStatus::DrawHeadLabels() +{ + ExpireBotVoice(); + + if( !m_pHeadLabelMaterial ) + return; + + for(int i=0; i < VOICE_MAX_PLAYERS; i++) + { + if ( m_radioUntil[i] < gpGlobals->curtime ) + continue; + + IClientNetworkable *pClient = cl_entitylist->GetClientEntity( i+1 ); + + // Don't show an icon if the player is not in our PVS. + if ( !pClient || pClient->IsDormant() ) + continue; + + C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer*>(pClient); + if( !pPlayer ) + continue; + + // Don't show an icon for dead or spectating players (ie: invisible entities). + if( pPlayer->IsPlayerDead() ) + continue; + + // Place it above his head. + Vector vOrigin = pPlayer->WorldSpaceCenter(); + vOrigin.z += GetClientVoiceMgr()->GetHeadLabelOffset() + s_flHeadOffset; + + if ( GetClientVoiceMgr()->IsPlayerSpeaking( i+1 ) ) + { + vOrigin.z += g_flHeadIconSize; + } + + // Align it so it never points up or down. + Vector vUp( 0, 0, 1 ); + Vector vRight = CurrentViewRight(); + if ( fabs( vRight.z ) > 0.95 ) // don't draw it edge-on + continue; + + vRight.z = 0; + VectorNormalize( vRight ); + + + float flSize = s_flHeadIconSize; + + CMatRenderContextPtr pRenderContext( materials ); + + pRenderContext->Bind( m_pHeadLabelMaterial ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); + + meshBuilder.Color3f( 1.0, 1.0, 1.0 ); + meshBuilder.TexCoord2f( 0,0,0 ); + meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * flSize)).Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0, 1.0, 1.0 ); + meshBuilder.TexCoord2f( 0,1,0 ); + meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * flSize)).Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0, 1.0, 1.0 ); + meshBuilder.TexCoord2f( 0,1,1 ); + meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * -flSize)).Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0, 1.0, 1.0 ); + meshBuilder.TexCoord2f( 0,0,1 ); + meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * -flSize)).Base() ); + meshBuilder.AdvanceVertex(); + meshBuilder.End(); + pMesh->Draw(); + } +} + + +void CRadioStatus::UpdateRadioStatus(int entindex, float duration) +{ + if(entindex > 0 && entindex <= VOICE_MAX_PLAYERS) + { + int iClient = entindex - 1; + if(iClient < 0) + return; + + m_radioUntil[iClient] = gpGlobals->curtime + duration; + } +} + + +void CRadioStatus::UpdateVoiceStatus(int entindex, float duration) +{ + if(entindex > 0 && entindex <= VOICE_MAX_PLAYERS) + { + int iClient = entindex - 1; + if(iClient < 0) + return; + + m_voiceUntil[iClient] = gpGlobals->curtime + duration; + GetClientVoiceMgr()->UpdateSpeakerStatus( entindex, true ); + } +} + +void CRadioStatus::ExpireBotVoice( bool force ) +{ + for(int i=0; i < VOICE_MAX_PLAYERS; i++) + { + if ( m_voiceUntil[i] > 0.0f ) + { + bool expire = force; + + C_CSPlayer *player = static_cast<C_CSPlayer*>( cl_entitylist->GetEnt(i+1) ); + if ( !player ) + { + // player left the game + expire = true; + } + else if ( m_voiceUntil[i] < gpGlobals->curtime ) + { + // player is done speaking + expire = true; + } + + if ( expire ) + { + m_voiceUntil[i] = 0.0f; + GetClientVoiceMgr()->UpdateSpeakerStatus( i+1, false ); + } + } + } +} + diff --git a/game/client/cstrike/radio_status.h b/game/client/cstrike/radio_status.h new file mode 100644 index 0000000..a820ba8 --- /dev/null +++ b/game/client/cstrike/radio_status.h @@ -0,0 +1,51 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RADIO_STATUS_H +#define RADIO_STATUS_H +#pragma once + +class IMaterial; + +class CRadioStatus : public CAutoGameSystem +{ +public: + CRadioStatus(); + + virtual bool Init(); + virtual void Shutdown(); + + virtual void LevelInitPostEntity(); + virtual void LevelShutdownPreEntity(); + +public: + + // Called when a player uses the radio + void UpdateRadioStatus(int entindex, float duration); + + // Called when a player (bot) speaks a wav file + void UpdateVoiceStatus(int entindex, float duration); + + // Call from the HUD_CreateEntities function so it can add sprites above player heads. + void DrawHeadLabels(); + +private: + + + float m_radioUntil[MAX_PLAYERS]; // Who is currently talking. Indexed by client index. + IMaterial *m_pHeadLabelMaterial; // For labels above players' heads. + + void ExpireBotVoice( bool force = false ); + float m_voiceUntil[MAX_PLAYERS]; // Who is currently talking. Indexed by client index. +}; + + +// Get the (global) voice manager. +CRadioStatus* RadioManager(); + + +#endif // RADIO_STATUS_H diff --git a/game/client/cstrike/vgui_c4panel.cpp b/game/client/cstrike/vgui_c4panel.cpp new file mode 100644 index 0000000..668915d --- /dev/null +++ b/game/client/cstrike/vgui_c4panel.cpp @@ -0,0 +1,211 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "c_vguiscreen.h" +#include "vgui_controls/Label.h" +#include <vgui/IVGui.h> +#include "c_plantedc4.h" +#include "ienginevgui.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class CC4Panel : public CVGuiScreenPanel +{ + DECLARE_CLASS( CC4Panel, CVGuiScreenPanel ); + +public: + CC4Panel( vgui::Panel *parent, const char *panelName ); + ~CC4Panel(); + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick(); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + +private: + vgui::Label *m_pTimeLabel; + + float m_flNextDigitRandomizeTime; //next time to grab a new digit while scrolling random digits + //in un-decoded digits + int m_iLastRandomInt; //store the random digit between new rand calls + + bool m_bInitLabelColor; + + Color m_cArmed; + Color m_cDefused; + Color m_cInvisible; +}; + + +DECLARE_VGUI_SCREEN_FACTORY( CC4Panel, "c4_panel" ); + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +CC4Panel::CC4Panel( vgui::Panel *parent, const char *panelName ) + : BaseClass( parent, "CC4Panel", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/C4Panel.res", "ClientScheme" ) ) +{ + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + m_pTimeLabel = new vgui::Label( this, "TimerLabel", "" ); + + m_flNextDigitRandomizeTime = 0; + m_iLastRandomInt = 0; + + m_bInitLabelColor = true; +} + +CC4Panel::~CC4Panel() +{ +} + +void CC4Panel::ApplySchemeSettings( IScheme *pScheme ) +{ + assert( pScheme ); + + m_cArmed = pScheme->GetColor( "C4Panel_Armed", GetFgColor() ); + m_cDefused = pScheme->GetColor( "C4Panel_Defused", GetFgColor() ); + m_cInvisible = Color( 0, 0, 0, 0 ); + + if( m_bInitLabelColor ) + { + m_pTimeLabel->SetFgColor( m_cArmed ); + m_bInitLabelColor = false; + } +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool CC4Panel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + if (!BaseClass::Init(pKeyValues, pInitData)) + return false; + + return true; +} + +//how long to spend decoding each digit +float flTransitionTimes[] = { 0.9, 0.8, 0.6, 0.45, 0.25, 0.15, 0.0 }; + +//the defuse code, taken from the view model animation, v_c4.mdl +char cDefuseCode[] = { '7', '3', '5', '5', '6', '0', '8', '\0' }; +char cArmedDisplay[] = { '*', '*', '*', '*', '*', '*', '*', '\0' }; + +//convert an integer into the readable character version of that number +#define INT_TO_CHAR(i) ( '0' + (i) ) + +//----------------------------------------------------------------------------- +// Update the display string +//----------------------------------------------------------------------------- +void CC4Panel::OnTick() +{ + BaseClass::OnTick(); + + SetVisible( true ); + + float flProgress = 1.0; + + if ( g_PlantedC4s.Count() > 0 ) + { + C_PlantedC4 *pC4 = g_PlantedC4s[0]; + + if( pC4 ) + { + flProgress = pC4->GetDefuseProgress(); + } + else + return; + } + + m_pTimeLabel->SetFgColor( m_cArmed ); + + // If flProgress is less than 0, the bomb has been defused + if( flProgress < 0.0 ) + { + //Flash when the bomb has been defused + if( flProgress > -0.2 ) //flash for 2 seconds + { + int x = (int)( flProgress * 100 ); + + if( x % 2 == 0 ) + m_pTimeLabel->SetFgColor( m_cInvisible ); + else + m_pTimeLabel->SetFgColor( m_cDefused ); + } + else + m_pTimeLabel->SetFgColor( m_cDefused ); + + //Show the full, decoded defuse code + m_pTimeLabel->SetText( cDefuseCode ); + } + else if( flProgress < 1.0 ) //defuse in progress + { + //Initial display + char buf[8]; + Q_strncpy( buf, cArmedDisplay, MIN( sizeof(buf), sizeof(cArmedDisplay) ) ); + + int iDigitPos = 0; + while( flProgress < flTransitionTimes[iDigitPos] ) + { + //Fill in the previously decoded digits + buf[iDigitPos] = cDefuseCode[iDigitPos]; + iDigitPos++; + } + + //Animate the character that we're decoding + //Value drawn will be based on how long we've been + //decoding this character + float flTimeInThisChar = 1.0 - flTransitionTimes[0]; + + if( iDigitPos > 0 ) + flTimeInThisChar = flTransitionTimes[iDigitPos-1] - flTransitionTimes[iDigitPos]; + + + assert( flTimeInThisChar > 0.0 ); + + + float flPercentDecoding = ( flProgress - flTransitionTimes[iDigitPos] ) / flTimeInThisChar; + + //Determine when to next change the digit that we're decoding + if( m_flNextDigitRandomizeTime < gpGlobals->curtime ) + { + //Get a new random int to draw + m_iLastRandomInt = RandomInt( 0, 9 ); + + if( flPercentDecoding > 0.7 ) + m_flNextDigitRandomizeTime = gpGlobals->curtime + 0.05; + else if( flPercentDecoding > 0.5 ) + m_flNextDigitRandomizeTime = gpGlobals->curtime + 0.1; + else if( flPercentDecoding > 0.3 ) + m_flNextDigitRandomizeTime = gpGlobals->curtime + 0.15; + else + m_flNextDigitRandomizeTime = gpGlobals->curtime + 0.3; + } + + //Settle on the real value if we're close + if( flPercentDecoding < 0.2 ) + buf[iDigitPos] = cDefuseCode[iDigitPos]; + else //else use a random digit + buf[iDigitPos] = INT_TO_CHAR( m_iLastRandomInt ); + + + m_pTimeLabel->SetFgColor( m_cArmed ); + m_pTimeLabel->SetText( buf ); + } + else + { + //Not being defused - draw the armed string + m_pTimeLabel->SetFgColor( m_cArmed ); + m_pTimeLabel->SetText( cArmedDisplay ); + } +}
\ No newline at end of file diff --git a/game/client/cstrike/vgui_rootpanel_cs.cpp b/game/client/cstrike/vgui_rootpanel_cs.cpp new file mode 100644 index 0000000..b5856f2 --- /dev/null +++ b/game/client/cstrike/vgui_rootpanel_cs.cpp @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "vgui_int.h" +#include "ienginevgui.h" +#include "c_csrootpanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +C_CSRootPanel *g_pCSRootPanel = NULL; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VGUI_CreateClientDLLRootPanel( void ) +{ + g_pCSRootPanel = new C_CSRootPanel( enginevgui->GetPanel( PANEL_CLIENTDLL ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VGUI_DestroyClientDLLRootPanel( void ) +{ + delete g_pCSRootPanel; + g_pCSRootPanel = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Game specific root panel +// Output : vgui::Panel +//----------------------------------------------------------------------------- +vgui::VPANEL VGui_GetClientDLLRootPanel( void ) +{ + return g_pCSRootPanel->GetVPanel(); +}
\ No newline at end of file diff --git a/game/client/cstrike/vgui_viewc4panel.cpp b/game/client/cstrike/vgui_viewc4panel.cpp new file mode 100644 index 0000000..e5c7b04 --- /dev/null +++ b/game/client/cstrike/vgui_viewc4panel.cpp @@ -0,0 +1,117 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "c_vguiscreen.h" +#include "vgui_controls/Label.h" +#include <vgui/IVGui.h> +#include "weapon_c4.h" +#include "ienginevgui.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class CViewC4Panel : public CVGuiScreenPanel +{ + DECLARE_CLASS( CViewC4Panel, CVGuiScreenPanel ); + +public: + CViewC4Panel( vgui::Panel *parent, const char *panelName ); + ~CViewC4Panel(); + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick(); + + C_BaseCombatWeapon *GetOwningWeapon(); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + +private: + vgui::Label *m_pTimeLabel; +}; + + +DECLARE_VGUI_SCREEN_FACTORY( CViewC4Panel, "c4_view_panel" ); + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +CViewC4Panel::CViewC4Panel( vgui::Panel *parent, const char *panelName ) + : BaseClass( parent, "CViewC4Panel", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/C4Panel.res", "ClientScheme" ) ) +{ + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + m_pTimeLabel = new vgui::Label( this, "TimerLabel", "" ); +} + +CViewC4Panel::~CViewC4Panel() +{ +} + +void CViewC4Panel::ApplySchemeSettings( IScheme *pScheme ) +{ + if( pScheme ) + { + m_pTimeLabel->SetFgColor( pScheme->GetColor( "C4Panel_Armed", GetFgColor() ) ); + } +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool CViewC4Panel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + if (!BaseClass::Init(pKeyValues, pInitData)) + return false; + + return true; +} + +C_BaseCombatWeapon *CViewC4Panel::GetOwningWeapon() +{ + C_BaseEntity *pScreenEnt = GetEntity(); + if (!pScreenEnt) + return NULL; + + C_BaseEntity *pOwner = pScreenEnt->GetOwnerEntity(); + if (!pOwner) + return NULL; + + C_BaseViewModel *pViewModel = dynamic_cast< C_BaseViewModel * >( pOwner ); + if ( !pViewModel ) + return NULL; + + return pViewModel->GetOwningWeapon(); +} + + +//----------------------------------------------------------------------------- +// Update the screen with the latest string from the view model +//----------------------------------------------------------------------------- +void CViewC4Panel::OnTick() +{ + BaseClass::OnTick(); + + SetVisible( true ); + + C_BaseEntity *pEnt = GetOwningWeapon(); + + C_C4 *pViewC4 = dynamic_cast<C_C4*>( pEnt ); + + if( pViewC4 ) + { + char *display = pViewC4->GetScreenText(); + + if( display ) + { + m_pTimeLabel->SetText( display ); + } + } +}
\ No newline at end of file |