summaryrefslogtreecommitdiff
path: root/game/client/tf/vgui/tf_intromenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/vgui/tf_intromenu.cpp')
-rw-r--r--game/client/tf/vgui/tf_intromenu.cpp703
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
+//=============================================================================