diff options
Diffstat (limited to 'game/client/tf/vgui/tf_roundinfo.cpp')
| -rw-r--r-- | game/client/tf/vgui/tf_roundinfo.cpp | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_roundinfo.cpp b/game/client/tf/vgui/tf_roundinfo.cpp new file mode 100644 index 0000000..ccfba71 --- /dev/null +++ b/game/client/tf/vgui/tf_roundinfo.cpp @@ -0,0 +1,623 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui_controls/RichText.h> +#include <vgui_controls/Frame.h> +#include <game/client/iviewport.h> +#include <KeyValues.h> +#include <filesystem.h> +#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 <vgui/ILocalize.h> +#include <vgui/IVGui.h> +#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; i<m_ControlPoints.Count(); i++ ) + { + if ( m_ControlPoints[i].m_bHideIcon ) + continue; + + int x = m_ControlPoints[i].m_iXPos; + int y = m_ControlPoints[i].m_iYPos; + + switch( m_iMode ) + { + case 0: // Show previous state + { + if ( i != m_iLastCappedPoint ) + { + bool bBlueTeam = ( m_iPrevState & (1<<i) ); + DrawTeamIcon( x, y, bBlueTeam ); + } + } + break; + + case 1: // Animate the point being capped + { + bool bWasBlueTeam = ( m_iPrevState & (1<<i) ); + + if ( i == m_iLastCappedPoint ) + { + float flTimeUntilChange = m_flModeChangeTime - gpGlobals->curtime; + + 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<<i) ); + DrawTeamIcon( x, y, bBlueTeam, bPointInContention ? 1.4 : 1.0 ); // rescale? pop looks weird + } + break; + } + } + + if ( m_iMode == 2 ) + { + if ( m_iFoundPoints == 2 ) + { + if ( ( m_flModeChangeTime - gpGlobals->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<<i) ) + { + m_iNextRoundPoints[m_iFoundPoints] = i; + m_iFoundPoints++; + } + } + + // Make sure the blue point is in m_iNextRoundPoints[0] + if ( m_iFoundPoints >= 2 ) + { + if ( !( m_iCurrentState & (1<<m_iNextRoundPoints[0]) ) ) + { + // The first point is red! swap them + int temp = m_iNextRoundPoints[0]; + m_iNextRoundPoints[0] = m_iNextRoundPoints[1]; + m_iNextRoundPoints[1] = temp; + } + } + + m_iLastCappedPoint = -1; + + // Find the index of the point that was just capped + int iMaskedCappedPoint = m_iCurrentState ^ m_iPrevState; + + if ( iMaskedCappedPoint != 0 ) + { + int iIndex = 0; + + // Find the index of the point that changed + while ( !( iMaskedCappedPoint & 0x1 ) ) + { + iMaskedCappedPoint = iMaskedCappedPoint>>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(); + } +}
\ No newline at end of file |