//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include #include #include #include #include #include #include #include #include "materialsystem/imaterialvar.h" #include "IGameUIFuncs.h" // for key bindings #include "tf_controls.h" #include "tf_imagepanel.h" #include "c_team_objectiveresource.h" #include "c_tf_objective_resource.h" #include "c_tf_player.h" #include "tf_shareddefs.h" #include "tf_roundinfo.h" #include "vgui/ISurface.h" #include #include #include "engine/IEngineSound.h" using namespace vgui; const char *GetMapDisplayName( const char *mapName ); class RoundInfoOverlay : public vgui::EditablePanel { public: DECLARE_CLASS_SIMPLE( RoundInfoOverlay, vgui::EditablePanel ); RoundInfoOverlay( Panel *parent, const char *panelName ) : EditablePanel( parent, panelName ) { m_iMode = 0; m_flModeChangeTime = -1; vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); m_iBlueTeamTexture = vgui::surface()->CreateNewTextureID(); vgui::surface()->DrawSetTextureFile(m_iBlueTeamTexture, "overviews/blueteam", true, false); m_iRedTeamTexture = vgui::surface()->CreateNewTextureID(); vgui::surface()->DrawSetTextureFile(m_iRedTeamTexture, "overviews/redteam", true, false); m_iCapArrowTexture = vgui::surface()->CreateNewTextureID(); vgui::surface()->DrawSetTextureFile(m_iCapArrowTexture, "overviews/caparrows", true, false); m_iFoundPoints = 0; m_iNextRoundPoints[0] = -1; m_iNextRoundPoints[1] = -1; m_iLastCappedPoint = -1; } virtual ~RoundInfoOverlay( void ) { if ( vgui::surface() ) { if ( m_iBlueTeamTexture != -1 ) { vgui::surface()->DestroyTextureID( m_iBlueTeamTexture ); m_iBlueTeamTexture = -1; } if ( m_iRedTeamTexture != -1 ) { vgui::surface()->DestroyTextureID( m_iRedTeamTexture ); m_iRedTeamTexture = -1; } if ( m_iCapArrowTexture != -1 ) { vgui::surface()->DestroyTextureID( m_iCapArrowTexture ); m_iCapArrowTexture = -1; } } } void Update( const char *szMapName ); virtual void Paint(); void SetState( int iPrevState, int iCurrentState, int iNextBattles ); virtual void OnTick( void ); void DrawTeamIcon( int x, int y, bool bBlueTeam, float flBloat = 1.0f ); void DrawCapArrows( int x0, int y0, int x1, int y1 ); private: // structure to hold a single control point typedef struct { char m_szName[64]; int m_iXPos; int m_iYPos; bool m_bHideIcon; } roundinfo_control_point_t; CUtlVector < roundinfo_control_point_t > m_ControlPoints; int m_iPrevState; int m_iCurrentState; int m_iMiniRoundMask; // Time when we should change the text to the attack directive int m_iMode; // 0 - start, 1 - previous round victory anim, 2 - attack directive, new round float m_flModeChangeTime; int m_iBlueTeamTexture; int m_iRedTeamTexture; int m_iCapArrowTexture; int m_iLastCappedPoint; int m_iFoundPoints; int m_iNextRoundPoints[2]; }; DECLARE_BUILD_FACTORY( RoundInfoOverlay ); void RoundInfoOverlay::Paint( void ) { BaseClass::Paint(); if ( m_ControlPoints.Count() <= 0 ) { return; } // Draw the Cap Icons for ( int i=0; icurtime; if ( flTimeUntilChange < 0.4f ) { float flBloat = RemapVal( flTimeUntilChange, 0.0f, 0.4f, 1.0f, 2.5f ); DrawTeamIcon( x, y, !bWasBlueTeam, flBloat ); } } else { DrawTeamIcon( x, y, bWasBlueTeam ); } } break; case 2: // Draw the current state and the next battle arrows { bool bPointInContention = (m_iNextRoundPoints[0] == i || m_iNextRoundPoints[1] == i ); bool bBlueTeam = ( m_iCurrentState & (1<curtime ) < 3.5f ) { DrawCapArrows( m_ControlPoints[m_iNextRoundPoints[0]].m_iXPos, m_ControlPoints[m_iNextRoundPoints[0]].m_iYPos, m_ControlPoints[m_iNextRoundPoints[1]].m_iXPos, m_ControlPoints[m_iNextRoundPoints[1]].m_iYPos ); } } } } void RoundInfoOverlay::DrawCapArrows( int x0, int y0, int x1, int y1 ) { vgui::surface()->DrawSetColor( Color(255,255,255,255) ); vgui::surface()->DrawSetTexture( m_iCapArrowTexture ); Vector2D a( x0, y0 ); Vector2D b( x1, y1 ); Vector2D dir = b - a; Vector2D perp( -dir.y, dir.x ); perp.NormalizeInPlace(); perp *= YRES(50); float bloat = sin(4*gpGlobals->curtime) * 0.1f; Vector2D edgepoint = a + dir * 0.25f; Vector2D edgepoint2 = b - dir * 0.25f; edgepoint -= 0.25f * dir * bloat; edgepoint2 += 0.25f * dir * bloat; float uv1 = 0.0f, uv2 = 1.0f; Vector2D uv12( uv1, uv2 ); Vector2D uv11( uv1, uv1 ); Vector2D uv21( uv2, uv1 ); Vector2D uv22( uv2, uv2 ); vgui::Vertex_t verts[4]; verts[0].Init( edgepoint - perp * 0.5f, uv12 ); verts[1].Init( edgepoint2 - perp * 0.5f, uv11 ); verts[2].Init( edgepoint2 + perp * 0.5f, uv21 ); verts[3].Init( edgepoint + perp * 0.5f, uv22 ); vgui::surface()->DrawTexturedPolygon( 4, verts ); } void RoundInfoOverlay::DrawTeamIcon( int x, int y, bool bBlueTeam, float flBloat /* = 1.0f */ ) { float flWide = YRES(45) * flBloat; int xpos = x - flWide * 0.5f; int ypos = y - flWide * 0.5f; vgui::surface()->DrawSetColor( Color(255,255,255,255) ); vgui::surface()->DrawSetTexture( bBlueTeam ? m_iBlueTeamTexture : m_iRedTeamTexture ); vgui::surface()->DrawTexturedRect( xpos, ypos, xpos + flWide, ypos + flWide ); } void RoundInfoOverlay::Update( const char *szMapName ) { KeyValues *kvCapPoints = NULL; char strFullpath[MAX_PATH]; Q_strncpy( strFullpath, "resource/roundinfo/", MAX_PATH ); // Assume we must play out of the media directory Q_strncat( strFullpath, szMapName, MAX_PATH ); #ifdef _X360 char *pExt = Q_stristr( strFullpath, ".360" ); if ( pExt ) { *pExt = '\0'; } #endif Q_strncat( strFullpath, ".res", MAX_PATH ); // Assume we're a .res extension type if ( g_pFullFileSystem->FileExists( strFullpath ), "MOD" ) { kvCapPoints = new KeyValues( strFullpath ); if ( kvCapPoints ) { if ( kvCapPoints->LoadFromFile( g_pFullFileSystem, strFullpath ) ) { m_ControlPoints.RemoveAll(); for ( KeyValues *pData = kvCapPoints->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) { roundinfo_control_point_t point; Q_snprintf( point.m_szName, sizeof(point.m_szName), "%s", pData->GetName() ); // These x,y coords are relative to a 640x480 parent panel. int wide, tall; GetSize( wide, tall ); // can't use XRES, YRES because of widescreen point.m_iXPos = (int)( (float)pData->GetInt( "x", 0 ) * ( ( float )wide / 560.0f ) ); point.m_iYPos = (int)( (float)pData->GetInt( "y", 0 ) * ( ( float )tall / 280.0f ) ); point.m_bHideIcon = ( pData->GetInt( "hideicon", 0 ) > 0 ); m_ControlPoints.AddToTail( point ); } } kvCapPoints->deleteThis(); } } } void RoundInfoOverlay::SetState( int iPrevState, int iCurrentState, int iNextBattles ) { m_iPrevState = iPrevState; m_iCurrentState = iCurrentState; m_iMiniRoundMask = iNextBattles; m_iMode = 0; m_flModeChangeTime = gpGlobals->curtime + 0.5f; // Find the two points that are being fought over m_iFoundPoints = 0; for ( int i=0;i<8 && m_iFoundPoints<2;i++ ) { if ( m_iMiniRoundMask & (1<= 2 ) { if ( !( m_iCurrentState & (1<>1; iIndex++; } m_iLastCappedPoint = iIndex; } } ConVar tf_roundinfo_pause( "tf_roundinfo_pause", "0", FCVAR_DEVELOPMENTONLY ); void RoundInfoOverlay::OnTick( void ) { // Stop ticking when our parent is invisible Panel *parent = GetParent(); if ( m_iMode >= 0 && ( !parent || !parent->IsVisible() ) ) { m_iMode = -1; return; } BaseClass::OnTick(); if ( tf_roundinfo_pause.GetBool() == false && m_flModeChangeTime <= gpGlobals->curtime ) { switch( m_iMode ) { case 0: { // start showing previous round anim if ( m_iCurrentState != m_iPrevState ) { m_iMode = 1; m_flModeChangeTime = gpGlobals->curtime + 1.5f; } else { m_iMode = 2; m_flModeChangeTime = gpGlobals->curtime + 4.0f; } } break; case 1: { // start showing next round plan m_iMode = 2; m_flModeChangeTime = gpGlobals->curtime + 4.0f; CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Hud.EndRoundScored" ); } break; case 2: { // we're done, hide the panel //GetParent()->OnCommand( "continue" ); //m_iMode = -1; } break; default: break; } } } //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CTFRoundInfo::CTFRoundInfo( IViewPort *pViewPort ) : Frame( NULL, PANEL_ROUNDINFO ) { m_pViewPort = pViewPort; // load the new scheme early!! SetScheme( "ClientScheme" ); SetTitleBarVisible( false ); SetMinimizeButtonVisible( false ); SetMaximizeButtonVisible( false ); SetCloseButtonVisible( false ); SetSizeable( false ); SetMoveable( false ); SetProportional( true ); SetVisible( false ); SetKeyBoardInputEnabled( true ); m_pTitle = new CExLabel( this, "RoundTitle", " " ); m_pMapImage = new ImagePanel( this, "MapImage" ); #ifdef _X360 m_pFooter = new CTFFooter( this, "Footer" ); #else m_pContinue = new CExButton( this, "RoundContinue", "#TF_Continue" ); #endif m_pOverlay = new RoundInfoOverlay( this, "Overlay" ); ListenForGameEvent( "game_newmap" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::PerformLayout() { BaseClass::PerformLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::ApplySchemeSettings( vgui::IScheme *pScheme ) { LoadControlSettings( "Resource/UI/RoundInfo.res" ); BaseClass::ApplySchemeSettings( pScheme ); Update(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::ShowPanel( bool bShow ) { if ( IsVisible() == bShow ) return; if ( bShow ) { // look for the textures we want to use and don't show the roundinfo panel if any are missing char temp[255]; Q_snprintf( temp, sizeof( temp ), "VGUI/%s", m_szMapImage ); IMaterial *pMapMaterial = materials->FindMaterial( temp, TEXTURE_GROUP_VGUI, false ); // are we missing any of the images we want to show? if ( pMapMaterial && !IsErrorMaterial( pMapMaterial ) ) { Activate(); } else { SetVisible( false ); } } else { SetVisible( false ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::OnCommand( const char *command ) { if ( !Q_strcmp( command, "continue" ) ) { m_pViewPort->ShowPanel( this, false ); } else { BaseClass::OnCommand( command ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::UpdateImage( ImagePanel *pImagePanel, const char *pszImageName ) { if ( pImagePanel && ( Q_strlen( pszImageName ) > 0 ) ) { char szTemp[255]; Q_snprintf( szTemp, sizeof( szTemp ), "VGUI/%s", pszImageName ); IMaterial *pTemp = materials->FindMaterial( szTemp, TEXTURE_GROUP_VGUI, false ); if ( pTemp && !IsErrorMaterial( pTemp ) ) { pImagePanel->SetImage( pszImageName ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::Update() { char szMapName[MAX_MAP_NAME]; Q_FileBase( engine->GetLevelName(), szMapName, sizeof(szMapName) ); Q_strlower( szMapName ); SetDialogVariable( "mapname", GetMapDisplayName( szMapName ) ); if ( m_pMapImage ) { char temp[255]; Q_snprintf( temp, sizeof(temp), "../overviews/%s", szMapName ); Q_strncpy( m_szMapImage, temp, sizeof( m_szMapImage ) ); UpdateImage( m_pMapImage, m_szMapImage ); } if ( m_pOverlay ) { m_pOverlay->Update( szMapName ); } #ifndef _X360 if ( m_pContinue ) { m_pContinue->RequestFocus(); } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::OnKeyCodePressed( KeyCode code ) { if( code == KEY_SPACE || code == KEY_ENTER || code == KEY_XBUTTON_A || code == KEY_XBUTTON_B || code == STEAMCONTROLLER_A || code == STEAMCONTROLLER_B ) { OnCommand( "continue" ); } else { BaseClass::OnKeyCodePressed( code ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::SetData( KeyValues *data ) { if ( m_pOverlay ) { m_pOverlay->SetState( data->GetInt( "prev" ), data->GetInt( "cur" ), data->GetInt( "round" ) ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFRoundInfo::FireGameEvent( IGameEvent *event ) { if ( Q_strcmp( event->GetName(), "game_newmap" ) == 0 ) { Update(); } }