aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/c_sceneentity.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/c_sceneentity.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/c_sceneentity.cpp')
-rw-r--r--mp/src/game/client/c_sceneentity.cpp1211
1 files changed, 1211 insertions, 0 deletions
diff --git a/mp/src/game/client/c_sceneentity.cpp b/mp/src/game/client/c_sceneentity.cpp
new file mode 100644
index 00000000..d56a67c8
--- /dev/null
+++ b/mp/src/game/client/c_sceneentity.cpp
@@ -0,0 +1,1211 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "networkstringtable_clientdll.h"
+#include "dt_utlvector_recv.h"
+#include "choreoevent.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "filesystem.h"
+#include "ichoreoeventcallback.h"
+#include "scenefilecache/ISceneFileCache.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "tier2/tier2.h"
+#include "hud_closecaption.h"
+#include "tier0/icommandline.h"
+
+#include "c_sceneentity.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes animtime and notes when it changes
+// Input : *pStruct - ( C_BaseEntity * ) used to flag animtime is changine
+// *pVarData -
+// *pIn -
+// objectID -
+//-----------------------------------------------------------------------------
+void RecvProxy_ForcedClientTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_SceneEntity *pScene = reinterpret_cast< C_SceneEntity * >( pStruct );
+ *(float *)pOut = pData->m_Value.m_Float;
+ pScene->OnResetClientTime();
+}
+
+#if defined( CSceneEntity )
+#undef CSceneEntity
+#endif
+
+IMPLEMENT_CLIENTCLASS_DT(C_SceneEntity, DT_SceneEntity, CSceneEntity)
+ RecvPropInt(RECVINFO(m_nSceneStringIndex)),
+ RecvPropBool(RECVINFO(m_bIsPlayingBack)),
+ RecvPropBool(RECVINFO(m_bPaused)),
+ RecvPropBool(RECVINFO(m_bMultiplayer)),
+ RecvPropFloat(RECVINFO(m_flForceClientTime), 0, RecvProxy_ForcedClientTime ),
+ RecvPropUtlVector(
+ RECVINFO_UTLVECTOR( m_hActorList ),
+ MAX_ACTORS_IN_SCENE,
+ RecvPropEHandle(NULL, 0, 0)),
+END_RECV_TABLE()
+
+C_SceneEntity::C_SceneEntity( void )
+{
+ m_pScene = NULL;
+ m_bMultiplayer = false;
+
+ m_hOwner = NULL;
+ m_bClientOnly = false;
+}
+
+C_SceneEntity::~C_SceneEntity( void )
+{
+ UnloadScene();
+}
+
+void C_SceneEntity::OnResetClientTime()
+{
+ // In TF2 we ignore this as the scene is played entirely client-side.
+#ifndef TF_CLIENT_DLL
+ m_flCurrentTime = m_flForceClientTime;
+#endif
+}
+
+char const *C_SceneEntity::GetSceneFileName()
+{
+ return g_pStringTableClientSideChoreoScenes->GetString( m_nSceneStringIndex );
+}
+
+ConVar mp_usehwmvcds( "mp_usehwmvcds", "0", NULL, "Enable the use of the hw morph vcd(s). (-1 = never, 1 = always, 0 = based upon GPU)" ); // -1 = never, 0 = if hasfastvertextextures, 1 = always
+bool UseHWMorphVCDs()
+{
+// if ( mp_usehwmvcds.GetInt() == 0 )
+// return g_pMaterialSystemHardwareConfig->HasFastVertexTextures();
+// return mp_usehwmvcds.GetInt() > 0;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_SceneEntity::GetHWMorphSceneFileName( const char *pFilename, char *pHWMFilename )
+{
+ // Are we even using hardware morph?
+ if ( !UseHWMorphVCDs() )
+ return false;
+
+ // Multi-player only!
+ if ( !m_bMultiplayer )
+ return false;
+
+ // Do we have a valid filename?
+ if ( !( pFilename && pFilename[0] ) )
+ return false;
+
+ // Check to see if we already have an player/hwm/* filename.
+ if ( ( V_strstr( pFilename, "/high" ) != NULL ) || ( V_strstr( pFilename, "\\high" ) != NULL ) )
+ {
+ V_strcpy( pHWMFilename, pFilename );
+ return true;
+ }
+
+ // Find the hardware morph scene name and pass that along as well.
+ char szScene[MAX_PATH];
+ V_strcpy( szScene, pFilename );
+
+ char szSceneHWM[MAX_PATH];
+ szSceneHWM[0] = '\0';
+
+ char *pszToken = strtok( szScene, "/\\" );
+ while ( pszToken != NULL )
+ {
+ if ( !V_stricmp( pszToken, "low" ) )
+ {
+ V_strcat( szSceneHWM, "high", sizeof( szSceneHWM ) );
+ }
+ else
+ {
+ V_strcat( szSceneHWM, pszToken, sizeof( szSceneHWM ) );
+ }
+
+ pszToken = strtok( NULL, "/\\" );
+ if ( pszToken != NULL )
+ {
+ V_strcat( szSceneHWM, "\\", sizeof( szSceneHWM ) );
+ }
+ }
+
+ V_strcpy( pHWMFilename, szSceneHWM );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_SceneEntity::ResetActorFlexesForScene()
+{
+ int nActorCount = m_pScene->GetNumActors();
+ for( int iActor = 0; iActor < nActorCount; ++iActor )
+ {
+ CChoreoActor *pChoreoActor = m_pScene->GetActor( iActor );
+ if ( !pChoreoActor )
+ continue;
+
+ C_BaseFlex *pFlexActor = FindNamedActor( pChoreoActor );
+ if ( !pFlexActor )
+ continue;
+
+ CStudioHdr *pStudioHdr = pFlexActor->GetModelPtr();
+ if ( !pStudioHdr )
+ continue;
+
+ if ( pStudioHdr->numflexdesc() == 0 )
+ continue;
+
+ // Reset the flex weights to their starting position.
+ LocalFlexController_t iController;
+ for ( iController = LocalFlexController_t(0); iController < pStudioHdr->numflexcontrollers(); ++iController )
+ {
+ pFlexActor->SetFlexWeight( iController, 0.0f );
+ }
+
+ // Reset the prediction interpolation values.
+ pFlexActor->m_iv_flexWeight.Reset();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_SceneEntity::StopClientOnlyScene()
+{
+ if ( m_pScene )
+ {
+ m_pScene->ResetSimulation();
+
+ if ( m_hOwner.Get() )
+ {
+ m_hOwner->RemoveChoreoScene( m_pScene );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_SceneEntity::SetupClientOnlyScene( const char *pszFilename, C_BaseFlex *pOwner /* = NULL */, bool bMultiplayer /* = false */ )
+{
+ m_bIsPlayingBack = true;
+ m_bMultiplayer = bMultiplayer;
+ m_hOwner = pOwner;
+ m_bClientOnly = true;
+
+ char szFilename[128];
+ Assert( V_strlen( pszFilename ) < 128 );
+ V_strcpy( szFilename, pszFilename );
+
+ char szSceneHWM[128];
+ if ( GetHWMorphSceneFileName( szFilename, szSceneHWM ) )
+ {
+ V_strcpy( szFilename, szSceneHWM );
+ }
+
+ Assert( szFilename && szFilename[ 0 ] );
+ if ( szFilename && szFilename[ 0 ] )
+ {
+ LoadSceneFromFile( szFilename );
+
+ if (!CommandLine()->FindParm("-hushasserts"))
+ {
+ Assert( m_pScene );
+ }
+
+ // Should handle gestures and sequences client side.
+ if ( m_bMultiplayer )
+ {
+ if ( m_pScene )
+ {
+ int types[6];
+ types[0] = CChoreoEvent::FLEXANIMATION;
+ types[1] = CChoreoEvent::EXPRESSION;
+ types[2] = CChoreoEvent::GESTURE;
+ types[3] = CChoreoEvent::SEQUENCE;
+ types[4] = CChoreoEvent::SPEAK;
+ types[5] = CChoreoEvent::LOOP;
+ m_pScene->RemoveEventsExceptTypes( types, 6 );
+ }
+
+ PrefetchAnimBlocks( m_pScene );
+ }
+ else
+ {
+ if ( m_pScene )
+ {
+ int types[ 2 ];
+ types[ 0 ] = CChoreoEvent::FLEXANIMATION;
+ types[ 1 ] = CChoreoEvent::EXPRESSION;
+ m_pScene->RemoveEventsExceptTypes( types, 2 );
+ }
+ }
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+
+ if ( m_hOwner.Get() )
+ {
+ if (!CommandLine()->FindParm("-hushasserts"))
+ {
+ Assert( m_pScene );
+ }
+
+ if ( m_pScene )
+ {
+ ClearSceneEvents( m_pScene, false );
+
+ if ( m_bIsPlayingBack )
+ {
+ m_pScene->ResetSimulation();
+ m_hOwner->StartChoreoScene( m_pScene );
+ }
+ else
+ {
+ m_pScene->ResetSimulation();
+ m_hOwner->RemoveChoreoScene( m_pScene );
+ }
+
+ // Reset the flex weights when we start a new scene. This is normally done on the player model, but since
+ // we don't have a player here yet - we need to do this!
+ ResetActorFlexesForScene();
+ }
+ }
+ else
+ {
+ for( int i = 0; i < m_hActorList.Count() ; ++i )
+ {
+ C_BaseFlex *actor = m_hActorList[ i ].Get();
+ if ( !actor )
+ continue;
+
+ Assert( m_pScene );
+
+ if ( m_pScene )
+ {
+ ClearSceneEvents( m_pScene, false );
+
+ if ( m_bIsPlayingBack )
+ {
+ m_pScene->ResetSimulation();
+ actor->StartChoreoScene( m_pScene );
+ }
+ else
+ {
+ m_pScene->ResetSimulation();
+ actor->RemoveChoreoScene( m_pScene );
+ }
+ }
+ }
+ }
+}
+
+void C_SceneEntity::PostDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PostDataUpdate( updateType );
+
+ char const *str = GetSceneFileName();
+ char szFilename[MAX_PATH];
+ if ( str )
+ {
+ Assert( V_strlen( str ) < MAX_PATH );
+ V_strcpy( szFilename, str );
+ }
+ else
+ {
+ szFilename[0] = '\0';
+ }
+
+ char szSceneHWM[MAX_PATH];
+ if ( GetHWMorphSceneFileName( szFilename, szSceneHWM ) )
+ {
+ V_strcpy( szFilename, szSceneHWM );
+ }
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ Assert( szFilename && szFilename[ 0 ] );
+ if ( szFilename && szFilename[ 0 ] )
+ {
+ LoadSceneFromFile( szFilename );
+
+ // Kill everything except flex events
+ Assert( m_pScene );
+
+ // Should handle gestures and sequences clientside.
+ if ( m_bMultiplayer )
+ {
+ if ( m_pScene )
+ {
+ int types[6];
+ types[0] = CChoreoEvent::FLEXANIMATION;
+ types[1] = CChoreoEvent::EXPRESSION;
+ types[2] = CChoreoEvent::GESTURE;
+ types[3] = CChoreoEvent::SEQUENCE;
+ types[4] = CChoreoEvent::SPEAK;
+ types[5] = CChoreoEvent::LOOP;
+ m_pScene->RemoveEventsExceptTypes( types, 6 );
+ }
+
+ PrefetchAnimBlocks( m_pScene );
+ }
+ else
+ {
+ if ( m_pScene )
+ {
+ int types[ 2 ];
+ types[ 0 ] = CChoreoEvent::FLEXANIMATION;
+ types[ 1 ] = CChoreoEvent::EXPRESSION;
+ m_pScene->RemoveEventsExceptTypes( types, 2 );
+ }
+ }
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+ }
+
+ // Playback state changed...
+ if ( m_bWasPlaying != m_bIsPlayingBack )
+ {
+ for(int i = 0; i < m_hActorList.Count() ; ++i )
+ {
+ C_BaseFlex *actor = m_hActorList[ i ].Get();
+ if ( !actor )
+ continue;
+
+ Assert( m_pScene );
+
+ if ( m_pScene )
+ {
+ ClearSceneEvents( m_pScene, false );
+
+ if ( m_bIsPlayingBack )
+ {
+ m_pScene->ResetSimulation();
+ actor->StartChoreoScene( m_pScene );
+ }
+ else
+ {
+ m_pScene->ResetSimulation();
+ actor->RemoveChoreoScene( m_pScene );
+ }
+ }
+ }
+ }
+}
+
+void C_SceneEntity::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate( updateType );
+
+ m_bWasPlaying = m_bIsPlayingBack;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame that an event is active (Start/EndEvent as also
+// called)
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void C_SceneEntity::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ // For now we only need to process events if we go back in time.
+ if ( currenttime < event->m_flPrevTime )
+ {
+ //if ( !V_strstr( scene->GetFilename(), "idleloop" ) )
+ //{
+ // Msg( "ProcessEvent( %6.4f, %32s %6.4f ) %6.4f\n", currenttime, event->GetName(), event->m_flPrevTime, m_flCurrentTime );
+ //}
+
+ C_BaseFlex *pActor = NULL;
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ pActor = FindNamedActor( actor );
+ if ( NULL == pActor )
+ {
+ // TODO: QueueProcessEvent
+ // This can occur if we haven't been networked an actor yet... we need to queue it so that we can
+ // fire off the process event as soon as we have the actor resident on the client.
+ return;
+ }
+ }
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::GESTURE:
+ {
+ // Verify data.
+ Assert( m_bMultiplayer );
+ Assert( scene != NULL );
+ Assert( event != NULL );
+
+ if ( pActor )
+ {
+ DispatchProcessGesture( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ // Verify data.
+ Assert( m_bMultiplayer );
+ Assert( scene != NULL );
+ Assert( event != NULL );
+
+ if ( pActor )
+ {
+ DispatchProcessSequence( scene, pActor, event );
+ }
+ }
+ break;
+ }
+ }
+
+ event->m_flPrevTime = currenttime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called for events that are part of a pause condition
+// Input : *event -
+// Output : Returns true on event completed, false on non-completion.
+//-----------------------------------------------------------------------------
+bool C_SceneEntity::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ return true;
+}
+
+C_BaseFlex *C_SceneEntity::FindNamedActor( CChoreoActor *pChoreoActor )
+{
+ if ( !m_pScene )
+ return NULL;
+
+ if ( m_hOwner.Get() != NULL )
+ {
+ return m_hOwner.Get();
+ }
+
+ int idx = m_pScene->FindActorIndex( pChoreoActor );
+ if ( idx < 0 || idx >= m_hActorList.Count() )
+ return NULL;
+
+ return m_hActorList[ idx ].Get();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: All events are leading edge triggered
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event );
+
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ Scene_Printf( "%s : %8.2f: ignored %s\n", GetSceneFileName(), currenttime, event->GetDescription() );
+ return;
+ }
+
+
+ C_BaseFlex *pActor = NULL;
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ pActor = FindNamedActor( actor );
+ if ( NULL == pActor )
+ {
+ // This can occur if we haven't been networked an actor yet... we need to queue it so that we can
+ // fire off the start event as soon as we have the actor resident on the client.
+ QueueStartEvent( currenttime, scene, event );
+ return;
+ }
+ }
+
+ Scene_Printf( "%s : %8.2f: start %s\n", GetSceneFileName(), currenttime, event->GetDescription() );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ if ( pActor )
+ {
+ DispatchStartFlexAnimation( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::EXPRESSION:
+ {
+ if ( pActor )
+ {
+ DispatchStartExpression( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ // Verify data.
+ Assert( m_bMultiplayer );
+ Assert( scene != NULL );
+ Assert( event != NULL );
+
+ if ( pActor )
+ {
+ DispatchStartGesture( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ // Verify data.
+ Assert( m_bMultiplayer );
+ Assert( scene != NULL );
+ Assert( event != NULL );
+
+ if ( pActor )
+ {
+ DispatchStartSequence( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ // Verify data.
+ Assert( m_bMultiplayer );
+ Assert( scene != NULL );
+ Assert( event != NULL );
+
+ DispatchProcessLoop( scene, event );
+ }
+ case CChoreoEvent::SPEAK:
+ {
+ if ( IsClientOnly() && pActor )
+ {
+ // FIXME: dB hack. soundlevel needs to be moved into inside of wav?
+ soundlevel_t iSoundlevel = SNDLVL_TALKING;
+ if ( event->GetParameters2() )
+ {
+ iSoundlevel = (soundlevel_t)atoi( event->GetParameters2() );
+ if ( iSoundlevel == SNDLVL_NONE )
+ {
+ iSoundlevel = SNDLVL_TALKING;
+ }
+ }
+
+ DispatchStartSpeak( scene, pActor, event, iSoundlevel );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ event->m_flPrevTime = currenttime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::LOOP );
+
+ float backtime = (float)atof( event->GetParameters() );
+
+ bool process = true;
+ int counter = event->GetLoopCount();
+ if ( counter != -1 )
+ {
+ int remaining = event->GetNumLoopsRemaining();
+ if ( remaining <= 0 )
+ {
+ process = false;
+ }
+ else
+ {
+ event->SetNumLoopsRemaining( --remaining );
+ }
+ }
+
+ if ( !process )
+ return;
+
+ scene->LoopToTime( backtime );
+ SetCurrentTime( backtime, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Playback sound file that contains phonemes
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchStartSpeak( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel )
+{
+ // Emit sound
+ if ( IsClientOnly() && actor )
+ {
+ CSingleUserRecipientFilter filter( C_BasePlayer::GetLocalPlayer() );
+
+ float time_in_past = m_flCurrentTime - event->GetStartTime() ;
+ float soundtime = gpGlobals->curtime - time_in_past;
+
+ EmitSound_t es;
+ es.m_nChannel = CHAN_VOICE;
+ es.m_flVolume = 1;
+ es.m_SoundLevel = iSoundlevel;
+ es.m_flSoundTime = soundtime;
+
+ // No CC since we do it manually
+ // FIXME: This will change
+ es.m_bEmitCloseCaption = false;
+ es.m_pSoundName = event->GetParameters();
+
+ EmitSound( filter, actor->entindex(), es );
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+
+ // Close captioning only on master token no matter what...
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ bool validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) );
+ if ( validtoken )
+ {
+ CRC32_t tokenCRC;
+ CRC32_Init( &tokenCRC );
+
+ char lowercase[ 256 ];
+ Q_strncpy( lowercase, tok, sizeof( lowercase ) );
+ Q_strlower( lowercase );
+
+ CRC32_ProcessBuffer( &tokenCRC, lowercase, Q_strlen( lowercase ) );
+ CRC32_Final( &tokenCRC );
+
+ float endtime = event->GetLastSlaveEndTime();
+ float durationShort = event->GetDuration();
+ float durationLong = endtime - event->GetStartTime();
+ float duration = MAX( durationShort, durationLong );
+
+ CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption );
+ if ( hudCloseCaption )
+ {
+ hudCloseCaption->ProcessCaption( lowercase, duration );
+ }
+ }
+
+ }
+ }
+}
+
+void C_SceneEntity::DispatchEndSpeak( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ if ( IsClientOnly() )
+ {
+ actor->RemoveSceneEvent( scene, event, false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event );
+
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ return;
+ }
+
+ C_BaseFlex *pActor = NULL;
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ pActor = FindNamedActor( actor );
+ }
+
+ Scene_Printf( "%s : %8.2f: finish %s\n", GetSceneFileName(), currenttime, event->GetDescription() );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ if ( pActor )
+ {
+ DispatchEndFlexAnimation( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::EXPRESSION:
+ {
+ if ( pActor )
+ {
+ DispatchEndExpression( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ if ( pActor )
+ {
+ DispatchEndGesture( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ if ( pActor )
+ {
+ DispatchEndSequence( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ if ( IsClientOnly() && pActor )
+ {
+ DispatchEndSpeak( scene, pActor, event );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool CChoreoStringPool::GetString( short stringId, char *buff, int buffSize )
+{
+ // fetch from compiled pool
+ const char *pString = scenefilecache->GetSceneString( stringId );
+ if ( !pString )
+ {
+ V_strncpy( buff, "", buffSize );
+ return false;
+ }
+ V_strncpy( buff, pString, buffSize );
+ return true;
+}
+
+CChoreoStringPool g_ChoreoStringPool;
+
+CChoreoScene *C_SceneEntity::LoadScene( const char *filename )
+{
+ char loadfile[ 512 ];
+ Q_strncpy( loadfile, filename, sizeof( loadfile ) );
+ Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) );
+ Q_FixSlashes( loadfile );
+
+ char *pBuffer = NULL;
+ size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile );
+ if ( bufsize <= 0 )
+ return NULL;
+
+ pBuffer = new char[ bufsize ];
+ if ( !scenefilecache->GetSceneData( filename, (byte *)pBuffer, bufsize ) )
+ {
+ delete[] pBuffer;
+ return NULL;
+ }
+
+ CChoreoScene *pScene;
+ if ( IsBufferBinaryVCD( pBuffer, bufsize ) )
+ {
+ pScene = new CChoreoScene( this );
+ CUtlBuffer buf( pBuffer, bufsize, CUtlBuffer::READ_ONLY );
+ if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) )
+ {
+ Warning( "Unable to restore binary scene '%s'\n", loadfile );
+ delete pScene;
+ pScene = NULL;
+ }
+ else
+ {
+ pScene->SetPrintFunc( Scene_Printf );
+ pScene->SetEventCallbackInterface( this );
+ }
+ }
+ else
+ {
+ g_TokenProcessor.SetBuffer( pBuffer );
+ pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf );
+ }
+
+ delete[] pBuffer;
+ return pScene;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::LoadSceneFromFile( const char *filename )
+{
+ UnloadScene();
+ m_pScene = LoadScene( filename );
+}
+
+void C_SceneEntity::ClearSceneEvents( CChoreoScene *scene, bool canceled )
+{
+ if ( !m_pScene )
+ return;
+
+ Scene_Printf( "%s : %8.2f: clearing events\n", GetSceneFileName(), m_flCurrentTime );
+
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ C_BaseFlex *pActor = FindNamedActor( m_pScene->GetActor( i ) );
+ if ( !pActor )
+ continue;
+
+ // Clear any existing expressions
+ pActor->ClearSceneEvents( scene, canceled );
+ }
+
+ WipeQueuedEvents();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_SceneEntity::UnloadScene( void )
+{
+ WipeQueuedEvents();
+
+ if ( m_pScene )
+ {
+ ClearSceneEvents( m_pScene, false );
+ for ( int i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ C_BaseFlex *pTestActor = FindNamedActor( m_pScene->GetActor( i ) );
+
+ if ( !pTestActor )
+ continue;
+
+ pTestActor->RemoveChoreoScene( m_pScene );
+ }
+ }
+ delete m_pScene;
+ m_pScene = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchStartExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchEndExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchStartGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ // Ingore null gestures
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ return;
+
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchProcessGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ // Ingore null gestures
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ return;
+
+ actor->RemoveSceneEvent( scene, event, false );
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchEndGesture( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event )
+{
+ // Ingore null gestures
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ return;
+
+ actor->RemoveSceneEvent( scene, event, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchProcessSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, false );
+ actor->AddSceneEvent( scene, event, NULL, IsClientOnly() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_SceneEntity::DoThink( float frametime )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_bIsPlayingBack )
+ {
+ WipeQueuedEvents();
+ return;
+ }
+
+ CheckQueuedEvents();
+
+ if ( m_bPaused )
+ {
+ return;
+ }
+
+ // Msg( "CL: %d, %f for %s\n", gpGlobals->tickcount, m_flCurrentTime, m_pScene->GetFilename() );
+
+ // Tell scene to go
+ m_pScene->Think( m_flCurrentTime );
+ // Drive simulation time for scene
+ m_flCurrentTime += gpGlobals->frametime;
+}
+
+void C_SceneEntity::ClientThink()
+{
+ DoThink( gpGlobals->frametime );
+}
+
+void C_SceneEntity::CheckQueuedEvents()
+{
+// Check for duplicates
+ CUtlVector< QueuedEvents_t > events;
+ events = m_QueuedEvents;
+ m_QueuedEvents.RemoveAll();
+
+ int c = events.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ const QueuedEvents_t& check = events[ i ];
+
+ // Retry starting this event
+ StartEvent( check.starttime, check.scene, check.event );
+ }
+}
+
+void C_SceneEntity::WipeQueuedEvents()
+{
+ m_QueuedEvents.Purge();
+}
+
+void C_SceneEntity::QueueStartEvent( float starttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ // Check for duplicates
+ int c = m_QueuedEvents.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ const QueuedEvents_t& check = m_QueuedEvents[ i ];
+ if ( check.scene == scene &&
+ check.event == event )
+ return;
+ }
+
+ QueuedEvents_t qe;
+ qe.scene = scene;
+ qe.event = event;
+ qe.starttime = starttime;
+ m_QueuedEvents.AddToTail( qe );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets time such that the client version of the .vcd is also updated, if appropriate
+// Input : t -
+// forceClientSync - unused for now, we may want to reenable this at some point
+//-----------------------------------------------------------------------------
+void C_SceneEntity::SetCurrentTime( float t, bool forceClientSync )
+{
+ m_flCurrentTime = t;
+ m_flForceClientTime = t;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_SceneEntity::PrefetchAnimBlocks( CChoreoScene *pScene )
+{
+ if (!CommandLine()->FindParm("-hushasserts"))
+ {
+ Assert( pScene && m_bMultiplayer );
+ }
+ if ( !pScene || !m_bMultiplayer )
+ return;
+
+ // Build a fast lookup, too
+ CUtlMap<CChoreoActor*,CBaseFlex*> actorMap( 0, 0, DefLessFunc( CChoreoActor* ) );
+
+ int nSpew = 0;
+ int nResident = 0;
+ int nChecked = 0;
+
+ // Iterate events and precache necessary resources
+ for ( int i = 0; i < pScene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *pEvent = pScene->GetEvent( i );
+ if ( !pEvent )
+ continue;
+
+ // load any necessary data
+ switch ( pEvent->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SEQUENCE:
+ case CChoreoEvent::GESTURE:
+ {
+ CChoreoActor *pActor = pEvent->GetActor();
+ if ( pActor )
+ {
+ CBaseFlex *pFlex = NULL;
+ int idx = actorMap.Find( pActor );
+ if ( idx == actorMap.InvalidIndex() )
+ {
+ pFlex = FindNamedActor( pActor );
+ idx = actorMap.Insert( pActor, pFlex );
+ }
+ else
+ {
+ pFlex = actorMap[ idx ];
+ }
+
+ if ( pFlex )
+ {
+ int iSequence = pFlex->LookupSequence( pEvent->GetParameters() );
+ if ( iSequence >= 0 )
+ {
+ CStudioHdr *pStudioHdr = pFlex->GetModelPtr();
+ if ( pStudioHdr )
+ {
+ // Now look up the animblock
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence );
+ for ( int i = 0 ; i < seqdesc.groupsize[ 0 ] ; ++i )
+ {
+ for ( int j = 0; j < seqdesc.groupsize[ 1 ]; ++j )
+ {
+ int iAnimation = seqdesc.anim( i, j );
+ int iBaseAnimation = pStudioHdr->iRelativeAnim( iSequence, iAnimation );
+ mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( iBaseAnimation );
+
+ ++nChecked;
+
+ if ( nSpew != 0 )
+ {
+ Msg( "%s checking block %d\n", pStudioHdr->pszName(), animdesc.animblock );
+ }
+
+ // Async load the animation
+ int iFrame = 0;
+ const mstudioanim_t *panim = animdesc.pAnim( &iFrame );
+ if ( panim )
+ {
+ ++nResident;
+ if ( nSpew > 1 )
+ {
+ Msg( "%s:%s[%i:%i] was resident\n", pStudioHdr->pszName(), animdesc.pszName(), i, j );
+ }
+ }
+ else
+ {
+ if ( nSpew != 0 )
+ {
+ Msg( "%s:%s[%i:%i] async load\n", pStudioHdr->pszName(), animdesc.pszName(), i, j );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if ( !nSpew || nChecked <= 0 )
+ return;
+
+ Msg( "%d of %d animations resident\n", nResident, nChecked );
+} \ No newline at end of file