|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "c_func_passtime_goal.h"
#include "c_tf_passtime_ball.h"
#include "c_tf_passtime_logic.h"
#include "tf_hud_passtime.h"
#include "tf_hud_passtime_ball_offscreen_arrow.h"
#include "tf_weapon_passtime_gun.h"
#include "passtime_convars.h"
#include "passtime_game_events.h"
#include "tf_hud_freezepanel.h"
#include "tf_gamerules.h"
#include "c_tf_team.h"
#include "c_tf_player.h"
#include "c_tf_playerresource.h"
#include "iclientmode.h"
#include "vgui_controls/AnimationController.h"
#include "vgui_controls/CircularProgressBar.h"
#include "vgui_controls/ProgressBar.h"
#include "vgui/ISurface.h"
#include <algorithm>
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
// The team colors from g_PR are wrong for probably good reasons that I don't understand.
static Color GetTeamColor( int iTeam, byte alpha = 255 ) { switch ( iTeam ) { case TF_TEAM_RED: return Color( 159, 55, 34, alpha ); case TF_TEAM_BLUE: return Color( 76, 109, 128, alpha ); default: return Color( 245, 231, 222, alpha ); } }
//-----------------------------------------------------------------------------
static const char *GetProgressBallImageForTeam( int iTeam ) { switch( iTeam ) { case TF_TEAM_RED: return "../passtime/hud/passtime_ballcontrol_red"; case TF_TEAM_BLUE: return "../passtime/hud/passtime_ballcontrol_blue"; default: return "../passtime/hud/passtime_ballcontrol_none"; }; } static const char *GetProgressBallImageForTeam( C_BaseEntity *pEnt ) { if ( !pEnt ) { return "../passtime/hud/passtime_ball"; } return GetProgressBallImageForTeam( pEnt->GetTeamNumber() ); }
//-----------------------------------------------------------------------------
static const char *GetPlayerProgressPortrait( C_TFPlayer *pPlayer ) { if ( !pPlayer ) { return "../passtime/hud/portrait_scout_red"; }
int iTeam = pPlayer->GetTeamNumber(); int iClass = pPlayer->GetPlayerClass()->GetClassIndex();
switch(iClass) { case TF_CLASS_SOLDIER: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_soldier_red" : "../passtime/hud/portrait_soldier_blu"; case TF_CLASS_SCOUT: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_scout_red" : "../passtime/hud/portrait_scout_blu"; case TF_CLASS_SNIPER: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_sniper_red" : "../passtime/hud/portrait_sniper_blu"; case TF_CLASS_DEMOMAN: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_demo_red" : "../passtime/hud/portrait_demo_blu"; case TF_CLASS_MEDIC: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_medic_red" : "../passtime/hud/portrait_medic_blu"; case TF_CLASS_HEAVYWEAPONS: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_heavy_red" : "../passtime/hud/portrait_heavy_blu"; case TF_CLASS_PYRO: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_pyro_red" : "../passtime/hud/portrait_pyro_blu"; case TF_CLASS_SPY: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_spy_red" : "../passtime/hud/portrait_spy_blu"; case TF_CLASS_ENGINEER: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_eng_red" : "../passtime/hud/portrait_eng_blu"; default: return (iTeam == TF_TEAM_RED) ? "../passtime/hud/portrait_scout_red" : "../passtime/hud/portrait_scout_blu"; } }
//-----------------------------------------------------------------------------
static const char *GetProgressGoalImage( const C_FuncPasstimeGoal *pGoal ) { if ( !pGoal ) { return ""; }
// NOTE: keep in mind that a goal that's on team blue is the goal where blue scores
// and should look red on the hud since it's in the red part of the map. oops.
bool bRedIcon = pGoal->GetTeamNumber() == TF_TEAM_BLUE;
if ( pGoal->GetGoalType() == C_FuncPasstimeGoal::TYPE_TOWER ) { if ( pGoal->BGoalTriggerDisabled() ) { return bRedIcon ? "../passtime/hud/passtime_goal_red_locked" : "../passtime/hud/passtime_goal_blue_locked"; } else { return bRedIcon ? "../passtime/hud/passtime_goal_red_unlocked" : "../passtime/hud/passtime_goal_blue_unlocked"; } } else if ( pGoal->GetGoalType() == C_FuncPasstimeGoal::TYPE_ENDZONE ) { return bRedIcon ? "../passtime/hud/passtime_endzone_red_icon" : "../passtime/hud/passtime_endzone_blue_icon"; } else { return bRedIcon ? "../passtime/hud/passtime_goal_red_icon" : "../passtime/hud/passtime_goal_blue_icon"; } }
//-----------------------------------------------------------------------------
// CTFHudPasstimePanel
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CTFHudPasstimePanel::CTFHudPasstimePanel( vgui::Panel *pParent, const char* name ) : EditablePanel( pParent, name ) {}
//-----------------------------------------------------------------------------
bool CTFHudPasstimePanel::IsVisible() { if ( IsTakingAFreezecamScreenshot() ) { return false; } return BaseClass::IsVisible(); }
//-----------------------------------------------------------------------------
// CTFHudTeamScore
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CTFHudTeamScore::CTFHudTeamScore( vgui::Panel *pParent ) : CTFHudPasstimePanel( pParent, "HudTeamScore" ) , m_pPlayingToCluster( 0 ) { }
//-----------------------------------------------------------------------------
void CTFHudTeamScore::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/UI/HudPasstimeTeamScore.res", NULL, NULL, NULL ); m_pPlayingToCluster = FindControl<EditablePanel>( "PlayingToCluster" );
if ( m_pPlayingToCluster ) { m_pPlayingToCluster->SetVisible( true ); }
OnTick(); vgui::ivgui()->AddTickSignal( GetVPanel() ); }
//-----------------------------------------------------------------------------
void CTFHudTeamScore::OnTick() { // I would rather not do this every tick, but i couldn't find a reliable way
// to do it from events.
if( !g_pPasstimeLogic ) { return; }
int iBlueScore = 0; int iRedScore = 0;
C_TFTeam *pTeam = GetGlobalTFTeam( TF_TEAM_BLUE ); if ( pTeam ) { iBlueScore = pTeam->GetFlagCaptures(); }
pTeam = GetGlobalTFTeam( TF_TEAM_RED ); if ( pTeam ) { iRedScore = pTeam->GetFlagCaptures(); }
if ( m_pPlayingToCluster ) { m_pPlayingToCluster->SetDialogVariable( "rounds", tf_passtime_scores_per_round.GetInt() ); }
SetDialogVariable( "bluescore", iBlueScore ); SetDialogVariable( "redscore", iRedScore ); }
//-----------------------------------------------------------------------------
int CTFHudTeamScore::GetTeamScore( int iTeam ) { C_TFTeam *pTeam = GetGlobalTFTeam( iTeam ); return pTeam ? pTeam->Get_Score() : 0; }
//-----------------------------------------------------------------------------
// CTFHudPasstimePassNotify
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CTFHudPasstimePassNotify::CTFHudPasstimePassNotify( vgui::Panel *pParent ) : CTFHudPasstimePanel( pParent, "HudPasstimePassNotify" ) { }
//-----------------------------------------------------------------------------
void CTFHudPasstimePassNotify::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/UI/HudPasstimePassNotify.res", NULL, NULL, NULL );
m_pTextBox = FindControl<EditablePanel>( "TextBox" ); m_pTextInPassRange = m_pTextBox ? m_pTextBox->FindControl<Label>( "TextInPassRange" ) : NULL; m_pTextLockedOn = m_pTextBox ? m_pTextBox->FindControl<Label>( "TextLockedOn" ) : NULL; m_pTextPassIncoming = m_pTextBox ? m_pTextBox->FindControl<Label>( "TextPassIncoming" ) : NULL; m_pTextPlayerName = m_pTextBox ? m_pTextBox->FindControl<Label>( "TextPlayerName" ) : NULL; m_pSpeechIndicator = FindControl<ImagePanel>( "SpeechIndicator" ); m_pPassLockIndicator = FindControl<ImagePanel>( "PassLockIndicator" ); m_pTextBoxBorderNormal = pScheme->GetBorder( "TFFatLineBorder" ); m_pTextBoxBorderIncomingRed = pScheme->GetBorder( "TFFatLineBorderRedBG" ); m_pTextBoxBorderIncomingBlu = pScheme->GetBorder( "TFFatLineBorderBlueBG" ); vgui::ivgui()->AddTickSignal( GetVPanel() );
Assert( m_pTextInPassRange && m_pTextLockedOn && m_pTextPassIncoming && m_pTextPlayerName && m_pSpeechIndicator && m_pPassLockIndicator ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimePassNotify::OnTick() { C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer || !g_pPasstimeLogic || !g_pPasstimeLogic->GetBall() ) { // nothing can work
if ( m_pTextBox ) { m_pTextBox->SetVisible( false ); }
if ( m_pSpeechIndicator ) { m_pSpeechIndicator->SetVisible( false ); }
if ( m_pPassLockIndicator ) { m_pPassLockIndicator->SetVisible( false ); } return; }
C_PasstimeBall *pBall = g_pPasstimeLogic->GetBall(); CTFPlayer *pBallCarrier = pBall ? pBall->GetCarrier() : NULL; if ( !pBallCarrier ) { if ( pBall && pBall->GetHomingTarget() == pLocalPlayer ) { //
// Incoming pass
//
if ( m_pSpeechIndicator ) { m_pSpeechIndicator->SetVisible( false ); }
if ( m_pPassLockIndicator ) { m_pPassLockIndicator->SetVisible( false ); }
if ( m_pTextBox ) { m_pTextBox->SetVisible( true ); }
// this should really be GetThrower instead of PrevCarrier,
// but it doesn't exist on the client and this will have the
// desired value anyway.
C_TFPlayer *pThrower = pBall->GetPrevCarrier(); if ( pThrower ) { if ( m_pTextPlayerName ) { m_pTextPlayerName->SetText( pThrower->GetPlayerName() ); } } else { if ( m_pTextPlayerName ) { m_pTextPlayerName->SetText( "..." ); } }
if ( m_pTextLockedOn ) { m_pTextLockedOn->SetVisible( false ); }
if ( m_pTextPassIncoming ) { m_pTextPassIncoming->SetVisible( true ); }
if ( m_pTextInPassRange ) { m_pTextInPassRange->SetVisible( false ); }
if ( m_pTextBox ) { m_pTextBox->SetBorder( (pLocalPlayer->GetTeamNumber() == TF_TEAM_RED) ? m_pTextBoxBorderIncomingRed : m_pTextBoxBorderIncomingBlu ); }
return; } }
//
// Can't be an incoming pass at this point
//
if( !pBallCarrier || pLocalPlayer->IsObserver() || (pBallCarrier == pLocalPlayer) || (pBallCarrier->GetTeamNumber() != pLocalPlayer->GetTeamNumber()) ) { //
// No carrier, or carrier is on enemy team, or carrier is local player
//
if ( m_pTextBox ) { m_pTextBox->SetVisible( false ); }
if ( m_pSpeechIndicator ) { m_pSpeechIndicator->SetVisible( false ); }
if ( m_pPassLockIndicator ) { m_pPassLockIndicator->SetVisible( false ); } return; }
//
// Carrier has the ball and is on our team
//
float flMaxPassRangeSqr = g_pPasstimeLogic->GetMaxPassRange(); flMaxPassRangeSqr *= flMaxPassRangeSqr; bool bTargetable = pBallCarrier->EyePosition().DistToSqr( pLocalPlayer->EyePosition() ) < flMaxPassRangeSqr; if ( bTargetable ) { trace_t tr; CTraceFilterIgnorePlayers tracefilter( pLocalPlayer, COLLISION_GROUP_PROJECTILE ); UTIL_TraceLine( pBallCarrier->EyePosition(), pLocalPlayer->EyePosition(), MASK_PLAYERSOLID, &tracefilter, &tr ); bTargetable = tr.fraction == 1; }
if ( !bTargetable ) { if ( m_pTextBox ) { m_pTextBox->SetVisible( false ); }
if ( m_pSpeechIndicator ) { m_pSpeechIndicator->SetVisible( false ); }
if ( m_pPassLockIndicator ) { m_pPassLockIndicator->SetVisible( false ); } return; }
bool bTargeted = pLocalPlayer->m_Shared.IsTargetedForPasstimePass();
if ( m_pTextBox ) { m_pTextBox->SetVisible( true ); }
if ( m_pPassLockIndicator ) { m_pPassLockIndicator->SetVisible( bTargeted ); }
if ( m_pSpeechIndicator ) { m_pSpeechIndicator->SetVisible( pLocalPlayer->m_Shared.AskForBallTime() > gpGlobals->curtime ); }
if ( bTargeted ) { if ( m_pPassLockIndicator ) { m_pPassLockIndicator->SetDrawColor( GetTeamColor( pLocalPlayer->GetTeamNumber() ) ); }
if ( m_pTextLockedOn ) { m_pTextLockedOn->SetVisible( true ); }
if ( m_pTextPassIncoming ) { m_pTextPassIncoming->SetVisible( false ); }
if ( m_pTextInPassRange ) { m_pTextInPassRange->SetVisible( false ); } } else if ( bTargetable ) { if ( m_pTextLockedOn ) { m_pTextLockedOn->SetVisible( false ); }
if ( m_pTextPassIncoming ) { m_pTextPassIncoming->SetVisible( false ); }
if ( m_pTextInPassRange ) { m_pTextInPassRange->SetVisible( true ); } } //else if ( !bTargetable )
//{
// m_pTopText->SetText( "CAN'T SEE YOU" );
//}
if ( m_pTextPlayerName ) { m_pTextPlayerName->SetText( pBallCarrier->GetPlayerName() ); }
if ( m_pTextBox ) { m_pTextBox->SetBorder( m_pTextBoxBorderNormal ); } }
//-----------------------------------------------------------------------------
// CTFHudPasstimeEventText
//-----------------------------------------------------------------------------
namespace HudPasstimeEventText { static const float flInSec = 0.1f; static const float flOutSec = 0.25f; static const float flShowSec = 3.0f; static const float flPauseSec = 0.1f; static const int iQueueDepthPanic = 3; static char const * const pKeyTeam = "team"; static char const * const pKeySubject = "subject"; static char const * const pKeySource = "source"; }
//-----------------------------------------------------------------------------
CTFHudPasstimeEventText::QueueElement::QueueElement() { title[0] = (wchar_t)0; detail[0] = (wchar_t)0; bonus[0] = (wchar_t)0; }
//-----------------------------------------------------------------------------
CTFHudPasstimeEventText::CTFHudPasstimeEventText() : m_localizeKeys( "" ) { m_bValid = false; m_pTitleLabel = 0; m_pDetailLabel = 0; m_pBonusLabel = 0; m_state = State::Idle; }
//-----------------------------------------------------------------------------
CTFHudPasstimeEventText::~CTFHudPasstimeEventText() { Clear(); }
//-----------------------------------------------------------------------------
// static
void CTFHudPasstimeEventText::SetLabelText( vgui::Label *pLabel, const wchar_t *pText ) { if ( pText && pText[0] ) { pLabel->SetVisible( true ); pLabel->SetText( pText ); } else { pLabel->SetVisible( false ); pLabel->SetText( L"" ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::EnterState( State state, float duration ) { m_state = state; // move things faster if the queue is backlogged, but State::Pause is always the same duration
if ( (state != State::Pause) && (m_queue.Count() >= HudPasstimeEventText::iQueueDepthPanic) ) { duration /= 2.0f; } m_displayTimer.Start( duration ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::SetAlpha( int ia ) { if ( m_pTitleLabel ) { m_pTitleLabel->SetAlpha( ia ); m_pTitleLabel->SetVisible( ia != 0 ); }
if ( m_pDetailLabel ) { m_pDetailLabel->SetAlpha( ia ); m_pDetailLabel->SetVisible( ia != 0 ); }
if ( m_pBonusLabel ) { m_pBonusLabel->SetAlpha( ia ); m_pBonusLabel->SetVisible( ia != 0 ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::Tick() { if ( !m_bValid ) return;
switch( m_state ) { case State::Idle: if ( !m_queue.IsEmpty() ) { SetAlpha( 1 ); auto msg = m_queue.RemoveAtHead(); if ( m_pTitleLabel ) { SetLabelText( m_pTitleLabel, msg.title ); } if ( m_pDetailLabel ) { SetLabelText( m_pDetailLabel, msg.detail ); } if ( m_pBonusLabel ) { SetLabelText( m_pBonusLabel, msg.bonus ); } EnterState( State::In, HudPasstimeEventText::flInSec ); } else { SetAlpha( 0 ); } break;
case State::In: if ( m_displayTimer.IsElapsed() ) { EnterState( State::Show, HudPasstimeEventText::flShowSec ); SetAlpha( 255 ); } else { // animate alpha
// note: GetElapsedTime()
float flFrac = m_displayTimer.GetElapsedTime() / m_displayTimer.GetCountdownDuration(); SetAlpha( (int) (flFrac * flFrac * 255.0f) ); } break;
case State::Show: if ( m_displayTimer.IsElapsed() ) { EnterState( State::Out, HudPasstimeEventText::flOutSec ); } break;
case State::Out: if ( m_displayTimer.IsElapsed() ) { EnterState( State::Pause, HudPasstimeEventText::flPauseSec ); SetAlpha( 0 ); } else { // animate alpha
// note: GetRemainingTime()
float flFrac = m_displayTimer.GetRemainingTime() / m_displayTimer.GetCountdownDuration(); SetAlpha( (int) (flFrac * flFrac * 255.0f) ); } break;
case State::Pause: if ( m_displayTimer.IsElapsed() ) { m_state = State::Idle; } break; } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::Clear() { while( !m_queue.IsEmpty() ) { m_queue.RemoveAtTail(); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::SetControls( vgui::Label *pTitleLabel, vgui::Label *pDetailLabel, vgui::Label *pBonusLabel ) { m_pTitleLabel = pTitleLabel; m_pDetailLabel = pDetailLabel; m_pBonusLabel = pBonusLabel; m_bValid = pTitleLabel && pDetailLabel && pBonusLabel; }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::SetPlayerName( C_TFPlayer *pPlayer, const char *pKey ) { if ( pPlayer ) { g_pVGuiLocalize->ConvertANSIToUnicode( pPlayer->GetPlayerName(), m_pwcsBuf, sizeof(m_pwcsBuf) ); m_localizeKeys->SetWString( pKey, m_pwcsBuf ); } else { m_localizeKeys->SetWString( pKey, L"" ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::SetTeam( C_TFPlayer *pPlayer ) { if ( pPlayer ) { C_TFTeam *pTeam = GetGlobalTFTeam( pPlayer->GetTeamNumber() ); m_localizeKeys->SetWString( "team", pTeam ? pTeam->Get_Localized_Name() : L"" ); } else { m_localizeKeys->SetWString( "team", L"" ); } }
//-----------------------------------------------------------------------------
template< int TArraySize > void CTFHudPasstimeEventText::ConstructNewString( const char *pLocTag, wchar_t (&out)[TArraySize] ) { // FIXME calling find is redundant
if ( pLocTag && pLocTag[0] && g_pVGuiLocalize->Find( pLocTag ) ) { g_pVGuiLocalize->ConstructString_safe( out, pLocTag, m_localizeKeys ); } else { out[0] = (wchar_t)0; } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::Enqueue( C_TFPlayer *pSource, C_TFPlayer *pSubject, const char *pTitle, const char *pDetail, const char *pBonus ) { if ( !m_bValid || !pSubject ) return;
SetTeam( pSubject ); SetPlayerName( pSubject, HudPasstimeEventText::pKeySubject ); SetPlayerName( pSource, HudPasstimeEventText::pKeySource );
auto *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); auto bShowBonus = (pSubject == pLocalPlayer) || (pLocalPlayer->IsObserver() && pLocalPlayer->GetObserverTarget() == pLocalPlayer); QueueElement e; ConstructNewString( pTitle, e.title ); ConstructNewString( pDetail, e.detail ); ConstructNewString( bShowBonus ? pBonus : nullptr, e.bonus ); m_queue.Insert( e ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::EnqueueGeneric( const char *pTitle, const char *pDetail, const char *pBonus ) { QueueElement e; ConstructNewString( pTitle, e.title ); ConstructNewString( pDetail, e.detail ); ConstructNewString( pBonus, e.bonus ); m_queue.Insert( e ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::EnqueueSteal( C_TFPlayer *pVictim, C_TFPlayer *pStealer ) { Enqueue( pVictim, pStealer, "#Msg_PasstimeEventStealTitle", "#Msg_PasstimeEventStealDetail", "#Msg_PasstimeEventStealBonus" ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::EnqueuePass( C_TFPlayer *pThrower, C_TFPlayer *pCatcher ) { Enqueue( pThrower, pCatcher, "#Msg_PasstimeEventPassTitle", "#Msg_PasstimeEventPassDetail", "#Msg_PasstimeEventPassBonus" ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::EnqueueInterception( C_TFPlayer *pThrower, C_TFPlayer *pCatcher ) { Enqueue( pThrower, pCatcher, "#Msg_PasstimeEventInterceptTitle", "#Msg_PasstimeEventInterceptDetail", "#Msg_PasstimeEventInterceptBonus" ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeEventText::EnqueueScore( C_TFPlayer *pThrower, C_TFPlayer *pAssister ) { if ( pAssister ) Enqueue( pAssister, pThrower, "#Msg_PasstimeEventScoreTitle", "#Msg_PasstimeEventScoreDetail_Assist", "#Msg_PasstimeEventScoreBonus" ); else Enqueue( pAssister, pThrower, "#Msg_PasstimeEventScoreTitle", "#Msg_PasstimeEventScoreDetail_NoAssist", "#Msg_PasstimeEventScoreBonus" ); }
//-----------------------------------------------------------------------------
// CTFHudPasstimeBallStatus
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CTFHudPasstimeBallStatus::CTFHudPasstimeBallStatus( Panel *pParent ) : CTFHudPasstimePanel( pParent, "HudPasstimeBallStatus" ) { m_bInitialized = false; m_bReset = false; m_bGoalsFound = false; m_iXBlueProgress = -100.0f; m_iXRedProgress = -100.0f; m_iYBlueProgress = -100.0f; m_iYRedProgress = -100.0f; memset( m_pGoalIconsBlue, 0, sizeof( m_pGoalIconsBlue ) ); memset( m_pGoalIconsRed, 0, sizeof( m_pGoalIconsRed ) ); memset( m_pPlayerIcons, 0, sizeof( m_pPlayerIcons ) ); m_pProgressBall = 0; m_pProgressBallCarrierName = 0; m_pProgressLevelBar = 0; m_pSelfPlayerIcon = 0; m_pEventText = new CTFHudPasstimeEventText(); m_pBallPowerMeterFillContainer = nullptr; m_pBallPowerMeterFill = nullptr; m_pBallPowerMeterFrame = nullptr; }
//-----------------------------------------------------------------------------
CTFHudPasstimeBallStatus::~CTFHudPasstimeBallStatus() { }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); m_bInitialized = false;
LoadControlSettings( "resource/UI/HudPasstimeBallStatus.res", NULL, NULL, NULL ); m_pPowerCluster = FindControl<EditablePanel>( "BallPowerCluster" ); m_pProgressBall = FindControl<ImagePanel>( "ProgressBallIcon" ); m_pProgressBallCarrierName = FindControl<Label>( "ProgressBallCarrierName" ); m_pProgressLevelBar = FindControl<Panel>( "ProgressLevelBar" ); m_pSelfPlayerIcon = FindControl<ImagePanel>( "ProgressSelfPlayerIcon" );
Panel *pBallPowerRoot = FindControl<Panel>( "BallPowerCluster" ); if ( !pBallPowerRoot ) { pBallPowerRoot = this; } m_pBallPowerMeterFillContainer = pBallPowerRoot->FindControl<Panel>( "BallPowerMeterFillContainer", true ); m_pBallPowerMeterFill = pBallPowerRoot->FindControl<ImagePanel>( "BallPowerMeterFill", true ); m_pBallPowerMeterFrame = pBallPowerRoot->FindControl<Panel>( "BallPowerMeterFrame", true ); m_pBallPowerMeterFinalSection = pBallPowerRoot->FindControl<Panel>( "BallPowerMeterFinalSectionContainer", true );
m_pEventText->SetControls( FindControl<Label>( "EventTitleLabel" ), FindControl<Label>( "EventDetailLabel" ), FindControl<Label>( "EventBonusLabel" ) );
m_bInitialized = m_pProgressBall && m_pProgressBallCarrierName && m_pProgressLevelBar && m_pSelfPlayerIcon && m_pBallPowerMeterFillContainer && m_pBallPowerMeterFill && m_pBallPowerMeterFinalSection && m_pBallPowerMeterFrame;
if ( !m_bInitialized ) { // just bail if the res file is missing required controls
// this prevents a lot of stupid null checks in the future
return; }
// set up some ballpower stuff
m_iBallPowerMeterFillWidth = m_pBallPowerMeterFillContainer ? m_pBallPowerMeterFillContainer->GetWide() : 0; int iFinalSectionWidth = (int)(m_iBallPowerMeterFillWidth * 0.2f); if ( m_pBallPowerMeterFinalSection ) { m_pBallPowerMeterFinalSection->SetWide( iFinalSectionWidth ); m_pBallPowerMeterFinalSection->SetPos( m_pBallPowerMeterFinalSection->GetXPos() + (m_iBallPowerMeterFillWidth - iFinalSectionWidth), m_pBallPowerMeterFinalSection->GetYPos() ); } m_iPrevBallPower = g_pPasstimeLogic ? g_pPasstimeLogic->GetBallPower() : 0; // find the left/right markers in the res files
{ auto *pRedEnd = FindChildByName( "RedProgressEnd" ); auto *pBlueEnd = FindChildByName( "BlueProgressEnd" ); if ( pRedEnd && pBlueEnd ) { pRedEnd->GetPos( m_iXRedProgress, m_iYRedProgress ); pBlueEnd->GetPos( m_iXBlueProgress, m_iYBlueProgress ); } else { // no markers in the res file, just give it offscreen but valid coords
m_iXBlueProgress = -100.0f; m_iXRedProgress = -100.0f; m_iYBlueProgress = -100.0f; m_iYRedProgress = -100.0f; } }
// find all the goal icon image panels
{ char buf[16]; for ( auto i = 0; i < NumGoalIcons; ++i ) { V_sprintf_safe( buf, "GoalRed%i", i ); m_pGoalIconsRed[i] = FindControl<vgui::ImagePanel>( buf ); V_sprintf_safe( buf, "GoalBlue%i", i ); m_pGoalIconsBlue[i] = FindControl<vgui::ImagePanel>( buf ); } }
ListenForGameEvent( PasstimeGameEvents::BallFree::s_eventName ); ListenForGameEvent( PasstimeGameEvents::BallGet::s_eventName ); ListenForGameEvent( PasstimeGameEvents::BallStolen::s_eventName ); ListenForGameEvent( PasstimeGameEvents::PassCaught::s_eventName ); ListenForGameEvent( PasstimeGameEvents::Score::s_eventName ); Reset(); // this ensures players will try to guess game state for the hud if they join a game in progress
vgui::ivgui()->AddTickSignal( GetVPanel() );
// find all the player icon image panels
{ char controlname[32]; for ( auto i = 0; i < MAX_PLAYERS; ++i ) { V_sprintf_safe( controlname, "playericon%i", i ); // ugh
m_pPlayerIcons[i] = FindControl<vgui::ImagePanel>( controlname ); Assert( m_pPlayerIcons[i] ); m_pPlayerIcons[i]->SetEnabled( true ); m_pPlayerIcons[i]->SetShouldScaleImage( true ); } } }
//-----------------------------------------------------------------------------
bool CTFHudPasstimeBallStatus::BShouldDraw() const { CBasePlayer *pPlayer = CBasePlayer::GetLocalPlayer(); if ( !pPlayer || !pPlayer->IsAlive() || ( pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM ) ) { return false; }
gamerules_roundstate_t roundstate = TFGameRules()->State_Get(); return ((roundstate == GR_STATE_RND_RUNNING) || (roundstate == GR_STATE_STALEMATE)) && !TFGameRules()->InSetup(); }
//-----------------------------------------------------------------------------
// FIXME copypasta with tf_passtime_logic.cpp
static float CalcProgressFrac( const Vector& vecOrigin ) { Assert( g_pPasstimeLogic && (g_pPasstimeLogic->GetNumSections() > 0) );
Vector arrTrackPoints[16]; g_pPasstimeLogic->GetTrackPoints( arrTrackPoints );
float flBestDist = FLT_MAX; float flBestLen = 0; float flTotalLen = 1; // don't set 0 so div by zero is impossible
Vector vecThisPoint; Vector vecPointOnLine; Vector vecPrevPoint = arrTrackPoints[0]; float flThisFrac = 0; float flThisLen = 0; float flThisDist = 0; for ( int i = 1; i < ARRAYSIZE(arrTrackPoints); ++i ) { vecThisPoint = arrTrackPoints[i]; if ( vecThisPoint.IsZero() ) { break; } flThisLen = (vecThisPoint - vecPrevPoint).Length(); flTotalLen += flThisLen; CalcClosestPointOnLineSegment( vecOrigin, vecPrevPoint, vecThisPoint, vecPointOnLine, &flThisFrac ); flThisDist = (vecPointOnLine - vecOrigin).Length(); if ( flThisDist < flBestDist ) { flBestDist = flThisDist; flBestLen = flTotalLen - (flThisLen * (1.0f - flThisFrac)); } vecPrevPoint = vecThisPoint; }
return (float)(flBestLen / flTotalLen); }
//-----------------------------------------------------------------------------
// FIXME copypasta with tf_passtime_logic.cpp
static float CalcBallProgressFrac() { Assert( g_pPasstimeLogic && g_pPasstimeLogic->GetBall() && (g_pPasstimeLogic->GetNumSections() > 0) ); CPasstimeBall *pBall = g_pPasstimeLogic->GetBall(); CTFPlayer *pCarrier = pBall->GetCarrier(); return CalcProgressFrac( pCarrier ? pCarrier->GetNetworkOrigin() : pBall->GetNetworkOrigin() ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::UpdateGoalIcon( vgui::ImagePanel *pIcon, C_FuncPasstimeGoal *pGoal ) { Assert( m_bInitialized ); // TODO: animations on enable/disable?
if ( !pIcon ) { return; }
if ( !pGoal ) { pIcon->SetVisible( false ); return; }
pIcon->SetVisible( true );
const auto iDisabledAlpha = 50; const auto iEnabledAlpha = 255; pIcon->SetAlpha( ( pGoal->BGoalTriggerDisabled() && ( pGoal->GetGoalType() != C_FuncPasstimeGoal::TYPE_TOWER ) ) ? iDisabledAlpha : iEnabledAlpha );
// TODO don't call SetImage(char*) every frame, wtf.
pIcon->SetImage( GetProgressGoalImage( pGoal ) );
const float flProgressFrac = CalcProgressFrac( pGoal->GetAbsOrigin() ); const int iActualBarHalfHeight = m_pProgressLevelBar ? m_pProgressLevelBar->GetTall() / 6 : 0; // magic number because NPOT waste in image
int iX = Lerp( flProgressFrac, m_iXBlueProgress, m_iXRedProgress ) - ( pIcon->GetWide() / 2 ); int iY = Lerp( flProgressFrac, m_iYBlueProgress, m_iYRedProgress ) - iActualBarHalfHeight; pIcon->SetPos( iX, iY ); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnTickHidden() { m_bGoalsFound = false; if ( m_pProgressBall ) { m_pProgressBall->SetVisible( false ); }
if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetVisible( false ); }
if ( m_pSelfPlayerIcon ) { m_pSelfPlayerIcon->SetVisible( false ); }
if ( m_pProgressLevelBar ) { m_pProgressLevelBar->SetVisible( false ); }
if ( m_pPowerCluster ) { m_pPowerCluster->SetVisible( false ); }
for ( int i = 0; i < MAX_PLAYERS; ++i ) { m_pPlayerIcons[i]->SetVisible( false ); }
m_iPrevBallPower = g_pPasstimeLogic ? g_pPasstimeLogic->GetBallPower() : 0;
HideGoalIcons(); }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnTickVisible( C_TFPlayer *pLocalPlayer, C_PasstimeBall *pBall) { if ( m_pProgressLevelBar ) { m_pProgressLevelBar->SetVisible( true ); }
//
// Update ball icon
//
float flBallProgressFrac = CalcBallProgressFrac(); int iX_Ball = m_pProgressBall ? Lerp( flBallProgressFrac, m_iXBlueProgress, m_iXRedProgress ) - ( m_pProgressBall->GetWide() / 2 ) : 0; int iY_Ball = m_pProgressBall ? Lerp( flBallProgressFrac, m_iYBlueProgress, m_iYRedProgress ) - ( m_pProgressBall->GetTall() / 2 ) : 0;
if ( m_pProgressBall ) { m_pProgressBall->SetVisible( true ); m_pProgressBall->SetPos( iX_Ball, iY_Ball ); }
// todo setimage from event, not every frame
// todo look in to letting entities manage their own hud elements
CTFPlayer *pCarrier = pBall->GetCarrier(); int iTeam = pCarrier ? pCarrier->GetTeamNumber() : pBall->GetTeamNumber(); if ( m_pProgressBall ) { m_pProgressBall->SetImage( GetProgressBallImageForTeam( iTeam ) ); }
//
// Update ball power
//
if ( m_pPowerCluster ) { m_pPowerCluster->SetVisible( true ); } { // update the power bar
int iCurPower = g_pPasstimeLogic->GetBallPower(); int iThreshold = tf_passtime_powerball_threshold.GetInt(); int iAlpha = (iCurPower > iThreshold) ? FLerp( 150, 255, (1 + sin(gpGlobals->curtime*5)) / 2.0f) : 222; if ( m_pBallPowerMeterFill ) { m_pBallPowerMeterFill->SetDrawColor( GetTeamColor( iTeam, iAlpha ) ); } float flPowerFrac = (iCurPower / 100.f); if ( m_pBallPowerMeterFillContainer ) { m_pBallPowerMeterFillContainer->SetWide( m_iBallPowerMeterFillWidth * flPowerFrac ); }
// show power up/down notifications
// this should really be done with a game event but this is a temp experimental feature for now
bool bWasAboveThreshold = m_iPrevBallPower > iThreshold; bool bIsAboveThreshold = iCurPower > iThreshold; m_iPrevBallPower = iCurPower; if ( bWasAboveThreshold && !bIsAboveThreshold ) { m_pEventText->Clear(); // push everything else out of the way
m_pEventText->EnqueueGeneric( "#Msg_PasstimeEventPowerDownTitle", "#Msg_PasstimeEventPowerDownDetail", "#Msg_PasstimeEventPowerDownBonus" ); } else if ( !bWasAboveThreshold && bIsAboveThreshold ) { m_pEventText->Clear(); // push everything else out of the way
m_pEventText->EnqueueGeneric( "#Msg_PasstimeEventPowerUpTitle", "#Msg_PasstimeEventPowerUpDetail", "#Msg_PasstimeEventPowerUpBonus" ); } }
//
// Update player icons
// TODO make less bad
//
C_TFPlayer *pSpecTarget = ToTFPlayer( pLocalPlayer->GetObserverTarget() ); if ( m_pSelfPlayerIcon ) { m_pSelfPlayerIcon->SetVisible( false ); } const int iActualBarHalfHeight = m_pProgressLevelBar ? m_pProgressLevelBar->GetTall() / 7 : 0; // magic number because NPOT waste in image
for ( int iEntIndex = 1; iEntIndex <= MAX_PLAYERS; iEntIndex++ ) { vgui::ImagePanel *pIcon = m_pPlayerIcons[iEntIndex - 1]; if ( !g_TF_PR->IsConnected( iEntIndex ) ) { pIcon->SetVisible( false ); continue; }
int iEntTeam = g_TF_PR->GetTeam( iEntIndex ); if ( !g_TF_PR->IsAlive( iEntIndex ) // no dead people; not the same as IsDead
|| ( ( iEntTeam != TF_TEAM_RED ) && ( iEntTeam != TF_TEAM_BLUE) ) ) // no spectators
{ pIcon->SetVisible( false ); continue; }
// needed for origin
// dormant means "not available on this client right now"
C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iEntIndex ) ); if ( !pPlayer || pPlayer->IsDormant() ) { pIcon->SetVisible( false ); continue; }
// don't show cloaked spies
bool bSpy = g_TF_PR->GetPlayerClass( iEntIndex ) == TF_CLASS_SPY; if ( bSpy ) { if ( pPlayer->m_Shared.IsFullyInvisible() ) { pIcon->SetVisible( false ); continue; }
int iDisguiseTeam = pPlayer->m_Shared.GetDisguiseTeam(); if ( iDisguiseTeam == TF_TEAM_RED || iDisguiseTeam == TF_TEAM_BLUE ) { iEntTeam = iDisguiseTeam; } }
const float flProgressFrac = CalcProgressFrac( pPlayer->GetNetworkOrigin() ); // Player pips
{ pIcon->SetVisible( true ); if ( iEntTeam == TF_TEAM_RED ) { int iX = Lerp( flProgressFrac, m_iXBlueProgress, m_iXRedProgress ) - (pIcon->GetWide() / 2); int iY = Lerp( flProgressFrac, m_iYBlueProgress, m_iYRedProgress ) - pIcon->GetTall() - iActualBarHalfHeight; pIcon->SetPos( iX, iY ); pIcon->SetImage( "../passtime/hud/passtime_ballcontrol_team_red" ); } else { int iX = Lerp( flProgressFrac, m_iXBlueProgress, m_iXRedProgress ) - (pIcon->GetWide() / 2); int iY = Lerp( flProgressFrac, m_iYBlueProgress, m_iYRedProgress ) + iActualBarHalfHeight; pIcon->SetPos( iX, iY ); pIcon->SetImage( "../passtime/hud/passtime_ballcontrol_team_blue" ); } }
// Local player image
if ( g_TF_PR->IsLocalPlayer( iEntIndex ) || ( pLocalPlayer->IsObserver() && ( pPlayer == pSpecTarget ) ) ) { if ( m_pSelfPlayerIcon ) { // TODO don't call SetImage(char*) every frame, wtf.
if ( pLocalPlayer->IsObserver() && pSpecTarget ) { m_pSelfPlayerIcon->SetImage( GetPlayerProgressPortrait( pSpecTarget ) ); } else { m_pSelfPlayerIcon->SetImage( GetPlayerProgressPortrait( pLocalPlayer ) ); }
int iX = Lerp( flProgressFrac, m_iXBlueProgress, m_iXRedProgress ) - (m_pSelfPlayerIcon->GetWide() / 2); int iY = Lerp( flProgressFrac, m_iYBlueProgress, m_iYRedProgress ) - m_pSelfPlayerIcon->GetTall() - iActualBarHalfHeight; m_pSelfPlayerIcon->SetVisible( true ); m_pSelfPlayerIcon->SetPos( iX, iY ); } } }
//
// Refresh goal icons if necessary
//
bool bReadyToFindGoals = (g_pPasstimeLogic->GetNumSections() > 0); if ( !m_bGoalsFound && bReadyToFindGoals ) { // release any existing handles to goals
for ( auto i = 0; i < NumGoalIcons; ++i ) { m_hGoalsBlue[i].Term(); m_hGoalsRed[i].Term(); }
// Update the goal list and pair hud icons with actual goals
const int iMaxSortedGoals = 8; // arbitrary
const auto &goals = C_FuncPasstimeGoal::GetAutoList(); const int iNumGoals = goals.Count(); if ( ( iNumGoals > 1 ) && ( iNumGoals < iMaxSortedGoals ) ) { // sort goals by position in world, from blue to red (blue progress is always 0, red is always 1))
C_FuncPasstimeGoal* sortedgoals[iMaxSortedGoals]; std::copy( goals.begin(), goals.end(), sortedgoals ); std::sort( sortedgoals, sortedgoals + iNumGoals, []( C_FuncPasstimeGoal* a, C_FuncPasstimeGoal* b ) { // this is wasteful but it should only happen one frame per round
// The order of the icons in the hud res file determines which direction to sort these
// so that the iteration below visits the goals in the same order.
return CalcProgressFrac( a->GetAbsOrigin() ) < CalcProgressFrac( b->GetAbsOrigin() ); });
// Pair goals and icons so the tick function can update the icon state based on the actual goal state
// This should work for any number of goals and icons in any order.
// If there are more goals than icons, just ignore them, but make sure the hud shows at least
// the goals farthest from the middle of the map in a symmetrical way, so iterate from both ends
// of the array of sorted goals.
// pair first N blue goals in left-right order
// first icon in array is leftmost blue goal on hud is first goal in sorted array
int iRedIcon = 0; int iBlueIcon = 0; for ( int iGoal = 0; iGoal < iNumGoals; ++iGoal ) { // NOTE: goal's teams are backwards: the blue-colored goals on the blue side of the map are
// on team red because that's where red scores. doh.
auto *pGoal = sortedgoals[iGoal]; if ( pGoal && ( iBlueIcon < NumGoalIcons ) && ( pGoal->GetTeamNumber() == TF_TEAM_RED ) ) { m_hGoalsBlue[iBlueIcon++] = pGoal; } pGoal = sortedgoals[iNumGoals - iGoal - 1]; if ( pGoal && ( iRedIcon < NumGoalIcons ) && ( pGoal->GetTeamNumber() == TF_TEAM_BLUE ) ) { m_hGoalsRed[iRedIcon++] = pGoal; } }
m_bGoalsFound = true; } } //
// Update goal icons
//
if ( m_bGoalsFound ) { for ( auto i = 0; i < NumGoalIcons; ++i ) { UpdateGoalIcon( m_pGoalIconsBlue[i], m_hGoalsBlue[i].Get() ); UpdateGoalIcon( m_pGoalIconsRed[i], m_hGoalsRed[i].Get() ); } } else { HideGoalIcons(); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::HideGoalIcons() { for ( int i = 0; i < NumGoalIcons; ++i ) { auto *pIcon = m_pGoalIconsBlue[i]; if ( pIcon ) pIcon->SetVisible( false ); pIcon = m_pGoalIconsRed[i]; if ( pIcon ) pIcon->SetVisible( false ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnTick() { if ( !g_pPasstimeLogic || !m_bInitialized ) { // happens randomly during map load
m_pEventText->Clear(); return; }
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) { // can happen during exit
return; }
// Tick the event text regardless of the state of the rest of the hud so
// that messages can continue to display even after the round ends or whatever.
m_pEventText->Tick();
// I wish there were a reliable way to do this in an event-driven way instead of every frame
C_PasstimeBall *pBall = g_pPasstimeLogic->GetBall(); if ( !BShouldDraw() || !pBall ) { OnTickHidden(); // this is the easiest way I could find to refresh the goals when switching maps
// todo this is dumb
m_bGoalsFound = false; } else { OnTickVisible( pLocalPlayer, pBall ); }
//
// Try to figure out game state that's usually managed by events if hud was reloaded mid-game
// This happens at the end to ensure that the hud is fully initialized before trying to reset
// on the first frame a player joins a game in progress
//
if ( m_bReset ) { m_pEventText->Clear();
// I don't *think* it's possible that this could accidentally end up
// running every frame.
// Order of operations here is important, see functions for details
// todo make less bad
if ( TryForceBallGet() || TryForceBallFree() ) { m_bReset = false; } }; }
//-----------------------------------------------------------------------------
bool CTFHudPasstimeBallStatus::TryForceBallGet() { Assert( m_bInitialized );
// The HUD was reset during play, or possibly due to joining a game inprogroess,
// so try to get into a BallGet state if possible
if ( !g_TF_PR ) { // is this even possible?
return false; }
for ( int iPlayer = 1 ; iPlayer <= MAX_PLAYERS; iPlayer++ ) { if ( !g_TF_PR->IsConnected( iPlayer ) ) { continue; }
C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayer ) ); if ( !pPlayer || !pPlayer->m_Shared.HasPasstimeBall() ) { continue; } if ( g_TF_PR->IsLocalPlayer( iPlayer ) ) { OnBallGetSelf( iPlayer ); } else { OnBallGetOther( iPlayer ); }
return true; } return false; }
//-----------------------------------------------------------------------------
bool CTFHudPasstimeBallStatus::TryForceBallFree() { Assert( m_bInitialized ); // The HUD was reset during play, or possibly due to joining a game inprogroess,
// so try to get into a BallGet state if possible
if ( !g_pPasstimeLogic ) { return false; }
C_PasstimeBall *pBall = g_pPasstimeLogic->GetBall(); if ( !m_bInitialized || !pBall ) { return false; }
if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetVisible( false ); } return true; }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnBallGet( int getterIndex ) { Assert( m_bInitialized && g_PR );
if ( g_PR->IsLocalPlayer( getterIndex ) ) { OnBallGetSelf( getterIndex ); } else { OnBallGetOther( getterIndex ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::FireGameEvent( IGameEvent *pEvent ) { if ( !m_bInitialized || !g_TF_PR || !g_PR ) { return; }
const char *pszEventName = pEvent->GetName(); if ( FStrEq( pszEventName, PasstimeGameEvents::BallFree::s_eventName ) ) { PasstimeGameEvents::BallFree ev( pEvent ); C_TFPlayer *pOwner = ToTFPlayer( UTIL_PlayerByIndex( ev.ownerIndex ) ); C_TFPlayer *pAttacker = ToTFPlayer( UTIL_PlayerByIndex( ev.attackerIndex ) ); const bool wasMyBall = pOwner == C_TFPlayer::GetLocalPlayer(); if ( wasMyBall ) { OnBallFreeSelf( pOwner, pAttacker ); } else { OnBallFreeOther( pOwner, pAttacker ); } } else if ( FStrEq( pszEventName, PasstimeGameEvents::BallGet::s_eventName ) ) { OnBallGet( PasstimeGameEvents::BallGet( pEvent ).ownerIndex ); } else if ( FStrEq( pszEventName, PasstimeGameEvents::BallStolen::s_eventName ) ) { PasstimeGameEvents::BallStolen ballStolenEvent( pEvent ); OnBallGet( ballStolenEvent.attackerIndex );
auto *pAttacker = ToTFPlayer( UTIL_PlayerByIndex(ballStolenEvent.attackerIndex) ); auto *pVictim = ToTFPlayer( UTIL_PlayerByIndex(ballStolenEvent.victimIndex) ); m_pEventText->EnqueueSteal( pVictim, pAttacker ); } else if ( FStrEq( pszEventName, PasstimeGameEvents::PassCaught::s_eventName ) ) { PasstimeGameEvents::PassCaught passCaughtEvent( pEvent ); OnBallGet( passCaughtEvent.catcherIndex ); auto *pCatcher = ToTFPlayer( UTIL_PlayerByIndex( passCaughtEvent.catcherIndex ) ); auto *pThrower = ToTFPlayer( UTIL_PlayerByIndex( passCaughtEvent.passerIndex ) ); if ( pCatcher && pThrower ) { if ( pCatcher->GetTeamNumber() == pThrower->GetTeamNumber() ) { m_pEventText->EnqueuePass( pThrower, pCatcher ); } else { m_pEventText->EnqueueInterception( pThrower, pCatcher ); } } } else if ( FStrEq( pszEventName, PasstimeGameEvents::Score::s_eventName ) ) { OnBallScore(); PasstimeGameEvents::Score scoreEvent( pEvent ); auto *pScorer = ToTFPlayer( UTIL_PlayerByIndex( scoreEvent.scorerIndex ) ); auto *pAssister = ToTFPlayer( UTIL_PlayerByIndex( scoreEvent.assisterIndex ) ); m_pEventText->EnqueueScore( pScorer, pAssister ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::Reset() { m_bReset = true;
// delay the actual reset for a bit because Reset is called before
// the game entities are settled into their new state (e.g. the ball
// is about to be deleted).
}
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnBallFreeOther( C_TFPlayer *pOwner, C_TFPlayer *pAttacker ) { Assert( m_bInitialized ); if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetVisible( false ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnBallFreeSelf( C_TFPlayer *pOwner, C_TFPlayer *pAttacker ) { Assert( m_bInitialized ); if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetVisible( false ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnBallGetOther( int iPlayer ) { Assert( m_bInitialized ); Assert( g_PR );
wchar_t wszFinalText[128]; wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; wchar_t *pwszFormatString = g_pVGuiLocalize->Find( "#TF_Passtime_CarrierName" ); if ( !pwszFormatString ) { pwszFormatString = L"%s1"; } g_pVGuiLocalize->ConvertANSIToUnicode( ( iPlayer > 0 ) ? g_PR->GetPlayerName( iPlayer ) : "", wszPlayerName, sizeof( wszPlayerName ) ); g_pVGuiLocalize->ConstructString_safe( wszFinalText, pwszFormatString, 1, wszPlayerName );
if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetText( wszFinalText ); m_pProgressBallCarrierName->SetVisible( true ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnBallGetSelf( int iPlayer ) { Assert( m_bInitialized ); Assert( g_PR );
wchar_t wszFinalText[128]; wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH]; wchar_t *pwszFormatString = g_pVGuiLocalize->Find( "#TF_Passtime_CarrierName" ); if ( !pwszFormatString ) { pwszFormatString = L"%s1"; } g_pVGuiLocalize->ConvertANSIToUnicode( ( iPlayer > 0 ) ? g_PR->GetPlayerName( iPlayer ) : "", wszPlayerName, sizeof( wszPlayerName ) ); g_pVGuiLocalize->ConstructString_safe( wszFinalText, pwszFormatString, 1, wszPlayerName );
if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetText( wszFinalText ); m_pProgressBallCarrierName->SetVisible( true ); } }
//-----------------------------------------------------------------------------
void CTFHudPasstimeBallStatus::OnBallScore() { Assert( m_bInitialized ); if ( m_pProgressBallCarrierName ) { m_pProgressBallCarrierName->SetVisible( false ); } }
//-----------------------------------------------------------------------------
// CTFHudPasstime
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CTFHudPasstime::CTFHudPasstime( Panel *pParent ) : CTFHudPasstimePanel( pParent, "HudPasstime" ) , m_pBallStatus( new CTFHudPasstimeBallStatus( this ) ) , m_pTeamScore( new CTFHudTeamScore( this ) ) , m_pBallOffscreenArrow( new CTFHudPasstimeBallOffscreenArrow( this ) ) , m_pPassNotify( new CTFHudPasstimePassNotify( this ) ) { for ( int i = 0; i < MAX_PLAYERS; ++i ) { m_pPlayerArrows[i] = new CTFHudPasstimePlayerOffscreenArrow( this, i ); } }
//-----------------------------------------------------------------------------
CTFHudPasstime::~CTFHudPasstime() { // this is only called when the game quits, so don't bother deleting members
}
//-----------------------------------------------------------------------------
void CTFHudPasstime::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/UI/HudPasstime.res", NULL, NULL, NULL ); vgui::ivgui()->AddTickSignal( GetVPanel() ); OnTick(); }
//-----------------------------------------------------------------------------
void CTFHudPasstime::Reset() { if ( m_pBallStatus ) { m_pBallStatus->Reset(); } }
//-----------------------------------------------------------------------------
void CTFHudPasstime::OnTick() { if ( m_pTeamScore ) { m_pTeamScore->SetVisible( IsVisible() ); } }
|