Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

648 lines
17 KiB

//========= 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();
}