From 302885d39c169dd77e2a91b20883e16f56e136e9 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sat, 6 Mar 2021 01:13:49 -0600 Subject: [PATCH 1/4] Added video panel code from Alien Swarm SDK + custom looping support --- sp/src/game/client/vgui_video.cpp | 251 ++++++++++++++++++++++-------- sp/src/game/client/vgui_video.h | 15 +- 2 files changed, 203 insertions(+), 63 deletions(-) diff --git a/sp/src/game/client/vgui_video.cpp b/sp/src/game/client/vgui_video.cpp index 6df78893..39ab6b6c 100644 --- a/sp/src/game/client/vgui_video.cpp +++ b/sp/src/game/client/vgui_video.cpp @@ -16,20 +16,42 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +using namespace vgui; + +static CUtlVector< VideoPanel * > g_vecVideoPanels; + +// Thiis is a hack due to the fact that the user can type quit with the video panel up, but it's parented to the GameUI dll root panel, which is already gone so +// we would crash in the destructor +void VGui_ClearVideoPanels() +{ + for ( int i = g_vecVideoPanels.Count() - 1; i >= 0; --i ) + { + if ( g_vecVideoPanels[ i ] ) + { + delete g_vecVideoPanels[ i ]; + } + } + g_vecVideoPanels.RemoveAll(); +} VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHeight, unsigned int nWidth, bool allowAlternateMedia ) : BaseClass( NULL, "VideoPanel" ), m_VideoMaterial( NULL ), m_nPlaybackWidth( 0 ), m_nPlaybackHeight( 0 ), - m_bAllowAlternateMedia( allowAlternateMedia ) + m_nShutdownCount( 0 ), + m_bLooping( false ), + m_bStopAllSounds( true ), + m_bAllowInterruption( true ), + m_bAllowAlternateMedia( allowAlternateMedia ), + m_bStarted( false ) { - #ifdef MAPBASE vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_ROOT ); #else vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_GAMEUIDLL ); #endif + SetParent( pParent ); SetVisible( false ); @@ -53,6 +75,11 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe SetScheme(vgui::scheme()->LoadSchemeFromFile( "resource/VideoPanelScheme.res", "VideoPanelScheme")); LoadControlSettings("resource/UI/VideoPanel.res"); + + // Let us update + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + g_vecVideoPanels.AddToTail( this ); } //----------------------------------------------------------------------------- @@ -60,6 +87,8 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe //----------------------------------------------------------------------------- VideoPanel::~VideoPanel( void ) { + g_vecVideoPanels.FindAndRemove( this ); + SetParent( (vgui::Panel *) NULL ); // Shut down this video, destroy the video material @@ -70,13 +99,39 @@ VideoPanel::~VideoPanel( void ) } } +//----------------------------------------------------------------------------- +// Purpose: Keeps a tab on when the movie is ending and allows a frame to pass to prevent threading issues +//----------------------------------------------------------------------------- +void VideoPanel::OnTick( void ) +{ + if ( m_nShutdownCount > 0 ) + { + m_nShutdownCount++; + + if ( m_nShutdownCount > 10 ) + { + OnClose(); + m_nShutdownCount = 0; + } + } + + BaseClass::OnTick(); +} + +void VideoPanel::OnVideoOver() +{ + StopPlayback(); +} + //----------------------------------------------------------------------------- // Purpose: Begins playback of a movie // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool VideoPanel::BeginPlayback( const char *pFilename ) { - // Who the heck hacked this in? + if ( !pFilename || pFilename[ 0 ] == '\0' ) + return false; + #ifdef _X360 XVIDEO_MODE videoMode; XGetVideoMode( &videoMode ); @@ -106,9 +161,18 @@ bool VideoPanel::BeginPlayback( const char *pFilename ) if ( m_VideoMaterial == NULL ) return false; + if ( m_bLooping ) + { + m_VideoMaterial->SetLooping( true ); + } + + m_bStarted = true; + // We want to be the sole audio source - // FIXME: This may not always be true! - enginesound->NotifyBeginMoviePlayback(); + if ( m_bStopAllSounds ) + { + enginesound->NotifyBeginMoviePlayback(); + } int nWidth, nHeight; m_VideoMaterial->GetVideoImageSize( &nWidth, &nHeight ); @@ -168,9 +232,10 @@ void VideoPanel::DoModal( void ) //----------------------------------------------------------------------------- void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) { - if ( code == KEY_ESCAPE ) + bool bInterruptKeyPressed = ( code == KEY_ESCAPE ); + if ( m_bAllowInterruption && bInterruptKeyPressed ) { - OnClose(); + StopPlayback(); } else { @@ -181,34 +246,54 @@ void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) //----------------------------------------------------------------------------- // Purpose: Handle keys that should cause us to close //----------------------------------------------------------------------------- -void VideoPanel::OnKeyCodePressed( vgui::KeyCode code ) +void VideoPanel::OnKeyCodePressed( vgui::KeyCode keycode ) { + vgui::KeyCode code = GetBaseButtonCode( keycode ); + + // All these keys will interrupt playback + bool bInterruptKeyPressed = ( code == KEY_ESCAPE || + code == KEY_BACKQUOTE || + code == KEY_SPACE || + code == KEY_ENTER || + code == KEY_XBUTTON_A || + code == KEY_XBUTTON_B || + code == KEY_XBUTTON_X || + code == KEY_XBUTTON_Y || + code == KEY_XBUTTON_START || + code == KEY_XBUTTON_BACK ); + // These keys cause the panel to shutdown - if ( code == KEY_ESCAPE || - code == KEY_BACKQUOTE || - code == KEY_SPACE || - code == KEY_ENTER || - code == KEY_XBUTTON_A || - code == KEY_XBUTTON_B || - code == KEY_XBUTTON_X || - code == KEY_XBUTTON_Y || - code == KEY_XBUTTON_START || - code == KEY_XBUTTON_BACK ) + if ( m_bAllowInterruption && bInterruptKeyPressed ) { - OnClose(); + StopPlayback(); } else { - BaseClass::OnKeyCodePressed( code ); + BaseClass::OnKeyCodePressed( keycode ); } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VideoPanel::StopPlayback( void ) +{ + SetVisible( false ); + + // Start the deferred shutdown process + m_nShutdownCount = 1; +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void VideoPanel::OnClose( void ) { - enginesound->NotifyEndMoviePlayback(); + if ( m_bStopAllSounds ) + { + enginesound->NotifyEndMoviePlayback(); + } + BaseClass::OnClose(); if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) @@ -224,7 +309,6 @@ void VideoPanel::OnClose( void ) engine->ClientCmd( m_szExitCommand ); } - SetVisible( false ); MarkForDeletion(); } @@ -251,12 +335,13 @@ void VideoPanel::Paint( void ) { // Issue a close command OnVideoOver(); - OnClose(); + //OnClose(); } // Sit in the "center" int xpos, ypos; GetPanelPos( xpos, ypos ); + LocalToScreen( xpos, ypos ); // Black out the background (we could omit drawing under the video surface, but this is straight-forward) if ( m_bBlackBackground ) @@ -340,16 +425,26 @@ void VideoPanel::Paint( void ) bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, unsigned int nWidth, unsigned int nHeight, const char *pVideoFilename, - const char *pExitCommand /*= NULL*/) + const char *pExitCommand /*= NULL*/, + bool bAllowInterruption /*= true*/, + bool bLooping = false ) { // Create the base video panel - VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth, false ); + VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth ); if ( pVideoPanel == NULL ) return false; + // Toggle if we want the panel to allow interruption + pVideoPanel->SetAllowInterrupt( bAllowInterruption ); + // Set the command we'll call (if any) when the video is interrupted or completes pVideoPanel->SetExitCommand( pExitCommand ); +#ifdef MAPBASE + // Toggle if we want the panel to loop (inspired by Portal 2) + pVideoPanel->SetLooping( bLooping ); +#endif + // Start it going if ( pVideoPanel->BeginPlayback( pVideoFilename ) == false ) { @@ -364,8 +459,29 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, } //----------------------------------------------------------------------------- -// Purpose: Used to launch a video playback (Debug) - -// user must include file extension +// Purpose: Create a video panel with the supplied commands +//----------------------------------------------------------------------------- +void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, int nWidth, int nHeight, bool bAllowInterruption, bool bLooping = false ) +{ + char strFullpath[MAX_PATH]; + Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory + char strFilename[MAX_PATH]; + Q_StripExtension( lpszFilename, strFilename, MAX_PATH ); + Q_strncat( strFullpath, lpszFilename, MAX_PATH ); + + // Use the full screen size if they haven't specified an override + unsigned int nScreenWidth = ( nWidth != 0 ) ? nWidth : ScreenWidth(); + unsigned int nScreenHeight = ( nHeight != 0 ) ? nHeight : ScreenHeight(); + + // Create the panel and go! + if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, lpszExitCommand, bAllowInterruption, bLooping ) == false ) + { + Warning( "Unable to play video: %s\n", strFullpath ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback //----------------------------------------------------------------------------- CON_COMMAND( playvideo, "Plays a video: [width height]" ) @@ -376,53 +492,66 @@ CON_COMMAND( playvideo, "Plays a video: [width height]" ) unsigned int nScreenWidth = Q_atoi( args[2] ); unsigned int nScreenHeight = Q_atoi( args[3] ); - char strFullpath[MAX_PATH]; - Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory - char strFilename[MAX_PATH]; - Q_StripExtension( args[1], strFilename, MAX_PATH ); - Q_strncat( strFullpath, args[1], MAX_PATH ); - - if ( nScreenWidth == 0 ) - { - nScreenWidth = ScreenWidth(); - } - - if ( nScreenHeight == 0 ) - { - nScreenHeight = ScreenHeight(); - } + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, true ); +} - // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath ) == false ) - { - Warning( "Unable to play video: %s\n", strFullpath ); - } +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_nointerrupt, "Plays a video without ability to skip: [width height]" ) +{ + if ( args.ArgC() < 2 ) + return; + + unsigned int nScreenWidth = Q_atoi( args[2] ); + unsigned int nScreenHeight = Q_atoi( args[3] ); + + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback and fire a command on completion +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command when it is stopped or finishes: " ) +{ + if ( args.ArgC() < 2 ) + return; + + // Pull out the exit command we want to use + char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); + + // Check if we should loop + bool bLoop = atoi(args.Arg(3)) != 0; + + CreateVideoPanel( args[1], pExitCommand, 0, 0, true, bLoop ); } //----------------------------------------------------------------------------- // Purpose: Used to launch a video playback and fire a command on completion //----------------------------------------------------------------------------- -CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command when it is stopped or finishes: " ) +CON_COMMAND( playvideo_exitcommand_nointerrupt, "Plays a video (without interruption) and fires and exit command when it is stopped or finishes: " ) { if ( args.ArgC() < 2 ) return; - unsigned int nScreenWidth = ScreenWidth(); - unsigned int nScreenHeight = ScreenHeight(); - - char strFullpath[MAX_PATH]; - Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory - char strFilename[MAX_PATH]; - Q_StripExtension( args[1], strFilename, MAX_PATH ); - Q_strncat( strFullpath, args[1], MAX_PATH ); - + // Pull out the exit command we want to use char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); - // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, pExitCommand ) == false ) + CreateVideoPanel( args[1], pExitCommand, 0, 0, false, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Cause all playback to stop +//----------------------------------------------------------------------------- + +CON_COMMAND( stopvideos, "Stops all videos playing to the screen" ) +{ + FOR_EACH_VEC( g_vecVideoPanels, itr ) { - Warning( "Unable to play video: %s\n", strFullpath ); - engine->ClientCmd( pExitCommand ); + g_vecVideoPanels[itr]->StopPlayback(); } } diff --git a/sp/src/game/client/vgui_video.h b/sp/src/game/client/vgui_video.h index 61a980b1..f31cae24 100644 --- a/sp/src/game/client/vgui_video.h +++ b/sp/src/game/client/vgui_video.h @@ -45,14 +45,19 @@ public: } bool BeginPlayback( const char *pFilename ); + void StopPlayback( void ); void SetBlackBackground( bool bBlack ){ m_bBlackBackground = bBlack; } + void SetAllowInterrupt( bool bAllowInterrupt ) { m_bAllowInterruption = bAllowInterrupt; } +#ifdef MAPBASE + void SetLooping( bool bLooping ) { m_bLooping = bLooping; } +#endif protected: - virtual void OnTick( void ) { BaseClass::OnTick(); } + virtual void OnTick( void ); virtual void OnCommand( const char *pcCommand ) { BaseClass::OnCommand( pcCommand ); } - virtual void OnVideoOver(){} + virtual void OnVideoOver(); protected: IVideoMaterial *m_VideoMaterial; @@ -65,8 +70,14 @@ protected: float m_flU; // U,V ranges for video on its sheet float m_flV; + bool m_bLooping; + bool m_bStopAllSounds; + bool m_bAllowInterruption; bool m_bBlackBackground; bool m_bAllowAlternateMedia; + int m_nShutdownCount; + + bool m_bStarted; }; From a4a292975b9854831abb934834a016f0ffe45d9a Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sat, 6 Mar 2021 01:16:00 -0600 Subject: [PATCH 2/4] Added logic_playmovie and vgui_movie_display from Alien Swarm SDK --- sp/src/game/client/c_movie_display.cpp | 26 ++ sp/src/game/client/c_movie_display.h | 34 ++ sp/src/game/client/client_mapbase.vpc | 3 + sp/src/game/client/vgui_movie_display.cpp | 437 ++++++++++++++++++++++ sp/src/game/server/logic_playmovie.cpp | 101 +++++ sp/src/game/server/movie_display.cpp | 372 ++++++++++++++++++ sp/src/game/server/server_mapbase.vpc | 2 + 7 files changed, 975 insertions(+) create mode 100644 sp/src/game/client/c_movie_display.cpp create mode 100644 sp/src/game/client/c_movie_display.h create mode 100644 sp/src/game/client/vgui_movie_display.cpp create mode 100644 sp/src/game/server/logic_playmovie.cpp create mode 100644 sp/src/game/server/movie_display.cpp diff --git a/sp/src/game/client/c_movie_display.cpp b/sp/src/game/client/c_movie_display.cpp new file mode 100644 index 00000000..2285b97e --- /dev/null +++ b/sp/src/game/client/c_movie_display.cpp @@ -0,0 +1,26 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// +//=====================================================================================// +#include "cbase.h" +#include "c_movie_display.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT( C_MovieDisplay, DT_MovieDisplay, CMovieDisplay ) + RecvPropBool( RECVINFO( m_bEnabled ) ), + RecvPropBool( RECVINFO( m_bLooping ) ), + RecvPropString( RECVINFO( m_szMovieFilename ) ), + RecvPropString( RECVINFO( m_szGroupName ) ), +END_RECV_TABLE() + +C_MovieDisplay::C_MovieDisplay() +{ +} + +C_MovieDisplay::~C_MovieDisplay() +{ +} diff --git a/sp/src/game/client/c_movie_display.h b/sp/src/game/client/c_movie_display.h new file mode 100644 index 00000000..d133e82e --- /dev/null +++ b/sp/src/game/client/c_movie_display.h @@ -0,0 +1,34 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#ifndef C_MOVIE_DISPLAY_H +#define C_MOVIE_DISPLAY_H + +#include "cbase.h" + +class C_MovieDisplay : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_MovieDisplay, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_MovieDisplay(); + ~C_MovieDisplay(); + + bool IsEnabled( void ) const { return m_bEnabled; } + bool IsLooping( void ) const { return m_bLooping; } + + const char *GetMovieFilename( void ) const { return m_szMovieFilename; } + const char *GetGroupName( void ) const { return m_szGroupName; } + +private: + bool m_bEnabled; + bool m_bLooping; + char m_szMovieFilename[128]; + char m_szGroupName[128]; +}; + +#endif //C_MOVIE_DISPLAY_H \ No newline at end of file diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 243324f1..6f448c80 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -31,6 +31,9 @@ $Project $File "c_postprocesscontroller.cpp" $File "c_postprocesscontroller.h" $File "c_env_dof_controller.cpp" + $File "c_movie_display.cpp" + $File "c_movie_display.h" + $File "vgui_movie_display.cpp" $Folder "Mapbase" { diff --git a/sp/src/game/client/vgui_movie_display.cpp b/sp/src/game/client/vgui_movie_display.cpp new file mode 100644 index 00000000..0f474323 --- /dev/null +++ b/sp/src/game/client/vgui_movie_display.cpp @@ -0,0 +1,437 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "cbase.h" +#include "c_vguiscreen.h" +#include "vgui_controls/Label.h" +#include "vgui_BitmapPanel.h" +#include +#include "c_slideshow_display.h" +#include "ienginevgui.h" +#include "fmtstr.h" +#include "vgui_controls/ImagePanel.h" +#include +#include "video/ivideoservices.h" +#include "engine/ienginesound.h" +#include "VGUIMatSurface/IMatSystemSurface.h" +#include "c_movie_display.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +using namespace vgui; + +struct VideoPlaybackInfo_t +{ + VideoPlaybackInfo_t( void ) : + m_pMaterial ( NULL ), + m_nSourceHeight(0), m_nSourceWidth(0), + m_flU(0.0f),m_flV(0.0f) {} + + IMaterial *m_pMaterial; + int m_nSourceHeight, m_nSourceWidth; // Source movie's dimensions + float m_flU, m_flV; // U,V ranges for video on its sheet +}; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class CMovieDisplayScreen : public CVGuiScreenPanel +{ + DECLARE_CLASS( CMovieDisplayScreen, CVGuiScreenPanel ); + +public: + CMovieDisplayScreen( vgui::Panel *parent, const char *panelName ); + ~CMovieDisplayScreen( void ); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick( void ); + virtual void Paint( void ); + +private: + bool IsActive( void ); + + void SetupMovie( void ); + void UpdateMovie( void ); + bool BeginPlayback( const char *pFilename ); + void CalculatePlaybackDimensions( int nSrcWidth, int nSrcHeight ); + + inline void GetPanelPos( int &xpos, int &ypos ) + { + xpos = ( (float) ( GetWide() - m_nPlaybackWidth ) / 2 ); + ypos = ( (float) ( GetTall() - m_nPlaybackHeight ) / 2 ); + } + +private: + + // BINK playback info + IVideoMaterial *m_VideoMaterial; + VideoPlaybackInfo_t m_playbackInfo; + CHandle m_hVGUIScreen; + CHandle m_hScreenEntity; + + int m_nTextureId; + int m_nPlaybackHeight; // Playback dimensions (proper ration adjustments) + int m_nPlaybackWidth; + bool m_bBlackBackground; + bool m_bSlaved; + bool m_bInitialized; + + bool m_bLastActiveState; // HACK: I'd rather get a real callback... + + // VGUI specifics + Label *m_pDisplayTextLabel; + + Color m_cDefault; + Color m_cInvisible; + + bool bIsAlreadyVisible; +}; + +DECLARE_VGUI_SCREEN_FACTORY( CMovieDisplayScreen, "movie_display_screen" ); + +CUtlVector g_MovieDisplays; + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +CMovieDisplayScreen::CMovieDisplayScreen( vgui::Panel *parent, const char *panelName ) +: BaseClass( parent, "CMovieDisplayScreen", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/MovieDisplayScreen.res", "MovieDisplayScreen" ) ) +{ + m_pDisplayTextLabel = new vgui::Label( this, "NumberDisplay", "testing!"); + + m_VideoMaterial = NULL; + m_nTextureId = -1; + m_bBlackBackground = true; + m_bSlaved = false; + m_bInitialized = false; + + // Add ourselves to the global list of movie displays + g_MovieDisplays.AddToTail( this ); + + m_bLastActiveState = IsActive(); +} + +//----------------------------------------------------------------------------- +// Purpose: Clean up the movie +//----------------------------------------------------------------------------- +CMovieDisplayScreen::~CMovieDisplayScreen( void ) +{ + if ( g_pVideo != NULL && m_VideoMaterial != NULL ) + { + g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); + m_VideoMaterial = NULL; + } + + // Clean up our texture reference + g_pMatSystemSurface->DestroyTextureID( m_nTextureId ); + + // Remove ourselves from the global list of movie displays + g_MovieDisplays.FindAndRemove( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup our scheme +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::ApplySchemeSettings( IScheme *pScheme ) +{ + assert( pScheme ); + + m_cDefault = Color( 255, 255, 255, 255 ); + m_cInvisible = Color( 0, 0, 0, 0 ); + + m_pDisplayTextLabel->SetFgColor( m_cDefault ); + m_pDisplayTextLabel->SetText( "" ); + m_pDisplayTextLabel->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + if ( !BaseClass::Init( pKeyValues, pInitData ) ) + return false; + + // Save this for simplicity later on + m_hVGUIScreen = dynamic_cast( GetEntity() ); + if ( m_hVGUIScreen != NULL ) + { + // Also get the associated entity + m_hScreenEntity = dynamic_cast(m_hVGUIScreen->GetOwnerEntity()); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Helper function to check our active state +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::IsActive( void ) +{ + bool bScreenActive = false; + if ( m_hVGUIScreen != NULL ) + { + bScreenActive = m_hVGUIScreen->IsActive(); + } + + return bScreenActive; +} + +//----------------------------------------------------------------------------- +// Purpose: Either become the master of a group of screens, or become a slave to another +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::SetupMovie( void ) +{ + // Only bother if we haven't been setup yet + if ( m_bInitialized ) + return; + + const char *szGroupName = m_hScreenEntity->GetGroupName(); + + CMovieDisplayScreen *pMasterScreen = NULL; + for ( int i = 0; i < g_MovieDisplays.Count(); i++ ) + { + // Must be valid and not us + if ( g_MovieDisplays[i] == NULL || g_MovieDisplays[i] == this ) + continue; + + // Must have an associated movie entity + if ( g_MovieDisplays[i]->m_hScreenEntity == NULL ) + continue; + + // Must have a group name to care + if ( szGroupName[0] == NULL ) + continue; + + // Group names must match! + // FIXME: Use an ID instead? + const char *szTestGroupName = g_MovieDisplays[i]->m_hScreenEntity->GetGroupName(); + if ( Q_strnicmp( szTestGroupName, szGroupName, 128 ) ) + continue; + + // See if we've found a master display + if ( g_MovieDisplays[i]->m_bInitialized && g_MovieDisplays[i]->m_bSlaved == false ) + { + m_bSlaved = true; + + // Share the info from the master + m_playbackInfo = g_MovieDisplays[i]->m_playbackInfo; + + // We need to calculate our own playback dimensions as we may be a different size than our parent + CalculatePlaybackDimensions( m_playbackInfo.m_nSourceWidth, m_playbackInfo.m_nSourceHeight ); + + // Bind our texture + m_nTextureId = surface()->CreateNewTextureID( true ); + g_pMatSystemSurface->DrawSetTextureMaterial( m_nTextureId, m_playbackInfo.m_pMaterial ); + + // Hold this as the master screen + pMasterScreen = g_MovieDisplays[i]; + break; + } + } + + // We need to try again, we have no screen entity! + if ( m_hScreenEntity == NULL ) + return; + + // No master found, become one + if ( pMasterScreen == NULL ) + { + const char *szFilename = m_hScreenEntity->GetMovieFilename(); + BeginPlayback( szFilename ); + m_bSlaved = false; + } + + // Done + m_bInitialized = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Deal with the details of the video playback +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::UpdateMovie( void ) +{ + // Only the master in a group updates the bink file + if ( m_bSlaved ) + return; + + if ( m_VideoMaterial == NULL ) + return; + + // Get the current activity state of the screen + bool bScreenActive = IsActive(); + + // Pause if the game has paused + if ( engine->IsPaused() || engine->Con_IsVisible() ) + { + bScreenActive = false; + } + + // See if we've changed our activity state + if ( bScreenActive != m_bLastActiveState ) + { + m_VideoMaterial->SetPaused( !bScreenActive ); + } + + // Updated + m_bLastActiveState = bScreenActive; + + // Update the frame if we're currently enabled + if ( bScreenActive ) + { + // Update our frame + if ( m_VideoMaterial->Update() == false ) + { + // Issue a close command + // OnVideoOver(); + // StopPlayback(); + } + } +} + +//----------------------------------------------------------------------------- +// Update the display string +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::OnTick() +{ + BaseClass::OnTick(); + + // Create our playback or slave to another screen already playing + SetupMovie(); + + // Now update the movie + UpdateMovie(); +} + +//----------------------------------------------------------------------------- +// Purpose: Adjust the playback dimensions to properly account for our screen dimensions +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::CalculatePlaybackDimensions( int nSrcWidth, int nSrcHeight ) +{ + float flFrameRatio = ( (float) GetWide() / (float) GetTall() ); + float flVideoRatio = ( (float) nSrcWidth / (float) nSrcHeight ); + + if ( flVideoRatio > flFrameRatio ) + { + m_nPlaybackWidth = GetWide(); + m_nPlaybackHeight = ( GetWide() / flVideoRatio ); + } + else if ( flVideoRatio < flFrameRatio ) + { + m_nPlaybackWidth = ( GetTall() * flVideoRatio ); + m_nPlaybackHeight = GetTall(); + } + else + { + m_nPlaybackWidth = GetWide(); + m_nPlaybackHeight = GetTall(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Begins playback of a movie +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::BeginPlayback( const char *pFilename ) +{ + // need working video services + if ( g_pVideo == NULL ) + return false; + + // Create a new video material + if ( m_VideoMaterial != NULL ) + { + g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); + m_VideoMaterial = NULL; + } + + // Create a globally unique name for this material + char szMaterialName[256]; + + // Append our group name if we have one + const char *szGroupName = m_hScreenEntity->GetGroupName(); + if ( szGroupName[0] != NULL ) + { + Q_snprintf( szMaterialName, sizeof(szMaterialName), "%s_%s", pFilename, szGroupName ); + } + else + { + Q_strncpy( szMaterialName, pFilename, sizeof(szMaterialName) ); + } + + const char *pszMaterialName = CFmtStrN<128>( "VideoMaterial_", m_hScreenEntity->GetEntityName() ); + m_VideoMaterial = g_pVideo->CreateVideoMaterial( pszMaterialName, pFilename, "GAME", + VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS, + VideoSystem::DETERMINE_FROM_FILE_EXTENSION/*, m_bAllowAlternateMedia*/ ); + + if ( m_VideoMaterial == NULL ) + return false; + + m_VideoMaterial->SetMuted( true ); // FIXME: Allow? + + if ( m_hScreenEntity->IsLooping() ) + { + m_VideoMaterial->SetLooping( true ); + } + + if ( m_VideoMaterial->HasAudio() ) + { + // We want to be the sole audio source + enginesound->NotifyBeginMoviePlayback(); + } + + // Get our basic info from the movie + m_VideoMaterial->GetVideoImageSize( &m_playbackInfo.m_nSourceWidth, &m_playbackInfo.m_nSourceHeight ); + m_VideoMaterial->GetVideoTexCoordRange( &m_playbackInfo.m_flU, &m_playbackInfo.m_flV ); + m_playbackInfo.m_pMaterial = m_VideoMaterial->GetMaterial(); + + // Get our playback dimensions + CalculatePlaybackDimensions( m_playbackInfo.m_nSourceWidth, m_playbackInfo.m_nSourceHeight ); + + // Bind our texture + m_nTextureId = surface()->CreateNewTextureID( true ); + g_pMatSystemSurface->DrawSetTextureMaterial( m_nTextureId, m_playbackInfo.m_pMaterial ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update and draw the frame +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::Paint( void ) +{ + // Masters must keep the video updated + if ( m_bSlaved == false && m_VideoMaterial == NULL ) + { + BaseClass::Paint(); + return; + } + + // Sit in the "center" + int xpos, ypos; + GetPanelPos( xpos, ypos ); + + // Black out the background (we could omit drawing under the video surface, but this is straight-forward) + if ( m_bBlackBackground ) + { + surface()->DrawSetColor( 0, 0, 0, 255 ); + surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); + } + + // Draw it + surface()->DrawSetTexture( m_nTextureId ); + surface()->DrawSetColor( 255, 255, 255, 255 ); + surface()->DrawTexturedSubRect( xpos, ypos, xpos+m_nPlaybackWidth, ypos+m_nPlaybackHeight, 0.0f, 0.0f, m_playbackInfo.m_flU, m_playbackInfo.m_flV ); + + // Parent's turn + BaseClass::Paint(); +} diff --git a/sp/src/game/server/logic_playmovie.cpp b/sp/src/game/server/logic_playmovie.cpp new file mode 100644 index 00000000..1ea25fee --- /dev/null +++ b/sp/src/game/server/logic_playmovie.cpp @@ -0,0 +1,101 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: Plays a movie and reports on finish +// +//===========================================================================// + +#include "cbase.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLogicPlayMovie : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicPlayMovie, CLogicalEntity ); + DECLARE_DATADESC(); + + CLogicPlayMovie( void ) { } + ~CLogicPlayMovie( void ) { } + + virtual void Precache( void ); + virtual void Spawn( void ); + +private: + + void InputPlayMovie( inputdata_t &data ); + void InputMovieFinished( inputdata_t &data ); + + string_t m_strMovieFilename; + bool m_bAllowUserSkip; +#ifdef MAPBASE + bool m_bLooping; +#endif + + COutputEvent m_OnPlaybackFinished; +}; + +LINK_ENTITY_TO_CLASS( logic_playmovie, CLogicPlayMovie ); + +BEGIN_DATADESC( CLogicPlayMovie ) + + DEFINE_KEYFIELD( m_strMovieFilename, FIELD_STRING, "MovieFilename" ), + DEFINE_KEYFIELD( m_bAllowUserSkip, FIELD_BOOLEAN, "allowskip" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "loopvideo" ), +#endif + + DEFINE_INPUTFUNC( FIELD_VOID, "PlayMovie", InputPlayMovie ), + DEFINE_INPUTFUNC( FIELD_VOID, "__MovieFinished", InputMovieFinished ), + + DEFINE_OUTPUT( m_OnPlaybackFinished, "OnPlaybackFinished" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::Precache( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::Spawn( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputPlayMovie( inputdata_t &data ) +{ + const char *szVideoCommand = ( m_bAllowUserSkip ) ? "playvideo_exitcommand" : "playvideo_exitcommand_nointerrupt"; + // Build the hacked string + + char szClientCmd[256]; + Q_snprintf( szClientCmd, sizeof(szClientCmd), + "%s %s \"ent_fire %s __MovieFinished\" %s\n", + szVideoCommand, + STRING(m_strMovieFilename), + GetEntityNameAsCStr(), +#ifdef MAPBASE + m_bLooping ? "1" : "0" +#else + "0" +#endif + ); + + // Send it on + engine->ServerCommand( szClientCmd ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputMovieFinished( inputdata_t &data ) +{ + // Simply fire our output + m_OnPlaybackFinished.FireOutput( this, this ); +} diff --git a/sp/src/game/server/movie_display.cpp b/sp/src/game/server/movie_display.cpp new file mode 100644 index 00000000..5c31fb23 --- /dev/null +++ b/sp/src/game/server/movie_display.cpp @@ -0,0 +1,372 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: Allows movies to be played as a VGUI screen in the world +// +//=====================================================================================// + +#include "cbase.h" +#include "EnvMessage.h" +#include "fmtstr.h" +#include "vguiscreen.h" +#include "filesystem.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +class CMovieDisplay : public CBaseEntity +{ +public: + + DECLARE_CLASS( CMovieDisplay, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + virtual ~CMovieDisplay(); + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + virtual int UpdateTransmitState(); + virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); + + virtual void Spawn( void ); + virtual void Precache( void ); + virtual void OnRestore( void ); + + void ScreenVisible( bool bVisible ); + + void Disable( void ); + void Enable( void ); + + void InputDisable( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + + void InputSetDisplayText( inputdata_t &inputdata ); + +private: + + // Control panel + void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ); + void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ); + void SpawnControlPanels( void ); + void RestoreControlPanels( void ); + +private: + CNetworkVar( bool, m_bEnabled ); + CNetworkVar( bool, m_bLooping ); + + CNetworkString( m_szDisplayText, 128 ); + + // Filename of the movie to play + CNetworkString( m_szMovieFilename, 128 ); + string_t m_strMovieFilename; + + // "Group" name. Screens of the same group name will play the same movie at the same time + // Effectively this lets multiple screens tune to the same "channel" in the world + CNetworkString( m_szGroupName, 128 ); + string_t m_strGroupName; + + int m_iScreenWidth; + int m_iScreenHeight; + + bool m_bDoFullTransmit; + + CHandle m_hScreen; +}; + +LINK_ENTITY_TO_CLASS( vgui_movie_display, CMovieDisplay ); + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- +BEGIN_DATADESC( CMovieDisplay ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_AUTO_ARRAY_KEYFIELD( m_szDisplayText, FIELD_CHARACTER, "displaytext" ), + + DEFINE_AUTO_ARRAY( m_szMovieFilename, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_strMovieFilename, FIELD_STRING, "moviefilename" ), + + DEFINE_AUTO_ARRAY( m_szGroupName, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_strGroupName, FIELD_STRING, "groupname" ), + + DEFINE_KEYFIELD( m_iScreenWidth, FIELD_INTEGER, "width" ), + DEFINE_KEYFIELD( m_iScreenHeight, FIELD_INTEGER, "height" ), + DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "looping" ), + + DEFINE_FIELD( m_bDoFullTransmit, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_hScreen, FIELD_EHANDLE ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetDisplayText", InputSetDisplayText ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CMovieDisplay, DT_MovieDisplay ) + SendPropBool( SENDINFO( m_bEnabled ) ), + SendPropBool( SENDINFO( m_bLooping ) ), + SendPropString( SENDINFO( m_szMovieFilename ) ), + SendPropString( SENDINFO( m_szGroupName ) ), +END_SEND_TABLE() + +CMovieDisplay::~CMovieDisplay() +{ + DestroyVGuiScreen( m_hScreen.Get() ); +} + +//----------------------------------------------------------------------------- +// Read in Hammer data +//----------------------------------------------------------------------------- +bool CMovieDisplay::KeyValue( const char *szKeyName, const char *szValue ) +{ + // NOTE: Have to do these separate because they set two values instead of one + if( FStrEq( szKeyName, "angles" ) ) + { + Assert( GetMoveParent() == NULL ); + QAngle angles; + UTIL_StringToVector( angles.Base(), szValue ); + + // Because the vgui screen basis is strange (z is front, y is up, x is right) + // we need to rotate the typical basis before applying it + VMatrix mat, rotation, tmp; + MatrixFromAngles( angles, mat ); + MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 ); + MatrixMultiply( mat, rotation, tmp ); + MatrixBuildRotateZ( rotation, 90 ); + MatrixMultiply( tmp, rotation, mat ); + MatrixToAngles( mat, angles ); + SetAbsAngles( angles ); + + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMovieDisplay::UpdateTransmitState() +{ + if ( m_bDoFullTransmit ) + { + m_bDoFullTransmit = false; + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + return SetTransmitState( FL_EDICT_FULLCHECK ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) +{ + // Are we already marked for transmission? + if ( pInfo->m_pTransmitEdict->Get( entindex() ) ) + return; + + BaseClass::SetTransmit( pInfo, bAlways ); + + // Force our screen to be sent too. + m_hScreen->SetTransmit( pInfo, bAlways ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Spawn( void ) +{ + // Move the strings into a networkable form + Q_strcpy( m_szMovieFilename.GetForModify(), m_strMovieFilename.ToCStr() ); + Q_strcpy( m_szGroupName.GetForModify(), m_strGroupName.ToCStr() ); + + Precache(); + + BaseClass::Spawn(); + + m_bEnabled = false; + + SpawnControlPanels(); + + ScreenVisible( m_bEnabled ); + + m_bDoFullTransmit = true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheVGuiScreen( "video_display_screen" ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::OnRestore( void ) +{ + BaseClass::OnRestore(); + + RestoreControlPanels(); + + ScreenVisible( m_bEnabled ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::ScreenVisible( bool bVisible ) +{ + // Set its active state + m_hScreen->SetActive( bVisible ); + + if ( bVisible ) + { + m_hScreen->RemoveEffects( EF_NODRAW ); + } + else + { + m_hScreen->AddEffects( EF_NODRAW ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Disable( void ) +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + + ScreenVisible( false ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Enable( void ) +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + + ScreenVisible( true ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputSetDisplayText( inputdata_t &inputdata ) +{ + Q_strcpy( m_szDisplayText.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "movie_display_screen"; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "vgui_screen"; +} + +//----------------------------------------------------------------------------- +// This is called by the base object when it's time to spawn the control panels +//----------------------------------------------------------------------------- +void CMovieDisplay::SpawnControlPanels() +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + float flWidth = m_iScreenWidth; + float flHeight = m_iScreenHeight; + + CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, this, this, 0 ); + pScreen->ChangeTeam( GetTeamNumber() ); + pScreen->SetActualSize( flWidth, flHeight ); + pScreen->SetActive( true ); + pScreen->MakeVisibleOnlyToTeammates( false ); + pScreen->SetTransparency( true ); + m_hScreen = pScreen; + + return; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::RestoreControlPanels( void ) +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname ); + + while ( ( pScreen && pScreen->GetOwnerEntity() != this ) || Q_strcmp( pScreen->GetPanelName(), pScreenName ) != 0 ) + { + pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname ); + } + + if ( pScreen ) + { + m_hScreen = pScreen; + m_hScreen->SetActive( true ); + } + + return; + } +} diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 37146238..72f48e8d 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -27,6 +27,8 @@ $Project $File "postprocesscontroller.h" $File "env_dof_controller.cpp" $File "env_dof_controller.h" + $File "logic_playmovie.cpp" + $File "movie_display.cpp" $Folder "Mapbase" { From c4abb0b07fb287aac20bf508b6d2c4f890cfdf40 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sun, 7 Mar 2021 11:39:50 -0600 Subject: [PATCH 3/4] Made HUD movie parameters more flexible; Added WIP movie fade effects for replicating the Portal 2 keyvalues --- sp/src/game/client/vgui_video.cpp | 116 +++++++++++++++++++++---- sp/src/game/client/vgui_video.h | 5 ++ sp/src/game/server/logic_playmovie.cpp | 9 +- 3 files changed, 109 insertions(+), 21 deletions(-) diff --git a/sp/src/game/client/vgui_video.cpp b/sp/src/game/client/vgui_video.cpp index 39ab6b6c..2fa66cf7 100644 --- a/sp/src/game/client/vgui_video.cpp +++ b/sp/src/game/client/vgui_video.cpp @@ -34,6 +34,22 @@ void VGui_ClearVideoPanels() g_vecVideoPanels.RemoveAll(); } +struct VideoPanelParms_t +{ + VideoPanelParms_t( bool _interrupt = true, bool _loop = false, float _fadein = 0.0f, float _fadeout = 0.0f ) + { + bAllowInterrupt = _interrupt; + bLoop = _loop; + flFadeIn = _fadein; + flFadeOut = _fadeout; + } + + bool bAllowInterrupt; + bool bLoop; + float flFadeIn; + float flFadeOut; +}; + VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHeight, unsigned int nWidth, bool allowAlternateMedia ) : BaseClass( NULL, "VideoPanel" ), m_VideoMaterial( NULL ), @@ -331,6 +347,27 @@ void VideoPanel::Paint( void ) if ( m_VideoMaterial == NULL ) return; + float alpha = ((float)GetFgColor()[3]/255.0f); +#ifdef MAPBASE + if (m_flFadeIn != 0.0f || m_flFadeOut != 0.0f) + { + // GetCurrentVideoTime() and GetVideoDuration() are borked + float flFrameCount = m_VideoMaterial->GetFrameCount(); + float flEnd = flFrameCount / m_VideoMaterial->GetVideoFrameRate().GetFPS(); + float flTime = ((float)(m_VideoMaterial->GetCurrentFrame()) / flFrameCount) * flEnd; + float flFadeOutDelta = (flEnd - m_flFadeOut); + + if (flTime <= m_flFadeIn) + { + alpha = (flTime / m_flFadeIn); + } + else if (flTime >= flFadeOutDelta) + { + alpha = (1.0f - ((flTime - flFadeOutDelta) / m_flFadeOut)); + } + } +#endif + if ( m_VideoMaterial->Update() == false ) { // Issue a close command @@ -346,12 +383,16 @@ void VideoPanel::Paint( void ) // Black out the background (we could omit drawing under the video surface, but this is straight-forward) if ( m_bBlackBackground ) { - vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawSetColor( 0, 0, 0, alpha * 255.0f ); vgui::surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); } // Draw the polys to draw this out CMatRenderContextPtr pRenderContext( materials ); + +#ifdef MAPBASE + pRenderContext->ClearColor4ub( 255, 255, 255, alpha * 255.0f ); +#endif pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->PushMatrix(); @@ -391,8 +432,6 @@ void VideoPanel::Paint( void ) flTopY = FLerp( 1, -1, 0, vh ,flTopY ); flBottomY = FLerp( 1, -1, 0, vh, flBottomY ); - float alpha = ((float)GetFgColor()[3]/255.0f); - for ( int corner=0; corner<4; corner++ ) { bool bLeft = (corner==0) || (corner==3); @@ -426,8 +465,7 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, unsigned int nWidth, unsigned int nHeight, const char *pVideoFilename, const char *pExitCommand /*= NULL*/, - bool bAllowInterruption /*= true*/, - bool bLooping = false ) + const VideoPanelParms_t &parms ) { // Create the base video panel VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth ); @@ -435,14 +473,17 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, return false; // Toggle if we want the panel to allow interruption - pVideoPanel->SetAllowInterrupt( bAllowInterruption ); + pVideoPanel->SetAllowInterrupt( parms.bAllowInterrupt ); // Set the command we'll call (if any) when the video is interrupted or completes pVideoPanel->SetExitCommand( pExitCommand ); #ifdef MAPBASE // Toggle if we want the panel to loop (inspired by Portal 2) - pVideoPanel->SetLooping( bLooping ); + pVideoPanel->SetLooping( parms.bLoop ); + + // Fade parameters + pVideoPanel->SetFade( parms.flFadeIn, parms.flFadeOut ); #endif // Start it going @@ -461,7 +502,7 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, //----------------------------------------------------------------------------- // Purpose: Create a video panel with the supplied commands //----------------------------------------------------------------------------- -void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, int nWidth, int nHeight, bool bAllowInterruption, bool bLooping = false ) +void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, int nWidth, int nHeight, VideoPanelParms_t &parms ) { char strFullpath[MAX_PATH]; Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory @@ -474,7 +515,7 @@ void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, in unsigned int nScreenHeight = ( nHeight != 0 ) ? nHeight : ScreenHeight(); // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, lpszExitCommand, bAllowInterruption, bLooping ) == false ) + if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, lpszExitCommand, parms ) == false ) { Warning( "Unable to play video: %s\n", strFullpath ); } @@ -491,8 +532,11 @@ CON_COMMAND( playvideo, "Plays a video: [width height]" ) unsigned int nScreenWidth = Q_atoi( args[2] ); unsigned int nScreenHeight = Q_atoi( args[3] ); + + // New struct; functionally identical + VideoPanelParms_t parms; - CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, true ); + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, parms ); } //----------------------------------------------------------------------------- @@ -507,7 +551,10 @@ CON_COMMAND( playvideo_nointerrupt, "Plays a video without ability to skip: " ) +CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command when it is stopped or finishes: " ) { if ( args.ArgC() < 2 ) return; @@ -523,10 +570,10 @@ CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command wh // Pull out the exit command we want to use char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); - // Check if we should loop - bool bLoop = atoi(args.Arg(3)) != 0; + // New struct; functionally identical + VideoPanelParms_t parms; - CreateVideoPanel( args[1], pExitCommand, 0, 0, true, bLoop ); + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); } //----------------------------------------------------------------------------- @@ -541,7 +588,10 @@ CON_COMMAND( playvideo_exitcommand_nointerrupt, "Plays a video (without interrup // Pull out the exit command we want to use char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); - CreateVideoPanel( args[1], pExitCommand, 0, 0, false, false ); + // New struct; functionally identical + VideoPanelParms_t parms( false ); + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); } //----------------------------------------------------------------------------- @@ -555,3 +605,37 @@ CON_COMMAND( stopvideos, "Stops all videos playing to the screen" ) g_vecVideoPanels[itr]->StopPlayback(); } } + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback and fire a command on completion +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_complex, "Plays a video with various parameters to simplify logic_playmovie: " ) +{ + if ( args.ArgC() < 2 ) + return; + + // Pull out the exit command we want to use + char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); + + // Parameters + VideoPanelParms_t parms; + + if (args.ArgC() >= 3) + parms.bAllowInterrupt = atoi( args[3] ) != 1; + if (args.ArgC() >= 4) + parms.bLoop = atoi( args[4] ) != 0; + if (args.ArgC() >= 5) + parms.flFadeIn = atof( args[5] ); + if (args.ArgC() >= 6) + parms.flFadeOut = atof( args[6] ); + + // Stop a softlock + if (parms.bAllowInterrupt == false && parms.bLoop) + { + Warning( "WARNING: Tried to play video set to be uninterruptible and looping. This would cause a softlock because the video loops forever and there's no way to stop it.\n" ); + return; + } + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} diff --git a/sp/src/game/client/vgui_video.h b/sp/src/game/client/vgui_video.h index f31cae24..9e76ace0 100644 --- a/sp/src/game/client/vgui_video.h +++ b/sp/src/game/client/vgui_video.h @@ -51,6 +51,7 @@ public: void SetAllowInterrupt( bool bAllowInterrupt ) { m_bAllowInterruption = bAllowInterrupt; } #ifdef MAPBASE void SetLooping( bool bLooping ) { m_bLooping = bLooping; } + void SetFade( float flStartFade, float flEndFade ) { m_flFadeIn = flStartFade; m_flFadeOut = flEndFade; } #endif protected: @@ -71,6 +72,10 @@ protected: float m_flV; bool m_bLooping; +#ifdef MAPBASE + float m_flFadeIn; + float m_flFadeOut; +#endif bool m_bStopAllSounds; bool m_bAllowInterruption; bool m_bBlackBackground; diff --git a/sp/src/game/server/logic_playmovie.cpp b/sp/src/game/server/logic_playmovie.cpp index 1ea25fee..41ce74e9 100644 --- a/sp/src/game/server/logic_playmovie.cpp +++ b/sp/src/game/server/logic_playmovie.cpp @@ -71,19 +71,18 @@ void CLogicPlayMovie::Spawn( void ) //----------------------------------------------------------------------------- void CLogicPlayMovie::InputPlayMovie( inputdata_t &data ) { - const char *szVideoCommand = ( m_bAllowUserSkip ) ? "playvideo_exitcommand" : "playvideo_exitcommand_nointerrupt"; // Build the hacked string char szClientCmd[256]; Q_snprintf( szClientCmd, sizeof(szClientCmd), - "%s %s \"ent_fire %s __MovieFinished\" %s\n", - szVideoCommand, + "playvideo_complex %s \"ent_fire %s __MovieFinished\" %d %d\n", STRING(m_strMovieFilename), GetEntityNameAsCStr(), + m_bAllowUserSkip, #ifdef MAPBASE - m_bLooping ? "1" : "0" + m_bLooping #else - "0" + 0 #endif ); From 9432ecc40c4b86f618a453b7d9113e7fa87120bf Mon Sep 17 00:00:00 2001 From: Blixibon Date: Fri, 12 Mar 2021 10:15:09 -0600 Subject: [PATCH 4/4] Added new "mute" keyvalue and StopMovie input to logic_playmovie --- sp/src/game/client/vgui_video.cpp | 42 +++++++++++++++++++------- sp/src/game/client/vgui_video.h | 3 ++ sp/src/game/server/logic_playmovie.cpp | 40 ++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/sp/src/game/client/vgui_video.cpp b/sp/src/game/client/vgui_video.cpp index 2fa66cf7..17e28aa2 100644 --- a/sp/src/game/client/vgui_video.cpp +++ b/sp/src/game/client/vgui_video.cpp @@ -36,18 +36,19 @@ void VGui_ClearVideoPanels() struct VideoPanelParms_t { - VideoPanelParms_t( bool _interrupt = true, bool _loop = false, float _fadein = 0.0f, float _fadeout = 0.0f ) + VideoPanelParms_t( bool _interrupt = true, bool _loop = false, bool _mute = false ) { bAllowInterrupt = _interrupt; bLoop = _loop; - flFadeIn = _fadein; - flFadeOut = _fadeout; + bMute = _mute; } bool bAllowInterrupt; bool bLoop; - float flFadeIn; - float flFadeOut; + bool bMute; + + //float flFadeIn; + //float flFadeOut; }; VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHeight, unsigned int nWidth, bool allowAlternateMedia ) : @@ -182,6 +183,13 @@ bool VideoPanel::BeginPlayback( const char *pFilename ) m_VideoMaterial->SetLooping( true ); } +#ifdef MAPBASE + if ( m_bMuted ) + { + m_VideoMaterial->SetMuted( true ); + } +#endif + m_bStarted = true; // We want to be the sole audio source @@ -482,8 +490,17 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, // Toggle if we want the panel to loop (inspired by Portal 2) pVideoPanel->SetLooping( parms.bLoop ); - // Fade parameters - pVideoPanel->SetFade( parms.flFadeIn, parms.flFadeOut ); + // Toggle if we want the panel to be muted + pVideoPanel->SetMuted( parms.bMute ); + + // TODO: Unique "Stop All Sounds" parameter + if (parms.bMute) + { + pVideoPanel->SetStopAllSounds( false ); + } + + // Fade parameters (unfinished) + //pVideoPanel->SetFade( parms.flFadeIn, parms.flFadeOut ); #endif // Start it going @@ -622,13 +639,16 @@ CON_COMMAND( playvideo_complex, "Plays a video with various parameters to simpli VideoPanelParms_t parms; if (args.ArgC() >= 3) - parms.bAllowInterrupt = atoi( args[3] ) != 1; + parms.bAllowInterrupt = atoi( args[3] ) != 0; if (args.ArgC() >= 4) parms.bLoop = atoi( args[4] ) != 0; if (args.ArgC() >= 5) - parms.flFadeIn = atof( args[5] ); - if (args.ArgC() >= 6) - parms.flFadeOut = atof( args[6] ); + parms.bMute = atoi( args[5] ) != 0; + + //if (args.ArgC() >= 5) + // parms.flFadeIn = atof( args[5] ); + //if (args.ArgC() >= 6) + // parms.flFadeOut = atof( args[6] ); // Stop a softlock if (parms.bAllowInterrupt == false && parms.bLoop) diff --git a/sp/src/game/client/vgui_video.h b/sp/src/game/client/vgui_video.h index 9e76ace0..b413d832 100644 --- a/sp/src/game/client/vgui_video.h +++ b/sp/src/game/client/vgui_video.h @@ -49,8 +49,10 @@ public: void SetBlackBackground( bool bBlack ){ m_bBlackBackground = bBlack; } void SetAllowInterrupt( bool bAllowInterrupt ) { m_bAllowInterruption = bAllowInterrupt; } + void SetStopAllSounds( bool bStopAllSounds ) { m_bStopAllSounds = bStopAllSounds; } #ifdef MAPBASE void SetLooping( bool bLooping ) { m_bLooping = bLooping; } + void SetMuted( bool bMuted ) { m_bMuted = bMuted; } void SetFade( float flStartFade, float flEndFade ) { m_flFadeIn = flStartFade; m_flFadeOut = flEndFade; } #endif @@ -75,6 +77,7 @@ protected: #ifdef MAPBASE float m_flFadeIn; float m_flFadeOut; + bool m_bMuted; #endif bool m_bStopAllSounds; bool m_bAllowInterruption; diff --git a/sp/src/game/server/logic_playmovie.cpp b/sp/src/game/server/logic_playmovie.cpp index 41ce74e9..4e62f738 100644 --- a/sp/src/game/server/logic_playmovie.cpp +++ b/sp/src/game/server/logic_playmovie.cpp @@ -24,12 +24,18 @@ public: private: void InputPlayMovie( inputdata_t &data ); +#ifdef MAPBASE + void InputStopMovie( inputdata_t &data ); +#endif void InputMovieFinished( inputdata_t &data ); string_t m_strMovieFilename; bool m_bAllowUserSkip; #ifdef MAPBASE bool m_bLooping; + bool m_bMuted; + + bool m_bPlayingVideo; #endif COutputEvent m_OnPlaybackFinished; @@ -43,9 +49,15 @@ BEGIN_DATADESC( CLogicPlayMovie ) DEFINE_KEYFIELD( m_bAllowUserSkip, FIELD_BOOLEAN, "allowskip" ), #ifdef MAPBASE DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "loopvideo" ), + DEFINE_KEYFIELD( m_bMuted, FIELD_BOOLEAN, "mute" ), + + DEFINE_FIELD( m_bPlayingVideo, FIELD_BOOLEAN ), #endif DEFINE_INPUTFUNC( FIELD_VOID, "PlayMovie", InputPlayMovie ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "StopMovie", InputStopMovie ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "__MovieFinished", InputMovieFinished ), DEFINE_OUTPUT( m_OnPlaybackFinished, "OnPlaybackFinished" ), @@ -75,21 +87,41 @@ void CLogicPlayMovie::InputPlayMovie( inputdata_t &data ) char szClientCmd[256]; Q_snprintf( szClientCmd, sizeof(szClientCmd), - "playvideo_complex %s \"ent_fire %s __MovieFinished\" %d %d\n", + "playvideo_complex %s \"ent_fire %s __MovieFinished\" %d %d %d\n", STRING(m_strMovieFilename), GetEntityNameAsCStr(), m_bAllowUserSkip, #ifdef MAPBASE - m_bLooping + m_bLooping, + m_bMuted #else + 0, 0 #endif ); // Send it on engine->ServerCommand( szClientCmd ); + +#ifdef MAPBASE + m_bPlayingVideo = true; +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputStopMovie( inputdata_t &data ) +{ + if (m_bPlayingVideo) + { + // Send it on + engine->ServerCommand( "stopvideos\n" ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -97,4 +129,8 @@ void CLogicPlayMovie::InputMovieFinished( inputdata_t &data ) { // Simply fire our output m_OnPlaybackFinished.FireOutput( this, this ); + +#ifdef MAPBASE + m_bPlayingVideo = false; +#endif }