diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/vgui/tf_intromenu.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/vgui/tf_intromenu.cpp')
| -rw-r--r-- | game/client/tf/vgui/tf_intromenu.cpp | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_intromenu.cpp b/game/client/tf/vgui/tf_intromenu.cpp new file mode 100644 index 0000000..5cf7670 --- /dev/null +++ b/game/client/tf/vgui/tf_intromenu.cpp @@ -0,0 +1,703 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include <KeyValues.h> +#include <vgui/IVGui.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include <vgui_controls/AnimationController.h> +#include "iclientmode.h" +#include "clientmode_shared.h" +#include "shareddefs.h" +#include "tf_shareddefs.h" +#include "tf_controls.h" +#include "tf_gamerules.h" +#ifdef WIN32 +#include "winerror.h" +#endif +#include "ixboxsystem.h" +#include "intromenu.h" +#include "tf_intromenu.h" +#include "inputsystem/iinputsystem.h" + +// used to determine the action the intro menu should take when OnTick handles a think for us +enum +{ + INTRO_NONE, + INTRO_STARTVIDEO, + INTRO_BACK, + INTRO_CONTINUE, +}; + +using namespace vgui; + +// sort function for the list of captions that we're going to show +int CaptionsSort( CVideoCaption* const *p1, CVideoCaption* const *p2 ) +{ + // check the start time + if ( (*p2)->m_flStartTime < (*p1)->m_flStartTime ) + { + return 1; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CTFIntroMenu::CTFIntroMenu( IViewPort *pViewPort ) : BaseClass( pViewPort ) +{ + m_pVideo = new CTFVideoPanel( this, "VideoPanel" ); + m_pModel = new CModelPanel( this, "MenuBG" ); + m_pCaptionLabel = new CExLabel( this, "VideoCaption", "" ); + +#ifdef _X360 + m_pFooter = new CTFFooter( this, "Footer" ); +#else + m_pBack = new CExButton( this, "Back", "" ); + m_pOK = new CExButton( this, "Skip", "" ); + m_pReplayVideo = new CExButton( this, "ReplayVideo", "" ); + m_pContinue = new CExButton( this, "Continue", "" ); +#endif + + m_iCurrentCaption = 0; + m_flVideoStartTime = 0; + + m_flActionThink = -1; + m_iAction = INTRO_NONE; + + //============================================================================= + // HPE_BEGIN + // [msmith] Flag for weather or not we're playing an in game video. + //============================================================================= + m_bPlayingInGameVideo = false; + //============================================================================= + // HPE_END + //============================================================================= + + vgui::ivgui()->AddTickSignal( GetVPanel() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CTFIntroMenu::~CTFIntroMenu() +{ + m_Captions.PurgeAndDeleteElements(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + if ( ::input->IsSteamControllerActive() ) + { + LoadControlSettings( "Resource/UI/IntroMenu_SC.res" ); + SetMouseInputEnabled( false ); + } + else + { + LoadControlSettings( "Resource/UI/IntroMenu.res" ); + SetMouseInputEnabled( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::SetNextThink( float flActionThink, int iAction ) +{ + m_flActionThink = flActionThink; + m_iAction = iAction; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::OnTick() +{ + // @note Tom Bui: (yuck) + // in training, never show the back button + // we do this late, because there's a race condition for when IsInTraining() will return true + if ( m_pBack->IsVisible() && TFGameRules() && TFGameRules()->IsInTraining() ) + { + m_pBack->SetVisible(false); + } + + //============================================================================= + // HPE_BEGIN + // [msmith] Used to play a movie during a map. For training videos. + //============================================================================= + if ( PendingInGameVideo() && !BaseClass::IsVisible() ) + { + m_pViewPort->ShowPanel( this, true ); + } + //============================================================================= + // HPE_END + //============================================================================= + + // do we have anything special to do? + else if ( m_flActionThink > 0 && m_flActionThink < gpGlobals->curtime ) + { + if ( m_iAction == INTRO_STARTVIDEO ) + { + + //============================================================================= + // HPE_BEGIN + // [msmith] Pulled start video into a separate function. + //============================================================================= + StartVideo(); + //============================================================================= + // HPE_END + //============================================================================= + } + else if ( m_iAction == INTRO_BACK ) + { + m_pViewPort->ShowPanel( this, false ); + m_pViewPort->ShowPanel( PANEL_MAPINFO, true ); + } + else if ( m_iAction == INTRO_CONTINUE ) + { + m_pViewPort->ShowPanel( this, false ); + + //============================================================================= + // HPE_BEGIN + // [msmith] Used for the client to tell the server that we're whatching a movie or not + //============================================================================= + tf_training_client_message.SetValue( "" ); + tf_training_client_message.SetValue( TRAINING_CLIENT_MESSAGE_NONE ); + //============================================================================= + // HPE_END + //============================================================================= + + + if ( GetLocalPlayerTeam() == TEAM_UNASSIGNED ) + { + if ( TFGameRules()->IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true ) + { + m_pViewPort->ShowPanel( PANEL_ARENA_TEAM, true ); + } + else if ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsCompetitiveMode() ) + { + engine->ClientCmd( "autoteam" ); + } + else + { + m_pViewPort->ShowPanel( PANEL_TEAM, true ); + } + } + else + { + C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); + + // only open the class menu if they're not on team Spectator and they haven't already picked a class + if ( pPlayer && + ( GetLocalPlayerTeam() != TEAM_SPECTATOR ) && + ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_UNDEFINED ) ) + { + if ( tf_arena_force_class.GetBool() == false ) + { + switch( GetLocalPlayerTeam() ) + { + case TF_TEAM_RED: + m_pViewPort->ShowPanel( PANEL_CLASS_RED, true ); + break; + + case TF_TEAM_BLUE: + m_pViewPort->ShowPanel( PANEL_CLASS_BLUE, true ); + break; + } + } + } + } + } + + // reset our think + SetNextThink( -1, INTRO_NONE ); + } + + // check if we need to update our captions + if ( m_pCaptionLabel && m_pCaptionLabel->IsVisible() ) + { + UpdateCaptions(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::OnThink() +{ + //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(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFIntroMenu::LoadCaptions( void ) +{ + bool bSuccess = false; + + // clear any current captions + m_Captions.PurgeAndDeleteElements(); + m_iCurrentCaption = 0; + + if ( m_pCaptionLabel ) + { + const char *szVideoFileName = GetVideoFileName( false ); + KeyValues *kvCaptions = NULL; + char strFullpath[MAX_PATH]; + if ( szVideoFileName != NULL ) + { + //============================================================================= + // HPE_BEGIN + // [msmith] The video may now be either a map video or an in game video. + // Made a function to decide which video name to give back. + //============================================================================= + Q_strncpy( strFullpath, szVideoFileName, MAX_PATH ); // Assume we must play out of the media directory + //============================================================================= + // HPE_END + //============================================================================= + + Q_strncat( strFullpath, ".res", MAX_PATH ); // Assume we're a .res extension type + + if ( g_pFullFileSystem->FileExists( strFullpath ) ) + { + kvCaptions = new KeyValues( strFullpath ); + + if ( kvCaptions ) + { + if ( kvCaptions->LoadFromFile( g_pFullFileSystem, strFullpath ) ) + { + for ( KeyValues *pData = kvCaptions->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + CVideoCaption *pCaption = new CVideoCaption; + if ( pCaption ) + { + pCaption->m_pszString = ReadAndAllocStringValue( pData, "string" ); + pCaption->m_flStartTime = pData->GetFloat( "start", 0.0 ); + pCaption->m_flDisplayTime = pData->GetFloat( "length", 3.0 ); + + m_Captions.AddToTail( pCaption ); + + // we have at least one caption to show + bSuccess = true; + } + } + } + + kvCaptions->deleteThis(); + } + } + } + } + + if ( bSuccess ) + { + // sort the captions so we show them in the correct order (they're not necessarily in order in the .res file) + m_Captions.Sort( CaptionsSort ); + } + + return bSuccess; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::UpdateCaptions( void ) +{ + //============================================================================= + // HPE_BEGIN + // [msmith] Timing should be realtime when playing in game becase the curtime is paused. + //============================================================================= + float testTime = m_bPlayingInGameVideo ? gpGlobals->realtime : gpGlobals->curtime; + //============================================================================= + // HPE_END + //============================================================================= + + if ( m_pCaptionLabel && m_pCaptionLabel->IsVisible() && ( m_Captions.Count() > 0 ) ) + { + CVideoCaption *pCaption = m_Captions[m_iCurrentCaption]; + + if ( pCaption ) + { + if ( ( pCaption->m_flCaptionStart >= 0 ) && ( pCaption->m_flCaptionStart + pCaption->m_flDisplayTime < testTime ) ) + { + // fade out the caption + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "VideoCaptionFadeOut" ); + + // move to the next caption + m_iCurrentCaption++; + + if ( !m_Captions.IsValidIndex( m_iCurrentCaption ) ) + { + // we're done showing captions + m_pCaptionLabel->SetVisible( false ); + } + } + // is it time to show the caption? + else if ( m_flVideoStartTime + pCaption->m_flStartTime < testTime ) + { + // have we already started this video? + if ( pCaption->m_flCaptionStart < 0 ) + { + m_pCaptionLabel->SetText( pCaption->m_pszString ); + pCaption->m_flCaptionStart = testTime; + + // fade in the next caption + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "VideoCaptionFadeIn" ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::ShowPanel( bool bShow ) +{ + + //============================================================================= + // HPE_BEGIN: + // [msmith] Don't show the back button when in training. You can only skip intro + // movies. + //============================================================================= + m_pBack->SetVisible(true); + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + m_pBack->SetVisible( false ); + if ( PendingInGameVideo() == false ) + { + VideoSystem_t playbackSystem = VideoSystem::NONE; + char resolvedFile[MAX_PATH]; + if ( g_pVideo != NULL && g_pVideo->LocatePlayableVideoFile( GetVideoFileName(), "GAME", &playbackSystem, resolvedFile, sizeof(resolvedFile) ) != VideoResult::SUCCESS ) + { + //If we have no movie, no need to show the intro screen on a training mission. + bShow = false; + } + } + } + //============================================================================= + // HPE_END + //============================================================================= + + + if ( BaseClass::IsVisible() == bShow ) + return; + + // reset our think + SetNextThink( -1, INTRO_NONE ); + + if ( bShow ) + { + InvalidateLayout( true, true ); + Activate(); + + if ( m_pVideo ) + { + //============================================================================= + // HPE_BEGIN + // [msmith] Pulled shutting down the video into a separate function. + // If we're showing an in game video, we need to enable pausing so that + // we can pause the game during the video. + // If we're showing an intro training movie, we also need to tell the server that + // we're whatching the intro movie so that the round does not start until it's over. + // If we are watching an in game video, we do NOT send a message for that because + // tf_training_client_message will contain the name of the video we're watching. + //============================================================================= + ShutdownVideo(); + SetNextThink( gpGlobals->curtime + m_pVideo->GetStartDelay(), INTRO_STARTVIDEO ); + + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + if ( PendingInGameVideo() ) + { + engine->ClientCmd( "sv_pausable 1" ); + } + else + { + tf_training_client_message.SetValue( TRAINING_CLIENT_MESSAGE_WATCHING_INTRO_MOVIE ); + } + } + //============================================================================= + // HPE_END + //============================================================================= + + } + + if ( m_pModel ) + { + m_pModel->SetPanelDirty(); + } + } + else + { + Shutdown(); + + SetVisible( false ); + + //============================================================================= + // HPE_BEGIN + // [msmith] We must disable the ability to pause. If we don't, it looks like + // some other function in TF2 causes the entire game to pause if sv_pausable is enabled. + //============================================================================= + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + engine->ClientCmd( "sv_pausable 0" ); + } + //============================================================================= + // HPE_END + //============================================================================= + + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::OnIntroFinished( void ) +{ + // in training we want to give the user the ability to replay the movie + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + m_pReplayVideo->SetVisible( true ); + m_pContinue->SetVisible( true ); + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "IntroMovieContinueBlink" ); + m_pOK->SetVisible( false ); + } + else + { + float flTime = gpGlobals->curtime; + + if ( m_pModel && m_pModel->SetSequence( "UpSlow" ) ) + { + // wait for the model sequence to finish before going to the next menu + flTime = gpGlobals->curtime + m_pVideo->GetEndDelay(); + } + + Shutdown(); + + SetNextThink( flTime, INTRO_CONTINUE ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::OnCommand( const char *command ) +{ + if ( !Q_strcmp( command, "back" ) ) + { + float flTime = gpGlobals->curtime; + + Shutdown(); + + // try to play the screenup sequence + if ( m_pModel && m_pModel->SetSequence( "Up" ) ) + { + flTime = gpGlobals->curtime + 0.35f; + } + + // wait for the model sequence to finish before going back to the mapinfo menu + SetNextThink( flTime, INTRO_BACK ); + } + else if ( !Q_strcmp( command, "skip" ) ) + { + Shutdown(); + + // continue right now + SetNextThink( gpGlobals->curtime, INTRO_CONTINUE ); + } + else if ( !Q_strcmp( command, "replayVideo" ) ) + { + ShutdownVideo(); + SetNextThink( gpGlobals->curtime, INTRO_STARTVIDEO ); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::OnKeyCodePressed( KeyCode code ) +{ + if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + OnCommand( "skip" ); + } + else if ( code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B ) + { + OnCommand( "back" ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFIntroMenu::Shutdown( void ) +{ + //============================================================================= + // HPE_BEGIN + // [msmith] Refactored the shutdown video logic into a containing function. + //============================================================================= + ShutdownVideo(); + //============================================================================= + // HPE_END + //============================================================================= + + if ( m_pCaptionLabel && m_pCaptionLabel->IsVisible() ) + { + m_pCaptionLabel->SetVisible( false ); + } + + m_iCurrentCaption = 0; + m_flVideoStartTime = 0; + +} + + + + +//============================================================================= +// HPE_BEGIN +// [msmith] New helper functions +//============================================================================= +void CTFIntroMenu::ShutdownVideo() +{ + if ( m_pVideo ) + { + m_pVideo->Shutdown(); // make sure we're not currently running + } + + //Make sure we unpause the game if it was paused from an in game play of a video. + if ( m_bPlayingInGameVideo ) + { + UnpauseGame(); + } + + m_bPlayingInGameVideo = false; +} + +bool CTFIntroMenu::PendingInGameVideo( void ) +{ + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + //If the message is a string, it's a video name. + return strlen( tf_training_client_message.GetString() ) > 3; + } + + return false; +} + +const char *CTFIntroMenu::GetVideoFileName( bool withExtension ) +{ + if ( PendingInGameVideo() ) + { + return TFGameRules()->FormatVideoName( tf_training_client_message.GetString(), withExtension ); + } + + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + ConVarRef training_map_video("training_map_video"); + if ( strlen( training_map_video.GetString() ) > 3 ) + { + return TFGameRules()->FormatVideoName( training_map_video.GetString(), withExtension ); + } + } + + return TFGameRules()->GetVideoFileForMap( withExtension ); +} + +void CTFIntroMenu::StartVideo() +{ + m_pOK->SetVisible( true ); + m_pReplayVideo->SetVisible( false ); + m_pContinue->SetVisible( false ); + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "IntroMovieContinueBlinkStop" ); + if ( m_pVideo ) + { + // turn on the captions if we have them + if ( LoadCaptions() ) + { + if ( m_pCaptionLabel && !m_pCaptionLabel->IsVisible() ) + { + m_pCaptionLabel->SetText( " " ); + m_pCaptionLabel->SetVisible( true ); + //Make sure the label is fully faded in when starting to play. + //It could have been faded out from a prior animation event form an animation effect in a previous video instance. + m_pCaptionLabel->SetAlpha( 255 ); + } + } + else + { + if ( m_pCaptionLabel && m_pCaptionLabel->IsVisible() ) + { + m_pCaptionLabel->SetVisible( false ); + } + } + + m_pVideo->Activate(); + + if ( PendingInGameVideo() ) + { + m_pVideo->BeginPlayback( GetVideoFileName() ); + PauseGame(); + m_bPlayingInGameVideo = true; + + //Since we have started playing the video, we can reset the message string to empty. + tf_training_client_message.SetValue( "" ); + } + else + { + m_pVideo->BeginPlayback( GetVideoFileName() ); + } + + m_pVideo->MoveToFront(); + + m_flVideoStartTime = m_bPlayingInGameVideo ? gpGlobals->realtime : gpGlobals->curtime; + } +} + +void CTFIntroMenu::UnpauseGame( void ) +{ + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + engine->ClientCmd( "unpause" ); + } +} + +void CTFIntroMenu::PauseGame( void ) +{ + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + engine->ClientCmd( "pause" ); + } +} +//============================================================================= +// HPE_END +//============================================================================= |