summaryrefslogtreecommitdiff
path: root/game/client/tf/tf_hud_account.cpp
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/tf/tf_hud_account.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/tf/tf_hud_account.cpp')
-rw-r--r--game/client/tf/tf_hud_account.cpp1224
1 files changed, 1224 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_account.cpp b/game/client/tf/tf_hud_account.cpp
new file mode 100644
index 0000000..6ba4388
--- /dev/null
+++ b/game/client/tf/tf_hud_account.cpp
@@ -0,0 +1,1224 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "cbase.h"
+#include "hud.h"
+#include "hudelement.h"
+#include "c_tf_player.h"
+#include "iclientmode.h"
+#include "ienginevgui.h"
+#include <vgui/ILocalize.h>
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+#include <vgui_controls/EditablePanel.h>
+#include <vgui_controls/ProgressBar.h>
+#include "view_scene.h"
+#include "view.h"
+#include "tf_gamerules.h"
+#include "tf_logic_halloween_2014.h"
+#include "tf_weapon_invis.h"
+#include <vgui_controls/AnimationController.h>
+
+#include "c_tf_objective_resource.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+extern ISoundEmitterSystemBase *soundemitterbase;
+
+// Floating delta text items, float off the top of the frame to
+// show changes to the metal account value
+typedef struct
+{
+ enum eAccountDeltaType_t
+ {
+ ACCOUNT_DELTA_INVALID,
+ ACCOUNT_DELTA_HEALING,
+ ACCOUNT_DELTA_DAMAGE,
+ ACCOUNT_DELTA_BONUS_POINTS,
+ ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_RED,
+ ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_BLUE,
+ };
+
+
+
+ // amount of delta
+ int m_iAmount;
+
+ bool m_bLargeFont; // display larger font
+ eAccountDeltaType_t m_eDataType;
+
+ // die time
+ float m_flDieTime;
+
+ // position
+ int m_nX; // X Pos in screen space & world space
+ int m_nXEnd; // Ending X Pos in screen space and world space
+ int m_nHStart; // Starting Y Pos in screen space, Z pos in world space
+ int m_nHEnd; // Ending Y Pos in screen space, Z pos in world space
+ int m_nY; // Y Coord in world space, not used in screen space
+ bool m_bWorldSpace;
+ float m_flBatchWindow;
+ int m_nSourceID; // Can be entindex, etc
+ Color m_color;
+ bool m_bShadows;
+
+ // append a bit of extra text to the end
+ wchar_t m_wzText[8];
+
+} account_delta_t;
+
+#define NUM_ACCOUNT_DELTA_ITEMS 10
+
+ConVar hud_combattext( "hud_combattext", "1", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX );
+ConVar hud_combattext_healing( "hud_combattext_healing", "1", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Shows health restored per-second over heal targets." );
+ConVar hud_combattext_batching( "hud_combattext_batching", "0", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "If set to 1, numbers that are too close together are merged." );
+ConVar hud_combattext_batching_window( "hud_combattext_batching_window", "0.2", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Maximum delay between damage events in order to batch numbers.", true, 0.1, true, 2.0 );
+ConVar hud_combattext_doesnt_block_overhead_text( "hud_combattext_doesnt_block_overhead_text", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "If set to 1, allow text like \"CRIT\" to still show over a victim's head." );
+
+ConVar tf_dingalingaling( "tf_dingalingaling", "0", FCVAR_ARCHIVE, "If set to 1, play a sound everytime you injure an enemy. The sound can be customized by replacing the 'tf/sound/ui/hitsound.wav' file." );
+ConVar tf_dingaling_volume( "tf_dingaling_volume", "0.75", FCVAR_ARCHIVE, "Desired volume of the hit sound.", true, 0.0, true, 1.0 );
+ConVar tf_dingaling_pitchmindmg( "tf_dingaling_pitchmindmg", "100", FCVAR_ARCHIVE, "Desired pitch of the hit sound when a minimal damage hit (<= 10 health) is done.", true, 1, true, 255 );
+ConVar tf_dingaling_pitchmaxdmg( "tf_dingaling_pitchmaxdmg", "100", FCVAR_ARCHIVE, "Desired pitch of the hit sound when a maximum damage hit (>= 150 health) is done.", true, 1, true, 255 );
+ConVar tf_dingaling_pitch_override( "tf_dingaling_pitch_override", "-1", FCVAR_NONE, "If set, pitch for all hit sounds." );
+
+ConVar tf_dingalingaling_lasthit( "tf_dingalingaling_lasthit", "0", FCVAR_ARCHIVE, "If set to 1, play a sound whenever one of your attacks kills an enemy. The sound can be customized by replacing the 'tf/sound/ui/killsound.wav' file." );
+ConVar tf_dingaling_lasthit_volume( "tf_dingaling_lasthit_volume", "0.75", FCVAR_ARCHIVE, "Desired volume of the last hit sound.", true, 0.0, true, 1.0 );
+ConVar tf_dingaling_lasthit_pitchmindmg( "tf_dingaling_lasthit_pitchmindmg", "100", FCVAR_ARCHIVE, "Desired pitch of the last hit sound when a minimal damage hit (<= 10 health) is done.", true, 1, true, 255 );
+ConVar tf_dingaling_lasthit_pitchmaxdmg( "tf_dingaling_lasthit_pitchmaxdmg", "100", FCVAR_ARCHIVE, "Desired pitch of the last hit sound when a maximum damage hit (>= 150 health) is done.", true, 1, true, 255 );
+ConVar tf_dingaling_lasthit_pitch_override( "tf_dingaling_lasthit_pitch_override", "-1", FCVAR_NONE, "If set, pitch for last hit sounds." );
+
+ConVar tf_dingalingaling_repeat_delay( "tf_dingalingaling_repeat_delay", "0.0", FCVAR_ARCHIVE, "Desired repeat delay of the hit sound. Set to 0 to play a sound for every instance of damage dealt.", true, 0.f, false, 0.f );
+
+ConVar hud_damagemeter( "hud_damagemeter", "0", FCVAR_CHEAT, "Display damage-per-second information in the lower right corner of the screen." );
+ConVar hud_damagemeter_period( "hud_damagemeter_period", "0", FCVAR_NONE, "When set to zero, average damage-per-second across all recent damage events, otherwise average damage across defined period (number of seconds)." );
+ConVar hud_damagemeter_ooctimer( "hud_damagemeter_ooctimer", "1", FCVAR_NONE, "How many seconds after the last damage event before we consider the player out of combat." );
+
+struct hitsound_params_t
+{
+ hitsound_params_t( const char * pszName, int minpitch, int maxpitch )
+ {
+ m_pszName = pszName;
+ m_iMinPitch = minpitch;
+ m_iMaxPitch = maxpitch;
+ }
+
+ float GetPitchMin( bool bLastHit ) const
+ {
+ return bLastHit ? tf_dingaling_lasthit_pitchmindmg.GetInt() : tf_dingaling_pitchmindmg.GetInt();
+ //return RemapValClamped( tf_dingaling_pitchmindmg.GetInt(), 0, 100, m_iMinPitch, m_iMaxPitch );
+ }
+
+ float GetPitchMax( bool bLastHit ) const
+ {
+ return bLastHit ? tf_dingaling_lasthit_pitchmaxdmg.GetInt() : tf_dingaling_pitchmaxdmg.GetInt();
+ //return RemapValClamped( tf_dingaling_pitchmaxdmg.GetInt(), 0, 100, m_iMinPitch, m_iMaxPitch );
+ }
+
+ float GetPitchFromDamage( int damage, bool bLastHit ) const
+ {
+ if ( bLastHit && tf_dingaling_lasthit_pitch_override.GetInt() > 0 )
+ {
+ return tf_dingaling_lasthit_pitch_override.GetFloat();
+ }
+ else if ( tf_dingaling_pitch_override.GetInt() > 0 )
+ {
+ return tf_dingaling_pitch_override.GetFloat();
+ }
+
+ return RemapValClamped( damage, 10, 150, GetPitchMin( bLastHit ), GetPitchMax( bLastHit ) );
+ }
+
+ const char *m_pszName;
+ int m_iMinPitch;
+ int m_iMaxPitch;
+};
+
+static const hitsound_params_t g_HitSounds[] =
+{
+ hitsound_params_t( "Player.HitSoundDefaultDing", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundElectro", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundNotes", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundPercussion", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundRetro", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundSpace", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundBeepo", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundVortex", 1, 255 ),
+ hitsound_params_t( "Player.HitSoundSquasher", 1, 255 ),
+};
+
+static const hitsound_params_t g_LastHitSounds[] =
+{
+ hitsound_params_t( "Player.KillSoundDefaultDing", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundElectro", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundNotes", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundPercussion", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundRetro", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundSpace", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundBeepo", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundVortex", 1, 255 ),
+ hitsound_params_t( "Player.KillSoundSquasher", 1, 255 ),
+};
+
+ConVar tf_dingalingaling_effect( "tf_dingalingaling_effect", "0", FCVAR_ARCHIVE, "Which Dingalingaling sound is used", true, 0, true, ARRAYSIZE( g_HitSounds )-1 );
+ConVar tf_dingalingaling_last_effect( "tf_dingalingaling_last_effect", "0", FCVAR_ARCHIVE, "Which final hit sound to play when the target expires.", true, 0, true, ARRAYSIZE( g_LastHitSounds )-1 );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CAccountPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CAccountPanel, EditablePanel );
+
+public:
+ CAccountPanel( Panel *parent, const char *name )
+ : EditablePanel( parent, name )
+ {
+ m_nBGTexture = -1;
+ m_bNegativeFlipDir = false;
+ SetDialogVariable( "metal", 0 );
+ }
+
+ virtual void ApplySchemeSettings( IScheme *scheme ) OVERRIDE;
+ virtual void ApplySettings( KeyValues *inResourceData ) OVERRIDE;
+ virtual void Paint( void ) OVERRIDE;
+
+ virtual account_delta_t *OnAccountValueChanged( int iOldValue, int iNewValue, account_delta_t::eAccountDeltaType_t type );
+
+ virtual const char *GetResFileName( void ) { return "resource/UI/HudAccountPanel.res"; }
+
+protected:
+ virtual Color GetColor( const account_delta_t::eAccountDeltaType_t& type );
+
+ CUtlVector <account_delta_t> m_AccountDeltaItems;
+
+ int m_nBGTexture;
+ bool m_bNegativeFlipDir;
+
+ CPanelAnimationVarAliasType( float, m_flDeltaItemStartPos, "delta_item_start_y", "100", "proportional_float" );
+ CPanelAnimationVarAliasType( float, m_flDeltaItemEndPos, "delta_item_end_y", "0", "proportional_float" );
+
+ CPanelAnimationVarAliasType( float, m_flDeltaItemX, "delta_item_x", "0", "proportional_float" );
+ CPanelAnimationVarAliasType( float, m_flDeltaItemXEndPos, "delta_item_end_x", "0", "proportional_float" );
+
+ CPanelAnimationVarAliasType( float, m_flBGImageX, "bg_image_x", "0", "proportional_float" );
+ CPanelAnimationVarAliasType( float, m_flBGImageY, "bg_image_y", "0", "proportional_float" );
+ CPanelAnimationVarAliasType( float, m_flBGImageWide, "bg_image_wide", "0", "proportional_float" );
+ CPanelAnimationVarAliasType( float, m_flBGImageTall, "bg_image_tall", "0", "proportional_float" );
+
+ CPanelAnimationVar( Color, m_DeltaPositiveColor, "PositiveColor", "0 255 0 255" );
+ CPanelAnimationVar( Color, m_DeltaNegativeColor, "NegativeColor", "255 0 0 255" );
+ CPanelAnimationVar( Color, m_DeltaEventColor, "EventColor", "255 0 255 255" );
+ CPanelAnimationVar( Color, m_DeltaRedRobotScoreColor, "RedRobotScoreColor", "255 0 0 255" );
+ CPanelAnimationVar( Color, m_DeltaBlueRobotScoreColor, "BlueRobotScoreColor", "0 166 255 255" );
+
+ CPanelAnimationVar( float, m_flDeltaLifetime, "delta_lifetime", "2.0" );
+
+ CPanelAnimationVar( vgui::HFont, m_hDeltaItemFont, "delta_item_font", "Default" );
+ CPanelAnimationVar( vgui::HFont, m_hDeltaItemFontBig, "delta_item_font_big", "Default" );
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CHudAccountPanel : public CHudElement, public CAccountPanel
+{
+ DECLARE_CLASS_SIMPLE( CHudAccountPanel, CAccountPanel );
+
+public:
+ CHudAccountPanel( const char *pElementName )
+ : CHudElement( pElementName )
+ , CAccountPanel( NULL, pElementName )
+ {
+ Panel *pParent = g_pClientMode->GetViewport();
+ SetParent( pParent );
+ SetHiddenBits( HIDEHUD_MISCSTATUS );
+ ListenForGameEvent( "player_account_changed" );
+ }
+
+ virtual void LevelInit( void ) OVERRIDE
+ {
+ CHudElement::LevelInit();
+ }
+
+ virtual void FireGameEvent( IGameEvent *event ) OVERRIDE
+ {
+ const char * type = event->GetName();
+
+ if ( Q_strcmp(type, "player_account_changed") == 0 )
+ {
+ int iOldValue = event->GetInt( "old_account" );
+ int iNewValue = event->GetInt( "new_account" );
+ account_delta_t::eAccountDeltaType_t deltaType = ( iNewValue - iOldValue >= 0 ) ? account_delta_t::ACCOUNT_DELTA_HEALING
+ : account_delta_t::ACCOUNT_DELTA_DAMAGE;
+
+ OnAccountValueChanged( iOldValue, iNewValue, deltaType );
+ }
+ else
+ {
+ CHudElement::FireGameEvent( event );
+ }
+ }
+
+ virtual bool ShouldDraw( void ) OVERRIDE
+ {
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer || !pPlayer->IsAlive() || !pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ m_AccountDeltaItems.RemoveAll();
+ return false;
+ }
+
+ CTFPlayer *pTFPlayer = CTFPlayer::GetLocalTFPlayer();
+ if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
+ return false;
+
+ if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
+ return false;
+
+ if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
+ return false;
+
+ return CHudElement::ShouldDraw();
+ }
+};
+
+DECLARE_HUDELEMENT( CHudAccountPanel );
+
+// Derived account panels
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CHealthAccountPanel : public CHudAccountPanel
+{
+ DECLARE_CLASS_SIMPLE( CHealthAccountPanel, CHudAccountPanel );
+public:
+ CHealthAccountPanel( const char *pElementName ) : CHudAccountPanel(pElementName)
+ {
+ ListenForGameEvent( "player_healonhit" );
+ }
+
+ virtual const char *GetResFileName( void ) { return "resource/UI/HudHealthAccount.res"; }
+
+ void FireGameEvent( IGameEvent *event )
+ {
+ const char * type = event->GetName();
+
+ if ( Q_strcmp(type, "player_healonhit") == 0 )
+ {
+ int iAmount = event->GetInt( "amount" );
+ int iPlayer = event->GetInt( "entindex" );
+ CTFPlayer *pEventPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayer ) );
+ if ( pEventPlayer && !pEventPlayer->IsDormant() )
+ {
+ if ( pEventPlayer == C_TFPlayer::GetLocalTFPlayer() )
+ {
+ OnAccountValueChanged( 0, iAmount, account_delta_t::ACCOUNT_DELTA_HEALING );
+ }
+ else
+ {
+ CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ bool bEnemySpy = !pLocalPlayer->InSameTeam( pEventPlayer ) && pEventPlayer->IsPlayerClass( TF_CLASS_SPY );
+ bool bSneakyEnemySpy = bEnemySpy && ( pEventPlayer->m_Shared.IsStealthed() || pLocalPlayer->m_Shared.IsSpyDisguisedAsMyTeam( pEventPlayer ) );
+ bool bShouldSpawnRedParticle = ( pEventPlayer->GetTeamNumber() == TF_TEAM_RED );
+ if ( bSneakyEnemySpy )
+ {
+ bShouldSpawnRedParticle = ( GetLocalPlayerTeam() == TF_TEAM_RED );
+ }
+
+ const char *pEffectName;
+ if ( iAmount < 0 )
+ {
+ pEffectName = bShouldSpawnRedParticle ? "healthlost_red" : "healthlost_blu";
+ }
+ else if ( iAmount >= 100 )
+ {
+ if ( pEventPlayer->IsMiniBoss() )
+ {
+ pEffectName = bShouldSpawnRedParticle ? "healthgained_red_giant" : "healthgained_blu_giant";
+
+ }
+ else
+ {
+ pEffectName = bShouldSpawnRedParticle ? "healthgained_red_large" : "healthgained_blu_large";
+ }
+ }
+ else
+ {
+ pEffectName = bShouldSpawnRedParticle ? "healthgained_red" : "healthgained_blu";
+ }
+
+ pEventPlayer->ParticleProp()->Create( pEffectName, PATTACH_POINT, "head" );
+ }
+ }
+ }
+ }
+ else
+ {
+ CHudElement::FireGameEvent( event );
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ bool ShouldDraw( void )
+ {
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer || !pPlayer->IsAlive() )
+ {
+ m_AccountDeltaItems.RemoveAll();
+ }
+
+ if ( !m_AccountDeltaItems.Count() )
+ return false;
+
+ if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
+ return false;
+
+ if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
+ return false;
+
+ if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
+ return false;
+
+ return CHudElement::ShouldDraw();
+ }
+};
+DECLARE_HUDELEMENT( CHealthAccountPanel );
+
+class CScoreAccountPanel : public CAccountPanel, public CGameEventListener
+{
+ DECLARE_CLASS_SIMPLE( CScoreAccountPanel, CAccountPanel );
+public:
+ CScoreAccountPanel( Panel *parent, const char *name )
+ : CAccountPanel( parent, name )
+ , m_nTeam( TF_TEAM_COUNT )
+ {}
+
+ virtual const char *GetResFileName( void ) { return "resource/UI/HudScoreAccount.res"; }
+
+ virtual void FireGameEvent( IGameEvent *event ) OVERRIDE
+ {
+ const char * pszEventName = event->GetName();
+
+ if ( Q_strcmp(pszEventName, m_pszEventName) == 0 )
+ {
+ CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ int nTeam = event->GetInt( "team" );
+ if ( m_nTeam != nTeam )
+ return;
+
+ const int nPoints = event->GetInt( "points" );
+ if ( !nPoints )
+ return;
+
+ account_delta_t* pNewAccount = OnAccountValueChanged( 0, nPoints, account_delta_t::ACCOUNT_DELTA_BONUS_POINTS );
+ if ( pNewAccount )
+ {
+ pNewAccount->m_bShadows = true;
+ pNewAccount->m_flBatchWindow = pNewAccount->m_flDieTime;
+ pNewAccount->m_nSourceID = (( nPoints > 0 ) ? 0 : 1 ) + (nTeam * 2);
+
+ if ( ( GetLocalPlayerTeam() == nTeam && nPoints > 0 ) || ( GetLocalPlayerTeam() != nTeam && nPoints < 0 ) )
+ {
+ pNewAccount->m_color = m_DeltaPositiveColor;
+ }
+ else
+ {
+ pNewAccount->m_color = m_DeltaNegativeColor;
+ }
+ }
+ }
+ }
+
+ void ApplySettings( KeyValues *inResourceData )
+ {
+ BaseClass::ApplySettings( inResourceData );
+
+ Q_strncpy( m_pszEventName, inResourceData->GetString( "event" ), sizeof( m_pszEventName ) );
+ if ( m_pszEventName )
+ {
+ ListenForGameEvent( m_pszEventName );
+ }
+
+ const char *pszTeam = inResourceData->GetString( "team" );
+ if ( Q_stricmp( pszTeam, "red" ) == 0 )
+ {
+ m_nTeam = TF_TEAM_RED;
+ }
+ else
+ {
+ m_nTeam = TF_TEAM_BLUE;
+ }
+ }
+
+private:
+ char m_pszEventName[32]; // max length of event names
+ int m_nTeam;
+};
+
+DECLARE_BUILD_FACTORY( CScoreAccountPanel );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CDamageAccountPanel : public CHudAccountPanel
+{
+ DECLARE_CLASS_SIMPLE( CDamageAccountPanel, CHudAccountPanel );
+public:
+ CDamageAccountPanel( const char *pElementName ) : CHudAccountPanel(pElementName)
+ {
+ ListenForGameEvent( "player_hurt" );
+ ListenForGameEvent( "npc_hurt" );
+ ListenForGameEvent( "player_healed" );
+ ListenForGameEvent( "player_bonuspoints" );
+ ListenForGameEvent( "building_healed" );
+
+ ResetDamageVars();
+ vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
+ }
+
+ virtual void OnTick( void );
+ virtual const char *GetResFileName( void ) { return "resource/UI/HudDamageAccount.res"; }
+ virtual void Paint( void );
+
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ bool ShouldDrawDPSMeter( void )
+ {
+ return hud_damagemeter.GetBool();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void DisplayDamageFeedback( CTFPlayer *pAttacker, CBaseCombatCharacter *pVictim, int iDamage, int iHealth, bool bIsCrit )
+ {
+ if ( iDamage <= 0 ) // zero value (invuln?)
+ return;
+
+ CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ if ( !pAttacker || !pVictim )
+ return;
+
+ // Show the attacker, or when healing the player that is
+ if ( ( pAttacker == pLocalPlayer ) ||
+ ( pLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) && ( pLocalPlayer->MedicGetHealTarget() == pAttacker ) ) )
+ {
+ bool bDeadRingerSpy = false;
+ C_TFPlayer *pVictimPlayer = ToTFPlayer( pVictim );
+ if ( pVictimPlayer )
+ {
+ // Player hurt self
+ if ( pAttacker == pVictimPlayer )
+ return;
+
+ // Don't show damage on stealthed and/or disguised enemy spies
+ if ( pVictimPlayer->IsPlayerClass( TF_CLASS_SPY ) && pVictimPlayer->GetTeamNumber() != pLocalPlayer->GetTeamNumber() )
+ {
+ CTFWeaponInvis *pWpn = (CTFWeaponInvis *)pVictimPlayer->Weapon_OwnsThisID( TF_WEAPON_INVIS );
+ if ( pWpn && pWpn->HasFeignDeath() )
+ {
+ if ( pVictimPlayer->m_Shared.IsFeignDeathReady() )
+ {
+ bDeadRingerSpy = true;
+ }
+ }
+
+ if ( !bDeadRingerSpy )
+ {
+ if ( pVictimPlayer->m_Shared.GetDisguiseTeam() == pLocalPlayer->GetTeamNumber() || pVictimPlayer->m_Shared.IsStealthed() )
+ return;
+ }
+ }
+ }
+
+ if ( pAttacker == pLocalPlayer )
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "DamagedPlayer" );
+
+ bool bHitEnabled = ( tf_dingalingaling.GetBool() );
+ bool bLastHitEnabled = ( tf_dingalingaling_lasthit.GetBool() );
+ bool bLastHit = ( iHealth <= 0 ) || bDeadRingerSpy;
+ if ( bLastHitEnabled && bLastHit )
+ {
+ // Always allow the last hit sound
+ m_flLastDingTime = 0.f;
+ }
+
+ // Play hitbeeps
+ if ( ( bHitEnabled || bLastHitEnabled ) &&
+ ( gpGlobals->curtime > ( m_flLastDingTime + tf_dingalingaling_repeat_delay.GetFloat() ) || tf_dingalingaling_repeat_delay.GetFloat() == 0.f ) )
+ {
+ m_flLastDingTime = gpGlobals->curtime;
+
+ CSoundParameters params;
+ CLocalPlayerFilter filter;
+ const char *pszSound = NULL;
+ const hitsound_params_t *pHitSound = NULL;
+
+ if ( bLastHit && bLastHitEnabled )
+ {
+ pszSound = g_LastHitSounds[tf_dingalingaling_last_effect.GetInt()].m_pszName;
+ pHitSound = &g_LastHitSounds[tf_dingalingaling_last_effect.GetInt()];
+ if ( pszSound && pHitSound && CBaseEntity::GetParametersForSound( pszSound, params, NULL ) )
+ {
+ EmitSound_t es( params );
+ es.m_nPitch = pHitSound->GetPitchFromDamage( iDamage, bLastHit );
+ es.m_flVolume = tf_dingaling_lasthit_volume.GetFloat();
+ pLocalPlayer->EmitSound( filter, pLocalPlayer->entindex(), es );
+ }
+ }
+ else if ( bHitEnabled )
+ {
+ pszSound = g_HitSounds[tf_dingalingaling_effect.GetInt()].m_pszName;
+ pHitSound = &g_HitSounds[tf_dingalingaling_effect.GetInt()];
+ if ( pszSound && pHitSound && CBaseEntity::GetParametersForSound( pszSound, params, NULL ) )
+ {
+ EmitSound_t es( params );
+ es.m_nPitch = pHitSound->GetPitchFromDamage( iDamage, false );
+ es.m_flVolume = tf_dingaling_volume.GetFloat();
+ pLocalPlayer->EmitSound( filter, pLocalPlayer->entindex(), es );
+ }
+ }
+ }
+ }
+
+ if ( hud_combattext.GetBool() )
+ {
+ // Ignore damage events on targets that we can't see, so it's not a cheat
+ trace_t tr;
+ UTIL_TraceLine( pVictim->WorldSpaceCenter(), MainViewOrigin(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction >= 1.f )
+ {
+ account_delta_t *pNewAccount = OnAccountValueChanged( 0, -iDamage, account_delta_t::ACCOUNT_DELTA_DAMAGE );
+ if ( pNewAccount )
+ {
+ Vector vecPos = pVictim->GetAbsOrigin();
+ Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
+ int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
+ vecPos.z += (VEC_HULL_MAX_SCALED( pVictim ).z + nHeightoffset);
+ pNewAccount->m_nX = vecPos.x;
+ pNewAccount->m_nXEnd = pNewAccount->m_nX;
+ pNewAccount->m_nY = vecPos.y;
+ pNewAccount->m_nHStart = vecPos.z;
+ pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 32; // How many units to float up
+ pNewAccount->m_bWorldSpace = true;
+ pNewAccount->m_nSourceID = pVictim->entindex();
+ pNewAccount->m_flBatchWindow = hud_combattext_batching.GetBool() ? hud_combattext_batching_window.GetFloat() : 0.f;
+ pNewAccount->m_bLargeFont = bIsCrit;
+ // V_swprintf_safe( pNewAccount->m_wzText, L" (%d)", m_nQueuedDamageEvents );
+ }
+ }
+ }
+
+ m_flLastDamageEventTime = gpGlobals->curtime;
+
+ // Damage meter tracking
+ if ( hud_damagemeter_period.GetFloat() > 0.f )
+ {
+ // Store events and average across a sliding window
+ DamageHistory_t damage = { (float)iDamage, m_flLastDamageEventTime };
+ m_DamageHistory.AddToTail( damage );
+ }
+ else
+ {
+ // Running tally until we hit the out-of-combat timer
+ if ( m_flFirstDamageEventTime == 0.f )
+ {
+ m_flFirstDamageEventTime = m_flLastDamageEventTime;
+ m_flDamagePerSecond = 0.f;
+ m_flDamageMeterTotal = 0.f;
+ }
+
+ m_flDamageMeterTotal += iDamage;
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void FireGameEvent( IGameEvent *event )
+ {
+ if ( FStrEq( event->GetName(), "player_hurt" ) )
+ {
+ const int iDamage = event->GetInt( "damageamount" );
+ const int iHealth = event->GetInt( "health" );
+
+ const int iAttacker = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
+ C_TFPlayer *pAttacker = ToTFPlayer( UTIL_PlayerByIndex( iAttacker ) );
+
+ const int iVictim = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
+ C_TFPlayer *pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictim ) );
+
+ DisplayDamageFeedback( pAttacker, pVictim, iDamage, iHealth, event->GetBool( "crit", 0 ) );
+ }
+ else if ( FStrEq( event->GetName(), "npc_hurt" ) )
+ {
+ const int iDamage = event->GetInt( "damageamount" );
+ const int iHealth = event->GetInt( "health" );
+
+ const int iAttacker = engine->GetPlayerForUserID( event->GetInt( "attacker_player" ) );
+ C_TFPlayer *pAttacker = ToTFPlayer( UTIL_PlayerByIndex( iAttacker ) );
+
+ C_BaseCombatCharacter *pVictim = (C_BaseCombatCharacter *)ClientEntityList().GetClientEntity( event->GetInt( "entindex" ) );
+
+ DisplayDamageFeedback( pAttacker, pVictim, iDamage, iHealth, event->GetBool( "crit", 0 ) );
+ }
+ else if ( FStrEq( event->GetName(), "player_healed" ) )
+ {
+ if ( hud_combattext.GetBool() && hud_combattext_healing.GetBool() )
+ {
+ CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ const int iHealer = engine->GetPlayerForUserID( event->GetInt( "healer" ) );
+ CBasePlayer *pHealer = UTIL_PlayerByIndex( iHealer );
+ if ( pHealer && pHealer == pLocalPlayer )
+ {
+ const int iPatient = engine->GetPlayerForUserID( event->GetInt( "patient" ) );
+ CBasePlayer *pPatient = UTIL_PlayerByIndex( iPatient );
+
+ if ( pPatient )
+ {
+ const int iHealedAmt = event->GetInt( "amount" );
+
+ account_delta_t *pNewAccount = OnAccountValueChanged( 0, iHealedAmt, account_delta_t::ACCOUNT_DELTA_HEALING );
+ if ( pNewAccount )
+ {
+ Vector vecPos = pPatient->GetAbsOrigin();
+ Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
+ int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
+ vecPos.z += ( VEC_HULL_MAX_SCALED( pPatient ).z + nHeightoffset );
+ pNewAccount->m_nX = vecPos.x;
+ pNewAccount->m_nXEnd = pNewAccount->m_nX;
+ pNewAccount->m_nY = vecPos.y;
+ pNewAccount->m_nHStart = vecPos.z;
+ pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 32; // Float 32 units up in worldspace
+ pNewAccount->m_bWorldSpace = true;
+ }
+ }
+ }
+ }
+ }
+ else if ( FStrEq( event->GetName(), "player_bonuspoints" ) )
+ {
+ if ( hud_combattext.GetBool() )
+ {
+ CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ const int nPoints = ( event->GetInt( "points" ) / 10 );
+ if ( !nPoints )
+ return;
+
+ const int iPlayer = event->GetInt( "player_entindex" );
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayer );
+ if ( pPlayer && pPlayer == pLocalPlayer )
+ {
+ const int iSource = event->GetInt( "source_entindex" );
+ C_BaseEntity *pSource = ClientEntityList().GetBaseEntity( iSource );
+ if ( !pSource )
+ return;
+
+ account_delta_t *pNewAccount = OnAccountValueChanged( 0, nPoints, account_delta_t::ACCOUNT_DELTA_BONUS_POINTS );
+ if ( pNewAccount )
+ {
+ Vector vecPos = pSource->GetAbsOrigin();
+ Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
+ int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
+ vecPos.z += ( pSource->IsPlayer() ) ? (VEC_HULL_MAX_SCALED( pSource->GetBaseAnimating() ).z + nHeightoffset) : 0;
+ pNewAccount->m_nX = vecPos.x;
+ pNewAccount->m_nXEnd = pNewAccount->m_nX;
+ pNewAccount->m_nY = vecPos.y;
+ pNewAccount->m_nHStart = vecPos.z;
+ pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 16;
+ pNewAccount->m_bWorldSpace = true;
+ }
+ }
+ }
+ }
+ else if ( FStrEq( event->GetName(), "building_healed" ) )
+ {
+ if ( !hud_combattext.GetBool() || !hud_combattext_healing.GetBool() )
+ return;
+
+ CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
+ return;
+
+ CBaseEntity *pHealer = ClientEntityList().GetEnt( event->GetInt( "healer" ) );
+ if ( pHealer && pHealer == pLocalPlayer )
+ {
+ CBaseEntity *pBuilding = ClientEntityList().GetEnt( event->GetInt( "building" ) );
+ if ( !pBuilding )
+ return;
+
+ const int iHealedAmt = event->GetInt( "amount" );
+
+ account_delta_t *pNewAccount = OnAccountValueChanged( 0, iHealedAmt, account_delta_t::ACCOUNT_DELTA_HEALING );
+ if ( pNewAccount )
+ {
+ Vector vecPos = pBuilding->GetAbsOrigin();
+ Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
+ int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
+ vecPos.z += ( 64 + nHeightoffset );
+ pNewAccount->m_nX = vecPos.x;
+ pNewAccount->m_nXEnd = pNewAccount->m_nX;
+ pNewAccount->m_nY = vecPos.y;
+ pNewAccount->m_nHStart = vecPos.z;
+ pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 32; // Float 32 units up in worldspace
+ pNewAccount->m_bWorldSpace = true;
+ }
+
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ bool ShouldDraw( void )
+ {
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer || !pPlayer->IsAlive() )
+ {
+ m_AccountDeltaItems.RemoveAll();
+ }
+
+ if ( ShouldDrawDPSMeter() )
+ return true;
+
+ if ( !m_AccountDeltaItems.Count() )
+ return false;
+
+ return CHudElement::ShouldDraw();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: called whenever a new level is starting
+ //-----------------------------------------------------------------------------
+ virtual void LevelInit( void ) OVERRIDE
+ {
+ ResetDamageVars();
+
+ BaseClass::LevelInit();
+ }
+
+private:
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void ResetDamageVars( void )
+ {
+ m_flFirstDamageEventTime = 0.f;
+ m_flLastDamageEventTime = 0.f;
+ m_flDamagePerSecond = 0.f;
+ m_flDamageMeterTotal = 0.f;
+ m_flLastDingTime = 0.f;
+ }
+
+private:
+
+ // DamageMeter
+ float m_flFirstDamageEventTime;
+ float m_flLastDamageEventTime;
+ float m_flDamagePerSecond;
+ float m_flDamageMeterTotal;
+ struct DamageHistory_t
+ {
+ float flDamage;
+ float flDamageTime;
+ };
+ CUtlVector< DamageHistory_t > m_DamageHistory;
+
+ // Dings
+ float m_flLastDingTime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDamageAccountPanel::OnTick( void )
+{
+ if ( ShouldDrawDPSMeter() )
+ {
+ // We're out of combat - nuke everything
+ if ( m_flLastDamageEventTime < gpGlobals->curtime - hud_damagemeter_ooctimer.GetFloat() )
+ {
+ m_DamageHistory.RemoveAll();
+ m_flFirstDamageEventTime = 0.f;
+ }
+ else
+ {
+ float flPeriod = hud_damagemeter_period.GetFloat();
+
+ // Period-based calculation (averaged across a defined range)
+ if ( flPeriod > 0.f )
+ {
+ m_flDamageMeterTotal = 0.f;
+
+ FOR_EACH_VEC_BACK( m_DamageHistory, i )
+ {
+ if ( flPeriod > 0.f )
+ {
+ // This method averages across a fixed period, so nuke entires outside the period
+ if ( gpGlobals->curtime - flPeriod > m_DamageHistory[i].flDamageTime )
+ {
+ m_DamageHistory.Remove( i );
+ continue;
+ }
+ }
+
+ // What's left is within the period (sliding window)
+ m_flDamageMeterTotal += m_DamageHistory[i].flDamage;
+ }
+
+ m_flDamagePerSecond = m_flDamageMeterTotal / flPeriod;
+ }
+ // Event-based calculation (absolute dps)
+ else if ( m_flFirstDamageEventTime > 0.f )
+ {
+ flPeriod = Max( m_flLastDamageEventTime - m_flFirstDamageEventTime, 1.f );
+ m_flDamagePerSecond = m_flDamageMeterTotal / flPeriod;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDamageAccountPanel::Paint( void )
+{
+ BaseClass::Paint();
+
+ if ( ShouldDrawDPSMeter() )
+ {
+ int iScreenWide, iScreenTall;
+ GetHudSize( iScreenWide, iScreenTall );
+ int nX = iScreenWide / 1.15;
+ int nY = iScreenTall / 1.20;
+
+ int r = 255, g = 255, b = 255;
+ if ( m_flLastDamageEventTime < gpGlobals->curtime - hud_damagemeter_ooctimer.GetFloat() )
+ {
+ r = 255, g = 0, b = 0;
+ }
+ Color cDPS( r, g, b, 255 );
+
+ vgui::surface()->DrawSetTextFont( m_hDeltaItemFontBig );
+ vgui::surface()->DrawSetTextColor( cDPS );
+ vgui::surface()->DrawSetTextPos( nX, nY );
+
+ wchar_t wDPSBuf[20];
+ V_swprintf_safe( wDPSBuf, L"%d DPS", (int)m_flDamagePerSecond );
+ vgui::surface()->DrawPrintText( wDPSBuf, wcslen( wDPSBuf ), FONT_DRAW_NONADDITIVE );
+ }
+}
+
+DECLARE_HUDELEMENT( CDamageAccountPanel );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAccountPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ // load control settings...
+ LoadControlSettings( GetResFileName() );
+
+ BaseClass::ApplySchemeSettings( pScheme );
+}
+
+void CAccountPanel::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ // Backwards compatibility. If we DONT find "delta_item_end_x" specified in the keyvalues,
+ // then just take the starting x-pos as the ending x-pos.
+ if ( inResourceData->FindKey( "delta_item_end_x" ) == NULL )
+ {
+ m_flDeltaItemXEndPos = m_flDeltaItemX;
+ }
+
+ m_bNegativeFlipDir = inResourceData->FindKey( "negative_flip_dir", false );
+
+ const char *pszBGTextureName = inResourceData->GetString( "bg_texture", NULL );
+ if ( m_nBGTexture == -1 && pszBGTextureName && pszBGTextureName[0] )
+ {
+ m_nBGTexture = vgui::surface()->CreateNewTextureID();
+ vgui::surface()->DrawSetTextureFile( m_nBGTexture , pszBGTextureName, true, false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+account_delta_t *CAccountPanel::OnAccountValueChanged( int iOldValue, int iNewValue, account_delta_t::eAccountDeltaType_t type )
+{
+ // update the account value
+ SetDialogVariable( "metal", iNewValue );
+
+ int iDelta = iNewValue - iOldValue;
+
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( iDelta != 0 && pPlayer && pPlayer->IsAlive() )
+ {
+ int index = m_AccountDeltaItems.AddToTail();
+ account_delta_t *pNewDeltaItem = &m_AccountDeltaItems[index];
+
+ pNewDeltaItem->m_flDieTime = gpGlobals->curtime + m_flDeltaLifetime;
+ pNewDeltaItem->m_iAmount = iDelta;
+ pNewDeltaItem->m_nX = m_flDeltaItemX;
+ pNewDeltaItem->m_nXEnd = m_flDeltaItemXEndPos;
+ pNewDeltaItem->m_nHStart = m_flDeltaItemStartPos;
+ pNewDeltaItem->m_nHEnd = m_flDeltaItemEndPos;
+ pNewDeltaItem->m_bWorldSpace = false;
+ pNewDeltaItem->m_nSourceID = -1;
+ pNewDeltaItem->m_flBatchWindow = 0.f;
+ pNewDeltaItem->m_bLargeFont = false;
+ pNewDeltaItem->m_eDataType = type;
+ pNewDeltaItem->m_wzText[0] = NULL;
+ pNewDeltaItem->m_color = GetColor( type );
+ pNewDeltaItem->m_bShadows = false;
+ return &m_AccountDeltaItems[index];
+ }
+
+ return NULL;
+}
+
+Color CAccountPanel::GetColor( const account_delta_t::eAccountDeltaType_t& type )
+{
+ if ( type == account_delta_t::ACCOUNT_DELTA_BONUS_POINTS )
+ {
+ return m_DeltaEventColor;
+ }
+ else if ( type == account_delta_t::ACCOUNT_DELTA_HEALING )
+ {
+ return m_DeltaPositiveColor;
+ }
+ else if ( type == account_delta_t::ACCOUNT_DELTA_DAMAGE )
+ {
+ return m_DeltaNegativeColor;
+ }
+ else if ( type == account_delta_t::ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_BLUE )
+ {
+ return m_DeltaBlueRobotScoreColor;
+ }
+ else if ( type == account_delta_t::ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_RED )
+ {
+ return m_DeltaRedRobotScoreColor;
+ }
+
+ return Color( 255, 255, 255, 255 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Paint the deltas
+//-----------------------------------------------------------------------------
+void CAccountPanel::Paint( void )
+{
+ BaseClass::Paint();
+
+ FOR_EACH_VEC_BACK( m_AccountDeltaItems, i )
+ {
+ // Reduce lifetime when count grows too high
+ float flTimeMod = m_AccountDeltaItems.Count() > NUM_ACCOUNT_DELTA_ITEMS ? RemapValClamped( m_AccountDeltaItems.Count(), 10.f, 15.f, 0.5f, 1.5f ) : 0.f;
+
+ // update all the valid delta items
+ if ( ( m_AccountDeltaItems[i].m_flDieTime - flTimeMod ) > gpGlobals->curtime )
+ {
+ // position and alpha are determined from the lifetime
+ Color c = m_AccountDeltaItems[i].m_color;
+
+ float flLifetimePercent = ( m_flDeltaLifetime - ( m_AccountDeltaItems[i].m_flDieTime - gpGlobals->curtime ) ) / m_flDeltaLifetime;
+ // fade out after half our lifetime
+ int nAlpha = flLifetimePercent > 0.5 ? (int)( 255.0f * ( ( 0.5f - flLifetimePercent ) / 0.5f ) ) : 255;
+ c[3] = nAlpha;
+
+
+
+ // Some items want to be batched together as they're super frequent (i.e. damage events from a flamethrower, or minigun)
+ if ( m_AccountDeltaItems[i].m_flBatchWindow > 0.f && m_AccountDeltaItems[i].m_nSourceID != -1 && m_AccountDeltaItems.IsValidIndex( i - 1 ) )
+ {
+ // If next item is from the same source and too close, merge
+ float flDelay = m_AccountDeltaItems[i].m_flBatchWindow;
+ if ( m_AccountDeltaItems[i].m_flDieTime - m_AccountDeltaItems[i-1].m_flDieTime <= flDelay &&
+ m_AccountDeltaItems[i-1].m_nSourceID == m_AccountDeltaItems[i].m_nSourceID )
+ {
+ m_AccountDeltaItems[i].m_iAmount += m_AccountDeltaItems[i-1].m_iAmount;
+ m_AccountDeltaItems.Remove( i - 1 );
+ continue;
+ }
+ }
+
+ float flHeight = m_AccountDeltaItems[i].m_nHEnd - m_AccountDeltaItems[i].m_nHStart;
+ float flWidth = m_AccountDeltaItems[i].m_nXEnd - m_AccountDeltaItems[i].m_nX;
+
+ // We can be told to go the opposite direction if we're negative
+ if ( m_bNegativeFlipDir && m_AccountDeltaItems[i].m_iAmount < 0 )
+ {
+ flHeight = -flHeight;
+ flWidth = -flWidth;
+ }
+
+ float flYPos = m_AccountDeltaItems[i].m_nHStart + ( flLifetimePercent * flHeight );
+ float flXPos = m_AccountDeltaItems[i].m_nX + ( flLifetimePercent * flWidth );
+ if ( m_AccountDeltaItems[i].m_bWorldSpace )
+ {
+ Vector vecWorld( m_AccountDeltaItems[i].m_nX, m_AccountDeltaItems[i].m_nY, flYPos );
+ int iX,iY;
+ if ( !GetVectorInHudSpace( vecWorld, iX, iY ) ) // Tested - NOT GetVectorInScreenSpace
+ continue;
+
+ flXPos = iX;
+ flYPos = iY;
+ }
+
+ // If we have a background texture, then draw it!
+ if ( m_nBGTexture != -1 )
+ {
+ vgui::surface()->DrawSetColor(255,255,255,nAlpha);
+ vgui::surface()->DrawSetTexture(m_nBGTexture);
+ vgui::surface()->DrawTexturedRect( flXPos + m_flBGImageX, flYPos + m_flBGImageY, flXPos + m_flBGImageX + m_flBGImageWide, flYPos + m_flBGImageY + m_flBGImageTall );
+ }
+
+ wchar_t wBuf[20];
+
+ if ( m_AccountDeltaItems[i].m_iAmount > 0 )
+ {
+ V_swprintf_safe( wBuf, L"+%d", m_AccountDeltaItems[i].m_iAmount );
+ }
+ else
+ {
+ V_swprintf_safe( wBuf, L"%d", m_AccountDeltaItems[i].m_iAmount );
+ }
+
+ // Append?
+ if ( m_AccountDeltaItems[i].m_wzText[0] )
+ {
+ wchar_t wAppend[8] = { 0 };
+ V_swprintf_safe( wAppend, L"%ls", m_AccountDeltaItems[i].m_wzText );
+ V_wcscat_safe( wBuf, wAppend );
+ }
+
+
+ if ( m_AccountDeltaItems[i].m_bLargeFont )
+ {
+ vgui::surface()->DrawSetTextFont( m_hDeltaItemFontBig );
+ }
+ else
+ {
+ vgui::surface()->DrawSetTextFont( m_hDeltaItemFont );
+ }
+
+ // If we're supposed to have shadows, then draw the text as black and offset a bit first.
+ // Things get ugly as we approach 0 alpha, so stop drawing the shadow a bit early.
+ if ( m_AccountDeltaItems[i].m_bShadows && c[3] > 10 )
+ {
+ vgui::surface()->DrawSetTextPos( (int)flXPos + XRES(1), (int)flYPos + YRES(1) );
+ vgui::surface()->DrawSetTextColor( COLOR_BLACK );
+ vgui::surface()->DrawPrintText( wBuf, wcslen(wBuf), FONT_DRAW_NONADDITIVE );
+ }
+
+ vgui::surface()->DrawSetTextPos( (int)flXPos, (int)flYPos );
+ vgui::surface()->DrawSetTextColor( c );
+ vgui::surface()->DrawPrintText( wBuf, wcslen(wBuf), FONT_DRAW_NONADDITIVE );
+ }
+ else
+ {
+ m_AccountDeltaItems.Remove( i );
+ }
+ }
+}
+
+#ifdef STAGING_ONLY
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CBountyAccountPanel : public CHudAccountPanel
+{
+ DECLARE_CLASS_SIMPLE( CBountyAccountPanel, CHudAccountPanel );
+public:
+ CBountyAccountPanel( const char *pElementName ) : CHudAccountPanel( pElementName )
+ {
+ }
+
+ virtual const char *GetResFileName( void ) { return "resource/UI/HudDamageAccount.res"; }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void Paint( void )
+ {
+ if ( TFGameRules() && ( !TFGameRules()->IsBountyMode() || TFGameRules()->IsMannVsMachineMode() ) )
+ return;
+
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer )
+ return;
+
+ int iScreenWide, iScreenTall;
+ GetHudSize( iScreenWide, iScreenTall );
+ int nX = iScreenWide * 0.06f;
+ int nY = iScreenTall * 0.97f;
+
+ Color cDPS( 25, 255, 25, 255 );
+ vgui::surface()->DrawSetTextFont( m_hDeltaItemFontBig );
+ vgui::surface()->DrawSetTextColor( cDPS );
+ vgui::surface()->DrawSetTextPos( nX, nY );
+
+ m_nCurrency = pPlayer->GetCurrency();
+ wchar_t wCurrency[20];
+ V_swprintf_safe( wCurrency, L"$%d", m_nCurrency );
+ vgui::surface()->DrawPrintText( wCurrency, wcslen( wCurrency ), FONT_DRAW_NONADDITIVE );
+
+ if ( pPlayer->GetCurrency() != m_nCurrency )
+ {
+ pPlayer->EmitSound( "Credits.Updated" );
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ bool ShouldDraw( void )
+ {
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pPlayer || !pPlayer->IsAlive() )
+ return false;
+
+ if ( TFGameRules() && ( !TFGameRules()->IsBountyMode() || TFGameRules()->IsMannVsMachineMode() ) )
+ return false;
+
+ return CHudElement::ShouldDraw();
+ }
+private:
+
+ int m_nCurrency;
+};
+
+DECLARE_HUDELEMENT( CBountyAccountPanel );
+#endif // STAGING_ONLY