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.
1242 lines
33 KiB
1242 lines
33 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "tf_hud_escort.h" |
|
#include <vgui/IVGui.h> |
|
#include "tf_hud_freezepanel.h" |
|
#include "hud_controlpointicons.h" |
|
#include "teamplayroundbased_gamerules.h" |
|
#include "c_team_train_watcher.h" |
|
#include "iclientmode.h" |
|
#include "tf_gamerules.h" |
|
|
|
using namespace vgui; |
|
|
|
#define TF_ESCORT_RECEDE_COUNTDOWN 20.0f |
|
|
|
#define TF_ESCORT_HILL_MATERIAL "hud/cart_track_arrow" |
|
#define TF_ESCORT_HILL_ALPHA_CYCLE_TIME 750 |
|
|
|
ConVar hud_escort_interp( "hud_escort_interp", "0.2" ); |
|
ConVar hud_escort_test_progress( "hud_escort_test_progress", "-1" ); |
|
ConVar hud_escort_test_speed( "hud_escort_test_speed", "-1" ); |
|
|
|
void AddSubKeyNamed( KeyValues *pKeys, const char *pszName ) |
|
{ |
|
KeyValues *pNewKey = new KeyValues( pszName ); |
|
if ( pNewKey ) |
|
{ |
|
pKeys->AddSubKey( pNewKey ); |
|
} |
|
} |
|
|
|
CEscortHillPanel::CEscortHillPanel( vgui::Panel *parent, const char *name ) : vgui::Panel( parent, name ) |
|
{ |
|
m_iTexture = vgui::surface()->DrawGetTextureId( TF_ESCORT_HILL_MATERIAL ); |
|
if ( m_iTexture == -1 ) // we didn't find it, so create a new one |
|
{ |
|
m_iTexture = vgui::surface()->CreateNewTextureID(); |
|
vgui::surface()->DrawSetTextureFile( m_iTexture, TF_ESCORT_HILL_MATERIAL, true, false ); |
|
} |
|
|
|
m_flUVScroll = 0.0f; |
|
m_flUVW = 0.0f; |
|
m_bOnHill = false; |
|
m_bFadingOut = true; |
|
m_bDownhill = false; |
|
|
|
ListenForGameEvent( "teamplay_round_start" ); |
|
|
|
vgui::ivgui()->AddTickSignal( GetVPanel(), TF_ESCORT_HILL_ALPHA_CYCLE_TIME ); |
|
} |
|
|
|
void CEscortHillPanel::OnTick( void ) |
|
{ |
|
if ( IsVisible() == false ) |
|
return; |
|
|
|
if ( m_bOnHill ) |
|
{ |
|
if ( m_bFadingOut ) |
|
{ |
|
GetAnimationController()->RunAnimationCommand( this, "alpha", 32, 0.0, (float)TF_ESCORT_HILL_ALPHA_CYCLE_TIME / 1000, vgui::AnimationController::INTERPOLATOR_LINEAR ); |
|
m_bFadingOut = false; |
|
} |
|
else |
|
{ |
|
GetAnimationController()->RunAnimationCommand( this, "alpha", 96, 0.0, (float)TF_ESCORT_HILL_ALPHA_CYCLE_TIME / 1000, vgui::AnimationController::INTERPOLATOR_LINEAR ); |
|
m_bFadingOut = true; |
|
} |
|
} |
|
else |
|
{ |
|
SetAlpha( 64 ); |
|
m_bFadingOut = true; |
|
} |
|
} |
|
|
|
void CEscortHillPanel::PerformLayout( void ) |
|
{ |
|
BaseClass::PerformLayout(); |
|
|
|
int nPanelX, nPanelY; |
|
GetBounds( nPanelX, nPanelY, m_nPanelWide, m_nPanelTall ); |
|
|
|
int nTextureWide, nTextureTall; |
|
surface()->DrawGetTextureSize( m_iTexture, nTextureWide, nTextureTall ); |
|
|
|
float flScale = (float)m_nPanelTall / (float)nTextureTall; |
|
float flNewTextureWide = nTextureWide * flScale; |
|
|
|
m_flUVW = m_nPanelWide / flNewTextureWide; |
|
|
|
SetAlpha( 64 ); |
|
} |
|
|
|
void CEscortHillPanel::Paint( void ) |
|
{ |
|
BaseClass::Paint(); |
|
|
|
if ( ObjectiveResource() ) |
|
{ |
|
m_bOnHill = ObjectiveResource()->IsTrainOnHill( m_nTeam, m_nHill ); |
|
m_bDownhill = ObjectiveResource()->IsHillDownhill( m_nTeam, m_nHill ); |
|
} |
|
|
|
if ( m_bOnHill ) |
|
{ |
|
m_flUVScroll += 0.02; |
|
|
|
if ( m_flUVScroll > 1.0 ) |
|
{ |
|
m_flUVScroll -= 1.0; |
|
} |
|
} |
|
|
|
Vertex_t vert[4]; |
|
surface()->DrawSetTexture( m_iTexture ); |
|
|
|
Vector2D uv11( m_flUVScroll, 0 ); |
|
Vector2D uv21( m_flUVW + m_flUVScroll, 0 ); |
|
Vector2D uv22( m_flUVW + m_flUVScroll, 1 ); |
|
Vector2D uv12( m_flUVScroll, 1 ); |
|
|
|
if ( m_bDownhill ) |
|
{ |
|
vert[0].Init( Vector2D( 0, 0 ), uv21 ); |
|
vert[1].Init( Vector2D( m_nPanelWide, 0 ), uv11 ); |
|
vert[2].Init( Vector2D( m_nPanelWide, m_nPanelTall ), uv12 ); |
|
vert[3].Init( Vector2D( 0, m_nPanelTall ), uv22 ); |
|
} |
|
else |
|
{ |
|
vert[0].Init( Vector2D( 0, 0 ), uv11 ); |
|
vert[1].Init( Vector2D( m_nPanelWide, 0 ), uv21 ); |
|
vert[2].Init( Vector2D( m_nPanelWide, m_nPanelTall ), uv22 ); |
|
vert[3].Init( Vector2D( 0, m_nPanelTall ), uv12 ); |
|
} |
|
|
|
surface()->DrawSetColor( Color(255,255,255,255) ); |
|
surface()->DrawTexturedPolygon( 4, vert ); |
|
} |
|
|
|
void CEscortHillPanel::FireGameEvent( IGameEvent * event ) |
|
{ |
|
if ( FStrEq( "teamplay_round_start", event->GetName() ) ) |
|
{ |
|
m_flUVScroll = 0.0f; |
|
} |
|
} |
|
|
|
//======================================================================================================================== |
|
// Escort status indicator that floats above the train when you are interacting with it |
|
//======================================================================================================================== |
|
CEscortStatusTeardrop::CEscortStatusTeardrop(Panel *parent) : vgui::EditablePanel( parent, "EscortTeardrop" ) |
|
{ |
|
m_pBarText = new vgui::Label( this, "ProgressText", "" ); |
|
m_pTeardrop = new CIconPanel( this, "Teardrop" ); |
|
m_pBlocked = new CIconPanel( this, "Blocked" ); |
|
m_pCapping = new ImagePanel( this, "Capping" ); |
|
m_iOrgHeight = 0; |
|
m_iMidGroupIndex = -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEscortStatusTeardrop::ApplySchemeSettings( IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
|
|
//LoadControlSettings( "resource/UI/EscortStatusTeardrop.res" ); /*//ControlPointProgressBar.res" );*/ |
|
|
|
m_iOrgHeight = GetTall(); |
|
|
|
m_iMidGroupIndex = gHUD.LookupRenderGroupIndexByName( "mid" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEscortStatusTeardrop::IsVisible( void ) |
|
{ |
|
if ( IsInFreezeCam() == true ) |
|
return false; |
|
|
|
if ( m_iMidGroupIndex != -1 && gHUD.IsRenderGroupLockedFor( NULL, m_iMidGroupIndex ) ) |
|
return false; |
|
|
|
return BaseClass::IsVisible(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEscortStatusTeardrop::SetupForPoint( int iCPIndex ) |
|
{ |
|
if ( !ObjectiveResource() ) |
|
return; |
|
|
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
if ( iCPIndex == -1 ) |
|
{ |
|
SetVisible( false ); |
|
return; |
|
} |
|
|
|
bool bInWinState = TeamplayRoundBasedRules() ? TeamplayRoundBasedRules()->RoundHasBeenWon() : false; |
|
|
|
if ( !bInWinState ) |
|
{ |
|
SetVisible( true ); |
|
|
|
int iCappingTeam = ObjectiveResource()->GetCappingTeam( iCPIndex ); |
|
int iOwnerTeam = ObjectiveResource()->GetOwningTeam( iCPIndex ); |
|
int iPlayerTeam = pPlayer->GetTeamNumber(); |
|
bool bCapBlocked = ObjectiveResource()->CapIsBlocked( iCPIndex ); |
|
|
|
if ( !bCapBlocked && iCappingTeam != TEAM_UNASSIGNED && iCappingTeam != iOwnerTeam && iCappingTeam == iPlayerTeam ) |
|
{ |
|
// it's not blocked and we're capping |
|
|
|
m_pBlocked->SetVisible( false ); |
|
|
|
// replace this with a capping icon |
|
m_pCapping->SetVisible( true ); |
|
|
|
m_pBarText->SetVisible( false ); |
|
} |
|
else |
|
{ |
|
// not capping |
|
|
|
m_pCapping->SetVisible( false ); |
|
|
|
m_pBlocked->SetVisible( true ); |
|
UpdateBarText( iCPIndex ); |
|
} |
|
|
|
InvalidateLayout(); |
|
} |
|
else |
|
{ |
|
SetVisible( false ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEscortStatusTeardrop::UpdateBarText( int iCPIndex ) |
|
{ |
|
if ( !ObjectiveResource() ) |
|
return; |
|
|
|
// If we're not escorting the train we don't show text |
|
if ( iCPIndex == -1 ) |
|
{ |
|
m_pBarText->SetVisible( false ); |
|
return; |
|
} |
|
|
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( !pPlayer || !m_pBarText ) |
|
return; |
|
|
|
m_pBarText->SetVisible( true ); |
|
|
|
int iCappingTeam = ObjectiveResource()->GetCappingTeam( iCPIndex ); |
|
int iPlayerTeam = pPlayer->GetTeamNumber(); |
|
|
|
if ( !TeamplayGameRules()->PointsMayBeCaptured() ) |
|
{ |
|
m_pBarText->SetText( "#Team_Capture_NotNow" ); |
|
return; |
|
} |
|
|
|
// Player is blocking an enemy capture |
|
if ( iCappingTeam != TEAM_UNASSIGNED && iCappingTeam != iPlayerTeam ) |
|
{ |
|
if ( ObjectiveResource()->IsCPBlocked( iCPIndex ) ) |
|
{ |
|
m_pBarText->SetText( "#Team_Blocking_Capture" ); |
|
return; |
|
} |
|
} |
|
|
|
// player is standing on an already owned cap |
|
if ( ObjectiveResource()->GetOwningTeam( iCPIndex ) == iPlayerTeam ) |
|
{ |
|
m_pBarText->SetText( "#Team_Capture_OwnPoint" ); |
|
return; |
|
} |
|
|
|
// disguised spies etc can't cap or block |
|
char szReason[256]; |
|
if ( !TeamplayGameRules()->PlayerMayCapturePoint( pPlayer, iCPIndex, szReason, sizeof(szReason) ) ) |
|
{ |
|
m_pBarText->SetText( szReason ); |
|
return; |
|
} |
|
|
|
// we can't capture because it's blocked |
|
if ( iCappingTeam == iPlayerTeam ) |
|
{ |
|
m_pBarText->SetText( "#Team_Progress_Blocked" ); |
|
return; |
|
} |
|
|
|
m_pBarText->SetText( "" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFHudEscortProgressBar::CTFHudEscortProgressBar( vgui::Panel *parent, const char *name ) : vgui::ImagePanel( parent, name ) |
|
{ |
|
m_flPercent = 0.0f; |
|
|
|
SetTeam( TF_TEAM_BLUE ); // default to blue |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the escort image position |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscortProgressBar::SetTeam( int nTeam ) |
|
{ |
|
m_nTeam = nTeam; |
|
|
|
const char *pszMaterial = "hud/cart_track_neutral_opaque"; |
|
|
|
if ( m_nTeam == TF_TEAM_RED ) |
|
{ |
|
pszMaterial = "hud/cart_track_red_opaque"; |
|
} |
|
else if ( m_nTeam == TF_TEAM_BLUE ) |
|
{ |
|
pszMaterial = "hud/cart_track_blue_opaque"; |
|
} |
|
|
|
m_iTexture = vgui::surface()->DrawGetTextureId( pszMaterial ); |
|
if ( m_iTexture == -1 ) // we didn't find it, so create a new one |
|
{ |
|
m_iTexture = vgui::surface()->CreateNewTextureID(); |
|
} |
|
|
|
vgui::surface()->DrawSetTextureFile( m_iTexture, pszMaterial, true, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the escort image position |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscortProgressBar::Paint() |
|
{ |
|
if ( m_flPercent > 0.0f ) |
|
{ |
|
Vertex_t vert[4]; |
|
float uv1 = 0.0f; |
|
float uv2 = 1.0f; |
|
|
|
surface()->DrawSetTexture( m_iTexture ); |
|
|
|
Vector2D uv11( uv1, uv1 ); |
|
Vector2D uv21( uv2, uv1 ); |
|
Vector2D uv22( uv2, uv2 ); |
|
Vector2D uv12( uv1, uv2 ); |
|
|
|
int nBarX, nBarY, nBarW, nBarH; |
|
GetBounds( nBarX, nBarY, nBarW, nBarH ); |
|
|
|
int nMiddle = (int)( (float)(nBarW) * m_flPercent ); |
|
|
|
vert[0].Init( Vector2D( 0, 0 ), uv11 ); |
|
vert[1].Init( Vector2D( nMiddle, 0 ), uv21 ); |
|
vert[2].Init( Vector2D( nMiddle, nBarH ), uv22 ); |
|
vert[3].Init( Vector2D( 0, nBarH ), uv12 ); |
|
|
|
surface()->DrawSetColor( Color(255,255,255,210) ); |
|
surface()->DrawTexturedPolygon( 4, vert ); |
|
|
|
surface()->DrawSetColor( Color(245,229,196,210) ); |
|
surface()->DrawLine( nMiddle - 1, 0, nMiddle - 1, nBarH ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFHudEscort::CTFHudEscort( Panel *parent, const char *name ) : EditablePanel( parent, name ), m_flProgressHistory("CTFHudEscort::m_flProgressHistory") |
|
{ |
|
m_flProgress = 0.0f; |
|
m_flProgressHistory.Setup( &m_flProgress, 0 ); |
|
|
|
ListenForGameEvent( "escort_speed" ); |
|
ListenForGameEvent( "escort_progress" ); |
|
ListenForGameEvent( "escort_recede" ); |
|
ListenForGameEvent( "controlpoint_initialized" ); |
|
ListenForGameEvent( "controlpoint_updateimages" ); |
|
ListenForGameEvent( "controlpoint_updatecapping" ); |
|
ListenForGameEvent( "controlpoint_starttouch" ); |
|
ListenForGameEvent( "controlpoint_endtouch" ); |
|
ListenForGameEvent( "teamplay_round_start" ); |
|
ListenForGameEvent( "localplayer_respawn" ); |
|
ListenForGameEvent( "localplayer_changeteam" ); |
|
|
|
m_pEscortItemPanel = new EditablePanel( this, "EscortItemPanel" ); |
|
|
|
Assert( m_pEscortItemPanel ); |
|
|
|
m_pSpeedBackwards = new ImagePanel( m_pEscortItemPanel, "Speed_Backwards" ); |
|
m_pCapPlayerImage = new ImagePanel( m_pEscortItemPanel, "CapPlayerImage" ); |
|
m_pCapNumPlayers = new CExLabel( m_pEscortItemPanel, "CapNumPlayers", "" ); |
|
m_pEscortItemImage = new ImagePanel( m_pEscortItemPanel, "EscortItemImage" ); |
|
m_pEscortItemImageBottom = new ImagePanel( m_pEscortItemPanel, "EscortItemImageBottom" ); |
|
m_pBlocked = new ImagePanel( m_pEscortItemPanel, "Blocked" ); |
|
|
|
m_pEscortItemImageAlert = new ImagePanel( m_pEscortItemPanel, "EscortItemImageAlert" ); |
|
|
|
m_pHomeCPIcon = new ImagePanel( this, "HomeCPIcon" ); |
|
|
|
m_pCPTemplate = new ImagePanel( this, "SimpleControlPointTemplate" ); |
|
|
|
// store the background bar so we can use its dimensions for CP placement |
|
m_pLevelBar = new ImagePanel( this, "LevelBar" ); |
|
|
|
m_pStatus = new CEscortStatusTeardrop( m_pEscortItemPanel ); |
|
|
|
m_pHilightSwoop = new CControlPointIconSwoop( this, "EscortHilightSwoop" ); |
|
m_pHilightSwoop->SetParent( g_pClientMode->GetViewport() ); |
|
m_pHilightSwoop->SetZPos( 10 ); |
|
m_pHilightSwoop->SetShouldScaleImage( true ); |
|
|
|
m_pProgressBar = new CTFHudEscortProgressBar( this, "ProgressBar" ); |
|
|
|
m_iCurrentCP = -1; |
|
|
|
m_flRecedeTime = 0; |
|
|
|
m_nTeam = TF_TEAM_BLUE; // blue team by default |
|
m_bTopPanel = true; |
|
|
|
m_bAlarm = false; |
|
|
|
vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); |
|
|
|
m_bMultipleTrains = false; |
|
m_nNumHills = 0; |
|
|
|
// blue material for multiple tracks colored overlay |
|
m_iBlueMaterialIndex = surface()->DrawGetTextureId( "hud/cart_track_blue" ); |
|
if ( m_iBlueMaterialIndex == -1 ) // we didn't find it, so create a new one |
|
{ |
|
m_iBlueMaterialIndex = surface()->CreateNewTextureID(); |
|
} |
|
|
|
surface()->DrawSetTextureFile( m_iBlueMaterialIndex, "hud/cart_track_blue", true, false ); |
|
|
|
// red material for multiple tracks colored overlay |
|
m_iRedMaterialIndex = surface()->DrawGetTextureId( "hud/cart_track_red" ); |
|
if ( m_iRedMaterialIndex == -1 ) // we didn't find it, so create a new one |
|
{ |
|
m_iRedMaterialIndex = surface()->CreateNewTextureID(); |
|
} |
|
|
|
surface()->DrawSetTextureFile( m_iRedMaterialIndex, "hud/cart_track_red", true, false ); |
|
|
|
for ( int i = 0 ; i < TEAM_TRAIN_MAX_HILLS ; i++ ) |
|
{ |
|
char szPanelName[32]; |
|
Q_snprintf( szPanelName, sizeof(szPanelName), "hill_panel%d", i ); |
|
m_pHillPanels[i] = new CEscortHillPanel( this, szPanelName ); |
|
|
|
if ( m_pHillPanels[i] ) |
|
{ |
|
m_pHillPanels[i]->SetHillNumber( i ); |
|
m_pHillPanels[i]->SetTeamNumber( m_nTeam ); |
|
} |
|
} |
|
} |
|
|
|
CTFHudEscort::~CTFHudEscort() |
|
{ |
|
if ( m_pHilightSwoop ) |
|
{ |
|
m_pHilightSwoop->MarkForDeletion(); |
|
m_pHilightSwoop = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Hide when we take a freeze cam shot |
|
//----------------------------------------------------------------------------- |
|
bool CTFHudEscort::IsVisible( void ) |
|
{ |
|
if( IsTakingAFreezecamScreenshot() ) |
|
return false; |
|
|
|
if ( IsInFreezeCam() ) |
|
return false; |
|
|
|
if ( !m_bHaveValidPointPositions ) |
|
return false; |
|
|
|
return BaseClass::IsVisible(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscort::PerformLayout( void ) |
|
{ |
|
BaseClass::PerformLayout(); |
|
|
|
for ( int i = 0 ; i < TEAM_TRAIN_MAX_HILLS ; i++ ) |
|
{ |
|
if ( m_pHillPanels[i] ) |
|
{ |
|
m_pHillPanels[i]->SetTeamNumber( m_nTeam ); |
|
m_pHillPanels[i]->SetVisible( false ); |
|
} |
|
} |
|
|
|
if ( ObjectiveResource() ) |
|
{ |
|
if ( m_nNumHills > 0 ) |
|
{ |
|
if ( m_pLevelBar ) |
|
{ |
|
int xpos, ypos, wide, tall, zpos; |
|
m_pLevelBar->GetBounds( xpos, ypos, wide, tall ); |
|
zpos = m_pLevelBar->GetZPos() + 1; |
|
|
|
for ( int i = 0 ; i < m_nNumHills ; i++ ) |
|
{ |
|
CEscortHillPanel *pPanel = m_pHillPanels[i]; |
|
|
|
if ( pPanel ) |
|
{ |
|
float flStart = 0, flEnd = 0; |
|
ObjectiveResource()->GetHillData( m_nTeam, i, flStart, flEnd ); |
|
|
|
pPanel->SetVisible( true ); |
|
|
|
int xOffset1 = wide * flStart; |
|
int xOffset2 = wide * flEnd; |
|
|
|
pPanel->SetBounds( xpos + xOffset1, ypos, xOffset2 - xOffset1, tall ); |
|
pPanel->SetZPos( zpos ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscort::ApplySchemeSettings( IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
|
|
KeyValues *pConditions = NULL; |
|
if ( m_nTeam > LAST_SHARED_TEAM ) |
|
{ |
|
pConditions = new KeyValues( "conditions" ); |
|
if ( pConditions ) |
|
{ |
|
if ( m_nTeam == TF_TEAM_RED ) |
|
{ |
|
AddSubKeyNamed( pConditions, "if_team_red" ); |
|
} |
|
else |
|
{ |
|
AddSubKeyNamed( pConditions, "if_team_blue" ); |
|
} |
|
|
|
if ( m_bMultipleTrains ) |
|
{ |
|
AddSubKeyNamed( pConditions, "if_multiple_trains" ); |
|
|
|
if ( m_nTeam == TF_TEAM_RED ) |
|
{ |
|
AddSubKeyNamed( pConditions, "if_multiple_trains_red" ); |
|
} |
|
else |
|
{ |
|
AddSubKeyNamed( pConditions, "if_multiple_trains_blue" ); |
|
} |
|
|
|
if ( m_bTopPanel ) |
|
{ |
|
AddSubKeyNamed( pConditions, "if_multiple_trains_top" ); |
|
} |
|
else |
|
{ |
|
AddSubKeyNamed( pConditions, "if_multiple_trains_bottom" ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( m_nNumHills > 0 ) |
|
{ |
|
// we have a single track map with hills |
|
AddSubKeyNamed( pConditions, "if_single_with_hills" ); |
|
|
|
if ( m_nTeam == TF_TEAM_RED ) |
|
{ |
|
AddSubKeyNamed( pConditions, "if_single_with_hills_red" ); |
|
} |
|
else |
|
{ |
|
AddSubKeyNamed( pConditions, "if_single_with_hills_blue" ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// load control settings... |
|
LoadControlSettings( "resource/UI/ObjectiveStatusEscort.res", NULL, NULL, pConditions ); |
|
|
|
UpdateCPImages(); |
|
|
|
if ( m_pEscortItemImage && m_pEscortItemImageBottom ) |
|
{ |
|
if ( m_bMultipleTrains == false ) |
|
{ |
|
m_pEscortItemImageBottom->SetVisible( false ); |
|
m_pEscortItemImage->SetVisible( true ); |
|
} |
|
else |
|
{ |
|
m_pEscortItemImageBottom->SetVisible( !m_bTopPanel ); |
|
m_pEscortItemImage->SetVisible( m_bTopPanel ); |
|
} |
|
} |
|
|
|
if ( pConditions ) |
|
{ |
|
pConditions->deleteThis(); |
|
} |
|
|
|
m_bAlarm = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscort::UpdateAlarmAnimations( void ) |
|
{ |
|
if ( m_bMultipleTrains ) |
|
{ |
|
if ( m_pEscortItemImageAlert ) |
|
{ |
|
if ( m_bAlarm ) |
|
{ |
|
if ( m_pEscortItemImageAlert->IsVisible() == false ) |
|
{ |
|
m_pEscortItemImageAlert->SetVisible( true ); |
|
} |
|
|
|
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pEscortItemPanel, "HudCartAlarmPulse" ); |
|
} |
|
else |
|
{ |
|
if ( m_pEscortItemImageAlert->IsVisible() == true ) |
|
{ |
|
m_pEscortItemImageAlert->SetVisible( false ); |
|
} |
|
|
|
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pEscortItemPanel, "eventHudCartAlarmPulseStop" ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if ( m_pEscortItemImageAlert && m_pEscortItemImageAlert->IsVisible() == true ) |
|
{ |
|
m_pEscortItemImageAlert->SetVisible( false ); |
|
} |
|
|
|
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pEscortItemPanel, "eventHudCartAlarmPulseStop" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the escort image position |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscort::OnTick() |
|
{ |
|
// don't need to do this on non-escort maps (unless we're trying to override the HUD type) |
|
if ( TFGameRules() && ( TFGameRules()->GetGameType() != TF_GAMETYPE_ESCORT ) && ( TFGameRules()->GetHUDType() != TF_HUDTYPE_ESCORT ) ) |
|
return; |
|
|
|
if ( !BaseClass::IsVisible() ) // intentionally skipping our version of IsVisible() to bypass the !m_bHaveValidPointPositions check |
|
return; |
|
|
|
bool bInvalidateLayout = false; |
|
|
|
if ( TFGameRules() ) |
|
{ |
|
if ( m_bMultipleTrains != TFGameRules()->HasMultipleTrains() ) |
|
{ |
|
// if we don't match, reload our settings |
|
m_bMultipleTrains = TFGameRules()->HasMultipleTrains(); |
|
bInvalidateLayout = true; |
|
} |
|
} |
|
|
|
if ( ObjectiveResource() ) |
|
{ |
|
if ( m_nNumHills != ObjectiveResource()->GetNumNodeHillData( m_nTeam ) ) |
|
{ |
|
m_nNumHills = ObjectiveResource()->GetNumNodeHillData( m_nTeam ); |
|
bInvalidateLayout = true; |
|
} |
|
} |
|
|
|
if ( bInvalidateLayout ) |
|
{ |
|
InvalidateLayout( false, true ); |
|
} |
|
|
|
// if we haven't found any valid points yet, keep trying until we do... |
|
if ( !m_bHaveValidPointPositions ) |
|
{ |
|
UpdateCPImages(); |
|
} |
|
|
|
bool bAlarm = ObjectiveResource() && ObjectiveResource()->GetTrackAlarm( m_nTeam ); |
|
if ( bAlarm != m_bAlarm ) |
|
{ |
|
m_bAlarm = bAlarm; |
|
UpdateAlarmAnimations(); |
|
} |
|
|
|
// The escort item position approaches its actual pos |
|
int x, y, w, h; |
|
m_pEscortItemPanel->GetBounds( x, y, w, h ); |
|
|
|
m_flProgressHistory.Interpolate( gpGlobals->curtime, hud_escort_interp.GetFloat() ); |
|
|
|
float flProgress = m_flProgress; |
|
|
|
if ( hud_escort_test_progress.GetFloat() >= 0 ) |
|
{ |
|
flProgress = hud_escort_test_progress.GetFloat(); |
|
} |
|
|
|
int iBarX, iBarY, iBarW, iBarH; |
|
m_pLevelBar->GetBounds( iBarX, iBarY, iBarW, iBarH ); |
|
|
|
int newX = iBarX + (int)( (float)(iBarW) * m_flProgress ); |
|
|
|
int iSwoopX = newX - XRES(12); |
|
|
|
newX -= ( w / 2 ); |
|
|
|
m_pEscortItemPanel->SetPos( newX, y ); |
|
|
|
if ( m_bShowSwoop ) |
|
{ |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
|
|
// Tell the cap highlight image to fire up if it's our point being capped |
|
if ( m_pHilightSwoop && pPlayer && pPlayer->GetTeamNumber() > LAST_SHARED_TEAM ) |
|
{ |
|
if ( IsVisible() ) |
|
{ |
|
ConVarRef cl_hud_minmode( "cl_hud_minmode", true ); |
|
|
|
int parentX, parentY; |
|
GetPos( parentX, parentY ); |
|
|
|
int nYOffset = YRES(138); |
|
int nXOffset = 0; |
|
int nWide = XRES(24); |
|
int nTall = YRES(100); |
|
|
|
if ( m_bMultipleTrains ) |
|
{ |
|
nWide = XRES(20); |
|
nTall = YRES(70); |
|
|
|
if ( pPlayer->GetTeamNumber() == m_nTeam ) |
|
{ |
|
// top bar |
|
nYOffset = YRES(97); |
|
nXOffset = XRES(2); |
|
} |
|
else |
|
{ |
|
// bottom bar |
|
nYOffset = YRES(61); |
|
nXOffset = XRES(2); |
|
} |
|
} |
|
else |
|
{ |
|
if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() ) |
|
{ |
|
nYOffset = YRES(96); |
|
nXOffset = XRES(4); |
|
nWide = XRES(16); |
|
nTall = YRES(70); |
|
} |
|
} |
|
|
|
int nMidBarY = iBarY + ( iBarH / 2 ); |
|
m_pHilightSwoop->SetPos( parentX + iSwoopX + nXOffset, parentY + nMidBarY - nYOffset ); |
|
m_pHilightSwoop->SetSize( nWide, nTall ); |
|
m_pHilightSwoop->SetVisible( true ); |
|
m_pHilightSwoop->StartSwoop(); |
|
} |
|
} |
|
|
|
m_bShowSwoop = false; |
|
} |
|
|
|
// char value[64]; |
|
|
|
// 5 second countdown to receding |
|
float flSecondsToRecede = MAX( 0, ( m_flRecedeTime <= 0 ) ? 0 : m_flRecedeTime - gpGlobals->curtime ); |
|
if ( flSecondsToRecede > 0.0f && flSecondsToRecede <= TF_ESCORT_RECEDE_COUNTDOWN ) |
|
{ |
|
int iDisplaySeconds = (int)( flSecondsToRecede ) + 1; |
|
m_pEscortItemPanel->SetDialogVariable( "recede", VarArgs( "%d", iDisplaySeconds ) ); |
|
|
|
// we should not be showing the blocked image if we're showing the countdown |
|
m_pBlocked->SetVisible( false ); |
|
} |
|
else |
|
{ |
|
m_pEscortItemPanel->SetDialogVariable( "recede", "" ); |
|
} |
|
|
|
// Debug string |
|
// Q_snprintf( value, sizeof(value), "speed: %d progress: %.3f recede %.1f\n", m_iSpeedLevel, flProgress, flSecondsToRecede ); |
|
// SetDialogVariable( "progress", value ); |
|
|
|
if ( m_bMultipleTrains ) |
|
{ |
|
if ( m_pProgressBar && !m_pProgressBar->IsVisible() ) |
|
{ |
|
m_pProgressBar->SetVisible( true ); |
|
} |
|
|
|
m_pProgressBar->SetPercentage( m_flProgress ); |
|
} |
|
else |
|
{ |
|
if ( m_pProgressBar && m_pProgressBar->IsVisible() ) |
|
{ |
|
m_pProgressBar->SetVisible( false ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Receive messages about changes in state |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscort::FireGameEvent( IGameEvent *event ) |
|
{ |
|
const char *eventName = event->GetName(); |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( !ObjectiveResource() ) |
|
return; |
|
|
|
// make sure we care about this message |
|
int iTeam = event->GetInt( "team", TEAM_UNASSIGNED ); |
|
if ( iTeam != TEAM_UNASSIGNED && ( iTeam != m_nTeam ) ) |
|
return; |
|
|
|
if ( FStrEq( eventName, "escort_progress" ) ) |
|
{ |
|
m_flProgress = event->GetFloat( "progress" ); |
|
|
|
// so we don't interp back to the start position on reset |
|
if ( event->GetBool( "reset" ) ) |
|
{ |
|
m_flProgressHistory.ClearHistory(); |
|
} |
|
|
|
m_flProgressHistory.NoteChanged( gpGlobals->curtime, false ); |
|
} |
|
else if ( FStrEq( eventName, "escort_speed" ) ) |
|
{ |
|
int iSpeedLevel = event->GetInt( "speed" ); |
|
int nPlayers = event->GetInt( "players" ); |
|
|
|
m_pSpeedBackwards->SetVisible( iSpeedLevel < 0 ); |
|
|
|
bool bShowPlayers = ( iSpeedLevel > 0 ) && ( nPlayers > 0 ); |
|
m_pCapPlayerImage->SetVisible( bShowPlayers ); |
|
m_pCapNumPlayers->SetVisible( bShowPlayers ); |
|
m_pEscortItemPanel->SetDialogVariable( "numcappers", nPlayers ); |
|
|
|
// make sure the point isn't being marked as blocked when we're showing players or moving backwards |
|
if ( bShowPlayers || ( iSpeedLevel < 0 ) ) |
|
{ |
|
m_pBlocked->SetVisible( false ); |
|
} |
|
|
|
m_pEscortItemPanel->InvalidateLayout(); |
|
|
|
if ( iSpeedLevel > 0 && m_iSpeedLevel == 0 ) |
|
{ |
|
m_bShowSwoop = true; |
|
} |
|
|
|
m_iSpeedLevel = iSpeedLevel; |
|
} |
|
if ( FStrEq( eventName, "escort_recede" ) ) |
|
{ |
|
m_flRecedeTime = event->GetFloat( "recedetime" ); |
|
} |
|
else if ( FStrEq( eventName, "controlpoint_initialized" ) ) |
|
{ |
|
UpdateCPImages(); |
|
} |
|
else if ( FStrEq( eventName, "controlpoint_updateimages" ) ) |
|
{ |
|
// Update the images of our control point icons |
|
int iIndex = event->GetInt( "index" ); |
|
|
|
bool bIsAnyCapBlocked = false; |
|
|
|
// Only invalidate the specified cap point |
|
// unless iIndex is -1, then do them all |
|
for (int i = 0; i < m_Icons.Count(); i++) |
|
{ |
|
if ( iIndex != -1 && m_Icons[i]->GetCapIndex() != iIndex ) |
|
continue; |
|
|
|
m_Icons[i]->SetForceOpaqueImages( !m_bMultipleTrains && ( m_nNumHills > 0 ) ); |
|
m_Icons[i]->UpdateImage(); |
|
|
|
if ( ObjectiveResource()->IsCPBlocked( m_Icons[i]->GetCapIndex() ) ) |
|
{ |
|
bIsAnyCapBlocked = true; |
|
} |
|
} |
|
|
|
// update our teardrop status |
|
UpdateStatusTeardropFor( m_iCurrentCP ); |
|
|
|
// Assume that there is only one cap area linked to the escort item |
|
m_pBlocked->SetVisible( bIsAnyCapBlocked ); |
|
} |
|
else if ( FStrEq( "controlpoint_updatecapping", eventName ) ) |
|
{ |
|
// Update the capping status of our control point icons |
|
int iIndex = event->GetInt( "index" ); |
|
UpdateStatusTeardropFor( iIndex ); |
|
return; |
|
} |
|
else if ( FStrEq( "controlpoint_starttouch", eventName ) ) |
|
{ |
|
int iPlayer = event->GetInt( "player" ); |
|
if ( pPlayer && iPlayer == pPlayer->entindex() ) |
|
{ |
|
m_iCurrentCP = event->GetInt( "area" ); |
|
UpdateStatusTeardropFor( m_iCurrentCP ); |
|
} |
|
} |
|
else if ( FStrEq( "controlpoint_endtouch", eventName ) ) |
|
{ |
|
int iPlayer = event->GetInt( "player" ); |
|
if ( pPlayer && iPlayer == pPlayer->entindex() ) |
|
{ |
|
m_iCurrentCP = -1; |
|
UpdateStatusTeardropFor( m_iCurrentCP ); |
|
} |
|
} |
|
else if ( FStrEq( "teamplay_round_start", eventName ) ) |
|
{ |
|
InvalidateLayout(); |
|
} |
|
else if ( FStrEq( "localplayer_respawn", eventName ) ) |
|
{ |
|
// reset the alarm (will be restarted in OnThink() if needed) |
|
m_bAlarm = false; |
|
UpdateAlarmAnimations(); |
|
} |
|
else if ( FStrEq( "localplayer_changeteam", eventName ) ) |
|
{ |
|
// reset the alarm (will be restarted in OnThink() if needed) |
|
m_bAlarm = false; |
|
UpdateAlarmAnimations(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudEscort::UpdateStatusTeardropFor( int iIndex ) |
|
{ |
|
if ( !ObjectiveResource() ) |
|
return; |
|
|
|
if ( m_bMultipleTrains ) |
|
{ |
|
// don't draw teardrops for points that aren't in the local group the player cares about (group numbers map to the teams) |
|
if ( iIndex != -1 && ( ObjectiveResource()->GetCPGroup( iIndex ) != ( m_nTeam - 2 ) ) ) // -2 to offset the team numbers to 0 |
|
return; |
|
|
|
// don't draw teardrops if you're not the same team as the local player (top bar is the only one to use the teamdrop) |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( pPlayer ) |
|
{ |
|
if ( m_nTeam != pPlayer->GetTeamNumber() ) |
|
return; |
|
} |
|
} |
|
|
|
if ( iIndex == -1 ) |
|
{ |
|
iIndex = m_iCurrentCP; |
|
} |
|
|
|
// Ignore requests to display progress bars for points we're not standing on |
|
if ( ( m_iCurrentCP != iIndex ) ) |
|
return; |
|
|
|
if ( iIndex >= ObjectiveResource()->GetNumControlPoints() ) |
|
{ |
|
iIndex = -1; |
|
} |
|
|
|
if ( m_pStatus ) |
|
{ |
|
m_pStatus->SetupForPoint( iIndex ); |
|
} |
|
} |
|
|
|
void CTFHudEscort::UpdateCPImages( void ) |
|
{ |
|
if ( !ObjectiveResource() ) |
|
return; |
|
|
|
// delete any existing control points |
|
for ( int i = 0; i < m_Icons.Count(); i++) |
|
{ |
|
m_Icons[i]->MarkForDeletion(); |
|
} |
|
m_Icons.RemoveAll(); |
|
|
|
Assert( m_pCPTemplate ); |
|
|
|
// dummy pos |
|
int x, y, w, h; |
|
m_pCPTemplate->GetBounds( x, y, w, h ); |
|
int nZPos = m_pCPTemplate->GetZPos(); |
|
|
|
//bool bSetHome = false; |
|
|
|
int iValidPointPos = 0; // hax, how many points are not at 0.0f |
|
|
|
// Create an icon for each visible control point in this miniround |
|
int iPoints = ObjectiveResource()->GetNumControlPoints(); |
|
for ( int i = 0; i < iPoints; i++ ) |
|
{ |
|
bool bValid = true; |
|
|
|
if ( !ObjectiveResource()->IsInMiniRound(i) || |
|
!ObjectiveResource()->IsCPVisible(i) || |
|
( m_bMultipleTrains && ( ObjectiveResource()->GetCPGroup( i ) != ( m_nTeam - 2 ) ) ) ) // -2 to offset the team numbers to 0 |
|
{ |
|
bValid = false; |
|
} |
|
|
|
if ( bValid ) |
|
{ |
|
CSimpleControlPoint *pIcon = new CSimpleControlPoint( this, "SimpleControlPointTemplate", i ); |
|
pIcon->SetName( VarArgs("cp_%d", i ) ); |
|
|
|
m_Icons.AddToTail( vgui::SETUP_PANEL(pIcon) ); |
|
|
|
float flDist = ObjectiveResource()->GetPathDistance(i); |
|
|
|
int iBarX, iBarY, iBarW, iBarH; |
|
m_pLevelBar->GetBounds( iBarX, iBarY, iBarW, iBarH ); |
|
|
|
int newX = iBarX + (int)( (float)iBarW * flDist ); |
|
|
|
newX -= ( w / 2 ); |
|
|
|
pIcon->SetBounds( newX, y, w, h ); |
|
pIcon->SetZPos( nZPos ); |
|
pIcon->InvalidateLayout(); |
|
|
|
if ( flDist > 0 ) |
|
{ |
|
iValidPointPos++; |
|
} |
|
|
|
pIcon->SetForceOpaqueImages( !m_bMultipleTrains && ( m_nNumHills > 0 ) ); |
|
pIcon->UpdateImage(); |
|
} |
|
|
|
} |
|
|
|
// we don't want to draw if we haven't got good point positions yet |
|
m_bHaveValidPointPositions = ( iValidPointPos > 0 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFHudMultipleEscort::CTFHudMultipleEscort( Panel *parent, const char *name ) : EditablePanel( parent, name ) |
|
{ |
|
m_pBluePanel = new CTFHudEscort( this, "BlueEscortPanel" ); |
|
m_pRedPanel = new CTFHudEscort( this, "RedEscortPanel" ); |
|
|
|
if ( m_pRedPanel ) |
|
{ |
|
m_pRedPanel->SetTeam( TF_TEAM_RED ); |
|
} |
|
|
|
ListenForGameEvent( "localplayer_changeteam" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFHudMultipleEscort::~CTFHudMultipleEscort() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudMultipleEscort::SetVisible( bool state ) |
|
{ |
|
if ( m_pBluePanel ) |
|
{ |
|
m_pBluePanel->SetVisible( state ); |
|
} |
|
|
|
if ( m_pRedPanel ) |
|
{ |
|
m_pRedPanel->SetVisible( state ); |
|
} |
|
|
|
BaseClass::SetVisible( state ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudMultipleEscort::Reset() |
|
{ |
|
if ( m_pBluePanel ) |
|
{ |
|
m_pBluePanel->Reset(); |
|
} |
|
|
|
if ( m_pRedPanel ) |
|
{ |
|
m_pRedPanel->Reset(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudMultipleEscort::ApplySchemeSettings( IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
|
|
KeyValues *pConditions = NULL; |
|
if ( TFGameRules() && TFGameRules()->HasMultipleTrains() ) |
|
{ |
|
pConditions = new KeyValues( "conditions" ); |
|
if ( pConditions ) |
|
{ |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
int iPlayerTeam = pPlayer ? pPlayer->GetTeamNumber() : TEAM_UNASSIGNED; |
|
|
|
if ( iPlayerTeam == TF_TEAM_BLUE ) |
|
{ |
|
AddSubKeyNamed( pConditions, "if_blue_is_top" ); |
|
} |
|
else |
|
{ |
|
AddSubKeyNamed( pConditions, "if_red_is_top" ); |
|
} |
|
} |
|
} |
|
|
|
// load control settings... |
|
LoadControlSettings( "resource/UI/ObjectiveStatusMultipleEscort.res", NULL, NULL, pConditions ); |
|
|
|
if ( pConditions ) |
|
{ |
|
pConditions->deleteThis(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudMultipleEscort::FireGameEvent( IGameEvent * event ) |
|
{ |
|
if ( FStrEq( "localplayer_changeteam", event->GetName() ) ) |
|
{ |
|
if ( m_pRedPanel && m_pBluePanel ) |
|
{ |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
int iPlayerTeam = pPlayer ? pPlayer->GetTeamNumber() : TEAM_UNASSIGNED; |
|
|
|
if ( iPlayerTeam == TF_TEAM_BLUE ) |
|
{ |
|
m_pBluePanel->SetTopPanel( true ); |
|
m_pRedPanel->SetTopPanel( false ); |
|
} |
|
else |
|
{ |
|
m_pBluePanel->SetTopPanel( false ); |
|
m_pRedPanel->SetTopPanel( true ); |
|
} |
|
} |
|
|
|
InvalidateLayout( false, true ); |
|
|
|
if ( m_pRedPanel && m_pBluePanel ) |
|
{ |
|
m_pRedPanel->InvalidateLayout( false, true ); |
|
m_pBluePanel->InvalidateLayout( false, true ); |
|
} |
|
} |
|
}
|
|
|