1174 lines
30 KiB
C++
1174 lines
30 KiB
C++
//========= Copyright <20> 1996-2005, 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 "choreoscene.h"
|
||
#include "filesystem.h"
|
||
#include "ichoreoeventcallback.h"
|
||
#include "scenefilecache/ISceneFileCache.h"
|
||
#include "materialsystem/imaterialsystemhardwareconfig.h"
|
||
#include "tier2/tier2.h"
|
||
#include "hud_closecaption.h"
|
||
#include "tier1/fmtstr.h"
|
||
#include "../../common/blackbox_helper.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()
|
||
{
|
||
m_flCurrentTime = m_flForceClientTime;
|
||
}
|
||
|
||
char const *C_SceneEntity::GetSceneFileName()
|
||
{
|
||
char const *pStr = g_pStringTableClientSideChoreoScenes->GetString( m_nSceneStringIndex );
|
||
if ( pStr )
|
||
return pStr;
|
||
|
||
static bool bFirst = true;
|
||
if ( bFirst )
|
||
{
|
||
bFirst = false;
|
||
Assert( 0 );
|
||
Warning( "GetSceneFilename() failed for scene index %d\n", m_nSceneStringIndex );
|
||
}
|
||
return "";
|
||
}
|
||
|
||
|
||
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
|
||
ConVar scene_vcdautosave( "scene_vcdautosave", "0", 0, "Create a savegame before VCD playback" );
|
||
|
||
bool UseHWMorphVCDs()
|
||
{
|
||
if ( mp_usehwmvcds.GetInt() == 0 )
|
||
return g_pMaterialSystemHardwareConfig->HasFastVertexTextures();
|
||
return mp_usehwmvcds.GetInt() > 0;
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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( gpGlobals->curtime );
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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 );
|
||
|
||
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() )
|
||
{
|
||
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];
|
||
Assert( V_strlen( str ) < MAX_PATH );
|
||
V_strcpy( szFilename, str );
|
||
|
||
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 )
|
||
{
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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() );
|
||
if ( IsPC() && event )
|
||
{
|
||
BlackBox_Record( "vcd", "%s : %8.2f: start %s", 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;
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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(), this );
|
||
|
||
// 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_FULLSCREEN_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;
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Binary compiled VCDs get their strings from a pool
|
||
//-----------------------------------------------------------------------------
|
||
class CChoreoStringPool : public IChoreoStringPool
|
||
{
|
||
public:
|
||
short FindOrAddString( const char *pString )
|
||
{
|
||
// huh?, no compilation at run time, only fetches
|
||
Assert( 0 );
|
||
return -1;
|
||
}
|
||
|
||
bool 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 )
|
||
{
|
||
// Save game if convar is set - useful when iterating on a scene with Foundry
|
||
if ( scene_vcdautosave.GetBool() )
|
||
{
|
||
char szVCDFileName[64];
|
||
char szSaveFileName[64];
|
||
char szClientCmd[128];
|
||
|
||
// Create the faceposer sub-directory under the root savegame directory
|
||
if (!g_pFullFileSystem->IsDirectory( "SAVE\\faceposer", "MOD" ))
|
||
{
|
||
g_pFullFileSystem->CreateDirHierarchy( "SAVE\\faceposer", "MOD" );
|
||
}
|
||
|
||
// Construct save command to send to the engine
|
||
V_FileBase( filename, szVCDFileName, sizeof(szVCDFileName) );
|
||
V_snprintf( szClientCmd, sizeof(szClientCmd), "save faceposer\\%s\n", szVCDFileName );
|
||
|
||
// Construct name of file that would be created if savegame occurs
|
||
V_snprintf( szSaveFileName, sizeof(szSaveFileName), "SAVE\\faceposer\\%s.sav", szVCDFileName );
|
||
|
||
// Only create a savegame for this VCD if there isn't one already
|
||
if (!g_pFullFileSystem->FileExists( szSaveFileName, "MOD" ))
|
||
{
|
||
engine->ClientCmd( szClientCmd );
|
||
}
|
||
}
|
||
|
||
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();
|
||
OnResetClientTime();
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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() || IsMultiplayer(), this );
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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() || IsMultiplayer(), this );
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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() || IsMultiplayer(), this );
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Input : *actor -
|
||
//-----------------------------------------------------------------------------
|
||
void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
|
||
{
|
||
actor->AddSceneEvent( scene, event, NULL, IsClientOnly() || IsMultiplayer(), this );
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Input : *actor -
|
||
//-----------------------------------------------------------------------------
|
||
void C_SceneEntity::DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
|
||
{
|
||
actor->RemoveSceneEvent( scene, event, false );
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// 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:
|
||
//-----------------------------------------------------------------------------
|
||
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 )
|
||
{
|
||
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 byte *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 );
|
||
} |