//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "hudelement.h" #include "iclientmode.h" #include #include #include #include #include #include #include #include #include #include #include #include "c_playerresource.h" #include "teamplay_round_timer.h" #include "utlvector.h" #include "entity_capture_flag.h" #include "c_tf_player.h" #include "c_team.h" #include "c_tf_team.h" #include "c_team_objectiveresource.h" #include "tf_hud_objectivestatus.h" #include "tf_spectatorgui.h" #include "teamplayroundbased_gamerules.h" #include "tf_gamerules.h" #include "tf_hud_freezepanel.h" #include "c_func_capture_zone.h" #include "clientmode_shared.h" #include "tf_hud_mediccallers.h" #include "view.h" #include "prediction.h" #include "tf_logic_robot_destruction.h" using namespace vgui; DECLARE_BUILD_FACTORY( CTFArrowPanel ); DECLARE_BUILD_FACTORY( CTFFlagStatus ); DECLARE_HUDELEMENT( CTFFlagCalloutPanel ); ConVar tf_rd_flag_ui_mode( "tf_rd_flag_ui_mode", "3", FCVAR_DEVELOPMENTONLY, "When flags are stolen and not visible: 0 = Show outlines (glows), 1 = Show most valuable enemy flag (icons), 2 = Show all enemy flags (icons), 3 = Show all flags (icons)." ); extern ConVar tf_flag_caps_per_round; void AddSubKeyNamed( KeyValues *pKeys, const char *pszName ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFArrowPanel::CTFArrowPanel( Panel *parent, const char *name ) : vgui::Panel( parent, name ) { m_RedMaterial.Init( "hud/objectives_flagpanel_compass_red", TEXTURE_GROUP_VGUI ); m_BlueMaterial.Init( "hud/objectives_flagpanel_compass_blue", TEXTURE_GROUP_VGUI ); m_NeutralMaterial.Init( "hud/objectives_flagpanel_compass_grey", TEXTURE_GROUP_VGUI ); m_NeutralRedMaterial.Init( "hud/objectives_flagpanel_compass_grey_with_red", TEXTURE_GROUP_VGUI ); m_RedMaterialNoArrow.Init( "hud/objectives_flagpanel_compass_red_noArrow", TEXTURE_GROUP_VGUI ); m_BlueMaterialNoArrow.Init( "hud/objectives_flagpanel_compass_blue_noArrow", TEXTURE_GROUP_VGUI ); m_pMaterial = m_NeutralMaterial; m_bUseRed = false; m_flNextColorSwitch = 0.0f; ivgui()->AddTickSignal( GetVPanel(), 100 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFArrowPanel::OnTick( void ) { if ( !m_hEntity.Get() ) return; C_BaseEntity *pEnt = m_hEntity.Get(); m_pMaterial = m_NeutralMaterial; C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) { if ( m_bUseRed ) { m_pMaterial = m_NeutralRedMaterial; } else { m_pMaterial = m_NeutralMaterial; } if ( pEnt && TFGameRules()->GetMannVsMachineAlarmStatus() == true ) { CCaptureFlag *pFlag = dynamic_cast( pEnt ); if ( pFlag && pFlag->IsStolen() ) { if ( m_flNextColorSwitch < gpGlobals->curtime ) { m_flNextColorSwitch = gpGlobals->curtime + 0.2f; m_bUseRed = !m_bUseRed; } } else { m_bUseRed = false; } } else { m_bUseRed = false; } } else { // figure out what material we need to use if ( pEnt->GetTeamNumber() == TF_TEAM_RED ) { m_pMaterial = m_RedMaterial; if ( pLocalPlayer && ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) ) { // is our target a player? C_BaseEntity *pTargetEnt = pLocalPlayer->GetObserverTarget(); if ( pTargetEnt && pTargetEnt->IsPlayer() ) { // does our target have the flag and are they carrying the flag we're currently drawing? C_TFPlayer *pTarget = static_cast< C_TFPlayer* >( pTargetEnt ); if ( pTarget->HasTheFlag() && ( pTarget->GetItem() == pEnt ) ) { m_pMaterial = m_RedMaterialNoArrow; } } } } else if ( pEnt->GetTeamNumber() == TF_TEAM_BLUE ) { m_pMaterial = m_BlueMaterial; if ( pLocalPlayer && ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) ) { // is our target a player? C_BaseEntity *pTargetEnt = pLocalPlayer->GetObserverTarget(); if ( pTargetEnt && pTargetEnt->IsPlayer() ) { // does our target have the flag and are they carrying the flag we're currently drawing? C_TFPlayer *pTarget = static_cast< C_TFPlayer* >( pTargetEnt ); if ( pTarget->HasTheFlag() && ( pTarget->GetItem() == pEnt ) ) { m_pMaterial = m_BlueMaterialNoArrow; } } } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CTFArrowPanel::GetAngleRotation( void ) { float flRetVal = 0.0f; C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() ); C_BaseEntity *pEnt = m_hEntity.Get(); if ( pPlayer && pEnt ) { QAngle vangles; Vector eyeOrigin; float zNear, zFar, fov; pPlayer->CalcView( eyeOrigin, vangles, zNear, zFar, fov ); Vector vecFlag = pEnt->WorldSpaceCenter() - eyeOrigin; vecFlag.z = 0; vecFlag.NormalizeInPlace(); Vector forward, right, up; AngleVectors( vangles, &forward, &right, &up ); forward.z = 0; right.z = 0; forward.NormalizeInPlace(); right.NormalizeInPlace(); float dot = DotProduct( vecFlag, forward ); float angleBetween = acos( dot ); dot = DotProduct( vecFlag, right ); if ( dot < 0.0f ) { angleBetween *= -1; } flRetVal = RAD2DEG( angleBetween ); } return flRetVal; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFArrowPanel::Paint() { int x = 0; int y = 0; ipanel()->GetAbsPos( GetVPanel(), x, y ); int nWidth = GetWide(); int nHeight = GetTall(); CMatRenderContextPtr pRenderContext( materials ); pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PushMatrix(); VMatrix panelRotation; panelRotation.Identity(); MatrixBuildRotationAboutAxis( panelRotation, Vector( 0, 0, 1 ), GetAngleRotation() ); // MatrixRotate( panelRotation, Vector( 1, 0, 0 ), 5 ); panelRotation.SetTranslation( Vector( x + nWidth/2, y + nHeight/2, 0 ) ); pRenderContext->LoadMatrix( panelRotation ); IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_pMaterial ); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.TexCoord2f( 0, 0, 0 ); meshBuilder.Position3f( -nWidth/2, -nHeight/2, 0 ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.TexCoord2f( 0, 1, 0 ); meshBuilder.Position3f( nWidth/2, -nHeight/2, 0 ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.TexCoord2f( 0, 1, 1 ); meshBuilder.Position3f( nWidth/2, nHeight/2, 0 ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.TexCoord2f( 0, 0, 1 ); meshBuilder.Position3f( -nWidth/2, nHeight/2, 0 ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); pRenderContext->PopMatrix(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFArrowPanel::IsVisible( void ) { if( IsTakingAFreezecamScreenshot() ) return false; return BaseClass::IsVisible(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFFlagStatus::CTFFlagStatus( Panel *parent, const char *name ) : EditablePanel( parent, name ) { m_pArrow = new CTFArrowPanel( this, "Arrow" ); m_pStatusIcon = new CTFImagePanel( this, "StatusIcon" ); m_pBriefcase = new CTFImagePanel( this, "Briefcase" ); m_hEntity = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagStatus::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); KeyValues *pConditions = NULL; if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) { pConditions = new KeyValues( "conditions" ); if ( pConditions ) { AddSubKeyNamed( pConditions, "if_mvm" ); } } // load control settings... LoadControlSettings( "resource/UI/FlagStatus.res", NULL, NULL, pConditions ); if ( pConditions ) { pConditions->deleteThis(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFFlagStatus::IsVisible( void ) { C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM ) return false; return BaseClass::IsVisible(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagStatus::UpdateStatus( void ) { if ( m_hEntity.Get() ) { CCaptureFlag *pFlag = dynamic_cast( m_hEntity.Get() ); if ( pFlag ) { const char *pszImage = "../hud/objectives_flagpanel_ico_flag_home"; const char *pszBombImage = "../hud/bomb_dropped"; if ( pFlag->IsDropped() ) { pszImage = "../hud/objectives_flagpanel_ico_flag_dropped"; } else if ( pFlag->IsStolen() ) { pszImage = "../hud/objectives_flagpanel_ico_flag_moving"; pszBombImage = "../hud/bomb_carried"; } if ( m_pStatusIcon ) { m_pStatusIcon->SetImage( pszImage ); } if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && m_pBriefcase ) { m_pBriefcase->SetImage( pszBombImage ); } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFHudFlagObjectives::CTFHudFlagObjectives( Panel *parent, const char *name ) : EditablePanel( parent, name ) { m_pCarriedImage = NULL; m_pPlayingTo = NULL; m_bFlagAnimationPlayed = false; m_bCarryingFlag = false; m_pSpecCarriedImage = NULL; m_pPoisonImage = NULL; m_pPoisonTimeLabel = NULL; m_pRedFlag = new CTFFlagStatus( this, "RedFlag" ); m_pBlueFlag = new CTFFlagStatus( this, "BlueFlag" ); m_bPlayingHybrid_CTF_CP = false; m_bPlayingSpecialDeliveryMode = false; vgui::ivgui()->AddTickSignal( GetVPanel(), 250 ); ListenForGameEvent( "flagstatus_update" ); m_nNumValidFlags = -1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFHudFlagObjectives::IsVisible( void ) { if( IsTakingAFreezecamScreenshot() ) return false; return BaseClass::IsVisible(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); KeyValues *pConditions = NULL; bool bHybrid = TFGameRules() && TFGameRules()->IsPlayingHybrid_CTF_CP(); bool bMVM = TFGameRules() && TFGameRules()->IsMannVsMachineMode(); bool bSpecialDeliveryMode = TFGameRules() && TFGameRules()->IsPlayingSpecialDeliveryMode(); int nNumFlags = 0; if ( m_pRedFlag && m_pRedFlag->GetEntity() != NULL ) { nNumFlags++; } if ( m_pBlueFlag && m_pBlueFlag->GetEntity() != NULL ) { nNumFlags++; } if ( nNumFlags == 2 && m_pRedFlag->GetEntity() == m_pBlueFlag->GetEntity() ) { // They're both pointing at the same flag! There's really only 1 nNumFlags = 1; } if ( nNumFlags == 0 ) { pConditions = new KeyValues( "conditions" ); if ( pConditions ) { AddSubKeyNamed( pConditions, "if_no_flags" ); } if ( bSpecialDeliveryMode ) { AddSubKeyNamed( pConditions, "if_specialdelivery" ); } } else { if ( bHybrid || ( nNumFlags == 1 ) || bMVM || bSpecialDeliveryMode ) { pConditions = new KeyValues( "conditions" ); if ( pConditions ) { if ( bHybrid ) { AddSubKeyNamed( pConditions, "if_hybrid" ); } if ( nNumFlags == 1 || bSpecialDeliveryMode ) { AddSubKeyNamed( pConditions, "if_hybrid_single" ); } else if ( nNumFlags == 2 ) { AddSubKeyNamed( pConditions, "if_hybrid_double" ); } if ( bMVM ) { AddSubKeyNamed( pConditions, "if_mvm" ); } if ( bSpecialDeliveryMode ) { AddSubKeyNamed( pConditions, "if_specialdelivery" ); } } } } // load control settings... LoadControlSettings( "resource/UI/HudObjectiveFlagPanel.res", NULL, NULL, pConditions ); m_pCarriedImage = dynamic_cast( FindChildByName( "CarriedImage" ) ); m_pPlayingTo = dynamic_cast( FindChildByName( "PlayingTo" ) ); m_pPlayingToBG = FindChildByName( "PlayingToBG" ); m_pCapturePoint = dynamic_cast( FindChildByName( "CaptureFlag" ) ); m_pSpecCarriedImage = dynamic_cast( FindChildByName( "SpecCarriedImage" ) ); m_pPoisonImage = dynamic_cast( FindChildByName( "PoisonIcon" ) ); m_pPoisonTimeLabel = dynamic_cast( FindChildByName( "PoisonTimeLabel" ) ); // outline is always on, so we need to init the alpha to 0 vgui::Panel *pOutline = FindChildByName( "OutlineImage" ); if ( pOutline ) { pOutline->SetAlpha( 0 ); } if ( pConditions ) { pConditions->deleteThis(); } UpdateStatus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::Reset() { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "FlagOutlineHide" ); UpdateStatus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::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 ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::OnTick() { int nNumValidFlags = 0; // check that our blue panel still points at a valid flag if ( m_pBlueFlag && m_pBlueFlag->GetEntity() ) { CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag* >( m_pBlueFlag->GetEntity() ); if ( !pFlag || pFlag->IsDisabled() ) { m_pBlueFlag->SetEntity( NULL ); } } // check that our red panel still points at a valid flag if ( m_pRedFlag && m_pRedFlag->GetEntity() ) { CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag* >( m_pRedFlag->GetEntity() ); if ( !pFlag || pFlag->IsDisabled() ) { m_pRedFlag->SetEntity( NULL ); } } // iterate through the flags to set their position in our HUD for ( int i = 0; i( ICaptureFlagAutoList::AutoList()[i] ); if ( !pFlag->IsDisabled() || pFlag->IsVisibleWhenDisabled() ) { if ( pFlag->GetTeamNumber() == TF_TEAM_RED ) { if ( m_pRedFlag ) { bool bNeedsUpdate = m_pRedFlag->GetEntity() != pFlag; m_pRedFlag->SetEntity( pFlag ); if ( bNeedsUpdate ) { UpdateStatus(); } } } else if ( pFlag->GetTeamNumber() == TF_TEAM_BLUE ) { if ( m_pBlueFlag ) { bool bNeedsUpdate = m_pBlueFlag->GetEntity() != pFlag; m_pBlueFlag->SetEntity( pFlag ); if ( bNeedsUpdate ) { UpdateStatus(); } } } else if ( pFlag->GetTeamNumber() == TEAM_UNASSIGNED ) { if ( m_pBlueFlag && !m_pBlueFlag->GetEntity() ) { m_pBlueFlag->SetEntity( pFlag ); if ( !m_pBlueFlag->IsVisible() ) { m_pBlueFlag->SetVisible( true ); } if ( m_pRedFlag && m_pRedFlag->IsVisible() ) { m_pRedFlag->SetVisible( false ); } } else if ( m_pRedFlag && !m_pRedFlag->GetEntity() ) { // make sure both panels aren't pointing at the same entity if ( !m_pBlueFlag || ( pFlag != m_pBlueFlag->GetEntity() ) ) { m_pRedFlag->SetEntity( pFlag ); if ( !m_pRedFlag->IsVisible() ) { m_pRedFlag->SetVisible( true ); } } } } nNumValidFlags++; } // VGUI callout panels if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() && CTFRobotDestructionLogic::GetRobotDestructionLogic()->GetType() == CTFRobotDestructionLogic::TYPE_ROBOT_DESTRUCTION ) { if ( tf_rd_flag_ui_mode.GetInt() && !pFlag->IsDisabled() && !pFlag->IsHome() ) { Vector vecLocation = pFlag->GetAbsOrigin() + Vector( 0.f, 0.f, 18.f ); CTFFlagCalloutPanel::AddFlagCalloutIfNotFound( pFlag, FLT_MAX, vecLocation ); } } } if ( m_nNumValidFlags != nNumValidFlags ) { m_nNumValidFlags = nNumValidFlags; InvalidateLayout( false, true ); } // are we playing captures for rounds? if ( !TFGameRules() || ( !TFGameRules()->IsPlayingHybrid_CTF_CP() && !TFGameRules()->IsPlayingSpecialDeliveryMode() && !TFGameRules()->IsMannVsMachineMode() ) ) { if ( tf_flag_caps_per_round.GetInt() > 0 ) { C_TFTeam *pTeam = GetGlobalTFTeam( TF_TEAM_BLUE ); if ( pTeam ) { SetDialogVariable( "bluescore", pTeam->GetFlagCaptures() ); } pTeam = GetGlobalTFTeam( TF_TEAM_RED ); if ( pTeam ) { SetDialogVariable( "redscore", pTeam->GetFlagCaptures() ); } SetPlayingToLabelVisible( true ); SetDialogVariable( "rounds", tf_flag_caps_per_round.GetInt() ); } else // we're just playing straight score { C_TFTeam *pTeam = GetGlobalTFTeam( TF_TEAM_BLUE ); if ( pTeam ) { SetDialogVariable( "bluescore", pTeam->Get_Score() ); } pTeam = GetGlobalTFTeam( TF_TEAM_RED ); if ( pTeam ) { SetDialogVariable( "redscore", pTeam->Get_Score() ); } SetPlayingToLabelVisible( false ); } } // check the local player to see if they're spectating, OBS_MODE_IN_EYE, and the target entity is carrying the flag bool bSpecCarriedImage = false; CCaptureFlag *pPoisonFlag = NULL; C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pPlayer ) { if ( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) { // does our target have the flag? C_BaseEntity *pEnt = pPlayer->GetObserverTarget(); if ( pEnt && pEnt->IsPlayer() ) { C_TFPlayer *pTarget = static_cast< C_TFPlayer* >( pEnt ); if ( pTarget->HasTheFlag() ) { bSpecCarriedImage = true; if ( pTarget->GetTeamNumber() == TF_TEAM_RED ) { if ( m_pSpecCarriedImage ) { m_pSpecCarriedImage->SetImage( "../hud/objectives_flagpanel_carried_blue" ); } } else { if ( m_pSpecCarriedImage ) { m_pSpecCarriedImage->SetImage( "../hud/objectives_flagpanel_carried_red" ); } } } } } if ( pPlayer->HasTheFlag() && TFGameRules()->IsPowerupMode() ) { CCaptureFlag *pFlag = dynamic_cast( pPlayer->GetItem() ); if ( pFlag ) { pPoisonFlag = pFlag; } } } if ( m_pSpecCarriedImage ) { m_pSpecCarriedImage->SetVisible( bSpecCarriedImage ); } if ( m_pPoisonImage ) { m_pPoisonImage->SetVisible( pPoisonFlag && pPoisonFlag->IsPoisonous() ); } if ( m_pPoisonTimeLabel ) { m_pPoisonTimeLabel->SetVisible( pPoisonFlag && pPoisonFlag->GetPoisonTime() > 0.f && !pPoisonFlag->IsPoisonous() ); if ( m_pPoisonTimeLabel->IsVisible() ) { int nNumSecondsToPoisonous = pPoisonFlag->GetPoisonTime() - gpGlobals->curtime; m_pPoisonTimeLabel->SetText( CFmtStr( "%d", nNumSecondsToPoisonous ) ); } } if ( TFGameRules() ) { if ( m_bPlayingHybrid_CTF_CP != TFGameRules()->IsPlayingHybrid_CTF_CP() ) { m_bPlayingHybrid_CTF_CP = TFGameRules()->IsPlayingHybrid_CTF_CP(); InvalidateLayout( false, true ); } if ( m_bPlayingSpecialDeliveryMode != TFGameRules()->IsPlayingSpecialDeliveryMode() ) { m_bPlayingSpecialDeliveryMode = TFGameRules()->IsPlayingSpecialDeliveryMode(); InvalidateLayout( false, true ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::SetCarriedImage( const char *pchIcon ) { if ( m_pCarriedImage ) { m_pCarriedImage->SetImage( pchIcon ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::UpdateStatus( C_BasePlayer *pNewOwner /*= NULL*/, C_BaseEntity *pFlagEntity /*= NULL*/ ) { C_TFPlayer *pLocalPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() ); // 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 ) { m_bCarryingFlag = true; // 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_pBlueFlag && m_pBlueFlag->IsVisible() ) { m_pBlueFlag->SetVisible( false ); } if ( m_pRedFlag && m_pRedFlag->IsVisible() ) { m_pRedFlag->SetVisible( false ); } 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 ) ); } char szImage[ MAX_PATH ]; pPlayerFlag->GetHudIcon( nTeam, szImage, sizeof( szImage ) ); SetCarriedImage( szImage ); m_pCarriedImage->SetVisible( true ); } if ( !m_bFlagAnimationPlayed ) { m_bFlagAnimationPlayed = true; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "FlagOutline" ); } if ( m_pCapturePoint && !m_pCapturePoint->IsVisible() ) { m_pCapturePoint->SetVisible( true ); } if ( pLocalPlayer && m_pCapturePoint ) { // go through all the capture zones and find ours for ( int i = 0; i( ICaptureZoneAutoList::AutoList()[i] ); if ( !pCaptureZone->IsDormant() ) { if ( pCaptureZone->GetTeamNumber() == pLocalPlayer->GetTeamNumber() && !pCaptureZone->IsDisabled() ) { m_pCapturePoint->SetEntity( pCaptureZone ); } } } } } else { // were we carrying the flag? if ( m_bCarryingFlag ) { m_bCarryingFlag = false; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "FlagOutline" ); } m_bFlagAnimationPlayed = false; if ( m_pCarriedImage && m_pCarriedImage->IsVisible() ) { m_pCarriedImage->SetVisible( false ); } if ( m_pCapturePoint && m_pCapturePoint->IsVisible() ) { m_pCapturePoint->SetVisible( false ); } if ( m_pBlueFlag ) { if ( m_pBlueFlag->GetEntity() != NULL ) { if ( !m_pBlueFlag->IsVisible() ) { m_pBlueFlag->SetVisible( true ); } m_pBlueFlag->UpdateStatus(); } else { if ( m_pBlueFlag->IsVisible() ) { m_pBlueFlag->SetVisible( false ); } } } if ( m_pRedFlag ) { if ( m_pRedFlag->GetEntity() != NULL ) { if ( !m_pRedFlag->IsVisible() ) { m_pRedFlag->SetVisible( true ); } m_pRedFlag->UpdateStatus(); } else { if ( m_pRedFlag->IsVisible() ) { m_pRedFlag->SetVisible( false ); } } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudFlagObjectives::FireGameEvent( IGameEvent *event ) { const char *eventName = event->GetName(); if ( FStrEq( eventName, "flagstatus_update" ) ) { int nVictimID = event->GetInt( "userid" ); C_BasePlayer *pNewOwner = USERID2PLAYER( nVictimID ); int nFlagEntIndex = event->GetInt( "entindex" ); C_BaseEntity *pFlagEntity = ClientEntityList().GetEnt( nFlagEntIndex ); UpdateStatus( pNewOwner, pFlagEntity ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- #define FLAG_CALLER_WIDE ( XRES( 30 ) ) #define FLAG_CALLER_TALL ( YRES( 30 ) ) #define FLAG_CALLER_ARROW_WIDE ( XRES( 8 ) ) #define FLAG_CALLER_ARROW_TALL ( YRES( 10 ) ) #define FLAG_CALLER_DISPLAY_ENEMY_ONE 1 #define FLAG_CALLER_DISPLAY_ENEMY_ALL 2 #define FLAG_CALLER_DISPLAY_ALL 3 CUtlVector< CTFFlagCalloutPanel* > CTFFlagCalloutPanel::m_FlagCalloutPanels; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFFlagCalloutPanel::CTFFlagCalloutPanel( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, pElementName ) { m_FlagCalloutPanels.AddToTail( this ); SetParent( g_pClientMode->GetViewport() ); RegisterForRenderGroup( "mid" ); RegisterForRenderGroup( "commentary" ); SetHiddenBits( HIDEHUD_MISCSTATUS ); // SetBounds( 0, 0, FLAG_CALLER_WIDE, FLAG_CALLER_TALL ); vgui::ivgui()->AddTickSignal( GetVPanel() ); m_pFlagCalloutPanel = new CTFImagePanel( this, "FlagCalloutPanel" ); m_pFlagValueLabel = new Label( this, "FlagValueLabel", "" ); m_pFlagStatusIcon = new CTFImagePanel( this, "StatusIcon" ); m_flRemoveTime = 1.f; m_flFirstDisplayTime = 1.f; m_pArrowMaterial = NULL; m_iDrawArrow = DRAW_ARROW_UP; m_bFlagVisible = false; // On screen, line-of-sight m_flPrevScale = 0.f; m_nPanelWideOrig = 0; m_nPanelTallOrig = 0; m_nLabelWideOrig = 0; m_nLabelTallOrig = 0; m_nIconWideOrig = 0; m_nIconTallOrig = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFFlagCalloutPanel::~CTFFlagCalloutPanel( void ) { bool bFound = false; FOR_EACH_VEC_BACK( m_FlagCalloutPanels, i ) { if ( m_FlagCalloutPanels[i] == this ) { m_FlagCalloutPanels.Remove( i ); bFound = true; break; } } // We should have found the panel and returned earlier Assert( bFound ); if ( m_pArrowMaterial ) { m_pArrowMaterial->DecrementReferenceCount(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/UI/FlagCalloutPanel.res" ); if ( m_pArrowMaterial ) { m_pArrowMaterial->DecrementReferenceCount(); } m_pArrowMaterial = materials->FindMaterial( "HUD/medic_arrow", TEXTURE_GROUP_VGUI ); m_pArrowMaterial->IncrementReferenceCount(); if ( !m_pFlagCalloutPanel ) return; if ( !m_pFlagValueLabel ) return; if ( !m_pFlagStatusIcon ) return; m_pFlagCalloutPanel->GetSize( m_nPanelWideOrig, m_nPanelTallOrig ); m_pFlagValueLabel->GetSize( m_nLabelWideOrig, m_nLabelTallOrig ); m_pFlagStatusIcon->GetSize( m_nIconWideOrig, m_nIconTallOrig ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::PerformLayout( void ) { BaseClass::PerformLayout(); // SetSize( FLAG_CALLER_WIDE, FLAG_CALLER_TALL ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::GetCalloutPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ) { // Player Data QAngle playerAngles = MainViewAngles(); Vector forward, right, up( 0.f, 0.f, 1.f ); AngleVectors( playerAngles, &forward, NULL, NULL ); forward.z = 0.f; VectorNormalize( forward ); CrossProduct( up, forward, right ); float front = DotProduct( vecDelta, forward ); float side = DotProduct( vecDelta, right ); *xpos = flRadius * -side; *ypos = flRadius * -front; // Get the rotation (yaw) *flRotation = atan2( *xpos, *ypos ) + M_PI; *flRotation *= 180.f / M_PI; float yawRadians = -( *flRotation ) * M_PI / 180.f; float ca = cos( yawRadians ); float sa = sin( yawRadians ); // Rotate it around the circle *xpos = (int)( ( ScreenWidth() / 2 ) + ( flRadius * sa ) ); *ypos = (int)( ( ScreenHeight() / 2 ) - ( flRadius * ca ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::OnTick( void ) { int nDisplayMode = tf_rd_flag_ui_mode.GetInt(); // Panels self-manage their existence and visibility C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalTFPlayer || !m_hFlag || m_hFlag->IsHome() || m_hFlag->IsDisabled() || !nDisplayMode ) { MarkForDeletion(); return; } bool bShouldDraw = ShouldShowFlagIconToLocalPlayer(); // Only show the most valuable enemy flag in this mode if ( nDisplayMode == FLAG_CALLER_DISPLAY_ENEMY_ONE ) { int nHighestValue = 0; CCaptureFlag *pMostValuableFlag = NULL; for ( int i = 0; i < ICaptureFlagAutoList::AutoList().Count(); ++i ) { CCaptureFlag *pFlag = static_cast< CCaptureFlag* >( ICaptureFlagAutoList::AutoList()[i] ); if ( pFlag && pFlag->GetPointValue() > nHighestValue ) { if ( pFlag->IsDisabled() ) continue; if ( pFlag->IsHome() ) continue; if ( pFlag->InSameTeam( pLocalTFPlayer ) ) continue; if ( pFlag->GetPointValue() > nHighestValue ) { nHighestValue = pFlag->GetPointValue(); pMostValuableFlag = pFlag; } } } // If we're not it if ( pMostValuableFlag != m_hFlag ) bShouldDraw = false; } if ( IsVisible() != bShouldDraw ) { if ( !IsVisible() ) { m_flFirstDisplayTime = gpGlobals->curtime; m_flPrevScale = 0.f; } SetVisible( bShouldDraw ); } if ( IsEnabled() != bShouldDraw ) { SetEnabled( bShouldDraw ); } if ( !bShouldDraw ) return; bool bCarried = ( !m_hFlag->IsDropped() && m_hFlag->GetPrevOwner() ); if ( bCarried && !prediction->IsFirstTimePredicted() ) return; // Adjust scale based on distance Vector vecDistance = m_hFlag->GetAbsOrigin() - pLocalTFPlayer->GetAbsOrigin(); ScaleAndPositionCallout( RemapValClamped( vecDistance.LengthSqr(), ( 1000.f * 1000.f ), ( 4000.f * 4000.f ), 1.f, 0.6f ) ); // Reposition the callout based on our target's position int iX, iY; Vector vecTarget = ( bCarried ) ? m_hFlag->GetPrevOwner()->GetAbsOrigin() : m_hFlag->GetAbsOrigin(); Vector vecDelta = vecTarget - MainViewOrigin(); bool bOnScreen = GetVectorInHudSpace( vecTarget, iX, iY ); int nHalfWidth = GetWide() / 2; if ( !bOnScreen || iX < nHalfWidth || iX > ScreenWidth() - nHalfWidth ) { // Only show side panel for a short period of time in this mode if ( TFGameRules() && TFGameRules()->IsPlayingRobotDestructionMode() && gpGlobals->curtime > m_flFirstDisplayTime + 5.f ) { m_iDrawArrow = DRAW_ARROW_UP; SetAlpha( 0 ); } else { // It's off the screen. Position the callout. VectorNormalize( vecDelta ); float xpos, ypos; float flRotation; float flRadius = YRES( 100 ); GetCalloutPosition( vecDelta, flRadius, &xpos, &ypos, &flRotation ); iX = xpos; iY = ypos; Vector vCenter = m_hFlag->WorldSpaceCenter( ); if ( MainViewRight().Dot( vCenter - MainViewOrigin() ) > 0 ) { m_iDrawArrow = DRAW_ARROW_RIGHT; } else { m_iDrawArrow = DRAW_ARROW_LEFT; } // Move the icon there SetPos( iX - nHalfWidth, iY - ( GetTall() / 2 ) ); SetAlpha( 128 ); } } else { // On screen // If our target isn't visible, we draw transparently trace_t tr; UTIL_TraceLine( vecTarget, MainViewOrigin(), MASK_VISIBLE, NULL, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction >= 1.f ) { m_bFlagVisible = true; SetAlpha( 0 ); return; } else { m_iDrawArrow = DRAW_ARROW_UP; SetAlpha( 128 ); SetPos( iX - nHalfWidth, iY - ( GetTall() / 2 ) ); } } m_bFlagVisible = false; if ( !m_pFlagCalloutPanel ) return; if ( !m_pFlagValueLabel ) return; if ( !m_pFlagStatusIcon ) return; m_pFlagCalloutPanel->SetImage( m_hFlag->GetTeamNumber() == TF_TEAM_BLUE ? "../hud/obj_briefcase_blue" : "../hud/obj_briefcase_red" ); m_pFlagValueLabel->SetText( CFmtStr( "%i", m_hFlag->GetPointValue() ) ); const char *pszImage = "../hud/objectives_flagpanel_ico_flag_home"; if ( m_hFlag->IsDropped() ) { pszImage = "../hud/objectives_flagpanel_ico_flag_dropped"; } else if ( m_hFlag->IsStolen() ) { pszImage = "../hud/objectives_flagpanel_ico_flag_moving"; } m_pFlagStatusIcon->SetImage( pszImage ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::PaintBackground( void ) { C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalTFPlayer ) return; if ( !m_hFlag ) { SetAlpha( 0 ); return; } BaseClass::PaintBackground(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::Paint( void ) { // Don't draw side panels if our target is visible. The particle effect will be doing it for us. if ( m_bFlagVisible ) return; BaseClass::Paint(); if ( m_iDrawArrow == DRAW_ARROW_UP ) return; float uA, uB, yA, yB; int x, y; GetPos( x, y ); if ( m_iDrawArrow == DRAW_ARROW_LEFT ) { uA = 1.f; uB = 0.f; yA = 0.f; yB = 1.f; x -= FLAG_CALLER_ARROW_WIDE; } else { uA = 0.f; uB = 1.f; yA = 0.f; yB = 1.f; x += m_pFlagCalloutPanel->GetWide(); } int iyindent = ( GetTall() - FLAG_CALLER_ARROW_TALL ) * 0.5f; y += iyindent; CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( m_pArrowMaterial ); IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( x, y, 0.f ); meshBuilder.TexCoord2f( 0, uA, yA ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x + FLAG_CALLER_ARROW_WIDE, y, 0.f ); meshBuilder.TexCoord2f( 0, uB, yA ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x + FLAG_CALLER_ARROW_WIDE, y + FLAG_CALLER_ARROW_TALL, 0.f ); meshBuilder.TexCoord2f( 0, uB, yB ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x, y + FLAG_CALLER_ARROW_TALL, 0.f ); meshBuilder.TexCoord2f( 0, uA, yB ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::SetFlag( CCaptureFlag *pFlag, float flDuration, Vector &vecOffset ) { m_hFlag = pFlag; m_flRemoveTime = gpGlobals->curtime + flDuration; m_vecOffset = vecOffset; m_flFirstDisplayTime = gpGlobals->curtime; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFFlagCalloutPanel *CTFFlagCalloutPanel::AddFlagCalloutIfNotFound( CCaptureFlag *pFlag, float flDuration, Vector &vecLocation ) { // How this system works: // CTFHudFlagObjectives::OnTick() will attempt to create one panel per-flag that is stolen. // CTFFlagCalloutPanel::OnTick() tries to manage whether or not the panel is visible, based on the UI mode. // See if we have a panel for this flag already FOR_EACH_VEC_BACK( m_FlagCalloutPanels, i ) { if ( m_FlagCalloutPanels[i]->m_hFlag == pFlag ) { return NULL; } } CTFFlagCalloutPanel *pCallout = new CTFFlagCalloutPanel( "FlagCalloutHUD" ); if ( pCallout ) { pCallout->SetFlag( pFlag, flDuration, vecLocation ); } return pCallout; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFFlagCalloutPanel::ShouldShowFlagIconToLocalPlayer( void ) { C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalTFPlayer ) return false; int nDisplayMode = tf_rd_flag_ui_mode.GetInt(); // In "show all" mode, don't show flags on the local player's team that are being carried if ( m_hFlag->IsStolen() && m_hFlag->InSameTeam( pLocalTFPlayer ) && nDisplayMode == FLAG_CALLER_DISPLAY_ALL ) return false; // In all other modes, don't show flags on the local player's team if ( m_hFlag->InSameTeam( pLocalTFPlayer ) && nDisplayMode < FLAG_CALLER_DISPLAY_ALL ) return false; // Don't show the player running this flag if ( m_hFlag->IsStolen() && pLocalTFPlayer == m_hFlag->GetPrevOwner() ) return false; return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlagCalloutPanel::ScaleAndPositionCallout( float flScale /*= 1.f*/ ) { if ( flScale == m_flPrevScale ) return; SetSize( ( FLAG_CALLER_WIDE * flScale ), ( FLAG_CALLER_TALL * flScale ) ); if ( !m_pFlagCalloutPanel ) return; if ( !m_pFlagValueLabel ) return; if ( !m_pFlagStatusIcon ) return; // Briefcase - top-left m_pFlagCalloutPanel->SetSize( ( m_nPanelWideOrig * flScale ), ( m_nPanelTallOrig * flScale ) ); m_pFlagCalloutPanel->SetPos( 0, 0 ); // Label - centered m_pFlagValueLabel->SetSize( ( m_nLabelWideOrig * flScale ), ( m_nLabelTallOrig * flScale ) ); m_pFlagValueLabel->SetPos( ( m_pFlagCalloutPanel->GetWide() - m_pFlagValueLabel->GetWide() ) * 0.5f, ( m_pFlagCalloutPanel->GetWide() - m_pFlagValueLabel->GetTall() ) * 0.65f ); // Icon - lower-right m_pFlagStatusIcon->SetSize( ( m_nIconWideOrig * flScale ), ( m_nIconTallOrig * flScale ) ); m_pFlagStatusIcon->SetPos( ( m_pFlagCalloutPanel->GetWide() - m_pFlagStatusIcon->GetWide() ) * 1.05f, ( m_pFlagCalloutPanel->GetWide() - m_pFlagStatusIcon->GetTall() ) * 0.85f ); m_flPrevScale = flScale; }