//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include #include #include #include #include #include #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( 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( 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(); }