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.
1225 lines
42 KiB
1225 lines
42 KiB
![]()
4 years ago
|
//========= 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
|