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.

1241 lines
42 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include <vgui_controls/AnimationController.h>
#include "tf_hud_freezepanel.h"
#include "clientmode_shared.h"
#include "tf_hud_robot_destruction_status.h"
#include "tf_logic_player_destruction.h"
#include "c_tf_objective_resource.h"
#include "c_func_capture_zone.h"
#define ATTACK_BLINK_TIME 2.f
using namespace vgui;
extern ConVar tf_rd_min_points_to_steal;
extern ConVar tf_rd_steal_rate;
extern ConVar tf_rd_points_per_steal;
extern ConVar tf_rd_points_approach_interval;
extern ConVar tf_rd_points_per_approach;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHudRobotDestruction_StateImage::CTFHudRobotDestruction_StateImage( Panel *parent, const char *name, const char *pszResFile )
: vgui::EditablePanel( parent, name )
, m_pImage( NULL )
, m_pRobotImage( NULL )
, m_pszResFile( pszResFile )
{
m_pImage = new ImagePanel( this, "Image" );
m_pRobotImage = new ImagePanel( this, "RobotImage" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_StateImage::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( m_pszResFile );
ImagePanel* pGlow = dynamic_cast<ImagePanel*>( FindChildByName( "GlowImage", true ) );
if ( pGlow )
{
pGlow->SetAlpha( 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_StateImage::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
int nTeam = TF_TEAM_RED;
const CTFHudRobotDestruction_RobotIndicator *pRobotImageParent = dynamic_cast< const CTFHudRobotDestruction_RobotIndicator* >( GetParent()->GetParent() );
if ( pRobotImageParent )
{
nTeam = pRobotImageParent->GetTeamNumber();
}
const char *pszKeyName = nTeam == TF_TEAM_RED ? "redimage" : "blueimage";
const char *pszImageName = inResourceData->GetString( pszKeyName );
if ( pszImageName && pszImageName[0] )
{
m_pImage->SetImage( pszImageName );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHudRobotDestruction_DeadImage::CTFHudRobotDestruction_DeadImage( Panel *parent, const char *name, const char *pszResFile )
: CTFHudRobotDestruction_StateImage( parent, name, pszResFile )
, m_pRespawnProgressBar( NULL )
{
m_pRespawnProgressBar = new CTFProgressBar( this, "RespawnProgressBar" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_DeadImage::SetProgress( float flProgress )
{
m_pRespawnProgressBar->SetPercentage( flProgress );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHudRobotDestruction_ActiveImage::CTFHudRobotDestruction_ActiveImage( Panel *parent, const char *name, const char *pszResFile )
: CTFHudRobotDestruction_StateImage( parent, name, pszResFile )
{}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_ActiveImage::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
const CTFHudRobotDestruction_RobotIndicator *pRobotImageParent = dynamic_cast< const CTFHudRobotDestruction_RobotIndicator* >( GetParent()->GetParent() );
if ( pRobotImageParent && pRobotImageParent->GetGroup() )
{
m_pRobotImage->SetImage( pRobotImageParent->GetGroup()->GetHUDIcon() );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHudRobotDestruction_RobotIndicator::CTFHudRobotDestruction_RobotIndicator( vgui::Panel *pParent, const char *pszName, CTFRobotDestruction_RobotGroup *pGroup )
: EditablePanel( pParent, pszName )
, m_hGroup( pGroup )
, m_pNextRobotIndicator( NULL )
, m_pPrevRobotIndicator( NULL )
{
Assert( pGroup );
m_pSwoop = new CControlPointIconSwoop( this, "Swoop" );
m_pSwoop->SetVisible( false );
m_pRobotStateContainer = new EditablePanel( this, "RobotStateContainer" );
m_pDeadPanel = new CTFHudRobotDestruction_DeadImage( m_pRobotStateContainer, "DeadState", "resource/UI/TFHudRobotDestruction_DeadState.res" );
m_pActivePanel = new CTFHudRobotDestruction_ActiveImage( m_pRobotStateContainer, "ActiveState", "resource/UI/TFHudRobotDestruction_ActiveState.res" );
m_pShieldedPanel = new CTFHudRobotDestruction_StateImage( m_pRobotStateContainer, "ShieldedState", "resource/UI/TFHudRobotDestruction_ShieldedState.res" );
m_flHealthPercentage = 0.f;
m_eState = ROBOT_STATE_DEAD;
vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_RobotIndicator::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/UI/TFHudRobotDestruction_RobotIndicator.res" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_RobotIndicator::PerformLayout()
{
BaseClass::PerformLayout();
m_pSwoop->SetBounds( 0, 0, GetWide(), GetTall() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_RobotIndicator::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_RobotIndicator::OnTick()
{
if ( !m_hGroup )
{
SetVisible( false );
vgui::ivgui()->RemoveTickSignal( GetVPanel() );
}
UpdateState();
DoUnderAttackBlink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_RobotIndicator::DoUnderAttackBlink()
{
if ( !m_hGroup.Get() )
return;
if ( gpGlobals->curtime < ( m_hGroup->GetLastAttackedTime() + ATTACK_BLINK_TIME ) && ( GetLocalPlayerTeam() == m_hGroup->GetTeamNumber() ) )
{
// Pulse red
ImagePanel* pGlow = dynamic_cast<ImagePanel*>( FindChildByName( "GlowImage", true ) );
if ( pGlow )
{
float flAlpha = fabs(sin( gpGlobals->curtime * 10.f )) * 255;
pGlow->SetAlpha( flAlpha );
}
}
else
{
// Stop pulsing, stop ticking
ImagePanel* pGlow = dynamic_cast<ImagePanel*>( FindChildByName( "GlowImage", true ) );
if ( pGlow )
{
pGlow->SetAlpha( 0 );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFHudRobotDestruction_RobotIndicator::GetGroupNumber() const
{
Assert( m_hGroup );
if ( !m_hGroup )
{
return 0;
}
return m_hGroup->GetGroupNumber();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFHudRobotDestruction_RobotIndicator::GetTeamNumber() const
{
Assert( m_hGroup );
if ( !m_hGroup )
{
return 0;
}
return m_hGroup->GetTeamNumber();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudRobotDestruction_RobotIndicator::UpdateState()
{
// Don't do anything if there's no group
if ( !m_hGroup.Get() )
return;
eRobotUIState eState = (eRobotUIState)m_hGroup->GetState();
// Get the time
float flStartTime = m_hGroup->GetRespawnStartTime();
float flEndTime = m_hGroup->GetRespawnEndTime();
// Show how much time is remaining
m_pDeadPanel->SetDialogVariable( "time", CFmtStr( "%0.0f", Max( 0.f, flEndTime - gpGlobals->curtime ) ) );
// Figure out what percentage we're at
float flDuration = flEndTime - flStartTime;
float flProgress = gpGlobals->curtime - flStartTime;
m_pDeadPanel->SetProgress( flProgress / flDuration );
bool bStateVisibility[ NUM_ROBOT_STATES ];
memset( bStateVisibility, false, sizeof( bStateVisibility ) );
bool bDraw = eState != ROBOT_STATE_INACIVE;
if ( bDraw )
{
m_pRobotStateContainer->SetVisible( true );
bStateVisibility[ eState ] = true;
}
else
{
m_pRobotStateContainer->SetVisible( false );
}
m_eState = eState;
bool bShowDead = m_eState == ROBOT_STATE_DEAD;
m_pActivePanel->SetImageVisible( !bShowDead );
m_pDeadPanel->SetVisible( bStateVisibility[ ROBOT_STATE_DEAD ] || bShowDead );
m_pActivePanel->SetVisible( bStateVisibility[ ROBOT_STATE_ACTIVE ] );
m_pShieldedPanel->SetVisible( bStateVisibility[ ROBOT_STATE_SHIELDED ] );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHUDRobotDestruction::CTFHUDRobotDestruction( Panel *parent, const char *name )
: EditablePanel( parent, name )
, m_bPlayingRD( false )
{
m_pPlayingTo = NULL;
m_pRobotIndicatorKVs = NULL;
m_pCarriedContainer = new EditablePanel( this, "CarriedContainer" );
m_pCarriedImage = new ImagePanel( m_pCarriedContainer, "CarriedImage" );
m_pCarriedFlagProgressBar = new CProgressPanel( m_pCarriedContainer, "CarriedProgressBar" );
m_pScoreContainer = new EditablePanel( this, "ScoreContainer" );
m_pBlueStolenContainer = new EditablePanel( m_pScoreContainer, "BlueStolenContainer" );
m_pBlueDroppedPanel = new EditablePanel( m_pBlueStolenContainer, "DroppedIntelContainer" );
m_pRedStolenContainer = new EditablePanel( m_pScoreContainer, "RedStolenContainer" );
m_pRedDroppedPanel = new EditablePanel( m_pRedStolenContainer, "DroppedIntelContainer" );
m_pBlueScoreValueContainer = new EditablePanel( m_pScoreContainer, "BlueScoreValueContainer" );
m_pRedScoreValueContainer = new EditablePanel( m_pScoreContainer, "RedScoreValueContainer" );
m_pProgressBarsContainer = new EditablePanel( m_pScoreContainer, "ProgressBarContainer" );
m_pBlueVictoryPanel = new EditablePanel( m_pProgressBarsContainer, "BlueVictoryContainer" );
m_pBlueProgressBar = new CProgressPanel( m_pProgressBarsContainer, "BlueProgressBarFill" );
m_pBlueProgressBarEscrow = new CProgressPanel( m_pProgressBarsContainer, "BlueProgressBarEscrow" );
m_pRedVictoryPanel = new EditablePanel( m_pProgressBarsContainer, "RedVictoryContainer" );
m_pRedProgressBar = new CProgressPanel( m_pProgressBarsContainer, "RedProgressBarFill" );
m_pRedProgressBarEscrow = new CProgressPanel( m_pProgressBarsContainer, "RedProgressBarEscrow" );
m_pCountdownContainer = NULL;
m_pTeamLeaderImage = NULL;
vgui::ivgui()->AddTickSignal( GetVPanel(), 50 );
ListenForGameEvent( "rd_rules_state_changed" );
ListenForGameEvent( "flagstatus_update" );
ListenForGameEvent( "rd_team_points_changed" );
ListenForGameEvent( "teamplay_round_start" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHUDRobotDestruction::~CTFHUDRobotDestruction()
{
if ( m_pRobotIndicatorKVs )
{
m_pRobotIndicatorKVs->deleteThis();
m_pRobotIndicatorKVs = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFHUDRobotDestruction::IsVisible( void )
{
if( IsTakingAFreezecamScreenshot() )
return false;
return BaseClass::IsVisible();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
KeyValues *pItemKV = inResourceData->FindKey( "robot_kv" );
if ( pItemKV )
{
if ( m_pRobotIndicatorKVs )
{
m_pRobotIndicatorKVs->deleteThis();
}
m_pRobotIndicatorKVs = new KeyValues( "robot_kv" );
pItemKV->CopySubkeys( m_pRobotIndicatorKVs );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int SortRobotVec( CTFHudRobotDestruction_RobotIndicator * const *p1, CTFHudRobotDestruction_RobotIndicator * const *p2 )
{
return (*p2)->GetGroupNumber() - (*p1)->GetGroupNumber();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
CTFRobotDestructionLogic* pRoboLogic = CTFRobotDestructionLogic::GetRobotDestructionLogic();
if ( !pRoboLogic )
return;
// load control settings...
LoadControlSettings( pRoboLogic->GetResFile() );
// Clear out any old robot panels and bars
m_vecRedRobots.PurgeAndDeleteElements();
m_vecBlueRobots.PurgeAndDeleteElements();
CUtlVector< CTFRobotDestruction_RobotGroup * > vecSeenGroups;
// Go through all the groups in the map, and create a UI element for each one
for ( int i=0; i<IRobotDestructionGroupAutoList::AutoList().Count(); ++i )
{
CTFRobotDestruction_RobotGroup *pGroup = static_cast< CTFRobotDestruction_RobotGroup* >( IRobotDestructionGroupAutoList::AutoList()[i] );
if ( pGroup->IsDormant() )
continue;
if ( vecSeenGroups.Find( pGroup ) != vecSeenGroups.InvalidIndex() )
{
Assert( 0 );
DevMsg( "[RD HUD]: %p seen multiple times!\n", pGroup);
continue;
}
vecSeenGroups.AddToTail( pGroup );
RobotVector_t &robotVec = pGroup->GetTeamNumber() == TF_TEAM_RED ? m_vecRedRobots : m_vecBlueRobots;
const char* pszPanelName = pGroup->GetTeamNumber() == TF_TEAM_RED ? "red" : "blue";
robotVec[ robotVec.AddToTail() ] = new CTFHudRobotDestruction_RobotIndicator( this, CFmtStr( "%s_group_%d", pszPanelName, robotVec.Count() ), pGroup );
}
// Sort them from lowest group to highest group
m_vecRedRobots.Sort( &SortRobotVec );
m_vecBlueRobots.Sort( &SortRobotVec );
m_pCarriedContainer->SetVisible( false );
m_pCountdownContainer = dynamic_cast<EditablePanel*>( FindChildByName( "CountdownContainer" ) );
m_pTeamLeaderImage = dynamic_cast<CTFImagePanel*>( m_pCarriedContainer->FindChildByName( "TeamLeaderImage" ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::PerformLayout()
{
BaseClass::PerformLayout();
// Sort them from lowest group to highest group
m_vecRedRobots.Sort( &SortRobotVec );
m_vecBlueRobots.Sort( &SortRobotVec );
PerformRobotLayout( m_vecRedRobots, TF_TEAM_RED );
PerformRobotLayout( m_vecBlueRobots, TF_TEAM_BLUE );
int nXPos, nYPos;
m_pProgressBarsContainer->GetPos( nXPos, nYPos );
// Store the edges of the score container
m_nStealLeftEdge = nXPos + m_nStealLeftEdgeOffset - ( m_pRedStolenContainer->GetWide() / 2.f );
m_nStealRightEdge = nXPos + m_pProgressBarsContainer->GetWide() - m_nStealRightEdgeOffset + ( m_pRedStolenContainer->GetWide() / 2.f );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::PerformRobotLayout( RobotVector_t& vecRobots, int nTeam )
{
int nParentX = 0, nParentY = 0, nParentWide = 0, nParentTall = 0;
GetBounds( nParentX, nParentY, nParentWide, nParentTall );
bool bIsRed = nTeam == TF_TEAM_RED;
const int nCenterX = nParentX + ( nParentWide * 0.5f );
int nActiveIndex = 0;
const int nXOffset = m_iRobotXOffset;
const int nYOffest = nParentTall - m_iRobotYOffset;
const int nXStep = m_iRobotXStep;
const int nYStep = m_iRobotYStep;
Panel *pPrevPanel = NULL;
// Position the robot panels, spanning out from the bottom center
FOR_EACH_VEC_BACK( vecRobots, i )
{
CTFHudRobotDestruction_RobotIndicator* pRobot = vecRobots[i];
if ( pRobot )
{
pRobot->ApplySettings( m_pRobotIndicatorKVs );
pRobot->UpdateState();
pRobot->SetZPos( vecRobots.Count() - i );
CTFHudRobotDestruction_RobotIndicator *pPrevRobot = dynamic_cast< CTFHudRobotDestruction_RobotIndicator * >( pPrevPanel );
if ( pPrevRobot )
{
pRobot->SetPrevRobotIndicator( pPrevRobot );
pPrevRobot->SetNextRobotIndicator( pRobot );
}
int nWide = pRobot->GetWide();
// The starting offset
int nStartPos = ( ( nXOffset ) + ( nWide * 0.5f ) ) * (bIsRed ? 1 : -1);
// The offset between each position
int nOffset = bIsRed ? nXStep : -nXStep;
int nXPos = nCenterX + nStartPos + ( nOffset * nActiveIndex ) - ( nWide * 0.5 );
pRobot->SetPos( nXPos, nYOffest - pRobot->GetTall() - ( nYStep * i ) );
pRobot->InvalidateLayout( true, true );
// If the state is anything but ROBOT_STATE_INACTIVE, then it's an active panel
if ( pRobot->GetState() != ROBOT_STATE_INACIVE )
{
pPrevPanel = pRobot;
++nActiveIndex;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::Reset()
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "FlagOutlineHide" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::SetPlayingToLabelVisible( bool bVisible )
{
if ( m_pPlayingTo && m_pPlayingToBG )
{
if ( m_pPlayingTo->IsVisible() != bVisible )
{
m_pPlayingTo->SetVisible( bVisible );
}
if ( m_pPlayingToBG->IsVisible() != bVisible )
{
m_pPlayingToBG->SetVisible( bVisible );
}
}
}
#ifdef STAGING_ONLY
ConVar rd_hud_test_bars( "rd_hud_test_bars", 0 );
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::OnTick()
{
CTFRobotDestructionLogic* pRoboLogic = CTFRobotDestructionLogic::GetRobotDestructionLogic();
bool bPlayindRD = ( pRoboLogic != NULL );
if ( m_bPlayingRD != bPlayindRD )
{
if ( bPlayindRD )
{
InvalidateLayout( true, true );
}
m_bPlayingRD = bPlayindRD;
}
if ( !pRoboLogic )
return;
m_pRedScoreValueContainer->SetDialogVariable( "score", pRoboLogic->GetScore( TF_TEAM_RED ) );
m_pBlueScoreValueContainer->SetDialogVariable( "score", pRoboLogic->GetScore( TF_TEAM_BLUE ) );
#ifdef STAGING_ONLY
if ( rd_hud_test_bars.GetBool() )
{
float flProgress = (sin( gpGlobals->curtime ) * 0.5f) + 0.5f;
m_pBlueProgressBar->SetProgress( flProgress, true );
m_pRedProgressBar->SetProgress( flProgress, true );
m_pBlueProgressBarEscrow->SetProgress( 0.f, true );
m_pRedProgressBarEscrow->SetProgress( 0.f, true );
m_pRedScoreValueContainer->SetDialogVariable( "score", flProgress );
m_pBlueScoreValueContainer->SetDialogVariable( "score", flProgress );
}
else
#endif
{
int nBlueEscrow = 0, nRedEscrow = 0;
if ( pRoboLogic->GetType() == CTFRobotDestructionLogic::TYPE_PLAYER_DESTRUCTION )
{
for ( int i=0; i<ICaptureFlagAutoList::AutoList().Count(); ++i )
{
CCaptureFlag *pFlag = static_cast< CCaptureFlag* >( ICaptureFlagAutoList::AutoList()[i] );
if ( pFlag->IsStolen() && pFlag->GetPrevOwner() )
{
int &nFriendScore = pFlag->GetPrevOwner()->GetTeamNumber() == TF_TEAM_RED ? nRedEscrow : nBlueEscrow;
nFriendScore += pFlag->GetPointValue();
m_pRedDroppedPanel->SetAlpha( 255 );
}
}
if ( m_pProgressBarsContainer )
{
m_pProgressBarsContainer->SetDialogVariable( "red_escrow", nRedEscrow );
m_pProgressBarsContainer->SetDialogVariable( "blue_escrow", nBlueEscrow );
}
// update the team leader image
if ( m_pTeamLeaderImage )
{
bool bLocalplayerIsLeader = false;
if ( CTFPlayer::GetLocalTFPlayer() && ( CTFPlayer::GetLocalTFPlayer()->GetTeamNumber() > LAST_SHARED_TEAM ) )
{
if ( CTFPlayer::GetLocalTFPlayer() == pRoboLogic->GetTeamLeader( GetLocalPlayerTeam() ) )
{
bLocalplayerIsLeader = true;
}
}
if ( m_pTeamLeaderImage->IsVisible() != bLocalplayerIsLeader )
{
m_pTeamLeaderImage->SetVisible( bLocalplayerIsLeader );
}
}
// update the countdowns if they exist
if ( m_pCountdownContainer )
{
float flTimeRemaining = pRoboLogic->GetCountdownEndTime() - gpGlobals->curtime;
if ( flTimeRemaining > -1 )
{
if ( !m_pCountdownContainer->IsVisible() )
{
m_pCountdownContainer->SetVisible( true );
}
ImagePanel* pCountdownImage = dynamic_cast<ImagePanel*>( m_pCountdownContainer->FindChildByName( "CountdownImage", true ) );
if ( pCountdownImage )
{
bool bVisible = true;
if ( pRoboLogic->IsUsingCustomCountdownImage() )
{
const char *pszImage = pRoboLogic->GetCountdownImage();
if ( pszImage && pszImage[0] )
{
pCountdownImage->SetImage( pszImage );
}
else
{
bVisible = false;
}
}
if ( pCountdownImage->IsVisible() != bVisible )
{
pCountdownImage->SetVisible( bVisible );
}
}
CExLabel* pCountdownTime = dynamic_cast<CExLabel*>( m_pCountdownContainer->FindChildByName( "CountdownTime", true ) );
CExLabel* pCountdownTimeShadow = dynamic_cast<CExLabel*>( m_pCountdownContainer->FindChildByName( "CountdownTimeShadow", true ) );
if ( pCountdownTime )
{
if ( !pCountdownTime->IsVisible() )
{
pCountdownTime->SetVisible( true );
}
}
if ( pCountdownTimeShadow )
{
if ( !pCountdownTimeShadow->IsVisible() )
{
pCountdownTimeShadow->SetVisible( true );
}
}
int nCountdownTime = (int)flTimeRemaining;
m_pCountdownContainer->SetDialogVariable( "countdowntime", ( nCountdownTime < 0 ) ? 0 : nCountdownTime );
}
else
{
if ( m_pCountdownContainer->IsVisible() )
{
m_pCountdownContainer->SetVisible( false );
}
}
}
}
else
{
// Find the flags if we dont have them yet
if ( !m_hRedFlag || !m_hBlueFlag )
{
for ( int i=0; i<ICaptureFlagAutoList::AutoList().Count(); ++i )
{
CCaptureFlag *pFlag = static_cast< CCaptureFlag* >( ICaptureFlagAutoList::AutoList()[i] );
if ( pFlag->GetTeamNumber() == TF_TEAM_BLUE )
{
m_hBlueFlag = pFlag;
}
else
{
m_hRedFlag = pFlag;
}
}
if ( pRoboLogic->GetType() == CTFRobotDestructionLogic::TYPE_ROBOT_DESTRUCTION )
{
Assert( m_hBlueFlag );
Assert( m_hRedFlag );
}
}
// A held flag counts towards the stealing team's escrow.
// A dropped flag counts towards the original team's escrow.
if ( m_hRedFlag && m_hBlueFlag )
{
if ( m_hRedFlag->IsDropped() )
{
nRedEscrow += m_hRedFlag->GetPointValue();
if ( m_hRedFlag->GetReturnProgress() > 0.8f )
{
// Blink when we're close to returning
int nAlpha = int( gpGlobals->curtime * 10 ) % 10 < 5 ? 255 : 0;
m_pRedDroppedPanel->SetAlpha( nAlpha );
}
}
else
{
nBlueEscrow += m_hRedFlag->GetPointValue();
m_pRedDroppedPanel->SetAlpha( 255 );
}
if ( m_hBlueFlag->IsDropped() )
{
nBlueEscrow += m_hBlueFlag->GetPointValue();
if ( m_hBlueFlag->GetReturnProgress() > 0.8f )
{
// Blink when we're close to returning
int nAlpha = int( gpGlobals->curtime * 10 ) % 10 < 5 ? 255 : 0;
m_pBlueDroppedPanel->SetAlpha( nAlpha );
}
}
else
{
nRedEscrow += m_hBlueFlag->GetPointValue();
m_pBlueDroppedPanel->SetAlpha( 255 );
}
}
}
const float flFinaleTime = pRoboLogic->GetFinaleLength();
// Get red finale progress. We hide the big scores and show the finale countdown if at max score.
float flFinaleProgress = clamp( pRoboLogic->GetFinaleWinTime( TF_TEAM_RED ) - gpGlobals->curtime, 0.f, pRoboLogic->GetFinaleLength() );
m_pRedVictoryPanel->SetVisible( flFinaleProgress < flFinaleTime );
m_pRedScoreValueContainer->SetVisible( flFinaleProgress >= flFinaleTime );
if ( flFinaleProgress < flFinaleTime )
{
m_pRedVictoryPanel->SetDialogVariable( "victorytime", (int)flFinaleProgress );
}
// Get blue finale progress. We hide the big scores and show the finale countdown if at max score.
flFinaleProgress = clamp( pRoboLogic->GetFinaleWinTime( TF_TEAM_BLUE ) - gpGlobals->curtime, 0.f, pRoboLogic->GetFinaleLength() );
m_pBlueVictoryPanel->SetVisible( flFinaleProgress < flFinaleTime );
m_pBlueScoreValueContainer->SetVisible( flFinaleProgress >= flFinaleTime );
if ( flFinaleProgress < flFinaleTime )
{
m_pBlueVictoryPanel->SetDialogVariable( "victorytime", (int)flFinaleProgress );
}
const float flMaxPoints = pRoboLogic->GetMaxPoints();
int nTargetPoints = pRoboLogic->GetTargetScore( TF_TEAM_BLUE );
m_pBlueProgressBar->SetProgress( nTargetPoints / flMaxPoints );
m_pBlueProgressBarEscrow->SetProgress( ( nTargetPoints + nBlueEscrow ) / flMaxPoints );
nTargetPoints = pRoboLogic->GetTargetScore( TF_TEAM_RED );
m_pRedProgressBar->SetProgress( nTargetPoints / flMaxPoints );
m_pRedProgressBarEscrow->SetProgress( ( nTargetPoints + nRedEscrow ) / flMaxPoints );
}
SetPlayingToLabelVisible( true );
SetDialogVariable( "rounds", pRoboLogic->GetMaxPoints() );
// HACK! Fix the events
UpdateCarriedFlagStatus( NULL, NULL );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::PaintBackground()
{
UpdateStolenPoints( TF_TEAM_RED, m_pRedStolenContainer );
UpdateStolenPoints( TF_TEAM_BLUE, m_pBlueStolenContainer );
BaseClass::PaintBackground();
}
void CTFHUDRobotDestruction::PaintPDPlayerScore( const CTFPlayer* pPlayer )
{
if ( !pPlayer )
return;
// Don't draw the number for ourselves
if ( pPlayer == C_BasePlayer::GetLocalPlayer() )
return;
Vector vecPos = pPlayer->GetAbsOrigin();
vecPos.z += VEC_HULL_MAX_SCALED( pPlayer ).z + 20;
int iX, iY;
Vector vecWorld( vecPos.x, vecPos.y, vecPos.z );
if ( GetVectorInHudSpace( vecWorld, iX, iY ) )
{
int iCurrentLeadingPoint = 0;
if ( pPlayer->HasItem() )
{
CCaptureFlag *pFlag = dynamic_cast<CCaptureFlag*>( pPlayer->GetItem() );
if ( pFlag )
{
iCurrentLeadingPoint = pFlag->GetPointValue();
}
}
wchar_t wszScore[3];
V_snwprintf( wszScore, ARRAYSIZE( wszScore ), L"%d", iCurrentLeadingPoint );
const int nWidth = V_wcslen( wszScore ) * 15;
// draw the name
vgui::surface()->DrawSetTextFont( m_hPDPlayerScoreFont );
vgui::surface()->DrawSetTextPos( iX - ( nWidth / 2 ), iY );
vgui::surface()->DrawSetTextColor( m_TextColor );
vgui::surface()->DrawPrintText( wszScore, wcslen( wszScore ), vgui::FONT_DRAW_NONADDITIVE );
}
}
void CTFHUDRobotDestruction::Paint()
{
CTFRobotDestructionLogic* pRoboLogic = CTFRobotDestructionLogic::GetRobotDestructionLogic();
if ( pRoboLogic && pRoboLogic->GetType() == CTFRobotDestructionLogic::TYPE_PLAYER_DESTRUCTION )
{
CTFPlayerDestructionLogic* pPDLogic = static_cast< CTFPlayerDestructionLogic* >( pRoboLogic );
PaintPDPlayerScore( pPDLogic->GetRedTeamLeader() );
PaintPDPlayerScore( pPDLogic->GetBlueTeamLeader() );
}
BaseClass::Paint();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::UpdateStolenPoints( int nTeam, EditablePanel* pContainer )
{
CTFRobotDestructionLogic* pRoboLogic = CTFRobotDestructionLogic::GetRobotDestructionLogic();
if ( pRoboLogic )
{
int nStolenPoints = 0;
// Get the stolen score for this team
CCaptureFlag* pTheirFlag = nTeam == TF_TEAM_RED ? m_hRedFlag : m_hBlueFlag;
if ( pTheirFlag )
{
nStolenPoints = pTheirFlag->GetPointValue();
}
// Show the stolen panels if the stolen score is anything
pContainer->SetVisible( nStolenPoints > 0 );
pContainer->SetDialogVariable( "intelvalue", nStolenPoints );
}
// Find our stolen flag
CCaptureFlag *pStolenFlag = nTeam == TF_TEAM_RED ? m_hRedFlag : m_hBlueFlag;
if ( pStolenFlag && pStolenFlag->IsHome() )
{
pStolenFlag = NULL;
}
C_CaptureZone *pStartCaptureZone = NULL, *pEndCaptureZone = NULL;
// Go through all the capture zones and find ours and theirs
for ( int i = 0; i<ICaptureZoneAutoList::AutoList().Count(); i++ )
{
C_CaptureZone *pCaptureZone = static_cast< C_CaptureZone* >( ICaptureZoneAutoList::AutoList()[i] );
if ( !pCaptureZone->IsDormant() && !pCaptureZone->IsDisabled() )
{
if ( pCaptureZone->GetTeamNumber() == nTeam )
{
pStartCaptureZone = pCaptureZone;
}
else
{
pEndCaptureZone = pCaptureZone;
}
}
}
if ( pStolenFlag && pStartCaptureZone && pEndCaptureZone )
{
// Use the player's pos if the flag is being carried
Vector vecFlagPos = pStolenFlag->GetMoveParent() ? pStolenFlag->GetMoveParent()->GetAbsOrigin() : pStolenFlag->GetAbsOrigin();
// Get the distance of the flag between the cap points
const float flTotalDist = ( pEndCaptureZone->WorldSpaceCenter() - pStartCaptureZone->WorldSpaceCenter() ).Length() - pEndCaptureZone->BoundingRadius() - pStartCaptureZone->BoundingRadius();
const float flFlagDist = ( pEndCaptureZone->WorldSpaceCenter() - vecFlagPos ).Length() - pEndCaptureZone->BoundingRadius();
const float flLerp = clamp( flFlagDist / flTotalDist, 0.f, 1.f );
// Flip for blue team
const float flProgress = nTeam == TF_TEAM_BLUE ? ( 1.f - flLerp ) : flLerp;
// Calc position
int nWide = pContainer->GetWide();
const int nXpos = ( ( m_nStealRightEdge - ( m_nStealLeftEdge + nWide ) ) * flProgress ) + m_nStealLeftEdge;
// Move the panel!
int nDummy, nYpos;
pContainer->GetPos( nDummy, nYpos );
pContainer->SetPos( nXpos, nYpos );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::UpdateCarriedFlagStatus( C_BasePlayer *pNewOwner /*= NULL*/, C_BaseEntity *pFlagEntity /*= NULL*/ )
{
C_TFPlayer *pLocalPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
// If this is about the other team, we dont care
if ( pNewOwner && pNewOwner->GetTeamNumber() != pLocalPlayer->GetTeamNumber() )
{
return;
}
// are we carrying a flag?
CCaptureFlag *pPlayerFlag = NULL;
if ( pLocalPlayer && pLocalPlayer->HasItem() && pLocalPlayer->GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG )
{
if ( !pNewOwner || pNewOwner == pLocalPlayer )
{
pPlayerFlag = dynamic_cast< CCaptureFlag* >( pLocalPlayer->GetItem() );
}
}
if ( !pPlayerFlag && pLocalPlayer && pLocalPlayer == pNewOwner )
{
pPlayerFlag = dynamic_cast< CCaptureFlag* >( pFlagEntity );
}
if ( pPlayerFlag && !pPlayerFlag->IsMarkedForDeletion() && !pPlayerFlag->IsDormant() )
{
m_pCarriedContainer->SetVisible( true );
m_pCarriedContainer->SetDialogVariable( "flagvalue", pPlayerFlag->GetPointValue() );
// make sure the panels are on, set the initial alpha values,
// set the color of the flag we're carrying, and start the animations
if ( m_pCarriedImage && !m_pCarriedImage->IsVisible() )
{
int nTeam;
if ( pPlayerFlag->GetType() == TF_FLAGTYPE_ATTACK_DEFEND ||
pPlayerFlag->GetType() == TF_FLAGTYPE_TERRITORY_CONTROL ||
pPlayerFlag->GetType() == TF_FLAGTYPE_INVADE ||
pPlayerFlag->GetType() == TF_FLAGTYPE_RESOURCE_CONTROL )
{
nTeam = ( ( GetLocalPlayerTeam() == TF_TEAM_BLUE ) ? ( TF_TEAM_BLUE ) : ( TF_TEAM_RED ) );
}
else
{
// normal CTF behavior (carrying the enemy flag)
nTeam = ( ( GetLocalPlayerTeam() == TF_TEAM_RED ) ? ( TF_TEAM_BLUE ) : ( TF_TEAM_RED ) );
}
m_pCarriedImage->SetVisible( true );
m_pCarriedFlagProgressBar->SetProgress( 0.f, true ); // Slam to 0 instantly
m_pCarriedFlagProgressBar->SetColor( pLocalPlayer->GetTeamNumber() == TF_TEAM_RED ? m_ColorRed : m_ColorBlue );
}
CTFRobotDestructionLogic* pRoboLogic = CTFRobotDestructionLogic::GetRobotDestructionLogic();
if ( pRoboLogic )
{
int nMinToSteal = tf_rd_min_points_to_steal.GetInt();
// Current progress
float flProgress = float( pPlayerFlag->GetPointValue() ) / float( pRoboLogic->GetMaxPoints() );
// What percentage needs to map to the dotted line
const float flProgressAtDottedLine = float( nMinToSteal ) / float( pRoboLogic->GetMaxPoints() );
// This is where in the texture the dotted line is
const float flWhereTheDottedLineIs = 0.25f;
// We want the progress bar range from [0, The dotted line] map to the progress value [0, Min to steal]
if ( flProgress <= flProgressAtDottedLine )
{
flProgress = RemapValClamped( flProgress, 0.f, flProgressAtDottedLine, 0.f, flWhereTheDottedLineIs );
}
else // Make the progress bar range from (The dotted line, 1] map to the progress value(Min to steal, 1]
{
flProgress = RemapValClamped( flProgress, flProgressAtDottedLine, 1.f, flWhereTheDottedLineIs, 1.f );
}
m_pCarriedFlagProgressBar->SetProgress( flProgress );
}
}
else if ( m_pCarriedImage && m_pCarriedImage->IsVisible() )
{
m_pCarriedContainer->SetVisible( false );
m_pCarriedImage->SetVisible( false );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::UpdateRobotElements()
{
m_vecRedRobots.PurgeAndDeleteElements();
m_vecBlueRobots.PurgeAndDeleteElements();
InvalidateLayout( false, true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::UpdateStolenFlagStatus( int nTeam, C_BaseEntity *pFlag )
{
CCaptureFlag *pPlayerFlag = dynamic_cast< CCaptureFlag* >( pFlag );
if ( pPlayerFlag )
{
EditablePanel *pStolenContainer = nTeam == TF_TEAM_RED ? m_pRedStolenContainer : m_pBlueStolenContainer;
Panel* pCarriedImage = pStolenContainer->FindChildByName( "IntelImage" );
Panel* pDownImage = pStolenContainer->FindChildByName( "DroppedIntelContainer" );
Assert( pCarriedImage && pDownImage );
if ( !pCarriedImage || !pDownImage )
return;
// Toggle the carried or dropped images
bool bIsDropped = pPlayerFlag->IsDropped();
pCarriedImage->SetVisible( !bIsDropped );
pDownImage->SetVisible( bIsDropped );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHUDRobotDestruction::FireGameEvent( IGameEvent * pEvent )
{
CTFRobotDestructionLogic* pRoboLogic = CTFRobotDestructionLogic::GetRobotDestructionLogic();
if ( !pRoboLogic )
return;
const char *pszEventName = pEvent->GetName();
if ( FStrEq( "rd_rules_state_changed", pszEventName ) )
{
UpdateRobotElements();
}
else if ( FStrEq( pszEventName, "flagstatus_update" ) )
{
int nVictimID = pEvent->GetInt( "userid" );
C_BasePlayer *pNewOwner = USERID2PLAYER( nVictimID );
int nFlagEntIndex = pEvent->GetInt( "entindex" );
C_BaseEntity *pFlagEntity = ClientEntityList().GetEnt( nFlagEntIndex );
if ( pFlagEntity )
{
UpdateCarriedFlagStatus( pNewOwner, pFlagEntity );
UpdateStolenFlagStatus( pFlagEntity->GetTeamNumber(), pFlagEntity );
}
}
else if ( FStrEq( pszEventName, "rd_team_points_changed" ) )
{
// Extract data
int nTeam = pEvent->GetInt( "team" );
int nPoints = pEvent->GetInt( "points" );
RDScoreMethod_t eMethod = RDScoreMethod_t( pEvent->GetInt( "method" ) );
// Figure out which panel and which anim
Panel *pPanel = nTeam == TF_TEAM_RED ? m_pRedScoreValueContainer : m_pBlueScoreValueContainer;
bool bPositive = ( nTeam == GetLocalPlayerTeam() && nPoints > 0 ) || ( nTeam != GetLocalPlayerTeam() && nPoints < 0 );
const char *pszAnimName = bPositive ? "RDPositiveScorePulse" : "RDNegativeScorePulse";
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( pPanel, pszAnimName );
// Make the progress bar blink
CProgressPanel *pProgressBar = nTeam == TF_TEAM_RED ? m_pRedProgressBar : m_pBlueProgressBar;
pProgressBar->Blink();
if ( eMethod == SCORE_REACTOR_STEAL )
{
// Make the OTHER team's escrow progress bar blink
CProgressPanel *pEscrowBar = nTeam != TF_TEAM_RED ? m_pRedProgressBarEscrow : m_pBlueProgressBarEscrow;
pEscrowBar->Blink();
}
}
else if ( FStrEq( pszEventName, "teamplay_round_start" ) )
{
// Recalculate the progress speed
float flApproachSpeed = ( tf_rd_points_per_approach.GetInt() / tf_rd_points_approach_interval.GetFloat() ) / pRoboLogic->GetMaxPoints();
m_pBlueProgressBar->SetApproachSpeed( flApproachSpeed );
m_pBlueProgressBarEscrow->SetApproachSpeed( flApproachSpeed );
m_pRedProgressBar->SetApproachSpeed( flApproachSpeed );
m_pRedProgressBarEscrow->SetApproachSpeed( flApproachSpeed );
}
}
CTFHUDRobotDestruction::CProgressPanel::CProgressPanel( vgui::Panel *parent, const char *name )
: BaseClass( parent, name )
, m_nXOrg( 0 )
, m_nYOrg( 0 )
, m_nWideOrg( 0 )
, m_nTallOrg( 0 )
, m_flLastScoreTime( 0.f )
, m_flEndProgress( 0.f )
, m_flCurrentProgress( 0.f )
, m_flLastTick( 0.f )
{
ListenForGameEvent( "teamplay_round_start" );
}
void CTFHUDRobotDestruction::CProgressPanel::CaptureBounds()
{
GetBounds( m_nXOrg, m_nYOrg, m_nWideOrg, m_nTallOrg );
if ( GetImage() )
{
GetImage()->SetSize( m_nWideOrg, m_nTallOrg );
}
}
void CTFHUDRobotDestruction::CProgressPanel::SetProgress( float flProgress, bool bInstant /*= false*/ )
{
if ( bInstant )
{
m_flEndProgress = m_flCurrentProgress = flProgress;
CalculateSize();
}
else
{
// Start ticking if the progress is different
if ( m_flEndProgress != flProgress )
{
vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
m_flLastTick = gpGlobals->curtime;
}
// Set end target
m_flEndProgress = flProgress;
}
}
void CTFHUDRobotDestruction::CProgressPanel::OnTick()
{
float flDelta = gpGlobals->curtime - m_flLastTick;
m_flLastTick = gpGlobals->curtime;
// Approach the target progress amount
m_flCurrentProgress = Approach( m_flEndProgress, m_flCurrentProgress, flDelta * m_flApproachSpeed );
// Stop ticking if we've met our progress
if ( m_flCurrentProgress == m_flEndProgress )
{
vgui::ivgui()->RemoveTickSignal( GetVPanel() );
}
CalculateSize();
}
void CTFHUDRobotDestruction::CProgressPanel::PaintBackground()
{
// Resize internal image in here. The other bars use this image too, so we have to move
// it right before we paint or else it will be out of position.
IImage *pImage = GetImage();
if ( pImage )
{
pImage->SetPos( m_bLeftToRight ? -m_nLeftOffset : -m_flXpos, m_nYOrg );
pImage->SetSize( m_nWideOrg, m_nTallOrg );
}
// Find out blink lerp time
const float flBlinkPeriod = 0.25f;
bool bPastBlink = gpGlobals->curtime > ( m_flLastScoreTime + flBlinkPeriod );
// Blink if it's blink time, or else pulse if within threshold, else just be the standard color
float flLerp = bPastBlink ? ( m_flCurrentProgress >= m_flBlinkThreshold ? ( ( sin( gpGlobals->curtime * m_flBlinkRate ) * 0.5f ) + 0.5f ) : 1.f )
: ( (gpGlobals->curtime - m_flLastScoreTime) / flBlinkPeriod );
flLerp = clamp( flLerp, 0.f, 1.f );
const float flInverseLerp = 1.f - flLerp;
// Get out lerped color
Color drawColor( flInverseLerp * m_BrightColor.r() + flLerp * m_StandardColor.r(),
flInverseLerp * m_BrightColor.g() + flLerp * m_StandardColor.g(),
flInverseLerp * m_BrightColor.b() + flLerp * m_StandardColor.b(),
255 );
// Change color in base class (it uses it in PaintBackground)
SetDrawColor( drawColor );
BaseClass::PaintBackground();
}
void CTFHUDRobotDestruction::CProgressPanel::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
int nXpos, nYpos;
GetPos( nXpos, nYpos );
SetPos( nXpos + m_nLeftOffset, nYpos );
CaptureBounds();
CalculateSize();
}
void CTFHUDRobotDestruction::CProgressPanel::Blink()
{
m_flLastScoreTime = gpGlobals->curtime;
}
void CTFHUDRobotDestruction::CProgressPanel::FireGameEvent( IGameEvent * pEvent )
{
const char *pszEventName = pEvent->GetName();
if ( FStrEq( pszEventName, "teamplay_round_start" ) )
{
// We need to reset the timers here in case we changelevel'd
m_flCurrentProgress = 0.f;
m_flEndProgress = 0.f;
// Resize
CalculateSize();
}
}
void CTFHUDRobotDestruction::CProgressPanel::CalculateSize()
{
// Find xpos
int nProgressWidth = m_nWideOrg - m_nRightOffset - m_nLeftOffset;
m_flXpos = m_bLeftToRight ? m_nXOrg
: ( 1.f - m_flCurrentProgress) * nProgressWidth + m_nXOrg;
// Find width
m_flWidth = m_flCurrentProgress * nProgressWidth;
// Resize
SetBounds( m_flXpos, m_nYOrg, m_flWidth, m_nTallOrg );
}