diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/tf_hud_account.cpp | |
| download | archived-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.cpp | 1224 |
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 |