|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include <vgui_controls/ImagePanel.h>
#include <vgui_controls/RichText.h>
#include <game/client/iviewport.h>
#include <vgui/ILocalize.h>
#include <KeyValues.h>
#include <filesystem.h>
#include "IGameUIFuncs.h" // for key bindings
#include "inputsystem/iinputsystem.h"
#include "ixboxsystem.h"
#include "tf_gamerules.h"
#include "tf_controls.h"
#include "tf_shareddefs.h"
#include "tf_mapinfomenu.h"
#include "video/ivideoservices.h"
using namespace vgui;
const char *GetMapDisplayName( const char *mapName );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CTFMapInfoMenu::CTFMapInfoMenu( IViewPort *pViewPort ) : Frame( NULL, PANEL_MAPINFO ) { 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, "MapInfoTitle", " " );
#ifdef _X360
m_pFooter = new CTFFooter( this, "Footer" ); #else
m_pContinue = new CExButton( this, "MapInfoContinue", "#TF_Continue" ); m_pBack = new CExButton( this, "MapInfoBack", "#TF_Back" ); m_pIntro = new CExButton( this, "MapInfoWatchIntro", "#TF_WatchIntro" ); #endif
// info window about this map
m_pMapInfo = new CExRichText( this, "MapInfoText" ); m_pMapImage = new ImagePanel( this, "MapImage" );
m_szMapName[0] = 0; }
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CTFMapInfoMenu::~CTFMapInfoMenu() { }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme );
if ( ::input->IsSteamControllerActive() ) { LoadControlSettings( "Resource/UI/MapInfoMenu_SC.res" ); m_pContinueHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "MapInfoContinueHintIcon" ) ); m_pBackHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "MapInfoBackHintIcon" ) ); m_pIntroHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "MapInfoIntroHintIcon" ) );
SetMouseInputEnabled( false ); } else { LoadControlSettings( "Resource/UI/MapInfoMenu.res" ); m_pContinueHintIcon = m_pBackHintIcon = m_pIntroHintIcon = nullptr; SetMouseInputEnabled( true ); }
CheckIntroState(); CheckBackContinueButtons();
char mapname[MAX_MAP_NAME];
Q_FileBase( engine->GetLevelName(), mapname, sizeof(mapname) );
// Save off the map name so we can re-load the page in ApplySchemeSettings().
Q_strncpy( m_szMapName, mapname, sizeof( m_szMapName ) ); Q_strupr( m_szMapName );
#ifdef _X360
char *pExt = Q_stristr( m_szMapName, ".360" ); if ( pExt ) { *pExt = '\0'; } #endif
LoadMapPage(); SetMapTitle();
#ifndef _X360
if ( m_pContinue ) { m_pContinue->RequestFocus(); } #endif
SetDialogVariable( "gamemode", g_pVGuiLocalize->Find( GetMapType( m_szMapName ) ) ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::ShowPanel( bool bShow ) { if ( IsVisible() == bShow ) return;
m_KeyRepeat.Reset();
if ( bShow ) { InvalidateLayout( true, true ); // Force scheme reload since the steam controller state may have changed.
Activate(); CheckIntroState(); } else { SetVisible( false ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFMapInfoMenu::CheckForIntroMovie() { const char *pVideoFileName = TFGameRules()->GetVideoFileForMap(); if ( pVideoFileName == NULL ) { return false; }
VideoSystem_t playbackSystem = VideoSystem::NONE; char resolvedFile[MAX_PATH]; if ( g_pVideo && g_pVideo->LocatePlayableVideoFile( pVideoFileName, "GAME", &playbackSystem, resolvedFile, sizeof(resolvedFile) ) == VideoResult::SUCCESS ) { return true; }
return false; }
const char *COM_GetModDirectory();
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFMapInfoMenu::HasViewedMovieForMap() { return ( UTIL_GetMapKeyCount( "viewed" ) > 0 ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::CheckIntroState() { if ( CheckForIntroMovie() && HasViewedMovieForMap() ) { #ifdef _X360
if ( m_pFooter ) { m_pFooter->ShowButtonLabel( "intro", true ); } #else
if ( m_pIntro && !m_pIntro->IsVisible() ) { m_pIntro->SetVisible( true ); if ( m_pIntroHintIcon ) { m_pIntroHintIcon->SetVisible( true ); } } #endif
} else { #ifdef _X360
if ( m_pFooter ) { m_pFooter->ShowButtonLabel( "intro", false ); } #else
if ( m_pIntro && m_pIntro->IsVisible() ) { m_pIntro->SetVisible( false ); if ( m_pIntroHintIcon ) { m_pIntroHintIcon->SetVisible( false ); } } #endif
} }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::CheckBackContinueButtons() { #ifndef _X360
if ( m_pBack && m_pContinue ) { if ( GetLocalPlayerTeam() == TEAM_UNASSIGNED ) { m_pBack->SetVisible( true ); if ( m_pBackHintIcon ) { m_pBackHintIcon->SetVisible( true ); } m_pContinue->SetText( "#TF_Continue" ); } else { m_pBack->SetVisible( false ); if ( m_pBackHintIcon ) { m_pBackHintIcon->SetVisible( false ); } m_pContinue->SetText( "#TF_Close" ); } } #endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::OnCommand( const char *command ) { m_KeyRepeat.Reset();
if ( !Q_strcmp( command, "back" ) ) { // only want to go back to the Welcome menu if we're not already on a team
if ( !IsX360() && ( GetLocalPlayerTeam() == TEAM_UNASSIGNED ) ) { m_pViewPort->ShowPanel( this, false ); m_pViewPort->ShowPanel( PANEL_INFO, true ); } } else if ( !Q_strcmp( command, "continue" ) ) { m_pViewPort->ShowPanel( this, false );
if ( CheckForIntroMovie() && !HasViewedMovieForMap() ) { m_pViewPort->ShowPanel( PANEL_INTRO, true );
UTIL_IncrementMapKey( "viewed" ); } else { // On console, we may already have a team due to the lobby assigning us one.
// We tell the server we're done with the map info menu, and it decides what to do with us.
if ( IsX360() ) { engine->ClientCmd( "closedwelcomemenu" ); } else if ( GetLocalPlayerTeam() == TEAM_UNASSIGNED ) { if ( TFGameRules()->IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true ) { m_pViewPort->ShowPanel( PANEL_ARENA_TEAM, true ); } else { engine->ClientCmd( "team_ui_setup" ); } }
UTIL_IncrementMapKey( "viewed" ); } } else if ( !Q_strcmp( command, "intro" ) ) { m_pViewPort->ShowPanel( this, false );
if ( CheckForIntroMovie() ) { m_pViewPort->ShowPanel( PANEL_INTRO, true ); } else { if ( TFGameRules()->IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true ) { m_pViewPort->ShowPanel( PANEL_ARENA_TEAM, true ); } else { engine->ClientCmd( "team_ui_setup" ); } } } else { BaseClass::OnCommand( command ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::Update() { InvalidateLayout( false, true ); }
//-----------------------------------------------------------------------------
// Purpose: chooses and loads the text page to display that describes mapName map
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::LoadMapPage() { if ( !m_szMapName[0] ) { m_pMapInfo->SetText( "" ); m_pMapImage->SetVisible( false ); return; }
// load the map image (if it exists for the current map)
char szMapImage[ MAX_PATH ]; Q_snprintf( szMapImage, sizeof( szMapImage ), "VGUI/maps/menu_photos_%s", m_szMapName ); Q_strlower( szMapImage );
IMaterial *pMapMaterial = materials->FindMaterial( szMapImage, TEXTURE_GROUP_VGUI, false ); if ( pMapMaterial && !IsErrorMaterial( pMapMaterial ) ) { if ( m_pMapImage ) { if ( !m_pMapImage->IsVisible() ) { m_pMapImage->SetVisible( true ); }
// take off the vgui/ at the beginning when we set the image
Q_snprintf( szMapImage, sizeof( szMapImage ), "maps/menu_photos_%s", m_szMapName ); Q_strlower( szMapImage ); m_pMapImage->SetImage( szMapImage ); } } else { if ( m_pMapImage && m_pMapImage->IsVisible() ) { m_pMapImage->SetVisible( false ); } }
// try loading map descriptions from the localization files first
char mapDescriptionKey[ 64 ]; Q_snprintf( mapDescriptionKey, sizeof( mapDescriptionKey ), "#%s_description", m_szMapName ); Q_strlower( mapDescriptionKey ); wchar_t* wszMapDescription = g_pVGuiLocalize->Find( mapDescriptionKey ); if( wszMapDescription ) { m_pMapInfo->SetText( wszMapDescription ); } else { // try loading map descriptions from .txt files first
char mapRES[ MAX_PATH ];
char uilanguage[ 64 ]; uilanguage[0] = 0; engine->GetUILanguage( uilanguage, sizeof( uilanguage ) );
Q_snprintf( mapRES, sizeof( mapRES ), "maps/%s_%s.txt", m_szMapName, uilanguage );
// try English if the file doesn't exist for our language
if( !g_pFullFileSystem->FileExists( mapRES, "GAME" ) ) { Q_snprintf( mapRES, sizeof( mapRES ), "maps/%s_english.txt", m_szMapName );
// if the file doesn't exist for English either, try the filename without any language extension
if( !g_pFullFileSystem->FileExists( mapRES, "GAME" ) ) { Q_snprintf( mapRES, sizeof( mapRES ), "maps/%s.txt", m_szMapName ); } }
// if no map specific description exists, load default text
if( g_pFullFileSystem->FileExists( mapRES, "GAME" ) ) { FileHandle_t f = g_pFullFileSystem->Open( mapRES, "rb" );
// read into a memory block
int fileSize = g_pFullFileSystem->Size(f); int dataSize = fileSize + sizeof( wchar_t ); if ( dataSize % 2 ) ++dataSize; wchar_t *memBlock = (wchar_t *)malloc(dataSize); memset( memBlock, 0x0, dataSize); int bytesRead = g_pFullFileSystem->Read(memBlock, fileSize, f); if ( bytesRead < fileSize ) { // NULL-terminate based on the length read in, since Read() can transform \r\n to \n and
// return fewer bytes than we were expecting.
char *data = reinterpret_cast<char *>( memBlock ); data[ bytesRead ] = 0; data[ bytesRead+1 ] = 0; }
#ifndef WIN32
if ( ((ucs2 *)memBlock)[0] == 0xFEFF ) { // convert the win32 ucs2 data to wchar_t
dataSize*=2;// need to *2 to account for ucs2 to wchar_t (4byte) growth
wchar_t *memBlockConverted = (wchar_t *)malloc(dataSize); V_UCS2ToUnicode( (ucs2 *)memBlock, memBlockConverted, dataSize ); free(memBlock); memBlock = memBlockConverted; } #else
// null-terminate the stream (redundant, since we memset & then trimmed the transformed buffer already)
memBlock[dataSize / sizeof(wchar_t) - 1] = 0x0000; #endif
// check the first character, make sure this a little-endian unicode file
#if defined( _X360 )
if ( memBlock[0] != 0xFFFE ) #else
if ( memBlock[0] != 0xFEFF ) #endif
{ // its a ascii char file
m_pMapInfo->SetText( reinterpret_cast<char *>( memBlock ) ); } else { // ensure little-endian unicode reads correctly on all platforms
CByteswap byteSwap; byteSwap.SetTargetBigEndian( false ); byteSwap.SwapBufferToTargetEndian( memBlock, memBlock, dataSize/sizeof(wchar_t) );
m_pMapInfo->SetText( memBlock+1 ); } // go back to the top of the text buffer
m_pMapInfo->GotoTextStart();
g_pFullFileSystem->Close( f ); free(memBlock); } else { // try loading map descriptions from localization files next
const char *pszDescription = NULL; char mapInfoKey[ 64 ];
if ( TFGameRules() && TFGameRules()->IsPowerupMode() && ( FStrEq( m_szMapName, "ctf_foundry" ) || FStrEq( m_szMapName, "ctf_gorge" ) ) ) { Q_snprintf( mapInfoKey, sizeof( mapInfoKey ), "#%s_beta", m_szMapName ); } else { Q_snprintf( mapInfoKey, sizeof( mapInfoKey ), "#%s", m_szMapName ); } Q_strlower( mapInfoKey );
if( !g_pVGuiLocalize->Find( mapInfoKey ) ) { if ( TFGameRules() ) { if ( TFGameRules()->IsMannVsMachineMode() ) { pszDescription = "#default_mvm_description"; } else { switch ( TFGameRules()->GetGameType() ) { case TF_GAMETYPE_CTF: pszDescription = "#default_ctf_description"; break; case TF_GAMETYPE_CP: if ( TFGameRules()->IsInKothMode() ) { pszDescription = "#default_koth_description"; } else { pszDescription = "#default_cp_description"; } break; case TF_GAMETYPE_ESCORT: if ( TFGameRules()->HasMultipleTrains() ) { pszDescription = "#default_payload_race_description"; } else { pszDescription = "#default_payload_description"; } break; case TF_GAMETYPE_ARENA: pszDescription = "#default_arena_description"; break; case TF_GAMETYPE_RD: pszDescription = "#default_rd_description"; break; case TF_GAMETYPE_PASSTIME: pszDescription = "#default_passtime_description"; break; case TF_GAMETYPE_PD: pszDescription = "#default_pd_description"; break; } } } } else { pszDescription = mapInfoKey; }
if ( pszDescription && pszDescription[0] ) { m_pMapInfo->SetText( pszDescription ); } else { m_pMapInfo->SetText( "" ); } } }
// we haven't loaded a valid map image for the current map
if ( m_pMapImage && !m_pMapImage->IsVisible() ) { if ( m_pMapInfo ) { m_pMapInfo->SetWide( m_pMapInfo->GetWide() + ( m_pMapImage->GetWide() * 0.75 ) ); // add in the extra space the images would have taken
} } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::SetMapTitle() { SetDialogVariable( "mapname", GetMapDisplayName( m_szMapName ) ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::OnKeyCodePressed( KeyCode code ) { m_KeyRepeat.KeyDown( code );
if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) { OnCommand( "continue" ); } else if ( code == STEAMCONTROLLER_B ) { OnCommand( "back" ); } else if ( code == KEY_XBUTTON_Y || code == STEAMCONTROLLER_Y ) { OnCommand( "intro" ); } else if( code == KEY_XBUTTON_UP || code == KEY_XSTICK1_UP || code == STEAMCONTROLLER_DPAD_UP ) { // Scroll class info text up
if ( m_pMapInfo ) { PostMessage( m_pMapInfo, new KeyValues("MoveScrollBarDirect", "delta", 1) ); } } else if( code == KEY_XBUTTON_DOWN || code == KEY_XSTICK1_DOWN || code == STEAMCONTROLLER_DPAD_DOWN ) { // Scroll class info text up
if ( m_pMapInfo ) { PostMessage( m_pMapInfo, new KeyValues("MoveScrollBarDirect", "delta", -1) ); } } else { BaseClass::OnKeyCodePressed( code ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::OnKeyCodeReleased( vgui::KeyCode code ) { m_KeyRepeat.KeyUp( code );
BaseClass::OnKeyCodeReleased( code ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMapInfoMenu::OnThink() { vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); if ( code ) { OnKeyCodePressed( code ); }
//Always hide the health... this needs to be done every frame because a message from the server keeps resetting this.
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if ( pLocalPlayer ) { pLocalPlayer->m_Local.m_iHideHUD |= HIDEHUD_HEALTH; }
BaseClass::OnThink(); }
|