summaryrefslogtreecommitdiff
path: root/game/client/cstrike
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/cstrike
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/cstrike')
-rw-r--r--game/client/cstrike/VGUI/achievement_stats_summary.cpp120
-rw-r--r--game/client/cstrike/VGUI/achievement_stats_summary.h62
-rw-r--r--game/client/cstrike/VGUI/achievements_page.cpp1061
-rw-r--r--game/client/cstrike/VGUI/achievements_page.h218
-rw-r--r--game/client/cstrike/VGUI/backgroundpanel.cpp676
-rw-r--r--game/client/cstrike/VGUI/backgroundpanel.h53
-rw-r--r--game/client/cstrike/VGUI/base_stats_page.cpp359
-rw-r--r--game/client/cstrike/VGUI/base_stats_page.h135
-rw-r--r--game/client/cstrike/VGUI/bordered_panel.cpp27
-rw-r--r--game/client/cstrike/VGUI/bordered_panel.h35
-rw-r--r--game/client/cstrike/VGUI/buymouseoverpanelbutton.h397
-rw-r--r--game/client/cstrike/VGUI/buypreset_imageinfo.cpp577
-rw-r--r--game/client/cstrike/VGUI/buypreset_listbox.cpp406
-rw-r--r--game/client/cstrike/VGUI/buypreset_listbox.h78
-rw-r--r--game/client/cstrike/VGUI/buypreset_panel.cpp447
-rw-r--r--game/client/cstrike/VGUI/buypreset_weaponsetlabel.h302
-rw-r--r--game/client/cstrike/VGUI/career_box.cpp1296
-rw-r--r--game/client/cstrike/VGUI/career_box.h202
-rw-r--r--game/client/cstrike/VGUI/career_button.cpp189
-rw-r--r--game/client/cstrike/VGUI/career_button.h57
-rw-r--r--game/client/cstrike/VGUI/counterstrikeviewport.cpp322
-rw-r--r--game/client/cstrike/VGUI/counterstrikeviewport.h61
-rw-r--r--game/client/cstrike/VGUI/cstrikebuyequipmenu.cpp111
-rw-r--r--game/client/cstrike/VGUI/cstrikebuyequipmenu.h73
-rw-r--r--game/client/cstrike/VGUI/cstrikebuymenu.cpp883
-rw-r--r--game/client/cstrike/VGUI/cstrikebuymenu.h102
-rw-r--r--game/client/cstrike/VGUI/cstrikebuysubmenu.h56
-rw-r--r--game/client/cstrike/VGUI/cstrikeclassmenu.cpp272
-rw-r--r--game/client/cstrike/VGUI/cstrikeclassmenu.h107
-rw-r--r--game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp1596
-rw-r--r--game/client/cstrike/VGUI/cstrikeclientscoreboard.h153
-rw-r--r--game/client/cstrike/VGUI/cstrikespectatorgui.cpp2324
-rw-r--r--game/client/cstrike/VGUI/cstrikespectatorgui.h279
-rw-r--r--game/client/cstrike/VGUI/cstriketeammenu.cpp201
-rw-r--r--game/client/cstrike/VGUI/cstriketeammenu.h54
-rw-r--r--game/client/cstrike/VGUI/cstriketextwindow.cpp146
-rw-r--r--game/client/cstrike/VGUI/cstriketextwindow.h49
-rw-r--r--game/client/cstrike/VGUI/lifetime_stats_page.cpp257
-rw-r--r--game/client/cstrike/VGUI/lifetime_stats_page.h40
-rw-r--r--game/client/cstrike/VGUI/match_stats_page.cpp334
-rw-r--r--game/client/cstrike/VGUI/match_stats_page.h39
-rw-r--r--game/client/cstrike/VGUI/stat_card.cpp106
-rw-r--r--game/client/cstrike/VGUI/stat_card.h49
-rw-r--r--game/client/cstrike/VGUI/stats_summary.cpp684
-rw-r--r--game/client/cstrike/VGUI/stats_summary.h84
-rw-r--r--game/client/cstrike/VGUI/win_panel_round.cpp470
-rw-r--r--game/client/cstrike/VGUI/win_panel_round.h65
-rw-r--r--game/client/cstrike/buy_presets/buy_preset.cpp1195
-rw-r--r--game/client/cstrike/buy_presets/buy_preset_debug.cpp65
-rw-r--r--game/client/cstrike/buy_presets/buy_preset_debug.h25
-rw-r--r--game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp364
-rw-r--r--game/client/cstrike/buy_presets/buy_presets.cpp454
-rw-r--r--game/client/cstrike/buy_presets/buy_presets.h276
-rw-r--r--game/client/cstrike/c_cs_hostage.cpp518
-rw-r--r--game/client/cstrike/c_cs_hostage.h123
-rw-r--r--game/client/cstrike/c_cs_player.cpp2553
-rw-r--r--game/client/cstrike/c_cs_player.h419
-rw-r--r--game/client/cstrike/c_cs_playerresource.cpp206
-rw-r--r--game/client/cstrike/c_cs_playerresource.h76
-rw-r--r--game/client/cstrike/c_cs_team.cpp33
-rw-r--r--game/client/cstrike/c_cs_team.h35
-rw-r--r--game/client/cstrike/c_csrootpanel.cpp86
-rw-r--r--game/client/cstrike/c_csrootpanel.h56
-rw-r--r--game/client/cstrike/c_plantedc4.cpp219
-rw-r--r--game/client/cstrike/c_plantedc4.h73
-rw-r--r--game/client/cstrike/c_te_radioicon.cpp74
-rw-r--r--game/client/cstrike/c_te_shotgun_shot.cpp100
-rw-r--r--game/client/cstrike/clientmode_csnormal.cpp1108
-rw-r--r--game/client/cstrike/clientmode_csnormal.h68
-rw-r--r--game/client/cstrike/cs_client_gamestats.cpp789
-rw-r--r--game/client/cstrike/cs_client_gamestats.h163
-rw-r--r--game/client/cstrike/cs_hud_achievement_announce.cpp415
-rw-r--r--game/client/cstrike/cs_hud_achievement_announce.h92
-rw-r--r--game/client/cstrike/cs_hud_achievement_tracker.cpp68
-rw-r--r--game/client/cstrike/cs_hud_ammo.cpp292
-rw-r--r--game/client/cstrike/cs_hud_chat.cpp355
-rw-r--r--game/client/cstrike/cs_hud_chat.h73
-rw-r--r--game/client/cstrike/cs_hud_damageindicator.cpp362
-rw-r--r--game/client/cstrike/cs_hud_freezepanel.cpp347
-rw-r--r--game/client/cstrike/cs_hud_freezepanel.h70
-rw-r--r--game/client/cstrike/cs_hud_health.cpp175
-rw-r--r--game/client/cstrike/cs_hud_playerhealth.cpp172
-rw-r--r--game/client/cstrike/cs_hud_playerhealth.h77
-rw-r--r--game/client/cstrike/cs_hud_scope.cpp203
-rw-r--r--game/client/cstrike/cs_hud_target_id.cpp379
-rw-r--r--game/client/cstrike/cs_hud_weaponselection.cpp845
-rw-r--r--game/client/cstrike/cs_in_main.cpp23
-rw-r--r--game/client/cstrike/cs_prediction.cpp55
-rw-r--r--game/client/cstrike/cs_replay.cpp227
-rw-r--r--game/client/cstrike/cs_replay.h71
-rw-r--r--game/client/cstrike/cs_view_scene.cpp325
-rw-r--r--game/client/cstrike/cs_view_scene.h40
-rw-r--r--game/client/cstrike/fx_cs_blood.cpp482
-rw-r--r--game/client/cstrike/fx_cs_blood.h20
-rw-r--r--game/client/cstrike/fx_cs_impacts.cpp44
-rw-r--r--game/client/cstrike/fx_cs_knifeslash.cpp72
-rw-r--r--game/client/cstrike/fx_cs_muzzleflash.cpp139
-rw-r--r--game/client/cstrike/fx_cs_weaponfx.cpp61
-rw-r--r--game/client/cstrike/hud_account.cpp66
-rw-r--r--game/client/cstrike/hud_armor.cpp124
-rw-r--r--game/client/cstrike/hud_c4.cpp128
-rw-r--r--game/client/cstrike/hud_deathnotice.cpp485
-rw-r--r--game/client/cstrike/hud_defuser.cpp79
-rw-r--r--game/client/cstrike/hud_flashbang.cpp57
-rw-r--r--game/client/cstrike/hud_hostagerescue.cpp80
-rw-r--r--game/client/cstrike/hud_progressbar.cpp124
-rw-r--r--game/client/cstrike/hud_radar.cpp522
-rw-r--r--game/client/cstrike/hud_radar.h93
-rw-r--r--game/client/cstrike/hud_roundtimer.cpp233
-rw-r--r--game/client/cstrike/hud_scenarioicon.cpp109
-rw-r--r--game/client/cstrike/hud_shopping_cart.cpp94
-rw-r--r--game/client/cstrike/radio_status.cpp421
-rw-r--r--game/client/cstrike/radio_status.h51
-rw-r--r--game/client/cstrike/vgui_c4panel.cpp211
-rw-r--r--game/client/cstrike/vgui_rootpanel_cs.cpp41
-rw-r--r--game/client/cstrike/vgui_viewc4panel.cpp117
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( &params, 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