|
|
//========= 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
//=============================================================================
|