You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
623 lines
15 KiB
623 lines
15 KiB
//========= 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(); |
|
} |
|
} |