Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1224 lines
42 KiB

//========= 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